Roo/bootstrap/PagingToolbar.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     }
558    
559 });
560
561  /*
562  * - LGPL
563  *
564  * button
565  * 
566  */
567
568 /**
569  * @class Roo.bootstrap.Button
570  * @extends Roo.bootstrap.Component
571  * Bootstrap Button class
572  * @cfg {String} html The button content
573  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
574  * @cfg {String} size ( lg | sm | xs)
575  * @cfg {String} tag ( a | input | submit)
576  * @cfg {String} href empty or href
577  * @cfg {Boolean} disabled default false;
578  * @cfg {Boolean} isClose default false;
579  * @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)
580  * @cfg {String} badge text for badge
581  * @cfg {String} theme (default|glow)  
582  * @cfg {Boolean} inverse dark themed version
583  * @cfg {Boolean} toggle is it a slidy toggle button
584  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
585  * @cfg {String} ontext text for on slidy toggle state
586  * @cfg {String} offtext text for off slidy toggle state
587  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
588  * @cfg {Boolean} removeClass remove the standard class..
589  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
590  * 
591  * @constructor
592  * Create a new button
593  * @param {Object} config The config object
594  */
595
596
597 Roo.bootstrap.Button = function(config){
598     Roo.bootstrap.Button.superclass.constructor.call(this, config);
599     this.weightClass = ["btn-default", 
600                        "btn-primary", 
601                        "btn-success", 
602                        "btn-info", 
603                        "btn-warning",
604                        "btn-danger",
605                        "btn-link"
606                       ],  
607     this.addEvents({
608         // raw events
609         /**
610          * @event click
611          * When a butotn is pressed
612          * @param {Roo.bootstrap.Button} btn
613          * @param {Roo.EventObject} e
614          */
615         "click" : true,
616          /**
617          * @event toggle
618          * After the button has been toggles
619          * @param {Roo.bootstrap.Button} btn
620          * @param {Roo.EventObject} e
621          * @param {boolean} pressed (also available as button.pressed)
622          */
623         "toggle" : true
624     });
625 };
626
627 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
628     html: false,
629     active: false,
630     weight: '',
631     size: '',
632     tag: 'button',
633     href: '',
634     disabled: false,
635     isClose: false,
636     glyphicon: '',
637     badge: '',
638     theme: 'default',
639     inverse: false,
640     
641     toggle: false,
642     ontext: 'ON',
643     offtext: 'OFF',
644     defaulton: true,
645     preventDefault: true,
646     removeClass: false,
647     name: false,
648     target: false,
649      
650     pressed : null,
651      
652     
653     getAutoCreate : function(){
654         
655         var cfg = {
656             tag : 'button',
657             cls : 'roo-button',
658             html: ''
659         };
660         
661         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
662             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
663             this.tag = 'button';
664         } else {
665             cfg.tag = this.tag;
666         }
667         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
668         
669         if (this.toggle == true) {
670             cfg={
671                 tag: 'div',
672                 cls: 'slider-frame roo-button',
673                 cn: [
674                     {
675                         tag: 'span',
676                         'data-on-text':'ON',
677                         'data-off-text':'OFF',
678                         cls: 'slider-button',
679                         html: this.offtext
680                     }
681                 ]
682             };
683             
684             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
685                 cfg.cls += ' '+this.weight;
686             }
687             
688             return cfg;
689         }
690         
691         if (this.isClose) {
692             cfg.cls += ' close';
693             
694             cfg["aria-hidden"] = true;
695             
696             cfg.html = "&times;";
697             
698             return cfg;
699         }
700         
701          
702         if (this.theme==='default') {
703             cfg.cls = 'btn roo-button';
704             
705             //if (this.parentType != 'Navbar') {
706             this.weight = this.weight.length ?  this.weight : 'default';
707             //}
708             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
709                 
710                 cfg.cls += ' btn-' + this.weight;
711             }
712         } else if (this.theme==='glow') {
713             
714             cfg.tag = 'a';
715             cfg.cls = 'btn-glow roo-button';
716             
717             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
718                 
719                 cfg.cls += ' ' + this.weight;
720             }
721         }
722    
723         
724         if (this.inverse) {
725             this.cls += ' inverse';
726         }
727         
728         
729         if (this.active || this.pressed === true) {
730             cfg.cls += ' active';
731         }
732         
733         if (this.disabled) {
734             cfg.disabled = 'disabled';
735         }
736         
737         if (this.items) {
738             Roo.log('changing to ul' );
739             cfg.tag = 'ul';
740             this.glyphicon = 'caret';
741         }
742         
743         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
744          
745         //gsRoo.log(this.parentType);
746         if (this.parentType === 'Navbar' && !this.parent().bar) {
747             Roo.log('changing to li?');
748             
749             cfg.tag = 'li';
750             
751             cfg.cls = '';
752             cfg.cn =  [{
753                 tag : 'a',
754                 cls : 'roo-button',
755                 html : this.html,
756                 href : this.href || '#'
757             }];
758             if (this.menu) {
759                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
760                 cfg.cls += ' dropdown';
761             }   
762             
763             delete cfg.html;
764             
765         }
766         
767        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
768         
769         if (this.glyphicon) {
770             cfg.html = ' ' + cfg.html;
771             
772             cfg.cn = [
773                 {
774                     tag: 'span',
775                     cls: 'glyphicon glyphicon-' + this.glyphicon
776                 }
777             ];
778         }
779         
780         if (this.badge) {
781             cfg.html += ' ';
782             
783             cfg.tag = 'a';
784             
785 //            cfg.cls='btn roo-button';
786             
787             cfg.href=this.href;
788             
789             var value = cfg.html;
790             
791             if(this.glyphicon){
792                 value = {
793                             tag: 'span',
794                             cls: 'glyphicon glyphicon-' + this.glyphicon,
795                             html: this.html
796                         };
797                 
798             }
799             
800             cfg.cn = [
801                 value,
802                 {
803                     tag: 'span',
804                     cls: 'badge',
805                     html: this.badge
806                 }
807             ];
808             
809             cfg.html='';
810         }
811         
812         if (this.menu) {
813             cfg.cls += ' dropdown';
814             cfg.html = typeof(cfg.html) != 'undefined' ?
815                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
816         }
817         
818         if (cfg.tag !== 'a' && this.href !== '') {
819             throw "Tag must be a to set href.";
820         } else if (this.href.length > 0) {
821             cfg.href = this.href;
822         }
823         
824         if(this.removeClass){
825             cfg.cls = '';
826         }
827         
828         if(this.target){
829             cfg.target = this.target;
830         }
831         
832         return cfg;
833     },
834     initEvents: function() {
835        // Roo.log('init events?');
836 //        Roo.log(this.el.dom);
837         // add the menu...
838         
839         if (typeof (this.menu) != 'undefined') {
840             this.menu.parentType = this.xtype;
841             this.menu.triggerEl = this.el;
842             this.addxtype(Roo.apply({}, this.menu));
843         }
844
845
846        if (this.el.hasClass('roo-button')) {
847             this.el.on('click', this.onClick, this);
848        } else {
849             this.el.select('.roo-button').on('click', this.onClick, this);
850        }
851        
852        if(this.removeClass){
853            this.el.on('click', this.onClick, this);
854        }
855        
856        this.el.enableDisplayMode();
857         
858     },
859     onClick : function(e)
860     {
861         if (this.disabled) {
862             return;
863         }
864         
865         Roo.log('button on click ');
866         if(this.preventDefault){
867             e.preventDefault();
868         }
869         
870         if (this.pressed === true || this.pressed === false) {
871             this.toggleActive(e);
872         }
873         
874         
875         this.fireEvent('click', this, e);
876     },
877     
878     /**
879      * Enables this button
880      */
881     enable : function()
882     {
883         this.disabled = false;
884         this.el.removeClass('disabled');
885     },
886     
887     /**
888      * Disable this button
889      */
890     disable : function()
891     {
892         this.disabled = true;
893         this.el.addClass('disabled');
894     },
895      /**
896      * sets the active state on/off, 
897      * @param {Boolean} state (optional) Force a particular state
898      */
899     setActive : function(v) {
900         
901         this.el[v ? 'addClass' : 'removeClass']('active');
902         this.pressed = v;
903     },
904      /**
905      * toggles the current active state 
906      */
907     toggleActive : function(e)
908     {
909         this.setActive(!this.pressed);
910         this.fireEvent('toggle', this, e, !this.pressed);
911     },
912      /**
913      * get the current active state
914      * @return {boolean} true if it's active
915      */
916     isActive : function()
917     {
918         return this.el.hasClass('active');
919     },
920     /**
921      * set the text of the first selected button
922      */
923     setText : function(str)
924     {
925         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
926     },
927     /**
928      * get the text of the first selected button
929      */
930     getText : function()
931     {
932         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
933     },
934     
935     setWeight : function(str)
936     {
937         this.el.removeClass(this.weightClass);
938         this.el.addClass('btn-' + str);        
939     }
940     
941     
942 });
943
944  /*
945  * - LGPL
946  *
947  * column
948  * 
949  */
950
951 /**
952  * @class Roo.bootstrap.Column
953  * @extends Roo.bootstrap.Component
954  * Bootstrap Column class
955  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
956  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
957  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
958  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
959  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
960  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
961  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
962  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
963  *
964  * 
965  * @cfg {Boolean} hidden (true|false) hide the element
966  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
967  * @cfg {String} fa (ban|check|...) font awesome icon
968  * @cfg {Number} fasize (1|2|....) font awsome size
969
970  * @cfg {String} icon (info-sign|check|...) glyphicon name
971
972  * @cfg {String} html content of column.
973  * 
974  * @constructor
975  * Create a new Column
976  * @param {Object} config The config object
977  */
978
979 Roo.bootstrap.Column = function(config){
980     Roo.bootstrap.Column.superclass.constructor.call(this, config);
981 };
982
983 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
984     
985     xs: false,
986     sm: false,
987     md: false,
988     lg: false,
989     xsoff: false,
990     smoff: false,
991     mdoff: false,
992     lgoff: false,
993     html: '',
994     offset: 0,
995     alert: false,
996     fa: false,
997     icon : false,
998     hidden : false,
999     fasize : 1,
1000     
1001     getAutoCreate : function(){
1002         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1003         
1004         cfg = {
1005             tag: 'div',
1006             cls: 'column'
1007         };
1008         
1009         var settings=this;
1010         ['xs','sm','md','lg'].map(function(size){
1011             //Roo.log( size + ':' + settings[size]);
1012             
1013             if (settings[size+'off'] !== false) {
1014                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1015             }
1016             
1017             if (settings[size] === false) {
1018                 return;
1019             }
1020             
1021             if (!settings[size]) { // 0 = hidden
1022                 cfg.cls += ' hidden-' + size;
1023                 return;
1024             }
1025             cfg.cls += ' col-' + size + '-' + settings[size];
1026             
1027         });
1028         
1029         if (this.hidden) {
1030             cfg.cls += ' hidden';
1031         }
1032         
1033         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1034             cfg.cls +=' alert alert-' + this.alert;
1035         }
1036         
1037         
1038         if (this.html.length) {
1039             cfg.html = this.html;
1040         }
1041         if (this.fa) {
1042             var fasize = '';
1043             if (this.fasize > 1) {
1044                 fasize = ' fa-' + this.fasize + 'x';
1045             }
1046             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1047             
1048             
1049         }
1050         if (this.icon) {
1051             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1052         }
1053         
1054         return cfg;
1055     }
1056    
1057 });
1058
1059  
1060
1061  /*
1062  * - LGPL
1063  *
1064  * page container.
1065  * 
1066  */
1067
1068
1069 /**
1070  * @class Roo.bootstrap.Container
1071  * @extends Roo.bootstrap.Component
1072  * Bootstrap Container class
1073  * @cfg {Boolean} jumbotron is it a jumbotron element
1074  * @cfg {String} html content of element
1075  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1076  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1077  * @cfg {String} header content of header (for panel)
1078  * @cfg {String} footer content of footer (for panel)
1079  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1080  * @cfg {String} tag (header|aside|section) type of HTML tag.
1081  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1082  * @cfg {String} fa font awesome icon
1083  * @cfg {String} icon (info-sign|check|...) glyphicon name
1084  * @cfg {Boolean} hidden (true|false) hide the element
1085  * @cfg {Boolean} expandable (true|false) default false
1086  * @cfg {Boolean} expanded (true|false) default true
1087  * @cfg {String} rheader contet on the right of header
1088  * @cfg {Boolean} clickable (true|false) default false
1089
1090  *     
1091  * @constructor
1092  * Create a new Container
1093  * @param {Object} config The config object
1094  */
1095
1096 Roo.bootstrap.Container = function(config){
1097     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1098     
1099     this.addEvents({
1100         // raw events
1101          /**
1102          * @event expand
1103          * After the panel has been expand
1104          * 
1105          * @param {Roo.bootstrap.Container} this
1106          */
1107         "expand" : true,
1108         /**
1109          * @event collapse
1110          * After the panel has been collapsed
1111          * 
1112          * @param {Roo.bootstrap.Container} this
1113          */
1114         "collapse" : true,
1115         /**
1116          * @event click
1117          * When a element is chick
1118          * @param {Roo.bootstrap.Container} this
1119          * @param {Roo.EventObject} e
1120          */
1121         "click" : true
1122     });
1123 };
1124
1125 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1126     
1127     jumbotron : false,
1128     well: '',
1129     panel : '',
1130     header: '',
1131     footer : '',
1132     sticky: '',
1133     tag : false,
1134     alert : false,
1135     fa: false,
1136     icon : false,
1137     expandable : false,
1138     rheader : '',
1139     expanded : true,
1140     clickable: false,
1141   
1142      
1143     getChildContainer : function() {
1144         
1145         if(!this.el){
1146             return false;
1147         }
1148         
1149         if (this.panel.length) {
1150             return this.el.select('.panel-body',true).first();
1151         }
1152         
1153         return this.el;
1154     },
1155     
1156     
1157     getAutoCreate : function(){
1158         
1159         var cfg = {
1160             tag : this.tag || 'div',
1161             html : '',
1162             cls : ''
1163         };
1164         if (this.jumbotron) {
1165             cfg.cls = 'jumbotron';
1166         }
1167         
1168         
1169         
1170         // - this is applied by the parent..
1171         //if (this.cls) {
1172         //    cfg.cls = this.cls + '';
1173         //}
1174         
1175         if (this.sticky.length) {
1176             
1177             var bd = Roo.get(document.body);
1178             if (!bd.hasClass('bootstrap-sticky')) {
1179                 bd.addClass('bootstrap-sticky');
1180                 Roo.select('html',true).setStyle('height', '100%');
1181             }
1182              
1183             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1184         }
1185         
1186         
1187         if (this.well.length) {
1188             switch (this.well) {
1189                 case 'lg':
1190                 case 'sm':
1191                     cfg.cls +=' well well-' +this.well;
1192                     break;
1193                 default:
1194                     cfg.cls +=' well';
1195                     break;
1196             }
1197         }
1198         
1199         if (this.hidden) {
1200             cfg.cls += ' hidden';
1201         }
1202         
1203         
1204         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1205             cfg.cls +=' alert alert-' + this.alert;
1206         }
1207         
1208         var body = cfg;
1209         
1210         if (this.panel.length) {
1211             cfg.cls += ' panel panel-' + this.panel;
1212             cfg.cn = [];
1213             if (this.header.length) {
1214                 
1215                 var h = [];
1216                 
1217                 if(this.expandable){
1218                     
1219                     cfg.cls = cfg.cls + ' expandable';
1220                     
1221                     h.push({
1222                         tag: 'i',
1223                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1224                     });
1225                     
1226                 }
1227                 
1228                 h.push(
1229                     {
1230                         tag: 'span',
1231                         cls : 'panel-title',
1232                         html : (this.expandable ? '&nbsp;' : '') + this.header
1233                     },
1234                     {
1235                         tag: 'span',
1236                         cls: 'panel-header-right',
1237                         html: this.rheader
1238                     }
1239                 );
1240                 
1241                 cfg.cn.push({
1242                     cls : 'panel-heading',
1243                     style : this.expandable ? 'cursor: pointer' : '',
1244                     cn : h
1245                 });
1246                 
1247             }
1248             
1249             body = false;
1250             cfg.cn.push({
1251                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1252                 html : this.html
1253             });
1254             
1255             
1256             if (this.footer.length) {
1257                 cfg.cn.push({
1258                     cls : 'panel-footer',
1259                     html : this.footer
1260                     
1261                 });
1262             }
1263             
1264         }
1265         
1266         if (body) {
1267             body.html = this.html || cfg.html;
1268             // prefix with the icons..
1269             if (this.fa) {
1270                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1271             }
1272             if (this.icon) {
1273                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1274             }
1275             
1276             
1277         }
1278         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1279             cfg.cls =  'container';
1280         }
1281         
1282         return cfg;
1283     },
1284     
1285     initEvents: function() 
1286     {
1287         if(this.expandable){
1288             var headerEl = this.headerEl();
1289         
1290             if(headerEl){
1291                 headerEl.on('click', this.onToggleClick, this);
1292             }
1293         }
1294         
1295         if(this.clickable){
1296             this.el.on('click', this.onClick, this);
1297         }
1298         
1299     },
1300     
1301     onToggleClick : function()
1302     {
1303         var headerEl = this.headerEl();
1304         
1305         if(!headerEl){
1306             return;
1307         }
1308         
1309         if(this.expanded){
1310             this.collapse();
1311             return;
1312         }
1313         
1314         this.expand();
1315     },
1316     
1317     expand : function()
1318     {
1319         if(this.fireEvent('expand', this)) {
1320             
1321             this.expanded = true;
1322             
1323             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1324             
1325             this.el.select('.panel-body',true).first().removeClass('hide');
1326             
1327             var toggleEl = this.toggleEl();
1328
1329             if(!toggleEl){
1330                 return;
1331             }
1332
1333             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1334         }
1335         
1336     },
1337     
1338     collapse : function()
1339     {
1340         if(this.fireEvent('collapse', this)) {
1341             
1342             this.expanded = false;
1343             
1344             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1345             this.el.select('.panel-body',true).first().addClass('hide');
1346         
1347             var toggleEl = this.toggleEl();
1348
1349             if(!toggleEl){
1350                 return;
1351             }
1352
1353             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1354         }
1355     },
1356     
1357     toggleEl : function()
1358     {
1359         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1360             return;
1361         }
1362         
1363         return this.el.select('.panel-heading .fa',true).first();
1364     },
1365     
1366     headerEl : function()
1367     {
1368         if(!this.el || !this.panel.length || !this.header.length){
1369             return;
1370         }
1371         
1372         return this.el.select('.panel-heading',true).first()
1373     },
1374     
1375     bodyEl : function()
1376     {
1377         if(!this.el || !this.panel.length){
1378             return;
1379         }
1380         
1381         return this.el.select('.panel-body',true).first()
1382     },
1383     
1384     titleEl : function()
1385     {
1386         if(!this.el || !this.panel.length || !this.header.length){
1387             return;
1388         }
1389         
1390         return this.el.select('.panel-title',true).first();
1391     },
1392     
1393     setTitle : function(v)
1394     {
1395         var titleEl = this.titleEl();
1396         
1397         if(!titleEl){
1398             return;
1399         }
1400         
1401         titleEl.dom.innerHTML = v;
1402     },
1403     
1404     getTitle : function()
1405     {
1406         
1407         var titleEl = this.titleEl();
1408         
1409         if(!titleEl){
1410             return '';
1411         }
1412         
1413         return titleEl.dom.innerHTML;
1414     },
1415     
1416     setRightTitle : function(v)
1417     {
1418         var t = this.el.select('.panel-header-right',true).first();
1419         
1420         if(!t){
1421             return;
1422         }
1423         
1424         t.dom.innerHTML = v;
1425     },
1426     
1427     onClick : function(e)
1428     {
1429         e.preventDefault();
1430         
1431         this.fireEvent('click', this, e);
1432     }
1433 });
1434
1435  /*
1436  * - LGPL
1437  *
1438  * image
1439  * 
1440  */
1441
1442
1443 /**
1444  * @class Roo.bootstrap.Img
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Img class
1447  * @cfg {Boolean} imgResponsive false | true
1448  * @cfg {String} border rounded | circle | thumbnail
1449  * @cfg {String} src image source
1450  * @cfg {String} alt image alternative text
1451  * @cfg {String} href a tag href
1452  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1453  * @cfg {String} xsUrl xs image source
1454  * @cfg {String} smUrl sm image source
1455  * @cfg {String} mdUrl md image source
1456  * @cfg {String} lgUrl lg image source
1457  * 
1458  * @constructor
1459  * Create a new Input
1460  * @param {Object} config The config object
1461  */
1462
1463 Roo.bootstrap.Img = function(config){
1464     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1465     
1466     this.addEvents({
1467         // img events
1468         /**
1469          * @event click
1470          * The img click event for the img.
1471          * @param {Roo.EventObject} e
1472          */
1473         "click" : true
1474     });
1475 };
1476
1477 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1478     
1479     imgResponsive: true,
1480     border: '',
1481     src: 'about:blank',
1482     href: false,
1483     target: false,
1484     xsUrl: '',
1485     smUrl: '',
1486     mdUrl: '',
1487     lgUrl: '',
1488
1489     getAutoCreate : function()
1490     {   
1491         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1492             return this.createSingleImg();
1493         }
1494         
1495         var cfg = {
1496             tag: 'div',
1497             cls: 'roo-image-responsive-group',
1498             cn: []
1499         };
1500         var _this = this;
1501         
1502         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1503             
1504             if(!_this[size + 'Url']){
1505                 return;
1506             }
1507             
1508             var img = {
1509                 tag: 'img',
1510                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1511                 html: _this.html || cfg.html,
1512                 src: _this[size + 'Url']
1513             };
1514             
1515             img.cls += ' roo-image-responsive-' + size;
1516             
1517             var s = ['xs', 'sm', 'md', 'lg'];
1518             
1519             s.splice(s.indexOf(size), 1);
1520             
1521             Roo.each(s, function(ss){
1522                 img.cls += ' hidden-' + ss;
1523             });
1524             
1525             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1526                 cfg.cls += ' img-' + _this.border;
1527             }
1528             
1529             if(_this.alt){
1530                 cfg.alt = _this.alt;
1531             }
1532             
1533             if(_this.href){
1534                 var a = {
1535                     tag: 'a',
1536                     href: _this.href,
1537                     cn: [
1538                         img
1539                     ]
1540                 };
1541
1542                 if(this.target){
1543                     a.target = _this.target;
1544                 }
1545             }
1546             
1547             cfg.cn.push((_this.href) ? a : img);
1548             
1549         });
1550         
1551         return cfg;
1552     },
1553     
1554     createSingleImg : function()
1555     {
1556         var cfg = {
1557             tag: 'img',
1558             cls: (this.imgResponsive) ? 'img-responsive' : '',
1559             html : null,
1560             src : 'about:blank'  // just incase src get's set to undefined?!?
1561         };
1562         
1563         cfg.html = this.html || cfg.html;
1564         
1565         cfg.src = this.src || cfg.src;
1566         
1567         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1568             cfg.cls += ' img-' + this.border;
1569         }
1570         
1571         if(this.alt){
1572             cfg.alt = this.alt;
1573         }
1574         
1575         if(this.href){
1576             var a = {
1577                 tag: 'a',
1578                 href: this.href,
1579                 cn: [
1580                     cfg
1581                 ]
1582             };
1583             
1584             if(this.target){
1585                 a.target = this.target;
1586             }
1587             
1588         }
1589         
1590         return (this.href) ? a : cfg;
1591     },
1592     
1593     initEvents: function() 
1594     {
1595         if(!this.href){
1596             this.el.on('click', this.onClick, this);
1597         }
1598         
1599     },
1600     
1601     onClick : function(e)
1602     {
1603         Roo.log('img onclick');
1604         this.fireEvent('click', this, e);
1605     },
1606     /**
1607      * Sets the url of the image - used to update it
1608      * @param {String} url the url of the image
1609      */
1610     
1611     setSrc : function(url)
1612     {
1613         this.src =  url;
1614         
1615         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1616             this.el.dom.src =  url;
1617             return;
1618         }
1619         
1620         this.el.select('img', true).first().dom.src =  url;
1621     }
1622     
1623     
1624    
1625 });
1626
1627  /*
1628  * - LGPL
1629  *
1630  * image
1631  * 
1632  */
1633
1634
1635 /**
1636  * @class Roo.bootstrap.Link
1637  * @extends Roo.bootstrap.Component
1638  * Bootstrap Link Class
1639  * @cfg {String} alt image alternative text
1640  * @cfg {String} href a tag href
1641  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1642  * @cfg {String} html the content of the link.
1643  * @cfg {String} anchor name for the anchor link
1644  * @cfg {String} fa - favicon
1645
1646  * @cfg {Boolean} preventDefault (true | false) default false
1647
1648  * 
1649  * @constructor
1650  * Create a new Input
1651  * @param {Object} config The config object
1652  */
1653
1654 Roo.bootstrap.Link = function(config){
1655     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1656     
1657     this.addEvents({
1658         // img events
1659         /**
1660          * @event click
1661          * The img click event for the img.
1662          * @param {Roo.EventObject} e
1663          */
1664         "click" : true
1665     });
1666 };
1667
1668 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1669     
1670     href: false,
1671     target: false,
1672     preventDefault: false,
1673     anchor : false,
1674     alt : false,
1675     fa: false,
1676
1677
1678     getAutoCreate : function()
1679     {
1680         var html = this.html || '';
1681         
1682         if (this.fa !== false) {
1683             html = '<i class="fa fa-' + this.fa + '"></i>';
1684         }
1685         var cfg = {
1686             tag: 'a'
1687         };
1688         // anchor's do not require html/href...
1689         if (this.anchor === false) {
1690             cfg.html = html;
1691             cfg.href = this.href || '#';
1692         } else {
1693             cfg.name = this.anchor;
1694             if (this.html !== false || this.fa !== false) {
1695                 cfg.html = html;
1696             }
1697             if (this.href !== false) {
1698                 cfg.href = this.href;
1699             }
1700         }
1701         
1702         if(this.alt !== false){
1703             cfg.alt = this.alt;
1704         }
1705         
1706         
1707         if(this.target !== false) {
1708             cfg.target = this.target;
1709         }
1710         
1711         return cfg;
1712     },
1713     
1714     initEvents: function() {
1715         
1716         if(!this.href || this.preventDefault){
1717             this.el.on('click', this.onClick, this);
1718         }
1719     },
1720     
1721     onClick : function(e)
1722     {
1723         if(this.preventDefault){
1724             e.preventDefault();
1725         }
1726         //Roo.log('img onclick');
1727         this.fireEvent('click', this, e);
1728     }
1729    
1730 });
1731
1732  /*
1733  * - LGPL
1734  *
1735  * header
1736  * 
1737  */
1738
1739 /**
1740  * @class Roo.bootstrap.Header
1741  * @extends Roo.bootstrap.Component
1742  * Bootstrap Header class
1743  * @cfg {String} html content of header
1744  * @cfg {Number} level (1|2|3|4|5|6) default 1
1745  * 
1746  * @constructor
1747  * Create a new Header
1748  * @param {Object} config The config object
1749  */
1750
1751
1752 Roo.bootstrap.Header  = function(config){
1753     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1754 };
1755
1756 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1757     
1758     //href : false,
1759     html : false,
1760     level : 1,
1761     
1762     
1763     
1764     getAutoCreate : function(){
1765         
1766         
1767         
1768         var cfg = {
1769             tag: 'h' + (1 *this.level),
1770             html: this.html || ''
1771         } ;
1772         
1773         return cfg;
1774     }
1775    
1776 });
1777
1778  
1779
1780  /*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790  
1791 /**
1792  * @class Roo.bootstrap.MenuMgr
1793  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1794  * @singleton
1795  */
1796 Roo.bootstrap.MenuMgr = function(){
1797    var menus, active, groups = {}, attached = false, lastShow = new Date();
1798
1799    // private - called when first menu is created
1800    function init(){
1801        menus = {};
1802        active = new Roo.util.MixedCollection();
1803        Roo.get(document).addKeyListener(27, function(){
1804            if(active.length > 0){
1805                hideAll();
1806            }
1807        });
1808    }
1809
1810    // private
1811    function hideAll(){
1812        if(active && active.length > 0){
1813            var c = active.clone();
1814            c.each(function(m){
1815                m.hide();
1816            });
1817        }
1818    }
1819
1820    // private
1821    function onHide(m){
1822        active.remove(m);
1823        if(active.length < 1){
1824            Roo.get(document).un("mouseup", onMouseDown);
1825             
1826            attached = false;
1827        }
1828    }
1829
1830    // private
1831    function onShow(m){
1832        var last = active.last();
1833        lastShow = new Date();
1834        active.add(m);
1835        if(!attached){
1836           Roo.get(document).on("mouseup", onMouseDown);
1837            
1838            attached = true;
1839        }
1840        if(m.parentMenu){
1841           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1842           m.parentMenu.activeChild = m;
1843        }else if(last && last.isVisible()){
1844           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1845        }
1846    }
1847
1848    // private
1849    function onBeforeHide(m){
1850        if(m.activeChild){
1851            m.activeChild.hide();
1852        }
1853        if(m.autoHideTimer){
1854            clearTimeout(m.autoHideTimer);
1855            delete m.autoHideTimer;
1856        }
1857    }
1858
1859    // private
1860    function onBeforeShow(m){
1861        var pm = m.parentMenu;
1862        if(!pm && !m.allowOtherMenus){
1863            hideAll();
1864        }else if(pm && pm.activeChild && active != m){
1865            pm.activeChild.hide();
1866        }
1867    }
1868
1869    // private this should really trigger on mouseup..
1870    function onMouseDown(e){
1871         Roo.log("on Mouse Up");
1872         
1873         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1874             Roo.log("MenuManager hideAll");
1875             hideAll();
1876             e.stopEvent();
1877         }
1878         
1879         
1880    }
1881
1882    // private
1883    function onBeforeCheck(mi, state){
1884        if(state){
1885            var g = groups[mi.group];
1886            for(var i = 0, l = g.length; i < l; i++){
1887                if(g[i] != mi){
1888                    g[i].setChecked(false);
1889                }
1890            }
1891        }
1892    }
1893
1894    return {
1895
1896        /**
1897         * Hides all menus that are currently visible
1898         */
1899        hideAll : function(){
1900             hideAll();  
1901        },
1902
1903        // private
1904        register : function(menu){
1905            if(!menus){
1906                init();
1907            }
1908            menus[menu.id] = menu;
1909            menu.on("beforehide", onBeforeHide);
1910            menu.on("hide", onHide);
1911            menu.on("beforeshow", onBeforeShow);
1912            menu.on("show", onShow);
1913            var g = menu.group;
1914            if(g && menu.events["checkchange"]){
1915                if(!groups[g]){
1916                    groups[g] = [];
1917                }
1918                groups[g].push(menu);
1919                menu.on("checkchange", onCheck);
1920            }
1921        },
1922
1923         /**
1924          * Returns a {@link Roo.menu.Menu} object
1925          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1926          * be used to generate and return a new Menu instance.
1927          */
1928        get : function(menu){
1929            if(typeof menu == "string"){ // menu id
1930                return menus[menu];
1931            }else if(menu.events){  // menu instance
1932                return menu;
1933            }
1934            /*else if(typeof menu.length == 'number'){ // array of menu items?
1935                return new Roo.bootstrap.Menu({items:menu});
1936            }else{ // otherwise, must be a config
1937                return new Roo.bootstrap.Menu(menu);
1938            }
1939            */
1940            return false;
1941        },
1942
1943        // private
1944        unregister : function(menu){
1945            delete menus[menu.id];
1946            menu.un("beforehide", onBeforeHide);
1947            menu.un("hide", onHide);
1948            menu.un("beforeshow", onBeforeShow);
1949            menu.un("show", onShow);
1950            var g = menu.group;
1951            if(g && menu.events["checkchange"]){
1952                groups[g].remove(menu);
1953                menu.un("checkchange", onCheck);
1954            }
1955        },
1956
1957        // private
1958        registerCheckable : function(menuItem){
1959            var g = menuItem.group;
1960            if(g){
1961                if(!groups[g]){
1962                    groups[g] = [];
1963                }
1964                groups[g].push(menuItem);
1965                menuItem.on("beforecheckchange", onBeforeCheck);
1966            }
1967        },
1968
1969        // private
1970        unregisterCheckable : function(menuItem){
1971            var g = menuItem.group;
1972            if(g){
1973                groups[g].remove(menuItem);
1974                menuItem.un("beforecheckchange", onBeforeCheck);
1975            }
1976        }
1977    };
1978 }();/*
1979  * - LGPL
1980  *
1981  * menu
1982  * 
1983  */
1984
1985 /**
1986  * @class Roo.bootstrap.Menu
1987  * @extends Roo.bootstrap.Component
1988  * Bootstrap Menu class - container for MenuItems
1989  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1990  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1991  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1992  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1993  * 
1994  * @constructor
1995  * Create a new Menu
1996  * @param {Object} config The config object
1997  */
1998
1999
2000 Roo.bootstrap.Menu = function(config){
2001     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2002     if (this.registerMenu && this.type != 'treeview')  {
2003         Roo.bootstrap.MenuMgr.register(this);
2004     }
2005     
2006     
2007     this.addEvents({
2008         /**
2009          * @event beforeshow
2010          * Fires before this menu is displayed
2011          * @param {Roo.menu.Menu} this
2012          */
2013         beforeshow : true,
2014         /**
2015          * @event beforehide
2016          * Fires before this menu is hidden
2017          * @param {Roo.menu.Menu} this
2018          */
2019         beforehide : true,
2020         /**
2021          * @event show
2022          * Fires after this menu is displayed
2023          * @param {Roo.menu.Menu} this
2024          */
2025         show : true,
2026         /**
2027          * @event hide
2028          * Fires after this menu is hidden
2029          * @param {Roo.menu.Menu} this
2030          */
2031         hide : true,
2032         /**
2033          * @event click
2034          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2035          * @param {Roo.menu.Menu} this
2036          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2037          * @param {Roo.EventObject} e
2038          */
2039         click : true,
2040         /**
2041          * @event mouseover
2042          * Fires when the mouse is hovering over 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         mouseover : true,
2048         /**
2049          * @event mouseout
2050          * Fires when the mouse exits this menu
2051          * @param {Roo.menu.Menu} this
2052          * @param {Roo.EventObject} e
2053          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2054          */
2055         mouseout : true,
2056         /**
2057          * @event itemclick
2058          * Fires when a menu item contained in this menu is clicked
2059          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2060          * @param {Roo.EventObject} e
2061          */
2062         itemclick: true
2063     });
2064     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2065 };
2066
2067 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2068     
2069    /// html : false,
2070     //align : '',
2071     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2072     type: false,
2073     /**
2074      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2075      */
2076     registerMenu : true,
2077     
2078     menuItems :false, // stores the menu items..
2079     
2080     hidden:true,
2081         
2082     parentMenu : false,
2083     
2084     stopEvent : true,
2085     
2086     isLink : false,
2087     
2088     getChildContainer : function() {
2089         return this.el;  
2090     },
2091     
2092     getAutoCreate : function(){
2093          
2094         //if (['right'].indexOf(this.align)!==-1) {
2095         //    cfg.cn[1].cls += ' pull-right'
2096         //}
2097         
2098         
2099         var cfg = {
2100             tag : 'ul',
2101             cls : 'dropdown-menu' ,
2102             style : 'z-index:1000'
2103             
2104         };
2105         
2106         if (this.type === 'submenu') {
2107             cfg.cls = 'submenu active';
2108         }
2109         if (this.type === 'treeview') {
2110             cfg.cls = 'treeview-menu';
2111         }
2112         
2113         return cfg;
2114     },
2115     initEvents : function() {
2116         
2117        // Roo.log("ADD event");
2118        // Roo.log(this.triggerEl.dom);
2119         
2120         this.triggerEl.on('click', this.onTriggerClick, this);
2121         
2122         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2123         
2124         
2125         if (this.triggerEl.hasClass('nav-item')) {
2126             // dropdown toggle on the 'a' in BS4?
2127             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2128         } else {
2129             this.triggerEl.addClass('dropdown-toggle');
2130         }
2131         if (Roo.isTouch) {
2132             this.el.on('touchstart'  , this.onTouch, this);
2133         }
2134         this.el.on('click' , this.onClick, this);
2135
2136         this.el.on("mouseover", this.onMouseOver, this);
2137         this.el.on("mouseout", this.onMouseOut, this);
2138         
2139     },
2140     
2141     findTargetItem : function(e)
2142     {
2143         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2144         if(!t){
2145             return false;
2146         }
2147         //Roo.log(t);         Roo.log(t.id);
2148         if(t && t.id){
2149             //Roo.log(this.menuitems);
2150             return this.menuitems.get(t.id);
2151             
2152             //return this.items.get(t.menuItemId);
2153         }
2154         
2155         return false;
2156     },
2157     
2158     onTouch : function(e) 
2159     {
2160         Roo.log("menu.onTouch");
2161         //e.stopEvent(); this make the user popdown broken
2162         this.onClick(e);
2163     },
2164     
2165     onClick : function(e)
2166     {
2167         Roo.log("menu.onClick");
2168         
2169         var t = this.findTargetItem(e);
2170         if(!t || t.isContainer){
2171             return;
2172         }
2173         Roo.log(e);
2174         /*
2175         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2176             if(t == this.activeItem && t.shouldDeactivate(e)){
2177                 this.activeItem.deactivate();
2178                 delete this.activeItem;
2179                 return;
2180             }
2181             if(t.canActivate){
2182                 this.setActiveItem(t, true);
2183             }
2184             return;
2185             
2186             
2187         }
2188         */
2189        
2190         Roo.log('pass click event');
2191         
2192         t.onClick(e);
2193         
2194         this.fireEvent("click", this, t, e);
2195         
2196         var _this = this;
2197         
2198         if(!t.href.length || t.href == '#'){
2199             (function() { _this.hide(); }).defer(100);
2200         }
2201         
2202     },
2203     
2204     onMouseOver : function(e){
2205         var t  = this.findTargetItem(e);
2206         //Roo.log(t);
2207         //if(t){
2208         //    if(t.canActivate && !t.disabled){
2209         //        this.setActiveItem(t, true);
2210         //    }
2211         //}
2212         
2213         this.fireEvent("mouseover", this, e, t);
2214     },
2215     isVisible : function(){
2216         return !this.hidden;
2217     },
2218      onMouseOut : function(e){
2219         var t  = this.findTargetItem(e);
2220         
2221         //if(t ){
2222         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2223         //        this.activeItem.deactivate();
2224         //        delete this.activeItem;
2225         //    }
2226         //}
2227         this.fireEvent("mouseout", this, e, t);
2228     },
2229     
2230     
2231     /**
2232      * Displays this menu relative to another element
2233      * @param {String/HTMLElement/Roo.Element} element The element to align to
2234      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2235      * the element (defaults to this.defaultAlign)
2236      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2237      */
2238     show : function(el, pos, parentMenu){
2239         this.parentMenu = parentMenu;
2240         if(!this.el){
2241             this.render();
2242         }
2243         this.fireEvent("beforeshow", this);
2244         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2245     },
2246      /**
2247      * Displays this menu at a specific xy position
2248      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2249      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2250      */
2251     showAt : function(xy, parentMenu, /* private: */_e){
2252         this.parentMenu = parentMenu;
2253         if(!this.el){
2254             this.render();
2255         }
2256         if(_e !== false){
2257             this.fireEvent("beforeshow", this);
2258             //xy = this.el.adjustForConstraints(xy);
2259         }
2260         
2261         //this.el.show();
2262         this.hideMenuItems();
2263         this.hidden = false;
2264         this.triggerEl.addClass('open');
2265         this.el.addClass('show');
2266         
2267         // reassign x when hitting right
2268         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2269             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2270         }
2271         
2272         // reassign y when hitting bottom
2273         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2274             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2275         }
2276         
2277         // but the list may align on trigger left or trigger top... should it be a properity?
2278         
2279         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2280             this.el.setXY(xy);
2281         }
2282         
2283         this.focus();
2284         this.fireEvent("show", this);
2285     },
2286     
2287     focus : function(){
2288         return;
2289         if(!this.hidden){
2290             this.doFocus.defer(50, this);
2291         }
2292     },
2293
2294     doFocus : function(){
2295         if(!this.hidden){
2296             this.focusEl.focus();
2297         }
2298     },
2299
2300     /**
2301      * Hides this menu and optionally all parent menus
2302      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2303      */
2304     hide : function(deep)
2305     {
2306         
2307         this.hideMenuItems();
2308         if(this.el && this.isVisible()){
2309             this.fireEvent("beforehide", this);
2310             if(this.activeItem){
2311                 this.activeItem.deactivate();
2312                 this.activeItem = null;
2313             }
2314             this.triggerEl.removeClass('open');;
2315             this.el.removeClass('show');
2316             this.hidden = true;
2317             this.fireEvent("hide", this);
2318         }
2319         if(deep === true && this.parentMenu){
2320             this.parentMenu.hide(true);
2321         }
2322     },
2323     
2324     onTriggerClick : function(e)
2325     {
2326         Roo.log('trigger click');
2327         
2328         var target = e.getTarget();
2329         
2330         Roo.log(target.nodeName.toLowerCase());
2331         
2332         if(target.nodeName.toLowerCase() === 'i'){
2333             e.preventDefault();
2334         }
2335         
2336     },
2337     
2338     onTriggerPress  : function(e)
2339     {
2340         Roo.log('trigger press');
2341         //Roo.log(e.getTarget());
2342        // Roo.log(this.triggerEl.dom);
2343        
2344         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2345         var pel = Roo.get(e.getTarget());
2346         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2347             Roo.log('is treeview or dropdown?');
2348             return;
2349         }
2350         
2351         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2352             return;
2353         }
2354         
2355         if (this.isVisible()) {
2356             Roo.log('hide');
2357             this.hide();
2358         } else {
2359             Roo.log('show');
2360             this.show(this.triggerEl, false, false);
2361         }
2362         
2363         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2364             e.stopEvent();
2365         }
2366         
2367     },
2368        
2369     
2370     hideMenuItems : function()
2371     {
2372         Roo.log("hide Menu Items");
2373         if (!this.el) { 
2374             return;
2375         }
2376         //$(backdrop).remove()
2377         this.el.select('.open',true).each(function(aa) {
2378             
2379             aa.removeClass('open');
2380           //var parent = getParent($(this))
2381           //var relatedTarget = { relatedTarget: this }
2382           
2383            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2384           //if (e.isDefaultPrevented()) return
2385            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2386         });
2387     },
2388     addxtypeChild : function (tree, cntr) {
2389         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2390           
2391         this.menuitems.add(comp);
2392         return comp;
2393
2394     },
2395     getEl : function()
2396     {
2397         Roo.log(this.el);
2398         return this.el;
2399     },
2400     
2401     clear : function()
2402     {
2403         this.getEl().dom.innerHTML = '';
2404         this.menuitems.clear();
2405     }
2406 });
2407
2408  
2409  /*
2410  * - LGPL
2411  *
2412  * menu item
2413  * 
2414  */
2415
2416
2417 /**
2418  * @class Roo.bootstrap.MenuItem
2419  * @extends Roo.bootstrap.Component
2420  * Bootstrap MenuItem class
2421  * @cfg {String} html the menu label
2422  * @cfg {String} href the link
2423  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2424  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2425  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2426  * @cfg {String} fa favicon to show on left of menu item.
2427  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2428  * 
2429  * 
2430  * @constructor
2431  * Create a new MenuItem
2432  * @param {Object} config The config object
2433  */
2434
2435
2436 Roo.bootstrap.MenuItem = function(config){
2437     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2438     this.addEvents({
2439         // raw events
2440         /**
2441          * @event click
2442          * The raw click event for the entire grid.
2443          * @param {Roo.bootstrap.MenuItem} this
2444          * @param {Roo.EventObject} e
2445          */
2446         "click" : true
2447     });
2448 };
2449
2450 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2451     
2452     href : false,
2453     html : false,
2454     preventDefault: false,
2455     isContainer : false,
2456     active : false,
2457     fa: false,
2458     
2459     getAutoCreate : function(){
2460         
2461         if(this.isContainer){
2462             return {
2463                 tag: 'li',
2464                 cls: 'dropdown-menu-item dropdown-item'
2465             };
2466         }
2467         var ctag = {
2468             tag: 'span',
2469             html: 'Link'
2470         };
2471         
2472         var anc = {
2473             tag : 'a',
2474             href : '#',
2475             cn : [  ]
2476         };
2477         
2478         if (this.fa !== false) {
2479             anc.cn.push({
2480                 tag : 'i',
2481                 cls : 'fa fa-' + this.fa
2482             });
2483         }
2484         
2485         anc.cn.push(ctag);
2486         
2487         
2488         var cfg= {
2489             tag: 'li',
2490             cls: 'dropdown-menu-item dropdown-item',
2491             cn: [ anc ]
2492         };
2493         if (this.parent().type == 'treeview') {
2494             cfg.cls = 'treeview-menu';
2495         }
2496         if (this.active) {
2497             cfg.cls += ' active';
2498         }
2499         
2500         
2501         
2502         anc.href = this.href || cfg.cn[0].href ;
2503         ctag.html = this.html || cfg.cn[0].html ;
2504         return cfg;
2505     },
2506     
2507     initEvents: function()
2508     {
2509         if (this.parent().type == 'treeview') {
2510             this.el.select('a').on('click', this.onClick, this);
2511         }
2512         
2513         if (this.menu) {
2514             this.menu.parentType = this.xtype;
2515             this.menu.triggerEl = this.el;
2516             this.menu = this.addxtype(Roo.apply({}, this.menu));
2517         }
2518         
2519     },
2520     onClick : function(e)
2521     {
2522         Roo.log('item on click ');
2523         
2524         if(this.preventDefault){
2525             e.preventDefault();
2526         }
2527         //this.parent().hideMenuItems();
2528         
2529         this.fireEvent('click', this, e);
2530     },
2531     getEl : function()
2532     {
2533         return this.el;
2534     } 
2535 });
2536
2537  
2538
2539  /*
2540  * - LGPL
2541  *
2542  * menu separator
2543  * 
2544  */
2545
2546
2547 /**
2548  * @class Roo.bootstrap.MenuSeparator
2549  * @extends Roo.bootstrap.Component
2550  * Bootstrap MenuSeparator class
2551  * 
2552  * @constructor
2553  * Create a new MenuItem
2554  * @param {Object} config The config object
2555  */
2556
2557
2558 Roo.bootstrap.MenuSeparator = function(config){
2559     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2560 };
2561
2562 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2563     
2564     getAutoCreate : function(){
2565         var cfg = {
2566             cls: 'divider',
2567             tag : 'li'
2568         };
2569         
2570         return cfg;
2571     }
2572    
2573 });
2574
2575  
2576
2577  
2578 /*
2579 * Licence: LGPL
2580 */
2581
2582 /**
2583  * @class Roo.bootstrap.Modal
2584  * @extends Roo.bootstrap.Component
2585  * Bootstrap Modal class
2586  * @cfg {String} title Title of dialog
2587  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2588  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2589  * @cfg {Boolean} specificTitle default false
2590  * @cfg {Array} buttons Array of buttons or standard button set..
2591  * @cfg {String} buttonPosition (left|right|center) default right
2592  * @cfg {Boolean} animate default true
2593  * @cfg {Boolean} allow_close default true
2594  * @cfg {Boolean} fitwindow default false
2595  * @cfg {String} size (sm|lg) default empty
2596  * @cfg {Number} max_width set the max width of modal
2597  *
2598  *
2599  * @constructor
2600  * Create a new Modal Dialog
2601  * @param {Object} config The config object
2602  */
2603
2604 Roo.bootstrap.Modal = function(config){
2605     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2606     this.addEvents({
2607         // raw events
2608         /**
2609          * @event btnclick
2610          * The raw btnclick event for the button
2611          * @param {Roo.EventObject} e
2612          */
2613         "btnclick" : true,
2614         /**
2615          * @event resize
2616          * Fire when dialog resize
2617          * @param {Roo.bootstrap.Modal} this
2618          * @param {Roo.EventObject} e
2619          */
2620         "resize" : true
2621     });
2622     this.buttons = this.buttons || [];
2623
2624     if (this.tmpl) {
2625         this.tmpl = Roo.factory(this.tmpl);
2626     }
2627
2628 };
2629
2630 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2631
2632     title : 'test dialog',
2633
2634     buttons : false,
2635
2636     // set on load...
2637
2638     html: false,
2639
2640     tmp: false,
2641
2642     specificTitle: false,
2643
2644     buttonPosition: 'right',
2645
2646     allow_close : true,
2647
2648     animate : true,
2649
2650     fitwindow: false,
2651     
2652      // private
2653     dialogEl: false,
2654     bodyEl:  false,
2655     footerEl:  false,
2656     titleEl:  false,
2657     closeEl:  false,
2658
2659     size: '',
2660     
2661     max_width: 0,
2662     
2663     max_height: 0,
2664     
2665     fit_content: false,
2666
2667     onRender : function(ct, position)
2668     {
2669         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2670
2671         if(!this.el){
2672             var cfg = Roo.apply({},  this.getAutoCreate());
2673             cfg.id = Roo.id();
2674             //if(!cfg.name){
2675             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2676             //}
2677             //if (!cfg.name.length) {
2678             //    delete cfg.name;
2679            // }
2680             if (this.cls) {
2681                 cfg.cls += ' ' + this.cls;
2682             }
2683             if (this.style) {
2684                 cfg.style = this.style;
2685             }
2686             this.el = Roo.get(document.body).createChild(cfg, position);
2687         }
2688         //var type = this.el.dom.type;
2689
2690
2691         if(this.tabIndex !== undefined){
2692             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2693         }
2694
2695         this.dialogEl = this.el.select('.modal-dialog',true).first();
2696         this.bodyEl = this.el.select('.modal-body',true).first();
2697         this.closeEl = this.el.select('.modal-header .close', true).first();
2698         this.headerEl = this.el.select('.modal-header',true).first();
2699         this.titleEl = this.el.select('.modal-title',true).first();
2700         this.footerEl = this.el.select('.modal-footer',true).first();
2701
2702         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2703         
2704         //this.el.addClass("x-dlg-modal");
2705
2706         if (this.buttons.length) {
2707             Roo.each(this.buttons, function(bb) {
2708                 var b = Roo.apply({}, bb);
2709                 b.xns = b.xns || Roo.bootstrap;
2710                 b.xtype = b.xtype || 'Button';
2711                 if (typeof(b.listeners) == 'undefined') {
2712                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2713                 }
2714
2715                 var btn = Roo.factory(b);
2716
2717                 btn.render(this.el.select('.modal-footer div').first());
2718
2719             },this);
2720         }
2721         // render the children.
2722         var nitems = [];
2723
2724         if(typeof(this.items) != 'undefined'){
2725             var items = this.items;
2726             delete this.items;
2727
2728             for(var i =0;i < items.length;i++) {
2729                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2730             }
2731         }
2732
2733         this.items = nitems;
2734
2735         // where are these used - they used to be body/close/footer
2736
2737
2738         this.initEvents();
2739         //this.el.addClass([this.fieldClass, this.cls]);
2740
2741     },
2742
2743     getAutoCreate : function()
2744     {
2745         var bdy = {
2746                 cls : 'modal-body',
2747                 html : this.html || ''
2748         };
2749
2750         var title = {
2751             tag: 'h4',
2752             cls : 'modal-title',
2753             html : this.title
2754         };
2755
2756         if(this.specificTitle){
2757             title = this.title;
2758
2759         };
2760
2761         var header = [];
2762         if (this.allow_close) {
2763             header.push({
2764                 tag: 'button',
2765                 cls : 'close',
2766                 html : '&times'
2767             });
2768         }
2769
2770         header.push(title);
2771
2772         var size = '';
2773
2774         if(this.size.length){
2775             size = 'modal-' + this.size;
2776         }
2777
2778         var modal = {
2779             cls: "modal",
2780              cn : [
2781                 {
2782                     cls: "modal-dialog " + size,
2783                     cn : [
2784                         {
2785                             cls : "modal-content",
2786                             cn : [
2787                                 {
2788                                     cls : 'modal-header',
2789                                     cn : header
2790                                 },
2791                                 bdy,
2792                                 {
2793                                     cls : 'modal-footer',
2794                                     cn : [
2795                                         {
2796                                             tag: 'div',
2797                                             cls: 'btn-' + this.buttonPosition
2798                                         }
2799                                     ]
2800
2801                                 }
2802
2803
2804                             ]
2805
2806                         }
2807                     ]
2808
2809                 }
2810             ]
2811         };
2812
2813         if(this.animate){
2814             modal.cls += ' fade';
2815         }
2816
2817         return modal;
2818
2819     },
2820     getChildContainer : function() {
2821
2822          return this.bodyEl;
2823
2824     },
2825     getButtonContainer : function() {
2826          return this.el.select('.modal-footer div',true).first();
2827
2828     },
2829     initEvents : function()
2830     {
2831         if (this.allow_close) {
2832             this.closeEl.on('click', this.hide, this);
2833         }
2834         Roo.EventManager.onWindowResize(this.resize, this, true);
2835
2836
2837     },
2838
2839     resize : function()
2840     {
2841         this.maskEl.setSize(
2842             Roo.lib.Dom.getViewWidth(true),
2843             Roo.lib.Dom.getViewHeight(true)
2844         );
2845         
2846         if (this.fitwindow) {
2847             this.setSize(
2848                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2849                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2850             );
2851             return;
2852         }
2853         
2854         if(this.max_width !== 0) {
2855             
2856             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2857             
2858             if(this.height) {
2859                 this.setSize(w, this.height);
2860                 return;
2861             }
2862             
2863             if(this.max_height) {
2864                 this.setSize(w,Math.min(
2865                     this.max_height,
2866                     Roo.lib.Dom.getViewportHeight(true) - 60
2867                 ));
2868                 
2869                 return;
2870             }
2871             
2872             if(!this.fit_content) {
2873                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2874                 return;
2875             }
2876             
2877             this.setSize(w, Math.min(
2878                 60 +
2879                 this.headerEl.getHeight() + 
2880                 this.footerEl.getHeight() + 
2881                 this.getChildHeight(this.bodyEl.dom.childNodes),
2882                 Roo.lib.Dom.getViewportHeight(true) - 60)
2883             );
2884         }
2885         
2886     },
2887
2888     setSize : function(w,h)
2889     {
2890         if (!w && !h) {
2891             return;
2892         }
2893         
2894         this.resizeTo(w,h);
2895     },
2896
2897     show : function() {
2898
2899         if (!this.rendered) {
2900             this.render();
2901         }
2902
2903         //this.el.setStyle('display', 'block');
2904         this.el.removeClass('hideing');        
2905         this.el.addClass('show');
2906  
2907         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2908             var _this = this;
2909             (function(){
2910                 this.el.addClass('in');
2911             }).defer(50, this);
2912         }else{
2913             this.el.addClass('in');
2914         }
2915
2916         // not sure how we can show data in here..
2917         //if (this.tmpl) {
2918         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2919         //}
2920
2921         Roo.get(document.body).addClass("x-body-masked");
2922         
2923         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2924         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2925         this.maskEl.addClass('show');
2926         
2927         this.resize();
2928         
2929         this.fireEvent('show', this);
2930
2931         // set zindex here - otherwise it appears to be ignored...
2932         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2933
2934         (function () {
2935             this.items.forEach( function(e) {
2936                 e.layout ? e.layout() : false;
2937
2938             });
2939         }).defer(100,this);
2940
2941     },
2942     hide : function()
2943     {
2944         if(this.fireEvent("beforehide", this) !== false){
2945             this.maskEl.removeClass('show');
2946             Roo.get(document.body).removeClass("x-body-masked");
2947             this.el.removeClass('in');
2948             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2949
2950             if(this.animate){ // why
2951                 this.el.addClass('hideing');
2952                 (function(){
2953                     if (!this.el.hasClass('hideing')) {
2954                         return; // it's been shown again...
2955                     }
2956                     this.el.removeClass('show');
2957                     this.el.removeClass('hideing');
2958                 }).defer(150,this);
2959                 
2960             }else{
2961                  this.el.removeClass('show');
2962             }
2963             this.fireEvent('hide', this);
2964         }
2965     },
2966     isVisible : function()
2967     {
2968         
2969         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2970         
2971     },
2972
2973     addButton : function(str, cb)
2974     {
2975
2976
2977         var b = Roo.apply({}, { html : str } );
2978         b.xns = b.xns || Roo.bootstrap;
2979         b.xtype = b.xtype || 'Button';
2980         if (typeof(b.listeners) == 'undefined') {
2981             b.listeners = { click : cb.createDelegate(this)  };
2982         }
2983
2984         var btn = Roo.factory(b);
2985
2986         btn.render(this.el.select('.modal-footer div').first());
2987
2988         return btn;
2989
2990     },
2991
2992     setDefaultButton : function(btn)
2993     {
2994         //this.el.select('.modal-footer').()
2995     },
2996     diff : false,
2997
2998     resizeTo: function(w,h)
2999     {
3000         // skip.. ?? why??
3001
3002         this.dialogEl.setWidth(w);
3003         if (this.diff === false) {
3004             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3005         }
3006
3007         this.bodyEl.setHeight(h - this.diff);
3008
3009         this.fireEvent('resize', this);
3010
3011     },
3012     setContentSize  : function(w, h)
3013     {
3014
3015     },
3016     onButtonClick: function(btn,e)
3017     {
3018         //Roo.log([a,b,c]);
3019         this.fireEvent('btnclick', btn.name, e);
3020     },
3021      /**
3022      * Set the title of the Dialog
3023      * @param {String} str new Title
3024      */
3025     setTitle: function(str) {
3026         this.titleEl.dom.innerHTML = str;
3027     },
3028     /**
3029      * Set the body of the Dialog
3030      * @param {String} str new Title
3031      */
3032     setBody: function(str) {
3033         this.bodyEl.dom.innerHTML = str;
3034     },
3035     /**
3036      * Set the body of the Dialog using the template
3037      * @param {Obj} data - apply this data to the template and replace the body contents.
3038      */
3039     applyBody: function(obj)
3040     {
3041         if (!this.tmpl) {
3042             Roo.log("Error - using apply Body without a template");
3043             //code
3044         }
3045         this.tmpl.overwrite(this.bodyEl, obj);
3046     },
3047     
3048     getChildHeight : function(child_nodes)
3049     {
3050         if(
3051             !child_nodes ||
3052             child_nodes.length == 0
3053         ) {
3054             return;
3055         }
3056         
3057         var child_height = 0;
3058         
3059         for(var i = 0; i < child_nodes.length; i++) {
3060             
3061             /*
3062             * for modal with tabs...
3063             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3064                 
3065                 var layout_childs = child_nodes[i].childNodes;
3066                 
3067                 for(var j = 0; j < layout_childs.length; j++) {
3068                     
3069                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3070                         
3071                         var layout_body_childs = layout_childs[j].childNodes;
3072                         
3073                         for(var k = 0; k < layout_body_childs.length; k++) {
3074                             
3075                             if(layout_body_childs[k].classList.contains('navbar')) {
3076                                 child_height += layout_body_childs[k].offsetHeight;
3077                                 continue;
3078                             }
3079                             
3080                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3081                                 
3082                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3083                                 
3084                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3085                                     
3086                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3087                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3088                                         continue;
3089                                     }
3090                                     
3091                                 }
3092                                 
3093                             }
3094                             
3095                         }
3096                     }
3097                 }
3098                 continue;
3099             }
3100             */
3101             
3102             child_height += child_nodes[i].offsetHeight;
3103             // Roo.log(child_nodes[i].offsetHeight);
3104         }
3105         
3106         return child_height;
3107     }
3108
3109 });
3110
3111
3112 Roo.apply(Roo.bootstrap.Modal,  {
3113     /**
3114          * Button config that displays a single OK button
3115          * @type Object
3116          */
3117         OK :  [{
3118             name : 'ok',
3119             weight : 'primary',
3120             html : 'OK'
3121         }],
3122         /**
3123          * Button config that displays Yes and No buttons
3124          * @type Object
3125          */
3126         YESNO : [
3127             {
3128                 name  : 'no',
3129                 html : 'No'
3130             },
3131             {
3132                 name  :'yes',
3133                 weight : 'primary',
3134                 html : 'Yes'
3135             }
3136         ],
3137
3138         /**
3139          * Button config that displays OK and Cancel buttons
3140          * @type Object
3141          */
3142         OKCANCEL : [
3143             {
3144                name : 'cancel',
3145                 html : 'Cancel'
3146             },
3147             {
3148                 name : 'ok',
3149                 weight : 'primary',
3150                 html : 'OK'
3151             }
3152         ],
3153         /**
3154          * Button config that displays Yes, No and Cancel buttons
3155          * @type Object
3156          */
3157         YESNOCANCEL : [
3158             {
3159                 name : 'yes',
3160                 weight : 'primary',
3161                 html : 'Yes'
3162             },
3163             {
3164                 name : 'no',
3165                 html : 'No'
3166             },
3167             {
3168                 name : 'cancel',
3169                 html : 'Cancel'
3170             }
3171         ],
3172         
3173         zIndex : 10001
3174 });
3175 /*
3176  * - LGPL
3177  *
3178  * messagebox - can be used as a replace
3179  * 
3180  */
3181 /**
3182  * @class Roo.MessageBox
3183  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3184  * Example usage:
3185  *<pre><code>
3186 // Basic alert:
3187 Roo.Msg.alert('Status', 'Changes saved successfully.');
3188
3189 // Prompt for user data:
3190 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3191     if (btn == 'ok'){
3192         // process text value...
3193     }
3194 });
3195
3196 // Show a dialog using config options:
3197 Roo.Msg.show({
3198    title:'Save Changes?',
3199    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3200    buttons: Roo.Msg.YESNOCANCEL,
3201    fn: processResult,
3202    animEl: 'elId'
3203 });
3204 </code></pre>
3205  * @singleton
3206  */
3207 Roo.bootstrap.MessageBox = function(){
3208     var dlg, opt, mask, waitTimer;
3209     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3210     var buttons, activeTextEl, bwidth;
3211
3212     
3213     // private
3214     var handleButton = function(button){
3215         dlg.hide();
3216         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3217     };
3218
3219     // private
3220     var handleHide = function(){
3221         if(opt && opt.cls){
3222             dlg.el.removeClass(opt.cls);
3223         }
3224         //if(waitTimer){
3225         //    Roo.TaskMgr.stop(waitTimer);
3226         //    waitTimer = null;
3227         //}
3228     };
3229
3230     // private
3231     var updateButtons = function(b){
3232         var width = 0;
3233         if(!b){
3234             buttons["ok"].hide();
3235             buttons["cancel"].hide();
3236             buttons["yes"].hide();
3237             buttons["no"].hide();
3238             //dlg.footer.dom.style.display = 'none';
3239             return width;
3240         }
3241         dlg.footerEl.dom.style.display = '';
3242         for(var k in buttons){
3243             if(typeof buttons[k] != "function"){
3244                 if(b[k]){
3245                     buttons[k].show();
3246                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3247                     width += buttons[k].el.getWidth()+15;
3248                 }else{
3249                     buttons[k].hide();
3250                 }
3251             }
3252         }
3253         return width;
3254     };
3255
3256     // private
3257     var handleEsc = function(d, k, e){
3258         if(opt && opt.closable !== false){
3259             dlg.hide();
3260         }
3261         if(e){
3262             e.stopEvent();
3263         }
3264     };
3265
3266     return {
3267         /**
3268          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3269          * @return {Roo.BasicDialog} The BasicDialog element
3270          */
3271         getDialog : function(){
3272            if(!dlg){
3273                 dlg = new Roo.bootstrap.Modal( {
3274                     //draggable: true,
3275                     //resizable:false,
3276                     //constraintoviewport:false,
3277                     //fixedcenter:true,
3278                     //collapsible : false,
3279                     //shim:true,
3280                     //modal: true,
3281                 //    width: 'auto',
3282                   //  height:100,
3283                     //buttonAlign:"center",
3284                     closeClick : function(){
3285                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3286                             handleButton("no");
3287                         }else{
3288                             handleButton("cancel");
3289                         }
3290                     }
3291                 });
3292                 dlg.render();
3293                 dlg.on("hide", handleHide);
3294                 mask = dlg.mask;
3295                 //dlg.addKeyListener(27, handleEsc);
3296                 buttons = {};
3297                 this.buttons = buttons;
3298                 var bt = this.buttonText;
3299                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3300                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3301                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3302                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3303                 //Roo.log(buttons);
3304                 bodyEl = dlg.bodyEl.createChild({
3305
3306                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3307                         '<textarea class="roo-mb-textarea"></textarea>' +
3308                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3309                 });
3310                 msgEl = bodyEl.dom.firstChild;
3311                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3312                 textboxEl.enableDisplayMode();
3313                 textboxEl.addKeyListener([10,13], function(){
3314                     if(dlg.isVisible() && opt && opt.buttons){
3315                         if(opt.buttons.ok){
3316                             handleButton("ok");
3317                         }else if(opt.buttons.yes){
3318                             handleButton("yes");
3319                         }
3320                     }
3321                 });
3322                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3323                 textareaEl.enableDisplayMode();
3324                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3325                 progressEl.enableDisplayMode();
3326                 
3327                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3328                 var pf = progressEl.dom.firstChild;
3329                 if (pf) {
3330                     pp = Roo.get(pf.firstChild);
3331                     pp.setHeight(pf.offsetHeight);
3332                 }
3333                 
3334             }
3335             return dlg;
3336         },
3337
3338         /**
3339          * Updates the message box body text
3340          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3341          * the XHTML-compliant non-breaking space character '&amp;#160;')
3342          * @return {Roo.MessageBox} This message box
3343          */
3344         updateText : function(text)
3345         {
3346             if(!dlg.isVisible() && !opt.width){
3347                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3348                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3349             }
3350             msgEl.innerHTML = text || '&#160;';
3351       
3352             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3353             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3354             var w = Math.max(
3355                     Math.min(opt.width || cw , this.maxWidth), 
3356                     Math.max(opt.minWidth || this.minWidth, bwidth)
3357             );
3358             if(opt.prompt){
3359                 activeTextEl.setWidth(w);
3360             }
3361             if(dlg.isVisible()){
3362                 dlg.fixedcenter = false;
3363             }
3364             // to big, make it scroll. = But as usual stupid IE does not support
3365             // !important..
3366             
3367             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3368                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3369                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3370             } else {
3371                 bodyEl.dom.style.height = '';
3372                 bodyEl.dom.style.overflowY = '';
3373             }
3374             if (cw > w) {
3375                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3376             } else {
3377                 bodyEl.dom.style.overflowX = '';
3378             }
3379             
3380             dlg.setContentSize(w, bodyEl.getHeight());
3381             if(dlg.isVisible()){
3382                 dlg.fixedcenter = true;
3383             }
3384             return this;
3385         },
3386
3387         /**
3388          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3389          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3390          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3391          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3392          * @return {Roo.MessageBox} This message box
3393          */
3394         updateProgress : function(value, text){
3395             if(text){
3396                 this.updateText(text);
3397             }
3398             
3399             if (pp) { // weird bug on my firefox - for some reason this is not defined
3400                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3401                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3402             }
3403             return this;
3404         },        
3405
3406         /**
3407          * Returns true if the message box is currently displayed
3408          * @return {Boolean} True if the message box is visible, else false
3409          */
3410         isVisible : function(){
3411             return dlg && dlg.isVisible();  
3412         },
3413
3414         /**
3415          * Hides the message box if it is displayed
3416          */
3417         hide : function(){
3418             if(this.isVisible()){
3419                 dlg.hide();
3420             }  
3421         },
3422
3423         /**
3424          * Displays a new message box, or reinitializes an existing message box, based on the config options
3425          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3426          * The following config object properties are supported:
3427          * <pre>
3428 Property    Type             Description
3429 ----------  ---------------  ------------------------------------------------------------------------------------
3430 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3431                                    closes (defaults to undefined)
3432 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3433                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3434 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3435                                    progress and wait dialogs will ignore this property and always hide the
3436                                    close button as they can only be closed programmatically.
3437 cls               String           A custom CSS class to apply to the message box element
3438 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3439                                    displayed (defaults to 75)
3440 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3441                                    function will be btn (the name of the button that was clicked, if applicable,
3442                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3443                                    Progress and wait dialogs will ignore this option since they do not respond to
3444                                    user actions and can only be closed programmatically, so any required function
3445                                    should be called by the same code after it closes the dialog.
3446 icon              String           A CSS class that provides a background image to be used as an icon for
3447                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3448 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3449 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3450 modal             Boolean          False to allow user interaction with the page while the message box is
3451                                    displayed (defaults to true)
3452 msg               String           A string that will replace the existing message box body text (defaults
3453                                    to the XHTML-compliant non-breaking space character '&#160;')
3454 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3455 progress          Boolean          True to display a progress bar (defaults to false)
3456 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3457 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3458 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3459 title             String           The title text
3460 value             String           The string value to set into the active textbox element if displayed
3461 wait              Boolean          True to display a progress bar (defaults to false)
3462 width             Number           The width of the dialog in pixels
3463 </pre>
3464          *
3465          * Example usage:
3466          * <pre><code>
3467 Roo.Msg.show({
3468    title: 'Address',
3469    msg: 'Please enter your address:',
3470    width: 300,
3471    buttons: Roo.MessageBox.OKCANCEL,
3472    multiline: true,
3473    fn: saveAddress,
3474    animEl: 'addAddressBtn'
3475 });
3476 </code></pre>
3477          * @param {Object} config Configuration options
3478          * @return {Roo.MessageBox} This message box
3479          */
3480         show : function(options)
3481         {
3482             
3483             // this causes nightmares if you show one dialog after another
3484             // especially on callbacks..
3485              
3486             if(this.isVisible()){
3487                 
3488                 this.hide();
3489                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3490                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3491                 Roo.log("New Dialog Message:" +  options.msg )
3492                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3493                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3494                 
3495             }
3496             var d = this.getDialog();
3497             opt = options;
3498             d.setTitle(opt.title || "&#160;");
3499             d.closeEl.setDisplayed(opt.closable !== false);
3500             activeTextEl = textboxEl;
3501             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3502             if(opt.prompt){
3503                 if(opt.multiline){
3504                     textboxEl.hide();
3505                     textareaEl.show();
3506                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3507                         opt.multiline : this.defaultTextHeight);
3508                     activeTextEl = textareaEl;
3509                 }else{
3510                     textboxEl.show();
3511                     textareaEl.hide();
3512                 }
3513             }else{
3514                 textboxEl.hide();
3515                 textareaEl.hide();
3516             }
3517             progressEl.setDisplayed(opt.progress === true);
3518             this.updateProgress(0);
3519             activeTextEl.dom.value = opt.value || "";
3520             if(opt.prompt){
3521                 dlg.setDefaultButton(activeTextEl);
3522             }else{
3523                 var bs = opt.buttons;
3524                 var db = null;
3525                 if(bs && bs.ok){
3526                     db = buttons["ok"];
3527                 }else if(bs && bs.yes){
3528                     db = buttons["yes"];
3529                 }
3530                 dlg.setDefaultButton(db);
3531             }
3532             bwidth = updateButtons(opt.buttons);
3533             this.updateText(opt.msg);
3534             if(opt.cls){
3535                 d.el.addClass(opt.cls);
3536             }
3537             d.proxyDrag = opt.proxyDrag === true;
3538             d.modal = opt.modal !== false;
3539             d.mask = opt.modal !== false ? mask : false;
3540             if(!d.isVisible()){
3541                 // force it to the end of the z-index stack so it gets a cursor in FF
3542                 document.body.appendChild(dlg.el.dom);
3543                 d.animateTarget = null;
3544                 d.show(options.animEl);
3545             }
3546             return this;
3547         },
3548
3549         /**
3550          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3551          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3552          * and closing the message box when the process is complete.
3553          * @param {String} title The title bar text
3554          * @param {String} msg The message box body text
3555          * @return {Roo.MessageBox} This message box
3556          */
3557         progress : function(title, msg){
3558             this.show({
3559                 title : title,
3560                 msg : msg,
3561                 buttons: false,
3562                 progress:true,
3563                 closable:false,
3564                 minWidth: this.minProgressWidth,
3565                 modal : true
3566             });
3567             return this;
3568         },
3569
3570         /**
3571          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3572          * If a callback function is passed it will be called after the user clicks the button, and the
3573          * id of the button that was clicked will be passed as the only parameter to the callback
3574          * (could also be the top-right close button).
3575          * @param {String} title The title bar text
3576          * @param {String} msg The message box body text
3577          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3578          * @param {Object} scope (optional) The scope of the callback function
3579          * @return {Roo.MessageBox} This message box
3580          */
3581         alert : function(title, msg, fn, scope)
3582         {
3583             this.show({
3584                 title : title,
3585                 msg : msg,
3586                 buttons: this.OK,
3587                 fn: fn,
3588                 closable : false,
3589                 scope : scope,
3590                 modal : true
3591             });
3592             return this;
3593         },
3594
3595         /**
3596          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3597          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3598          * You are responsible for closing the message box when the process is complete.
3599          * @param {String} msg The message box body text
3600          * @param {String} title (optional) The title bar text
3601          * @return {Roo.MessageBox} This message box
3602          */
3603         wait : function(msg, title){
3604             this.show({
3605                 title : title,
3606                 msg : msg,
3607                 buttons: false,
3608                 closable:false,
3609                 progress:true,
3610                 modal:true,
3611                 width:300,
3612                 wait:true
3613             });
3614             waitTimer = Roo.TaskMgr.start({
3615                 run: function(i){
3616                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3617                 },
3618                 interval: 1000
3619             });
3620             return this;
3621         },
3622
3623         /**
3624          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3625          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3626          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3627          * @param {String} title The title bar text
3628          * @param {String} msg The message box body text
3629          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3630          * @param {Object} scope (optional) The scope of the callback function
3631          * @return {Roo.MessageBox} This message box
3632          */
3633         confirm : function(title, msg, fn, scope){
3634             this.show({
3635                 title : title,
3636                 msg : msg,
3637                 buttons: this.YESNO,
3638                 fn: fn,
3639                 scope : scope,
3640                 modal : true
3641             });
3642             return this;
3643         },
3644
3645         /**
3646          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3647          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3648          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3649          * (could also be the top-right close button) and the text that was entered will be passed as the two
3650          * parameters to the callback.
3651          * @param {String} title The title bar text
3652          * @param {String} msg The message box body text
3653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654          * @param {Object} scope (optional) The scope of the callback function
3655          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3656          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3657          * @return {Roo.MessageBox} This message box
3658          */
3659         prompt : function(title, msg, fn, scope, multiline){
3660             this.show({
3661                 title : title,
3662                 msg : msg,
3663                 buttons: this.OKCANCEL,
3664                 fn: fn,
3665                 minWidth:250,
3666                 scope : scope,
3667                 prompt:true,
3668                 multiline: multiline,
3669                 modal : true
3670             });
3671             return this;
3672         },
3673
3674         /**
3675          * Button config that displays a single OK button
3676          * @type Object
3677          */
3678         OK : {ok:true},
3679         /**
3680          * Button config that displays Yes and No buttons
3681          * @type Object
3682          */
3683         YESNO : {yes:true, no:true},
3684         /**
3685          * Button config that displays OK and Cancel buttons
3686          * @type Object
3687          */
3688         OKCANCEL : {ok:true, cancel:true},
3689         /**
3690          * Button config that displays Yes, No and Cancel buttons
3691          * @type Object
3692          */
3693         YESNOCANCEL : {yes:true, no:true, cancel:true},
3694
3695         /**
3696          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3697          * @type Number
3698          */
3699         defaultTextHeight : 75,
3700         /**
3701          * The maximum width in pixels of the message box (defaults to 600)
3702          * @type Number
3703          */
3704         maxWidth : 600,
3705         /**
3706          * The minimum width in pixels of the message box (defaults to 100)
3707          * @type Number
3708          */
3709         minWidth : 100,
3710         /**
3711          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3712          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3713          * @type Number
3714          */
3715         minProgressWidth : 250,
3716         /**
3717          * An object containing the default button text strings that can be overriden for localized language support.
3718          * Supported properties are: ok, cancel, yes and no.
3719          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3720          * @type Object
3721          */
3722         buttonText : {
3723             ok : "OK",
3724             cancel : "Cancel",
3725             yes : "Yes",
3726             no : "No"
3727         }
3728     };
3729 }();
3730
3731 /**
3732  * Shorthand for {@link Roo.MessageBox}
3733  */
3734 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3735 Roo.Msg = Roo.Msg || Roo.MessageBox;
3736 /*
3737  * - LGPL
3738  *
3739  * navbar
3740  * 
3741  */
3742
3743 /**
3744  * @class Roo.bootstrap.Navbar
3745  * @extends Roo.bootstrap.Component
3746  * Bootstrap Navbar class
3747
3748  * @constructor
3749  * Create a new Navbar
3750  * @param {Object} config The config object
3751  */
3752
3753
3754 Roo.bootstrap.Navbar = function(config){
3755     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3756     this.addEvents({
3757         // raw events
3758         /**
3759          * @event beforetoggle
3760          * Fire before toggle the menu
3761          * @param {Roo.EventObject} e
3762          */
3763         "beforetoggle" : true
3764     });
3765 };
3766
3767 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3768     
3769     
3770    
3771     // private
3772     navItems : false,
3773     loadMask : false,
3774     
3775     
3776     getAutoCreate : function(){
3777         
3778         
3779         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3780         
3781     },
3782     
3783     initEvents :function ()
3784     {
3785         //Roo.log(this.el.select('.navbar-toggle',true));
3786         this.el.select('.navbar-toggle',true).on('click', function() {
3787             if(this.fireEvent('beforetoggle', this) !== false){
3788                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3789             }
3790             
3791         }, this);
3792         
3793         var mark = {
3794             tag: "div",
3795             cls:"x-dlg-mask"
3796         };
3797         
3798         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3799         
3800         var size = this.el.getSize();
3801         this.maskEl.setSize(size.width, size.height);
3802         this.maskEl.enableDisplayMode("block");
3803         this.maskEl.hide();
3804         
3805         if(this.loadMask){
3806             this.maskEl.show();
3807         }
3808     },
3809     
3810     
3811     getChildContainer : function()
3812     {
3813         if (this.el.select('.collapse').getCount()) {
3814             return this.el.select('.collapse',true).first();
3815         }
3816         
3817         return this.el;
3818     },
3819     
3820     mask : function()
3821     {
3822         this.maskEl.show();
3823     },
3824     
3825     unmask : function()
3826     {
3827         this.maskEl.hide();
3828     } 
3829     
3830     
3831     
3832     
3833 });
3834
3835
3836
3837  
3838
3839  /*
3840  * - LGPL
3841  *
3842  * navbar
3843  * 
3844  */
3845
3846 /**
3847  * @class Roo.bootstrap.NavSimplebar
3848  * @extends Roo.bootstrap.Navbar
3849  * Bootstrap Sidebar class
3850  *
3851  * @cfg {Boolean} inverse is inverted color
3852  * 
3853  * @cfg {String} type (nav | pills | tabs)
3854  * @cfg {Boolean} arrangement stacked | justified
3855  * @cfg {String} align (left | right) alignment
3856  * 
3857  * @cfg {Boolean} main (true|false) main nav bar? default false
3858  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3859  * 
3860  * @cfg {String} tag (header|footer|nav|div) default is nav 
3861
3862  * 
3863  * 
3864  * 
3865  * @constructor
3866  * Create a new Sidebar
3867  * @param {Object} config The config object
3868  */
3869
3870
3871 Roo.bootstrap.NavSimplebar = function(config){
3872     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3873 };
3874
3875 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3876     
3877     inverse: false,
3878     
3879     type: false,
3880     arrangement: '',
3881     align : false,
3882     
3883     
3884     
3885     main : false,
3886     
3887     
3888     tag : false,
3889     
3890     
3891     getAutoCreate : function(){
3892         
3893         
3894         var cfg = {
3895             tag : this.tag || 'div',
3896             cls : 'navbar'
3897         };
3898           
3899         
3900         cfg.cn = [
3901             {
3902                 cls: 'nav',
3903                 tag : 'ul'
3904             }
3905         ];
3906         
3907          
3908         this.type = this.type || 'nav';
3909         if (['tabs','pills'].indexOf(this.type)!==-1) {
3910             cfg.cn[0].cls += ' nav-' + this.type
3911         
3912         
3913         } else {
3914             if (this.type!=='nav') {
3915                 Roo.log('nav type must be nav/tabs/pills')
3916             }
3917             cfg.cn[0].cls += ' navbar-nav'
3918         }
3919         
3920         
3921         
3922         
3923         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3924             cfg.cn[0].cls += ' nav-' + this.arrangement;
3925         }
3926         
3927         
3928         if (this.align === 'right') {
3929             cfg.cn[0].cls += ' navbar-right';
3930         }
3931         
3932         if (this.inverse) {
3933             cfg.cls += ' navbar-inverse';
3934             
3935         }
3936         
3937         
3938         return cfg;
3939     
3940         
3941     }
3942     
3943     
3944     
3945 });
3946
3947
3948
3949  
3950
3951  
3952        /*
3953  * - LGPL
3954  *
3955  * navbar
3956  * navbar-fixed-top
3957  * navbar-expand-md  fixed-top 
3958  */
3959
3960 /**
3961  * @class Roo.bootstrap.NavHeaderbar
3962  * @extends Roo.bootstrap.NavSimplebar
3963  * Bootstrap Sidebar class
3964  *
3965  * @cfg {String} brand what is brand
3966  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3967  * @cfg {String} brand_href href of the brand
3968  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3969  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3970  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3971  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3972  * 
3973  * @constructor
3974  * Create a new Sidebar
3975  * @param {Object} config The config object
3976  */
3977
3978
3979 Roo.bootstrap.NavHeaderbar = function(config){
3980     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3981       
3982 };
3983
3984 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3985     
3986     position: '',
3987     brand: '',
3988     brand_href: false,
3989     srButton : true,
3990     autohide : false,
3991     desktopCenter : false,
3992    
3993     
3994     getAutoCreate : function(){
3995         
3996         var   cfg = {
3997             tag: this.nav || 'nav',
3998             cls: 'navbar navbar-expand-md',
3999             role: 'navigation',
4000             cn: []
4001         };
4002         
4003         var cn = cfg.cn;
4004         if (this.desktopCenter) {
4005             cn.push({cls : 'container', cn : []});
4006             cn = cn[0].cn;
4007         }
4008         
4009         if(this.srButton){
4010             cn.push({
4011                 tag: 'div',
4012                 cls: 'navbar-header',
4013                 cn: [
4014                     {
4015                         tag: 'button',
4016                         type: 'button',
4017                         cls: 'navbar-toggle navbar-toggler',
4018                         'data-toggle': 'collapse',
4019                         cn: [
4020                             {
4021                                 tag: 'span',
4022                                 cls: 'sr-only',
4023                                 html: 'Toggle navigation'
4024                             },
4025                             {
4026                                 tag: 'span',
4027                                 cls: 'icon-bar navbar-toggler-icon'
4028                             },
4029                             {
4030                                 tag: 'span',
4031                                 cls: 'icon-bar'
4032                             },
4033                             {
4034                                 tag: 'span',
4035                                 cls: 'icon-bar'
4036                             }
4037                         ]
4038                     }
4039                 ]
4040             });
4041         }
4042         
4043         cn.push({
4044             tag: 'div',
4045             cls: 'collapse navbar-collapse',
4046             cn : []
4047         });
4048         
4049         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4050         
4051         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4052             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4053             
4054             // tag can override this..
4055             
4056             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4057         }
4058         
4059         if (this.brand !== '') {
4060             cn[0].cn.push({
4061                 tag: 'a',
4062                 href: this.brand_href ? this.brand_href : '#',
4063                 cls: 'navbar-brand',
4064                 cn: [
4065                 this.brand
4066                 ]
4067             });
4068         }
4069         
4070         if(this.main){
4071             cfg.cls += ' main-nav';
4072         }
4073         
4074         
4075         return cfg;
4076
4077         
4078     },
4079     getHeaderChildContainer : function()
4080     {
4081         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4082             return this.el.select('.navbar-header',true).first();
4083         }
4084         
4085         return this.getChildContainer();
4086     },
4087     
4088     
4089     initEvents : function()
4090     {
4091         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4092         
4093         if (this.autohide) {
4094             
4095             var prevScroll = 0;
4096             var ft = this.el;
4097             
4098             Roo.get(document).on('scroll',function(e) {
4099                 var ns = Roo.get(document).getScroll().top;
4100                 var os = prevScroll;
4101                 prevScroll = ns;
4102                 
4103                 if(ns > os){
4104                     ft.removeClass('slideDown');
4105                     ft.addClass('slideUp');
4106                     return;
4107                 }
4108                 ft.removeClass('slideUp');
4109                 ft.addClass('slideDown');
4110                  
4111               
4112           },this);
4113         }
4114     }    
4115     
4116 });
4117
4118
4119
4120  
4121
4122  /*
4123  * - LGPL
4124  *
4125  * navbar
4126  * 
4127  */
4128
4129 /**
4130  * @class Roo.bootstrap.NavSidebar
4131  * @extends Roo.bootstrap.Navbar
4132  * Bootstrap Sidebar class
4133  * 
4134  * @constructor
4135  * Create a new Sidebar
4136  * @param {Object} config The config object
4137  */
4138
4139
4140 Roo.bootstrap.NavSidebar = function(config){
4141     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4142 };
4143
4144 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4145     
4146     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4147     
4148     getAutoCreate : function(){
4149         
4150         
4151         return  {
4152             tag: 'div',
4153             cls: 'sidebar sidebar-nav'
4154         };
4155     
4156         
4157     }
4158     
4159     
4160     
4161 });
4162
4163
4164
4165  
4166
4167  /*
4168  * - LGPL
4169  *
4170  * nav group
4171  * 
4172  */
4173
4174 /**
4175  * @class Roo.bootstrap.NavGroup
4176  * @extends Roo.bootstrap.Component
4177  * Bootstrap NavGroup class
4178  * @cfg {String} align (left|right)
4179  * @cfg {Boolean} inverse
4180  * @cfg {String} type (nav|pills|tab) default nav
4181  * @cfg {String} navId - reference Id for navbar.
4182
4183  * 
4184  * @constructor
4185  * Create a new nav group
4186  * @param {Object} config The config object
4187  */
4188
4189 Roo.bootstrap.NavGroup = function(config){
4190     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4191     this.navItems = [];
4192    
4193     Roo.bootstrap.NavGroup.register(this);
4194      this.addEvents({
4195         /**
4196              * @event changed
4197              * Fires when the active item changes
4198              * @param {Roo.bootstrap.NavGroup} this
4199              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4200              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4201          */
4202         'changed': true
4203      });
4204     
4205 };
4206
4207 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4208     
4209     align: '',
4210     inverse: false,
4211     form: false,
4212     type: 'nav',
4213     navId : '',
4214     // private
4215     
4216     navItems : false, 
4217     
4218     getAutoCreate : function()
4219     {
4220         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4221         
4222         cfg = {
4223             tag : 'ul',
4224             cls: 'nav' 
4225         };
4226         
4227         if (['tabs','pills'].indexOf(this.type)!==-1) {
4228             cfg.cls += ' nav-' + this.type
4229         } else {
4230             if (this.type!=='nav') {
4231                 Roo.log('nav type must be nav/tabs/pills')
4232             }
4233             cfg.cls += ' navbar-nav mr-auto'
4234         }
4235         
4236         if (this.parent() && this.parent().sidebar) {
4237             cfg = {
4238                 tag: 'ul',
4239                 cls: 'dashboard-menu sidebar-menu'
4240             };
4241             
4242             return cfg;
4243         }
4244         
4245         if (this.form === true) {
4246             cfg = {
4247                 tag: 'form',
4248                 cls: 'navbar-form'
4249             };
4250             
4251             if (this.align === 'right') {
4252                 cfg.cls += ' navbar-right';
4253             } else {
4254                 cfg.cls += ' navbar-left';
4255             }
4256         }
4257         
4258         if (this.align === 'right') {
4259             cfg.cls += ' navbar-right';
4260         }
4261         
4262         if (this.inverse) {
4263             cfg.cls += ' navbar-inverse';
4264             
4265         }
4266         
4267         
4268         return cfg;
4269     },
4270     /**
4271     * sets the active Navigation item
4272     * @param {Roo.bootstrap.NavItem} the new current navitem
4273     */
4274     setActiveItem : function(item)
4275     {
4276         var prev = false;
4277         Roo.each(this.navItems, function(v){
4278             if (v == item) {
4279                 return ;
4280             }
4281             if (v.isActive()) {
4282                 v.setActive(false, true);
4283                 prev = v;
4284                 
4285             }
4286             
4287         });
4288
4289         item.setActive(true, true);
4290         this.fireEvent('changed', this, item, prev);
4291         
4292         
4293     },
4294     /**
4295     * gets the active Navigation item
4296     * @return {Roo.bootstrap.NavItem} the current navitem
4297     */
4298     getActive : function()
4299     {
4300         
4301         var prev = false;
4302         Roo.each(this.navItems, function(v){
4303             
4304             if (v.isActive()) {
4305                 prev = v;
4306                 
4307             }
4308             
4309         });
4310         return prev;
4311     },
4312     
4313     indexOfNav : function()
4314     {
4315         
4316         var prev = false;
4317         Roo.each(this.navItems, function(v,i){
4318             
4319             if (v.isActive()) {
4320                 prev = i;
4321                 
4322             }
4323             
4324         });
4325         return prev;
4326     },
4327     /**
4328     * adds a Navigation item
4329     * @param {Roo.bootstrap.NavItem} the navitem to add
4330     */
4331     addItem : function(cfg)
4332     {
4333         var cn = new Roo.bootstrap.NavItem(cfg);
4334         this.register(cn);
4335         cn.parentId = this.id;
4336         cn.onRender(this.el, null);
4337         return cn;
4338     },
4339     /**
4340     * register a Navigation item
4341     * @param {Roo.bootstrap.NavItem} the navitem to add
4342     */
4343     register : function(item)
4344     {
4345         this.navItems.push( item);
4346         item.navId = this.navId;
4347     
4348     },
4349     
4350     /**
4351     * clear all the Navigation item
4352     */
4353    
4354     clearAll : function()
4355     {
4356         this.navItems = [];
4357         this.el.dom.innerHTML = '';
4358     },
4359     
4360     getNavItem: function(tabId)
4361     {
4362         var ret = false;
4363         Roo.each(this.navItems, function(e) {
4364             if (e.tabId == tabId) {
4365                ret =  e;
4366                return false;
4367             }
4368             return true;
4369             
4370         });
4371         return ret;
4372     },
4373     
4374     setActiveNext : function()
4375     {
4376         var i = this.indexOfNav(this.getActive());
4377         if (i > this.navItems.length) {
4378             return;
4379         }
4380         this.setActiveItem(this.navItems[i+1]);
4381     },
4382     setActivePrev : function()
4383     {
4384         var i = this.indexOfNav(this.getActive());
4385         if (i  < 1) {
4386             return;
4387         }
4388         this.setActiveItem(this.navItems[i-1]);
4389     },
4390     clearWasActive : function(except) {
4391         Roo.each(this.navItems, function(e) {
4392             if (e.tabId != except.tabId && e.was_active) {
4393                e.was_active = false;
4394                return false;
4395             }
4396             return true;
4397             
4398         });
4399     },
4400     getWasActive : function ()
4401     {
4402         var r = false;
4403         Roo.each(this.navItems, function(e) {
4404             if (e.was_active) {
4405                r = e;
4406                return false;
4407             }
4408             return true;
4409             
4410         });
4411         return r;
4412     }
4413     
4414     
4415 });
4416
4417  
4418 Roo.apply(Roo.bootstrap.NavGroup, {
4419     
4420     groups: {},
4421      /**
4422     * register a Navigation Group
4423     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4424     */
4425     register : function(navgrp)
4426     {
4427         this.groups[navgrp.navId] = navgrp;
4428         
4429     },
4430     /**
4431     * fetch a Navigation Group based on the navigation ID
4432     * @param {string} the navgroup to add
4433     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4434     */
4435     get: function(navId) {
4436         if (typeof(this.groups[navId]) == 'undefined') {
4437             return false;
4438             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4439         }
4440         return this.groups[navId] ;
4441     }
4442     
4443     
4444     
4445 });
4446
4447  /*
4448  * - LGPL
4449  *
4450  * row
4451  * 
4452  */
4453
4454 /**
4455  * @class Roo.bootstrap.NavItem
4456  * @extends Roo.bootstrap.Component
4457  * Bootstrap Navbar.NavItem class
4458  * @cfg {String} href  link to
4459  * @cfg {String} html content of button
4460  * @cfg {String} badge text inside badge
4461  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4462  * @cfg {String} glyphicon name of glyphicon
4463  * @cfg {String} icon name of font awesome icon
4464  * @cfg {Boolean} active Is item active
4465  * @cfg {Boolean} disabled Is item disabled
4466  
4467  * @cfg {Boolean} preventDefault (true | false) default false
4468  * @cfg {String} tabId the tab that this item activates.
4469  * @cfg {String} tagtype (a|span) render as a href or span?
4470  * @cfg {Boolean} animateRef (true|false) link to element default false  
4471   
4472  * @constructor
4473  * Create a new Navbar Item
4474  * @param {Object} config The config object
4475  */
4476 Roo.bootstrap.NavItem = function(config){
4477     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4478     this.addEvents({
4479         // raw events
4480         /**
4481          * @event click
4482          * The raw click event for the entire grid.
4483          * @param {Roo.EventObject} e
4484          */
4485         "click" : true,
4486          /**
4487             * @event changed
4488             * Fires when the active item active state changes
4489             * @param {Roo.bootstrap.NavItem} this
4490             * @param {boolean} state the new state
4491              
4492          */
4493         'changed': true,
4494         /**
4495             * @event scrollto
4496             * Fires when scroll to element
4497             * @param {Roo.bootstrap.NavItem} this
4498             * @param {Object} options
4499             * @param {Roo.EventObject} e
4500              
4501          */
4502         'scrollto': true
4503     });
4504    
4505 };
4506
4507 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4508     
4509     href: false,
4510     html: '',
4511     badge: '',
4512     icon: false,
4513     glyphicon: false,
4514     active: false,
4515     preventDefault : false,
4516     tabId : false,
4517     tagtype : 'a',
4518     disabled : false,
4519     animateRef : false,
4520     was_active : false,
4521     
4522     getAutoCreate : function(){
4523          
4524         var cfg = {
4525             tag: 'li',
4526             cls: 'nav-item'
4527             
4528         };
4529         
4530         if (this.active) {
4531             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4532         }
4533         if (this.disabled) {
4534             cfg.cls += ' disabled';
4535         }
4536         
4537         if (this.href || this.html || this.glyphicon || this.icon) {
4538             cfg.cn = [
4539                 {
4540                     tag: this.tagtype,
4541                     href : this.href || "#",
4542                     html: this.html || ''
4543                 }
4544             ];
4545             if (this.tagtype == 'a') {
4546                 cfg.cn[0].cls = 'nav-link';
4547             }
4548             if (this.icon) {
4549                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4550             }
4551
4552             if(this.glyphicon) {
4553                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4554             }
4555             
4556             if (this.menu) {
4557                 
4558                 cfg.cn[0].html += " <span class='caret'></span>";
4559              
4560             }
4561             
4562             if (this.badge !== '') {
4563                  
4564                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4565             }
4566         }
4567         
4568         
4569         
4570         return cfg;
4571     },
4572     initEvents: function() 
4573     {
4574         if (typeof (this.menu) != 'undefined') {
4575             this.menu.parentType = this.xtype;
4576             this.menu.triggerEl = this.el;
4577             this.menu = this.addxtype(Roo.apply({}, this.menu));
4578         }
4579         
4580         this.el.select('a',true).on('click', this.onClick, this);
4581         
4582         if(this.tagtype == 'span'){
4583             this.el.select('span',true).on('click', this.onClick, this);
4584         }
4585        
4586         // at this point parent should be available..
4587         this.parent().register(this);
4588     },
4589     
4590     onClick : function(e)
4591     {
4592         if (e.getTarget('.dropdown-menu-item')) {
4593             // did you click on a menu itemm.... - then don't trigger onclick..
4594             return;
4595         }
4596         
4597         if(
4598                 this.preventDefault || 
4599                 this.href == '#' 
4600         ){
4601             Roo.log("NavItem - prevent Default?");
4602             e.preventDefault();
4603         }
4604         
4605         if (this.disabled) {
4606             return;
4607         }
4608         
4609         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4610         if (tg && tg.transition) {
4611             Roo.log("waiting for the transitionend");
4612             return;
4613         }
4614         
4615         
4616         
4617         //Roo.log("fire event clicked");
4618         if(this.fireEvent('click', this, e) === false){
4619             return;
4620         };
4621         
4622         if(this.tagtype == 'span'){
4623             return;
4624         }
4625         
4626         //Roo.log(this.href);
4627         var ael = this.el.select('a',true).first();
4628         //Roo.log(ael);
4629         
4630         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4631             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4632             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4633                 return; // ignore... - it's a 'hash' to another page.
4634             }
4635             Roo.log("NavItem - prevent Default?");
4636             e.preventDefault();
4637             this.scrollToElement(e);
4638         }
4639         
4640         
4641         var p =  this.parent();
4642    
4643         if (['tabs','pills'].indexOf(p.type)!==-1) {
4644             if (typeof(p.setActiveItem) !== 'undefined') {
4645                 p.setActiveItem(this);
4646             }
4647         }
4648         
4649         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4650         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4651             // remove the collapsed menu expand...
4652             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4653         }
4654     },
4655     
4656     isActive: function () {
4657         return this.active
4658     },
4659     setActive : function(state, fire, is_was_active)
4660     {
4661         if (this.active && !state && this.navId) {
4662             this.was_active = true;
4663             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4664             if (nv) {
4665                 nv.clearWasActive(this);
4666             }
4667             
4668         }
4669         this.active = state;
4670         
4671         if (!state ) {
4672             this.el.removeClass('active');
4673         } else if (!this.el.hasClass('active')) {
4674             this.el.addClass('active');
4675         }
4676         if (fire) {
4677             this.fireEvent('changed', this, state);
4678         }
4679         
4680         // show a panel if it's registered and related..
4681         
4682         if (!this.navId || !this.tabId || !state || is_was_active) {
4683             return;
4684         }
4685         
4686         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4687         if (!tg) {
4688             return;
4689         }
4690         var pan = tg.getPanelByName(this.tabId);
4691         if (!pan) {
4692             return;
4693         }
4694         // if we can not flip to new panel - go back to old nav highlight..
4695         if (false == tg.showPanel(pan)) {
4696             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4697             if (nv) {
4698                 var onav = nv.getWasActive();
4699                 if (onav) {
4700                     onav.setActive(true, false, true);
4701                 }
4702             }
4703             
4704         }
4705         
4706         
4707         
4708     },
4709      // this should not be here...
4710     setDisabled : function(state)
4711     {
4712         this.disabled = state;
4713         if (!state ) {
4714             this.el.removeClass('disabled');
4715         } else if (!this.el.hasClass('disabled')) {
4716             this.el.addClass('disabled');
4717         }
4718         
4719     },
4720     
4721     /**
4722      * Fetch the element to display the tooltip on.
4723      * @return {Roo.Element} defaults to this.el
4724      */
4725     tooltipEl : function()
4726     {
4727         return this.el.select('' + this.tagtype + '', true).first();
4728     },
4729     
4730     scrollToElement : function(e)
4731     {
4732         var c = document.body;
4733         
4734         /*
4735          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4736          */
4737         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4738             c = document.documentElement;
4739         }
4740         
4741         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4742         
4743         if(!target){
4744             return;
4745         }
4746
4747         var o = target.calcOffsetsTo(c);
4748         
4749         var options = {
4750             target : target,
4751             value : o[1]
4752         };
4753         
4754         this.fireEvent('scrollto', this, options, e);
4755         
4756         Roo.get(c).scrollTo('top', options.value, true);
4757         
4758         return;
4759     }
4760 });
4761  
4762
4763  /*
4764  * - LGPL
4765  *
4766  * sidebar item
4767  *
4768  *  li
4769  *    <span> icon </span>
4770  *    <span> text </span>
4771  *    <span>badge </span>
4772  */
4773
4774 /**
4775  * @class Roo.bootstrap.NavSidebarItem
4776  * @extends Roo.bootstrap.NavItem
4777  * Bootstrap Navbar.NavSidebarItem class
4778  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4779  * {Boolean} open is the menu open
4780  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4781  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4782  * {String} buttonSize (sm|md|lg)the extra classes for the button
4783  * {Boolean} showArrow show arrow next to the text (default true)
4784  * @constructor
4785  * Create a new Navbar Button
4786  * @param {Object} config The config object
4787  */
4788 Roo.bootstrap.NavSidebarItem = function(config){
4789     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4790     this.addEvents({
4791         // raw events
4792         /**
4793          * @event click
4794          * The raw click event for the entire grid.
4795          * @param {Roo.EventObject} e
4796          */
4797         "click" : true,
4798          /**
4799             * @event changed
4800             * Fires when the active item active state changes
4801             * @param {Roo.bootstrap.NavSidebarItem} this
4802             * @param {boolean} state the new state
4803              
4804          */
4805         'changed': true
4806     });
4807    
4808 };
4809
4810 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4811     
4812     badgeWeight : 'default',
4813     
4814     open: false,
4815     
4816     buttonView : false,
4817     
4818     buttonWeight : 'default',
4819     
4820     buttonSize : 'md',
4821     
4822     showArrow : true,
4823     
4824     getAutoCreate : function(){
4825         
4826         
4827         var a = {
4828                 tag: 'a',
4829                 href : this.href || '#',
4830                 cls: '',
4831                 html : '',
4832                 cn : []
4833         };
4834         
4835         if(this.buttonView){
4836             a = {
4837                 tag: 'button',
4838                 href : this.href || '#',
4839                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4840                 html : this.html,
4841                 cn : []
4842             };
4843         }
4844         
4845         var cfg = {
4846             tag: 'li',
4847             cls: '',
4848             cn: [ a ]
4849         };
4850         
4851         if (this.active) {
4852             cfg.cls += ' active';
4853         }
4854         
4855         if (this.disabled) {
4856             cfg.cls += ' disabled';
4857         }
4858         if (this.open) {
4859             cfg.cls += ' open x-open';
4860         }
4861         // left icon..
4862         if (this.glyphicon || this.icon) {
4863             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4864             a.cn.push({ tag : 'i', cls : c }) ;
4865         }
4866         
4867         if(!this.buttonView){
4868             var span = {
4869                 tag: 'span',
4870                 html : this.html || ''
4871             };
4872
4873             a.cn.push(span);
4874             
4875         }
4876         
4877         if (this.badge !== '') {
4878             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4879         }
4880         
4881         if (this.menu) {
4882             
4883             if(this.showArrow){
4884                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4885             }
4886             
4887             a.cls += ' dropdown-toggle treeview' ;
4888         }
4889         
4890         return cfg;
4891     },
4892     
4893     initEvents : function()
4894     { 
4895         if (typeof (this.menu) != 'undefined') {
4896             this.menu.parentType = this.xtype;
4897             this.menu.triggerEl = this.el;
4898             this.menu = this.addxtype(Roo.apply({}, this.menu));
4899         }
4900         
4901         this.el.on('click', this.onClick, this);
4902         
4903         if(this.badge !== ''){
4904             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4905         }
4906         
4907     },
4908     
4909     onClick : function(e)
4910     {
4911         if(this.disabled){
4912             e.preventDefault();
4913             return;
4914         }
4915         
4916         if(this.preventDefault){
4917             e.preventDefault();
4918         }
4919         
4920         this.fireEvent('click', this);
4921     },
4922     
4923     disable : function()
4924     {
4925         this.setDisabled(true);
4926     },
4927     
4928     enable : function()
4929     {
4930         this.setDisabled(false);
4931     },
4932     
4933     setDisabled : function(state)
4934     {
4935         if(this.disabled == state){
4936             return;
4937         }
4938         
4939         this.disabled = state;
4940         
4941         if (state) {
4942             this.el.addClass('disabled');
4943             return;
4944         }
4945         
4946         this.el.removeClass('disabled');
4947         
4948         return;
4949     },
4950     
4951     setActive : function(state)
4952     {
4953         if(this.active == state){
4954             return;
4955         }
4956         
4957         this.active = state;
4958         
4959         if (state) {
4960             this.el.addClass('active');
4961             return;
4962         }
4963         
4964         this.el.removeClass('active');
4965         
4966         return;
4967     },
4968     
4969     isActive: function () 
4970     {
4971         return this.active;
4972     },
4973     
4974     setBadge : function(str)
4975     {
4976         if(!this.badgeEl){
4977             return;
4978         }
4979         
4980         this.badgeEl.dom.innerHTML = str;
4981     }
4982     
4983    
4984      
4985  
4986 });
4987  
4988
4989  /*
4990  * - LGPL
4991  *
4992  * row
4993  * 
4994  */
4995
4996 /**
4997  * @class Roo.bootstrap.Row
4998  * @extends Roo.bootstrap.Component
4999  * Bootstrap Row class (contains columns...)
5000  * 
5001  * @constructor
5002  * Create a new Row
5003  * @param {Object} config The config object
5004  */
5005
5006 Roo.bootstrap.Row = function(config){
5007     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5008 };
5009
5010 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5011     
5012     getAutoCreate : function(){
5013        return {
5014             cls: 'row clearfix'
5015        };
5016     }
5017     
5018     
5019 });
5020
5021  
5022
5023  /*
5024  * - LGPL
5025  *
5026  * element
5027  * 
5028  */
5029
5030 /**
5031  * @class Roo.bootstrap.Element
5032  * @extends Roo.bootstrap.Component
5033  * Bootstrap Element class
5034  * @cfg {String} html contents of the element
5035  * @cfg {String} tag tag of the element
5036  * @cfg {String} cls class of the element
5037  * @cfg {Boolean} preventDefault (true|false) default false
5038  * @cfg {Boolean} clickable (true|false) default false
5039  * 
5040  * @constructor
5041  * Create a new Element
5042  * @param {Object} config The config object
5043  */
5044
5045 Roo.bootstrap.Element = function(config){
5046     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5047     
5048     this.addEvents({
5049         // raw events
5050         /**
5051          * @event click
5052          * When a element is chick
5053          * @param {Roo.bootstrap.Element} this
5054          * @param {Roo.EventObject} e
5055          */
5056         "click" : true
5057     });
5058 };
5059
5060 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5061     
5062     tag: 'div',
5063     cls: '',
5064     html: '',
5065     preventDefault: false, 
5066     clickable: false,
5067     
5068     getAutoCreate : function(){
5069         
5070         var cfg = {
5071             tag: this.tag,
5072             // cls: this.cls, double assign in parent class Component.js :: onRender
5073             html: this.html
5074         };
5075         
5076         return cfg;
5077     },
5078     
5079     initEvents: function() 
5080     {
5081         Roo.bootstrap.Element.superclass.initEvents.call(this);
5082         
5083         if(this.clickable){
5084             this.el.on('click', this.onClick, this);
5085         }
5086         
5087     },
5088     
5089     onClick : function(e)
5090     {
5091         if(this.preventDefault){
5092             e.preventDefault();
5093         }
5094         
5095         this.fireEvent('click', this, e);
5096     },
5097     
5098     getValue : function()
5099     {
5100         return this.el.dom.innerHTML;
5101     },
5102     
5103     setValue : function(value)
5104     {
5105         this.el.dom.innerHTML = value;
5106     }
5107    
5108 });
5109
5110  
5111
5112  /*
5113  * - LGPL
5114  *
5115  * pagination
5116  * 
5117  */
5118
5119 /**
5120  * @class Roo.bootstrap.Pagination
5121  * @extends Roo.bootstrap.Component
5122  * Bootstrap Pagination class
5123  * @cfg {String} size xs | sm | md | lg
5124  * @cfg {Boolean} inverse false | true
5125  * 
5126  * @constructor
5127  * Create a new Pagination
5128  * @param {Object} config The config object
5129  */
5130
5131 Roo.bootstrap.Pagination = function(config){
5132     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5133 };
5134
5135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5136     
5137     cls: false,
5138     size: false,
5139     inverse: false,
5140     
5141     getAutoCreate : function(){
5142         var cfg = {
5143             tag: 'ul',
5144                 cls: 'pagination'
5145         };
5146         if (this.inverse) {
5147             cfg.cls += ' inverse';
5148         }
5149         if (this.html) {
5150             cfg.html=this.html;
5151         }
5152         if (this.cls) {
5153             cfg.cls += " " + this.cls;
5154         }
5155         return cfg;
5156     }
5157    
5158 });
5159
5160  
5161
5162  /*
5163  * - LGPL
5164  *
5165  * Pagination item
5166  * 
5167  */
5168
5169
5170 /**
5171  * @class Roo.bootstrap.PaginationItem
5172  * @extends Roo.bootstrap.Component
5173  * Bootstrap PaginationItem class
5174  * @cfg {String} html text
5175  * @cfg {String} href the link
5176  * @cfg {Boolean} preventDefault (true | false) default true
5177  * @cfg {Boolean} active (true | false) default false
5178  * @cfg {Boolean} disabled default false
5179  * 
5180  * 
5181  * @constructor
5182  * Create a new PaginationItem
5183  * @param {Object} config The config object
5184  */
5185
5186
5187 Roo.bootstrap.PaginationItem = function(config){
5188     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5189     this.addEvents({
5190         // raw events
5191         /**
5192          * @event click
5193          * The raw click event for the entire grid.
5194          * @param {Roo.EventObject} e
5195          */
5196         "click" : true
5197     });
5198 };
5199
5200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5201     
5202     href : false,
5203     html : false,
5204     preventDefault: true,
5205     active : false,
5206     cls : false,
5207     disabled: false,
5208     
5209     getAutoCreate : function(){
5210         var cfg= {
5211             tag: 'li',
5212             cn: [
5213                 {
5214                     tag : 'a',
5215                     href : this.href ? this.href : '#',
5216                     html : this.html ? this.html : ''
5217                 }
5218             ]
5219         };
5220         
5221         if(this.cls){
5222             cfg.cls = this.cls;
5223         }
5224         
5225         if(this.disabled){
5226             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5227         }
5228         
5229         if(this.active){
5230             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5231         }
5232         
5233         return cfg;
5234     },
5235     
5236     initEvents: function() {
5237         
5238         this.el.on('click', this.onClick, this);
5239         
5240     },
5241     onClick : function(e)
5242     {
5243         Roo.log('PaginationItem on click ');
5244         if(this.preventDefault){
5245             e.preventDefault();
5246         }
5247         
5248         if(this.disabled){
5249             return;
5250         }
5251         
5252         this.fireEvent('click', this, e);
5253     }
5254    
5255 });
5256
5257  
5258
5259  /*
5260  * - LGPL
5261  *
5262  * slider
5263  * 
5264  */
5265
5266
5267 /**
5268  * @class Roo.bootstrap.Slider
5269  * @extends Roo.bootstrap.Component
5270  * Bootstrap Slider class
5271  *    
5272  * @constructor
5273  * Create a new Slider
5274  * @param {Object} config The config object
5275  */
5276
5277 Roo.bootstrap.Slider = function(config){
5278     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5279 };
5280
5281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5282     
5283     getAutoCreate : function(){
5284         
5285         var cfg = {
5286             tag: 'div',
5287             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5288             cn: [
5289                 {
5290                     tag: 'a',
5291                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5292                 }
5293             ]
5294         };
5295         
5296         return cfg;
5297     }
5298    
5299 });
5300
5301  /*
5302  * Based on:
5303  * Ext JS Library 1.1.1
5304  * Copyright(c) 2006-2007, Ext JS, LLC.
5305  *
5306  * Originally Released Under LGPL - original licence link has changed is not relivant.
5307  *
5308  * Fork - LGPL
5309  * <script type="text/javascript">
5310  */
5311  
5312
5313 /**
5314  * @class Roo.grid.ColumnModel
5315  * @extends Roo.util.Observable
5316  * This is the default implementation of a ColumnModel used by the Grid. It defines
5317  * the columns in the grid.
5318  * <br>Usage:<br>
5319  <pre><code>
5320  var colModel = new Roo.grid.ColumnModel([
5321         {header: "Ticker", width: 60, sortable: true, locked: true},
5322         {header: "Company Name", width: 150, sortable: true},
5323         {header: "Market Cap.", width: 100, sortable: true},
5324         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5325         {header: "Employees", width: 100, sortable: true, resizable: false}
5326  ]);
5327  </code></pre>
5328  * <p>
5329  
5330  * The config options listed for this class are options which may appear in each
5331  * individual column definition.
5332  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5333  * @constructor
5334  * @param {Object} config An Array of column config objects. See this class's
5335  * config objects for details.
5336 */
5337 Roo.grid.ColumnModel = function(config){
5338         /**
5339      * The config passed into the constructor
5340      */
5341     this.config = config;
5342     this.lookup = {};
5343
5344     // if no id, create one
5345     // if the column does not have a dataIndex mapping,
5346     // map it to the order it is in the config
5347     for(var i = 0, len = config.length; i < len; i++){
5348         var c = config[i];
5349         if(typeof c.dataIndex == "undefined"){
5350             c.dataIndex = i;
5351         }
5352         if(typeof c.renderer == "string"){
5353             c.renderer = Roo.util.Format[c.renderer];
5354         }
5355         if(typeof c.id == "undefined"){
5356             c.id = Roo.id();
5357         }
5358         if(c.editor && c.editor.xtype){
5359             c.editor  = Roo.factory(c.editor, Roo.grid);
5360         }
5361         if(c.editor && c.editor.isFormField){
5362             c.editor = new Roo.grid.GridEditor(c.editor);
5363         }
5364         this.lookup[c.id] = c;
5365     }
5366
5367     /**
5368      * The width of columns which have no width specified (defaults to 100)
5369      * @type Number
5370      */
5371     this.defaultWidth = 100;
5372
5373     /**
5374      * Default sortable of columns which have no sortable specified (defaults to false)
5375      * @type Boolean
5376      */
5377     this.defaultSortable = false;
5378
5379     this.addEvents({
5380         /**
5381              * @event widthchange
5382              * Fires when the width of a column changes.
5383              * @param {ColumnModel} this
5384              * @param {Number} columnIndex The column index
5385              * @param {Number} newWidth The new width
5386              */
5387             "widthchange": true,
5388         /**
5389              * @event headerchange
5390              * Fires when the text of a header changes.
5391              * @param {ColumnModel} this
5392              * @param {Number} columnIndex The column index
5393              * @param {Number} newText The new header text
5394              */
5395             "headerchange": true,
5396         /**
5397              * @event hiddenchange
5398              * Fires when a column is hidden or "unhidden".
5399              * @param {ColumnModel} this
5400              * @param {Number} columnIndex The column index
5401              * @param {Boolean} hidden true if hidden, false otherwise
5402              */
5403             "hiddenchange": true,
5404             /**
5405          * @event columnmoved
5406          * Fires when a column is moved.
5407          * @param {ColumnModel} this
5408          * @param {Number} oldIndex
5409          * @param {Number} newIndex
5410          */
5411         "columnmoved" : true,
5412         /**
5413          * @event columlockchange
5414          * Fires when a column's locked state is changed
5415          * @param {ColumnModel} this
5416          * @param {Number} colIndex
5417          * @param {Boolean} locked true if locked
5418          */
5419         "columnlockchange" : true
5420     });
5421     Roo.grid.ColumnModel.superclass.constructor.call(this);
5422 };
5423 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5424     /**
5425      * @cfg {String} header The header text to display in the Grid view.
5426      */
5427     /**
5428      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5429      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5430      * specified, the column's index is used as an index into the Record's data Array.
5431      */
5432     /**
5433      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5434      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5435      */
5436     /**
5437      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5438      * Defaults to the value of the {@link #defaultSortable} property.
5439      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5440      */
5441     /**
5442      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5443      */
5444     /**
5445      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5446      */
5447     /**
5448      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5449      */
5450     /**
5451      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5452      */
5453     /**
5454      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5455      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5456      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5457      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5458      */
5459        /**
5460      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5461      */
5462     /**
5463      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5464      */
5465     /**
5466      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5467      */
5468     /**
5469      * @cfg {String} cursor (Optional)
5470      */
5471     /**
5472      * @cfg {String} tooltip (Optional)
5473      */
5474     /**
5475      * @cfg {Number} xs (Optional)
5476      */
5477     /**
5478      * @cfg {Number} sm (Optional)
5479      */
5480     /**
5481      * @cfg {Number} md (Optional)
5482      */
5483     /**
5484      * @cfg {Number} lg (Optional)
5485      */
5486     /**
5487      * Returns the id of the column at the specified index.
5488      * @param {Number} index The column index
5489      * @return {String} the id
5490      */
5491     getColumnId : function(index){
5492         return this.config[index].id;
5493     },
5494
5495     /**
5496      * Returns the column for a specified id.
5497      * @param {String} id The column id
5498      * @return {Object} the column
5499      */
5500     getColumnById : function(id){
5501         return this.lookup[id];
5502     },
5503
5504     
5505     /**
5506      * Returns the column for a specified dataIndex.
5507      * @param {String} dataIndex The column dataIndex
5508      * @return {Object|Boolean} the column or false if not found
5509      */
5510     getColumnByDataIndex: function(dataIndex){
5511         var index = this.findColumnIndex(dataIndex);
5512         return index > -1 ? this.config[index] : false;
5513     },
5514     
5515     /**
5516      * Returns the index for a specified column id.
5517      * @param {String} id The column id
5518      * @return {Number} the index, or -1 if not found
5519      */
5520     getIndexById : function(id){
5521         for(var i = 0, len = this.config.length; i < len; i++){
5522             if(this.config[i].id == id){
5523                 return i;
5524             }
5525         }
5526         return -1;
5527     },
5528     
5529     /**
5530      * Returns the index for a specified column dataIndex.
5531      * @param {String} dataIndex The column dataIndex
5532      * @return {Number} the index, or -1 if not found
5533      */
5534     
5535     findColumnIndex : function(dataIndex){
5536         for(var i = 0, len = this.config.length; i < len; i++){
5537             if(this.config[i].dataIndex == dataIndex){
5538                 return i;
5539             }
5540         }
5541         return -1;
5542     },
5543     
5544     
5545     moveColumn : function(oldIndex, newIndex){
5546         var c = this.config[oldIndex];
5547         this.config.splice(oldIndex, 1);
5548         this.config.splice(newIndex, 0, c);
5549         this.dataMap = null;
5550         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5551     },
5552
5553     isLocked : function(colIndex){
5554         return this.config[colIndex].locked === true;
5555     },
5556
5557     setLocked : function(colIndex, value, suppressEvent){
5558         if(this.isLocked(colIndex) == value){
5559             return;
5560         }
5561         this.config[colIndex].locked = value;
5562         if(!suppressEvent){
5563             this.fireEvent("columnlockchange", this, colIndex, value);
5564         }
5565     },
5566
5567     getTotalLockedWidth : function(){
5568         var totalWidth = 0;
5569         for(var i = 0; i < this.config.length; i++){
5570             if(this.isLocked(i) && !this.isHidden(i)){
5571                 this.totalWidth += this.getColumnWidth(i);
5572             }
5573         }
5574         return totalWidth;
5575     },
5576
5577     getLockedCount : function(){
5578         for(var i = 0, len = this.config.length; i < len; i++){
5579             if(!this.isLocked(i)){
5580                 return i;
5581             }
5582         }
5583         
5584         return this.config.length;
5585     },
5586
5587     /**
5588      * Returns the number of columns.
5589      * @return {Number}
5590      */
5591     getColumnCount : function(visibleOnly){
5592         if(visibleOnly === true){
5593             var c = 0;
5594             for(var i = 0, len = this.config.length; i < len; i++){
5595                 if(!this.isHidden(i)){
5596                     c++;
5597                 }
5598             }
5599             return c;
5600         }
5601         return this.config.length;
5602     },
5603
5604     /**
5605      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5606      * @param {Function} fn
5607      * @param {Object} scope (optional)
5608      * @return {Array} result
5609      */
5610     getColumnsBy : function(fn, scope){
5611         var r = [];
5612         for(var i = 0, len = this.config.length; i < len; i++){
5613             var c = this.config[i];
5614             if(fn.call(scope||this, c, i) === true){
5615                 r[r.length] = c;
5616             }
5617         }
5618         return r;
5619     },
5620
5621     /**
5622      * Returns true if the specified column is sortable.
5623      * @param {Number} col The column index
5624      * @return {Boolean}
5625      */
5626     isSortable : function(col){
5627         if(typeof this.config[col].sortable == "undefined"){
5628             return this.defaultSortable;
5629         }
5630         return this.config[col].sortable;
5631     },
5632
5633     /**
5634      * Returns the rendering (formatting) function defined for the column.
5635      * @param {Number} col The column index.
5636      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5637      */
5638     getRenderer : function(col){
5639         if(!this.config[col].renderer){
5640             return Roo.grid.ColumnModel.defaultRenderer;
5641         }
5642         return this.config[col].renderer;
5643     },
5644
5645     /**
5646      * Sets the rendering (formatting) function for a column.
5647      * @param {Number} col The column index
5648      * @param {Function} fn The function to use to process the cell's raw data
5649      * to return HTML markup for the grid view. The render function is called with
5650      * the following parameters:<ul>
5651      * <li>Data value.</li>
5652      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5653      * <li>css A CSS style string to apply to the table cell.</li>
5654      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5655      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5656      * <li>Row index</li>
5657      * <li>Column index</li>
5658      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5659      */
5660     setRenderer : function(col, fn){
5661         this.config[col].renderer = fn;
5662     },
5663
5664     /**
5665      * Returns the width for the specified column.
5666      * @param {Number} col The column index
5667      * @return {Number}
5668      */
5669     getColumnWidth : function(col){
5670         return this.config[col].width * 1 || this.defaultWidth;
5671     },
5672
5673     /**
5674      * Sets the width for a column.
5675      * @param {Number} col The column index
5676      * @param {Number} width The new width
5677      */
5678     setColumnWidth : function(col, width, suppressEvent){
5679         this.config[col].width = width;
5680         this.totalWidth = null;
5681         if(!suppressEvent){
5682              this.fireEvent("widthchange", this, col, width);
5683         }
5684     },
5685
5686     /**
5687      * Returns the total width of all columns.
5688      * @param {Boolean} includeHidden True to include hidden column widths
5689      * @return {Number}
5690      */
5691     getTotalWidth : function(includeHidden){
5692         if(!this.totalWidth){
5693             this.totalWidth = 0;
5694             for(var i = 0, len = this.config.length; i < len; i++){
5695                 if(includeHidden || !this.isHidden(i)){
5696                     this.totalWidth += this.getColumnWidth(i);
5697                 }
5698             }
5699         }
5700         return this.totalWidth;
5701     },
5702
5703     /**
5704      * Returns the header for the specified column.
5705      * @param {Number} col The column index
5706      * @return {String}
5707      */
5708     getColumnHeader : function(col){
5709         return this.config[col].header;
5710     },
5711
5712     /**
5713      * Sets the header for a column.
5714      * @param {Number} col The column index
5715      * @param {String} header The new header
5716      */
5717     setColumnHeader : function(col, header){
5718         this.config[col].header = header;
5719         this.fireEvent("headerchange", this, col, header);
5720     },
5721
5722     /**
5723      * Returns the tooltip for the specified column.
5724      * @param {Number} col The column index
5725      * @return {String}
5726      */
5727     getColumnTooltip : function(col){
5728             return this.config[col].tooltip;
5729     },
5730     /**
5731      * Sets the tooltip for a column.
5732      * @param {Number} col The column index
5733      * @param {String} tooltip The new tooltip
5734      */
5735     setColumnTooltip : function(col, tooltip){
5736             this.config[col].tooltip = tooltip;
5737     },
5738
5739     /**
5740      * Returns the dataIndex for the specified column.
5741      * @param {Number} col The column index
5742      * @return {Number}
5743      */
5744     getDataIndex : function(col){
5745         return this.config[col].dataIndex;
5746     },
5747
5748     /**
5749      * Sets the dataIndex for a column.
5750      * @param {Number} col The column index
5751      * @param {Number} dataIndex The new dataIndex
5752      */
5753     setDataIndex : function(col, dataIndex){
5754         this.config[col].dataIndex = dataIndex;
5755     },
5756
5757     
5758     
5759     /**
5760      * Returns true if the cell is editable.
5761      * @param {Number} colIndex The column index
5762      * @param {Number} rowIndex The row index - this is nto actually used..?
5763      * @return {Boolean}
5764      */
5765     isCellEditable : function(colIndex, rowIndex){
5766         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5767     },
5768
5769     /**
5770      * Returns the editor defined for the cell/column.
5771      * return false or null to disable editing.
5772      * @param {Number} colIndex The column index
5773      * @param {Number} rowIndex The row index
5774      * @return {Object}
5775      */
5776     getCellEditor : function(colIndex, rowIndex){
5777         return this.config[colIndex].editor;
5778     },
5779
5780     /**
5781      * Sets if a column is editable.
5782      * @param {Number} col The column index
5783      * @param {Boolean} editable True if the column is editable
5784      */
5785     setEditable : function(col, editable){
5786         this.config[col].editable = editable;
5787     },
5788
5789
5790     /**
5791      * Returns true if the column is hidden.
5792      * @param {Number} colIndex The column index
5793      * @return {Boolean}
5794      */
5795     isHidden : function(colIndex){
5796         return this.config[colIndex].hidden;
5797     },
5798
5799
5800     /**
5801      * Returns true if the column width cannot be changed
5802      */
5803     isFixed : function(colIndex){
5804         return this.config[colIndex].fixed;
5805     },
5806
5807     /**
5808      * Returns true if the column can be resized
5809      * @return {Boolean}
5810      */
5811     isResizable : function(colIndex){
5812         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5813     },
5814     /**
5815      * Sets if a column is hidden.
5816      * @param {Number} colIndex The column index
5817      * @param {Boolean} hidden True if the column is hidden
5818      */
5819     setHidden : function(colIndex, hidden){
5820         this.config[colIndex].hidden = hidden;
5821         this.totalWidth = null;
5822         this.fireEvent("hiddenchange", this, colIndex, hidden);
5823     },
5824
5825     /**
5826      * Sets the editor for a column.
5827      * @param {Number} col The column index
5828      * @param {Object} editor The editor object
5829      */
5830     setEditor : function(col, editor){
5831         this.config[col].editor = editor;
5832     }
5833 });
5834
5835 Roo.grid.ColumnModel.defaultRenderer = function(value)
5836 {
5837     if(typeof value == "object") {
5838         return value;
5839     }
5840         if(typeof value == "string" && value.length < 1){
5841             return "&#160;";
5842         }
5843     
5844         return String.format("{0}", value);
5845 };
5846
5847 // Alias for backwards compatibility
5848 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5849 /*
5850  * Based on:
5851  * Ext JS Library 1.1.1
5852  * Copyright(c) 2006-2007, Ext JS, LLC.
5853  *
5854  * Originally Released Under LGPL - original licence link has changed is not relivant.
5855  *
5856  * Fork - LGPL
5857  * <script type="text/javascript">
5858  */
5859  
5860 /**
5861  * @class Roo.LoadMask
5862  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5863  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5864  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5865  * element's UpdateManager load indicator and will be destroyed after the initial load.
5866  * @constructor
5867  * Create a new LoadMask
5868  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5869  * @param {Object} config The config object
5870  */
5871 Roo.LoadMask = function(el, config){
5872     this.el = Roo.get(el);
5873     Roo.apply(this, config);
5874     if(this.store){
5875         this.store.on('beforeload', this.onBeforeLoad, this);
5876         this.store.on('load', this.onLoad, this);
5877         this.store.on('loadexception', this.onLoadException, this);
5878         this.removeMask = false;
5879     }else{
5880         var um = this.el.getUpdateManager();
5881         um.showLoadIndicator = false; // disable the default indicator
5882         um.on('beforeupdate', this.onBeforeLoad, this);
5883         um.on('update', this.onLoad, this);
5884         um.on('failure', this.onLoad, this);
5885         this.removeMask = true;
5886     }
5887 };
5888
5889 Roo.LoadMask.prototype = {
5890     /**
5891      * @cfg {Boolean} removeMask
5892      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5893      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5894      */
5895     /**
5896      * @cfg {String} msg
5897      * The text to display in a centered loading message box (defaults to 'Loading...')
5898      */
5899     msg : 'Loading...',
5900     /**
5901      * @cfg {String} msgCls
5902      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5903      */
5904     msgCls : 'x-mask-loading',
5905
5906     /**
5907      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5908      * @type Boolean
5909      */
5910     disabled: false,
5911
5912     /**
5913      * Disables the mask to prevent it from being displayed
5914      */
5915     disable : function(){
5916        this.disabled = true;
5917     },
5918
5919     /**
5920      * Enables the mask so that it can be displayed
5921      */
5922     enable : function(){
5923         this.disabled = false;
5924     },
5925     
5926     onLoadException : function()
5927     {
5928         Roo.log(arguments);
5929         
5930         if (typeof(arguments[3]) != 'undefined') {
5931             Roo.MessageBox.alert("Error loading",arguments[3]);
5932         } 
5933         /*
5934         try {
5935             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5936                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5937             }   
5938         } catch(e) {
5939             
5940         }
5941         */
5942     
5943         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5944     },
5945     // private
5946     onLoad : function()
5947     {
5948         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5949     },
5950
5951     // private
5952     onBeforeLoad : function(){
5953         if(!this.disabled){
5954             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5955         }
5956     },
5957
5958     // private
5959     destroy : function(){
5960         if(this.store){
5961             this.store.un('beforeload', this.onBeforeLoad, this);
5962             this.store.un('load', this.onLoad, this);
5963             this.store.un('loadexception', this.onLoadException, this);
5964         }else{
5965             var um = this.el.getUpdateManager();
5966             um.un('beforeupdate', this.onBeforeLoad, this);
5967             um.un('update', this.onLoad, this);
5968             um.un('failure', this.onLoad, this);
5969         }
5970     }
5971 };/*
5972  * - LGPL
5973  *
5974  * table
5975  * 
5976  */
5977
5978 /**
5979  * @class Roo.bootstrap.Table
5980  * @extends Roo.bootstrap.Component
5981  * Bootstrap Table class
5982  * @cfg {String} cls table class
5983  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5984  * @cfg {String} bgcolor Specifies the background color for a table
5985  * @cfg {Number} border Specifies whether the table cells should have borders or not
5986  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5987  * @cfg {Number} cellspacing Specifies the space between cells
5988  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5989  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5990  * @cfg {String} sortable Specifies that the table should be sortable
5991  * @cfg {String} summary Specifies a summary of the content of a table
5992  * @cfg {Number} width Specifies the width of a table
5993  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5994  * 
5995  * @cfg {boolean} striped Should the rows be alternative striped
5996  * @cfg {boolean} bordered Add borders to the table
5997  * @cfg {boolean} hover Add hover highlighting
5998  * @cfg {boolean} condensed Format condensed
5999  * @cfg {boolean} responsive Format condensed
6000  * @cfg {Boolean} loadMask (true|false) default false
6001  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6002  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6003  * @cfg {Boolean} rowSelection (true|false) default false
6004  * @cfg {Boolean} cellSelection (true|false) default false
6005  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6006  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6007  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6008  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6009  
6010  * 
6011  * @constructor
6012  * Create a new Table
6013  * @param {Object} config The config object
6014  */
6015
6016 Roo.bootstrap.Table = function(config){
6017     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6018     
6019   
6020     
6021     // BC...
6022     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6023     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6024     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6025     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6026     
6027     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6028     if (this.sm) {
6029         this.sm.grid = this;
6030         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6031         this.sm = this.selModel;
6032         this.sm.xmodule = this.xmodule || false;
6033     }
6034     
6035     if (this.cm && typeof(this.cm.config) == 'undefined') {
6036         this.colModel = new Roo.grid.ColumnModel(this.cm);
6037         this.cm = this.colModel;
6038         this.cm.xmodule = this.xmodule || false;
6039     }
6040     if (this.store) {
6041         this.store= Roo.factory(this.store, Roo.data);
6042         this.ds = this.store;
6043         this.ds.xmodule = this.xmodule || false;
6044          
6045     }
6046     if (this.footer && this.store) {
6047         this.footer.dataSource = this.ds;
6048         this.footer = Roo.factory(this.footer);
6049     }
6050     
6051     /** @private */
6052     this.addEvents({
6053         /**
6054          * @event cellclick
6055          * Fires when a cell is clicked
6056          * @param {Roo.bootstrap.Table} this
6057          * @param {Roo.Element} el
6058          * @param {Number} rowIndex
6059          * @param {Number} columnIndex
6060          * @param {Roo.EventObject} e
6061          */
6062         "cellclick" : true,
6063         /**
6064          * @event celldblclick
6065          * Fires when a cell is double clicked
6066          * @param {Roo.bootstrap.Table} this
6067          * @param {Roo.Element} el
6068          * @param {Number} rowIndex
6069          * @param {Number} columnIndex
6070          * @param {Roo.EventObject} e
6071          */
6072         "celldblclick" : true,
6073         /**
6074          * @event rowclick
6075          * Fires when a row is clicked
6076          * @param {Roo.bootstrap.Table} this
6077          * @param {Roo.Element} el
6078          * @param {Number} rowIndex
6079          * @param {Roo.EventObject} e
6080          */
6081         "rowclick" : true,
6082         /**
6083          * @event rowdblclick
6084          * Fires when a row is double clicked
6085          * @param {Roo.bootstrap.Table} this
6086          * @param {Roo.Element} el
6087          * @param {Number} rowIndex
6088          * @param {Roo.EventObject} e
6089          */
6090         "rowdblclick" : true,
6091         /**
6092          * @event mouseover
6093          * Fires when a mouseover occur
6094          * @param {Roo.bootstrap.Table} this
6095          * @param {Roo.Element} el
6096          * @param {Number} rowIndex
6097          * @param {Number} columnIndex
6098          * @param {Roo.EventObject} e
6099          */
6100         "mouseover" : true,
6101         /**
6102          * @event mouseout
6103          * Fires when a mouseout occur
6104          * @param {Roo.bootstrap.Table} this
6105          * @param {Roo.Element} el
6106          * @param {Number} rowIndex
6107          * @param {Number} columnIndex
6108          * @param {Roo.EventObject} e
6109          */
6110         "mouseout" : true,
6111         /**
6112          * @event rowclass
6113          * Fires when a row is rendered, so you can change add a style to it.
6114          * @param {Roo.bootstrap.Table} this
6115          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6116          */
6117         'rowclass' : true,
6118           /**
6119          * @event rowsrendered
6120          * Fires when all the  rows have been rendered
6121          * @param {Roo.bootstrap.Table} this
6122          */
6123         'rowsrendered' : true,
6124         /**
6125          * @event contextmenu
6126          * The raw contextmenu event for the entire grid.
6127          * @param {Roo.EventObject} e
6128          */
6129         "contextmenu" : true,
6130         /**
6131          * @event rowcontextmenu
6132          * Fires when a row is right clicked
6133          * @param {Roo.bootstrap.Table} this
6134          * @param {Number} rowIndex
6135          * @param {Roo.EventObject} e
6136          */
6137         "rowcontextmenu" : true,
6138         /**
6139          * @event cellcontextmenu
6140          * Fires when a cell is right clicked
6141          * @param {Roo.bootstrap.Table} this
6142          * @param {Number} rowIndex
6143          * @param {Number} cellIndex
6144          * @param {Roo.EventObject} e
6145          */
6146          "cellcontextmenu" : true,
6147          /**
6148          * @event headercontextmenu
6149          * Fires when a header is right clicked
6150          * @param {Roo.bootstrap.Table} this
6151          * @param {Number} columnIndex
6152          * @param {Roo.EventObject} e
6153          */
6154         "headercontextmenu" : true
6155     });
6156 };
6157
6158 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6159     
6160     cls: false,
6161     align: false,
6162     bgcolor: false,
6163     border: false,
6164     cellpadding: false,
6165     cellspacing: false,
6166     frame: false,
6167     rules: false,
6168     sortable: false,
6169     summary: false,
6170     width: false,
6171     striped : false,
6172     scrollBody : false,
6173     bordered: false,
6174     hover:  false,
6175     condensed : false,
6176     responsive : false,
6177     sm : false,
6178     cm : false,
6179     store : false,
6180     loadMask : false,
6181     footerShow : true,
6182     headerShow : true,
6183   
6184     rowSelection : false,
6185     cellSelection : false,
6186     layout : false,
6187     
6188     // Roo.Element - the tbody
6189     mainBody: false,
6190     // Roo.Element - thead element
6191     mainHead: false,
6192     
6193     container: false, // used by gridpanel...
6194     
6195     lazyLoad : false,
6196     
6197     CSS : Roo.util.CSS,
6198     
6199     auto_hide_footer : false,
6200     
6201     getAutoCreate : function()
6202     {
6203         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6204         
6205         cfg = {
6206             tag: 'table',
6207             cls : 'table',
6208             cn : []
6209         };
6210         if (this.scrollBody) {
6211             cfg.cls += ' table-body-fixed';
6212         }    
6213         if (this.striped) {
6214             cfg.cls += ' table-striped';
6215         }
6216         
6217         if (this.hover) {
6218             cfg.cls += ' table-hover';
6219         }
6220         if (this.bordered) {
6221             cfg.cls += ' table-bordered';
6222         }
6223         if (this.condensed) {
6224             cfg.cls += ' table-condensed';
6225         }
6226         if (this.responsive) {
6227             cfg.cls += ' table-responsive';
6228         }
6229         
6230         if (this.cls) {
6231             cfg.cls+=  ' ' +this.cls;
6232         }
6233         
6234         // this lot should be simplifed...
6235         var _t = this;
6236         var cp = [
6237             'align',
6238             'bgcolor',
6239             'border',
6240             'cellpadding',
6241             'cellspacing',
6242             'frame',
6243             'rules',
6244             'sortable',
6245             'summary',
6246             'width'
6247         ].forEach(function(k) {
6248             if (_t[k]) {
6249                 cfg[k] = _t[k];
6250             }
6251         });
6252         
6253         
6254         if (this.layout) {
6255             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6256         }
6257         
6258         if(this.store || this.cm){
6259             if(this.headerShow){
6260                 cfg.cn.push(this.renderHeader());
6261             }
6262             
6263             cfg.cn.push(this.renderBody());
6264             
6265             if(this.footerShow){
6266                 cfg.cn.push(this.renderFooter());
6267             }
6268             // where does this come from?
6269             //cfg.cls+=  ' TableGrid';
6270         }
6271         
6272         return { cn : [ cfg ] };
6273     },
6274     
6275     initEvents : function()
6276     {   
6277         if(!this.store || !this.cm){
6278             return;
6279         }
6280         if (this.selModel) {
6281             this.selModel.initEvents();
6282         }
6283         
6284         
6285         //Roo.log('initEvents with ds!!!!');
6286         
6287         this.mainBody = this.el.select('tbody', true).first();
6288         this.mainHead = this.el.select('thead', true).first();
6289         this.mainFoot = this.el.select('tfoot', true).first();
6290         
6291         
6292         
6293         var _this = this;
6294         
6295         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6296             e.on('click', _this.sort, _this);
6297         });
6298         
6299         this.mainBody.on("click", this.onClick, this);
6300         this.mainBody.on("dblclick", this.onDblClick, this);
6301         
6302         // why is this done????? = it breaks dialogs??
6303         //this.parent().el.setStyle('position', 'relative');
6304         
6305         
6306         if (this.footer) {
6307             this.footer.parentId = this.id;
6308             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6309             
6310             if(this.lazyLoad){
6311                 this.el.select('tfoot tr td').first().addClass('hide');
6312             }
6313         } 
6314         
6315         if(this.loadMask) {
6316             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6317         }
6318         
6319         this.store.on('load', this.onLoad, this);
6320         this.store.on('beforeload', this.onBeforeLoad, this);
6321         this.store.on('update', this.onUpdate, this);
6322         this.store.on('add', this.onAdd, this);
6323         this.store.on("clear", this.clear, this);
6324         
6325         this.el.on("contextmenu", this.onContextMenu, this);
6326         
6327         this.mainBody.on('scroll', this.onBodyScroll, this);
6328         
6329         this.cm.on("headerchange", this.onHeaderChange, this);
6330         
6331         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6332         
6333     },
6334     
6335     onContextMenu : function(e, t)
6336     {
6337         this.processEvent("contextmenu", e);
6338     },
6339     
6340     processEvent : function(name, e)
6341     {
6342         if (name != 'touchstart' ) {
6343             this.fireEvent(name, e);    
6344         }
6345         
6346         var t = e.getTarget();
6347         
6348         var cell = Roo.get(t);
6349         
6350         if(!cell){
6351             return;
6352         }
6353         
6354         if(cell.findParent('tfoot', false, true)){
6355             return;
6356         }
6357         
6358         if(cell.findParent('thead', false, true)){
6359             
6360             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6361                 cell = Roo.get(t).findParent('th', false, true);
6362                 if (!cell) {
6363                     Roo.log("failed to find th in thead?");
6364                     Roo.log(e.getTarget());
6365                     return;
6366                 }
6367             }
6368             
6369             var cellIndex = cell.dom.cellIndex;
6370             
6371             var ename = name == 'touchstart' ? 'click' : name;
6372             this.fireEvent("header" + ename, this, cellIndex, e);
6373             
6374             return;
6375         }
6376         
6377         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6378             cell = Roo.get(t).findParent('td', false, true);
6379             if (!cell) {
6380                 Roo.log("failed to find th in tbody?");
6381                 Roo.log(e.getTarget());
6382                 return;
6383             }
6384         }
6385         
6386         var row = cell.findParent('tr', false, true);
6387         var cellIndex = cell.dom.cellIndex;
6388         var rowIndex = row.dom.rowIndex - 1;
6389         
6390         if(row !== false){
6391             
6392             this.fireEvent("row" + name, this, rowIndex, e);
6393             
6394             if(cell !== false){
6395             
6396                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6397             }
6398         }
6399         
6400     },
6401     
6402     onMouseover : function(e, el)
6403     {
6404         var cell = Roo.get(el);
6405         
6406         if(!cell){
6407             return;
6408         }
6409         
6410         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6411             cell = cell.findParent('td', false, true);
6412         }
6413         
6414         var row = cell.findParent('tr', false, true);
6415         var cellIndex = cell.dom.cellIndex;
6416         var rowIndex = row.dom.rowIndex - 1; // start from 0
6417         
6418         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6419         
6420     },
6421     
6422     onMouseout : function(e, el)
6423     {
6424         var cell = Roo.get(el);
6425         
6426         if(!cell){
6427             return;
6428         }
6429         
6430         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6431             cell = cell.findParent('td', false, true);
6432         }
6433         
6434         var row = cell.findParent('tr', false, true);
6435         var cellIndex = cell.dom.cellIndex;
6436         var rowIndex = row.dom.rowIndex - 1; // start from 0
6437         
6438         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6439         
6440     },
6441     
6442     onClick : function(e, el)
6443     {
6444         var cell = Roo.get(el);
6445         
6446         if(!cell || (!this.cellSelection && !this.rowSelection)){
6447             return;
6448         }
6449         
6450         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6451             cell = cell.findParent('td', false, true);
6452         }
6453         
6454         if(!cell || typeof(cell) == 'undefined'){
6455             return;
6456         }
6457         
6458         var row = cell.findParent('tr', false, true);
6459         
6460         if(!row || typeof(row) == 'undefined'){
6461             return;
6462         }
6463         
6464         var cellIndex = cell.dom.cellIndex;
6465         var rowIndex = this.getRowIndex(row);
6466         
6467         // why??? - should these not be based on SelectionModel?
6468         if(this.cellSelection){
6469             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6470         }
6471         
6472         if(this.rowSelection){
6473             this.fireEvent('rowclick', this, row, rowIndex, e);
6474         }
6475         
6476         
6477     },
6478         
6479     onDblClick : function(e,el)
6480     {
6481         var cell = Roo.get(el);
6482         
6483         if(!cell || (!this.cellSelection && !this.rowSelection)){
6484             return;
6485         }
6486         
6487         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6488             cell = cell.findParent('td', false, true);
6489         }
6490         
6491         if(!cell || typeof(cell) == 'undefined'){
6492             return;
6493         }
6494         
6495         var row = cell.findParent('tr', false, true);
6496         
6497         if(!row || typeof(row) == 'undefined'){
6498             return;
6499         }
6500         
6501         var cellIndex = cell.dom.cellIndex;
6502         var rowIndex = this.getRowIndex(row);
6503         
6504         if(this.cellSelection){
6505             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6506         }
6507         
6508         if(this.rowSelection){
6509             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6510         }
6511     },
6512     
6513     sort : function(e,el)
6514     {
6515         var col = Roo.get(el);
6516         
6517         if(!col.hasClass('sortable')){
6518             return;
6519         }
6520         
6521         var sort = col.attr('sort');
6522         var dir = 'ASC';
6523         
6524         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6525             dir = 'DESC';
6526         }
6527         
6528         this.store.sortInfo = {field : sort, direction : dir};
6529         
6530         if (this.footer) {
6531             Roo.log("calling footer first");
6532             this.footer.onClick('first');
6533         } else {
6534         
6535             this.store.load({ params : { start : 0 } });
6536         }
6537     },
6538     
6539     renderHeader : function()
6540     {
6541         var header = {
6542             tag: 'thead',
6543             cn : []
6544         };
6545         
6546         var cm = this.cm;
6547         this.totalWidth = 0;
6548         
6549         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6550             
6551             var config = cm.config[i];
6552             
6553             var c = {
6554                 tag: 'th',
6555                 cls : 'x-hcol-' + i,
6556                 style : '',
6557                 html: cm.getColumnHeader(i)
6558             };
6559             
6560             var hh = '';
6561             
6562             if(typeof(config.sortable) != 'undefined' && config.sortable){
6563                 c.cls = 'sortable';
6564                 c.html = '<i class="glyphicon"></i>' + c.html;
6565             }
6566             
6567             if(typeof(config.lgHeader) != 'undefined'){
6568                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6569             }
6570             
6571             if(typeof(config.mdHeader) != 'undefined'){
6572                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6573             }
6574             
6575             if(typeof(config.smHeader) != 'undefined'){
6576                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6577             }
6578             
6579             if(typeof(config.xsHeader) != 'undefined'){
6580                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6581             }
6582             
6583             if(hh.length){
6584                 c.html = hh;
6585             }
6586             
6587             if(typeof(config.tooltip) != 'undefined'){
6588                 c.tooltip = config.tooltip;
6589             }
6590             
6591             if(typeof(config.colspan) != 'undefined'){
6592                 c.colspan = config.colspan;
6593             }
6594             
6595             if(typeof(config.hidden) != 'undefined' && config.hidden){
6596                 c.style += ' display:none;';
6597             }
6598             
6599             if(typeof(config.dataIndex) != 'undefined'){
6600                 c.sort = config.dataIndex;
6601             }
6602             
6603            
6604             
6605             if(typeof(config.align) != 'undefined' && config.align.length){
6606                 c.style += ' text-align:' + config.align + ';';
6607             }
6608             
6609             if(typeof(config.width) != 'undefined'){
6610                 c.style += ' width:' + config.width + 'px;';
6611                 this.totalWidth += config.width;
6612             } else {
6613                 this.totalWidth += 100; // assume minimum of 100 per column?
6614             }
6615             
6616             if(typeof(config.cls) != 'undefined'){
6617                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6618             }
6619             
6620             ['xs','sm','md','lg'].map(function(size){
6621                 
6622                 if(typeof(config[size]) == 'undefined'){
6623                     return;
6624                 }
6625                 
6626                 if (!config[size]) { // 0 = hidden
6627                     c.cls += ' hidden-' + size;
6628                     return;
6629                 }
6630                 
6631                 c.cls += ' col-' + size + '-' + config[size];
6632
6633             });
6634             
6635             header.cn.push(c)
6636         }
6637         
6638         return header;
6639     },
6640     
6641     renderBody : function()
6642     {
6643         var body = {
6644             tag: 'tbody',
6645             cn : [
6646                 {
6647                     tag: 'tr',
6648                     cn : [
6649                         {
6650                             tag : 'td',
6651                             colspan :  this.cm.getColumnCount()
6652                         }
6653                     ]
6654                 }
6655             ]
6656         };
6657         
6658         return body;
6659     },
6660     
6661     renderFooter : function()
6662     {
6663         var footer = {
6664             tag: 'tfoot',
6665             cn : [
6666                 {
6667                     tag: 'tr',
6668                     cn : [
6669                         {
6670                             tag : 'td',
6671                             colspan :  this.cm.getColumnCount()
6672                         }
6673                     ]
6674                 }
6675             ]
6676         };
6677         
6678         return footer;
6679     },
6680     
6681     
6682     
6683     onLoad : function()
6684     {
6685 //        Roo.log('ds onload');
6686         this.clear();
6687         
6688         var _this = this;
6689         var cm = this.cm;
6690         var ds = this.store;
6691         
6692         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6693             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6694             if (_this.store.sortInfo) {
6695                     
6696                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6697                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6698                 }
6699                 
6700                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6701                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6702                 }
6703             }
6704         });
6705         
6706         var tbody =  this.mainBody;
6707               
6708         if(ds.getCount() > 0){
6709             ds.data.each(function(d,rowIndex){
6710                 var row =  this.renderRow(cm, ds, rowIndex);
6711                 
6712                 tbody.createChild(row);
6713                 
6714                 var _this = this;
6715                 
6716                 if(row.cellObjects.length){
6717                     Roo.each(row.cellObjects, function(r){
6718                         _this.renderCellObject(r);
6719                     })
6720                 }
6721                 
6722             }, this);
6723         }
6724         
6725         var tfoot = this.el.select('tfoot', true).first();
6726         
6727         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6728             
6729             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6730             
6731             var total = this.ds.getTotalCount();
6732             
6733             if(this.footer.pageSize < total){
6734                 this.mainFoot.show();
6735             }
6736         }
6737         
6738         Roo.each(this.el.select('tbody td', true).elements, function(e){
6739             e.on('mouseover', _this.onMouseover, _this);
6740         });
6741         
6742         Roo.each(this.el.select('tbody td', true).elements, function(e){
6743             e.on('mouseout', _this.onMouseout, _this);
6744         });
6745         this.fireEvent('rowsrendered', this);
6746         
6747         this.autoSize();
6748     },
6749     
6750     
6751     onUpdate : function(ds,record)
6752     {
6753         this.refreshRow(record);
6754         this.autoSize();
6755     },
6756     
6757     onRemove : function(ds, record, index, isUpdate){
6758         if(isUpdate !== true){
6759             this.fireEvent("beforerowremoved", this, index, record);
6760         }
6761         var bt = this.mainBody.dom;
6762         
6763         var rows = this.el.select('tbody > tr', true).elements;
6764         
6765         if(typeof(rows[index]) != 'undefined'){
6766             bt.removeChild(rows[index].dom);
6767         }
6768         
6769 //        if(bt.rows[index]){
6770 //            bt.removeChild(bt.rows[index]);
6771 //        }
6772         
6773         if(isUpdate !== true){
6774             //this.stripeRows(index);
6775             //this.syncRowHeights(index, index);
6776             //this.layout();
6777             this.fireEvent("rowremoved", this, index, record);
6778         }
6779     },
6780     
6781     onAdd : function(ds, records, rowIndex)
6782     {
6783         //Roo.log('on Add called');
6784         // - note this does not handle multiple adding very well..
6785         var bt = this.mainBody.dom;
6786         for (var i =0 ; i < records.length;i++) {
6787             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6788             //Roo.log(records[i]);
6789             //Roo.log(this.store.getAt(rowIndex+i));
6790             this.insertRow(this.store, rowIndex + i, false);
6791             return;
6792         }
6793         
6794     },
6795     
6796     
6797     refreshRow : function(record){
6798         var ds = this.store, index;
6799         if(typeof record == 'number'){
6800             index = record;
6801             record = ds.getAt(index);
6802         }else{
6803             index = ds.indexOf(record);
6804         }
6805         this.insertRow(ds, index, true);
6806         this.autoSize();
6807         this.onRemove(ds, record, index+1, true);
6808         this.autoSize();
6809         //this.syncRowHeights(index, index);
6810         //this.layout();
6811         this.fireEvent("rowupdated", this, index, record);
6812     },
6813     
6814     insertRow : function(dm, rowIndex, isUpdate){
6815         
6816         if(!isUpdate){
6817             this.fireEvent("beforerowsinserted", this, rowIndex);
6818         }
6819             //var s = this.getScrollState();
6820         var row = this.renderRow(this.cm, this.store, rowIndex);
6821         // insert before rowIndex..
6822         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6823         
6824         var _this = this;
6825                 
6826         if(row.cellObjects.length){
6827             Roo.each(row.cellObjects, function(r){
6828                 _this.renderCellObject(r);
6829             })
6830         }
6831             
6832         if(!isUpdate){
6833             this.fireEvent("rowsinserted", this, rowIndex);
6834             //this.syncRowHeights(firstRow, lastRow);
6835             //this.stripeRows(firstRow);
6836             //this.layout();
6837         }
6838         
6839     },
6840     
6841     
6842     getRowDom : function(rowIndex)
6843     {
6844         var rows = this.el.select('tbody > tr', true).elements;
6845         
6846         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6847         
6848     },
6849     // returns the object tree for a tr..
6850   
6851     
6852     renderRow : function(cm, ds, rowIndex) 
6853     {
6854         var d = ds.getAt(rowIndex);
6855         
6856         var row = {
6857             tag : 'tr',
6858             cls : 'x-row-' + rowIndex,
6859             cn : []
6860         };
6861             
6862         var cellObjects = [];
6863         
6864         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6865             var config = cm.config[i];
6866             
6867             var renderer = cm.getRenderer(i);
6868             var value = '';
6869             var id = false;
6870             
6871             if(typeof(renderer) !== 'undefined'){
6872                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6873             }
6874             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6875             // and are rendered into the cells after the row is rendered - using the id for the element.
6876             
6877             if(typeof(value) === 'object'){
6878                 id = Roo.id();
6879                 cellObjects.push({
6880                     container : id,
6881                     cfg : value 
6882                 })
6883             }
6884             
6885             var rowcfg = {
6886                 record: d,
6887                 rowIndex : rowIndex,
6888                 colIndex : i,
6889                 rowClass : ''
6890             };
6891
6892             this.fireEvent('rowclass', this, rowcfg);
6893             
6894             var td = {
6895                 tag: 'td',
6896                 cls : rowcfg.rowClass + ' x-col-' + i,
6897                 style: '',
6898                 html: (typeof(value) === 'object') ? '' : value
6899             };
6900             
6901             if (id) {
6902                 td.id = id;
6903             }
6904             
6905             if(typeof(config.colspan) != 'undefined'){
6906                 td.colspan = config.colspan;
6907             }
6908             
6909             if(typeof(config.hidden) != 'undefined' && config.hidden){
6910                 td.style += ' display:none;';
6911             }
6912             
6913             if(typeof(config.align) != 'undefined' && config.align.length){
6914                 td.style += ' text-align:' + config.align + ';';
6915             }
6916             if(typeof(config.valign) != 'undefined' && config.valign.length){
6917                 td.style += ' vertical-align:' + config.valign + ';';
6918             }
6919             
6920             if(typeof(config.width) != 'undefined'){
6921                 td.style += ' width:' +  config.width + 'px;';
6922             }
6923             
6924             if(typeof(config.cursor) != 'undefined'){
6925                 td.style += ' cursor:' +  config.cursor + ';';
6926             }
6927             
6928             if(typeof(config.cls) != 'undefined'){
6929                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6930             }
6931             
6932             ['xs','sm','md','lg'].map(function(size){
6933                 
6934                 if(typeof(config[size]) == 'undefined'){
6935                     return;
6936                 }
6937                 
6938                 if (!config[size]) { // 0 = hidden
6939                     td.cls += ' hidden-' + size;
6940                     return;
6941                 }
6942                 
6943                 td.cls += ' col-' + size + '-' + config[size];
6944
6945             });
6946             
6947             row.cn.push(td);
6948            
6949         }
6950         
6951         row.cellObjects = cellObjects;
6952         
6953         return row;
6954           
6955     },
6956     
6957     
6958     
6959     onBeforeLoad : function()
6960     {
6961         
6962     },
6963      /**
6964      * Remove all rows
6965      */
6966     clear : function()
6967     {
6968         this.el.select('tbody', true).first().dom.innerHTML = '';
6969     },
6970     /**
6971      * Show or hide a row.
6972      * @param {Number} rowIndex to show or hide
6973      * @param {Boolean} state hide
6974      */
6975     setRowVisibility : function(rowIndex, state)
6976     {
6977         var bt = this.mainBody.dom;
6978         
6979         var rows = this.el.select('tbody > tr', true).elements;
6980         
6981         if(typeof(rows[rowIndex]) == 'undefined'){
6982             return;
6983         }
6984         rows[rowIndex].dom.style.display = state ? '' : 'none';
6985     },
6986     
6987     
6988     getSelectionModel : function(){
6989         if(!this.selModel){
6990             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6991         }
6992         return this.selModel;
6993     },
6994     /*
6995      * Render the Roo.bootstrap object from renderder
6996      */
6997     renderCellObject : function(r)
6998     {
6999         var _this = this;
7000         
7001         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7002         
7003         var t = r.cfg.render(r.container);
7004         
7005         if(r.cfg.cn){
7006             Roo.each(r.cfg.cn, function(c){
7007                 var child = {
7008                     container: t.getChildContainer(),
7009                     cfg: c
7010                 };
7011                 _this.renderCellObject(child);
7012             })
7013         }
7014     },
7015     
7016     getRowIndex : function(row)
7017     {
7018         var rowIndex = -1;
7019         
7020         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7021             if(el != row){
7022                 return;
7023             }
7024             
7025             rowIndex = index;
7026         });
7027         
7028         return rowIndex;
7029     },
7030      /**
7031      * Returns the grid's underlying element = used by panel.Grid
7032      * @return {Element} The element
7033      */
7034     getGridEl : function(){
7035         return this.el;
7036     },
7037      /**
7038      * Forces a resize - used by panel.Grid
7039      * @return {Element} The element
7040      */
7041     autoSize : function()
7042     {
7043         //var ctr = Roo.get(this.container.dom.parentElement);
7044         var ctr = Roo.get(this.el.dom);
7045         
7046         var thd = this.getGridEl().select('thead',true).first();
7047         var tbd = this.getGridEl().select('tbody', true).first();
7048         var tfd = this.getGridEl().select('tfoot', true).first();
7049         
7050         var cw = ctr.getWidth();
7051         
7052         if (tbd) {
7053             
7054             tbd.setSize(ctr.getWidth(),
7055                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7056             );
7057             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7058             cw -= barsize;
7059         }
7060         cw = Math.max(cw, this.totalWidth);
7061         this.getGridEl().select('tr',true).setWidth(cw);
7062         // resize 'expandable coloumn?
7063         
7064         return; // we doe not have a view in this design..
7065         
7066     },
7067     onBodyScroll: function()
7068     {
7069         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7070         if(this.mainHead){
7071             this.mainHead.setStyle({
7072                 'position' : 'relative',
7073                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7074             });
7075         }
7076         
7077         if(this.lazyLoad){
7078             
7079             var scrollHeight = this.mainBody.dom.scrollHeight;
7080             
7081             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7082             
7083             var height = this.mainBody.getHeight();
7084             
7085             if(scrollHeight - height == scrollTop) {
7086                 
7087                 var total = this.ds.getTotalCount();
7088                 
7089                 if(this.footer.cursor + this.footer.pageSize < total){
7090                     
7091                     this.footer.ds.load({
7092                         params : {
7093                             start : this.footer.cursor + this.footer.pageSize,
7094                             limit : this.footer.pageSize
7095                         },
7096                         add : true
7097                     });
7098                 }
7099             }
7100             
7101         }
7102     },
7103     
7104     onHeaderChange : function()
7105     {
7106         var header = this.renderHeader();
7107         var table = this.el.select('table', true).first();
7108         
7109         this.mainHead.remove();
7110         this.mainHead = table.createChild(header, this.mainBody, false);
7111     },
7112     
7113     onHiddenChange : function(colModel, colIndex, hidden)
7114     {
7115         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7116         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7117         
7118         this.CSS.updateRule(thSelector, "display", "");
7119         this.CSS.updateRule(tdSelector, "display", "");
7120         
7121         if(hidden){
7122             this.CSS.updateRule(thSelector, "display", "none");
7123             this.CSS.updateRule(tdSelector, "display", "none");
7124         }
7125         
7126         this.onHeaderChange();
7127         this.onLoad();
7128     },
7129     
7130     setColumnWidth: function(col_index, width)
7131     {
7132         // width = "md-2 xs-2..."
7133         if(!this.colModel.config[col_index]) {
7134             return;
7135         }
7136         
7137         var w = width.split(" ");
7138         
7139         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7140         
7141         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7142         
7143         
7144         for(var j = 0; j < w.length; j++) {
7145             
7146             if(!w[j]) {
7147                 continue;
7148             }
7149             
7150             var size_cls = w[j].split("-");
7151             
7152             if(!Number.isInteger(size_cls[1] * 1)) {
7153                 continue;
7154             }
7155             
7156             if(!this.colModel.config[col_index][size_cls[0]]) {
7157                 continue;
7158             }
7159             
7160             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7161                 continue;
7162             }
7163             
7164             h_row[0].classList.replace(
7165                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7166                 "col-"+size_cls[0]+"-"+size_cls[1]
7167             );
7168             
7169             for(var i = 0; i < rows.length; i++) {
7170                 
7171                 var size_cls = w[j].split("-");
7172                 
7173                 if(!Number.isInteger(size_cls[1] * 1)) {
7174                     continue;
7175                 }
7176                 
7177                 if(!this.colModel.config[col_index][size_cls[0]]) {
7178                     continue;
7179                 }
7180                 
7181                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7182                     continue;
7183                 }
7184                 
7185                 rows[i].classList.replace(
7186                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7187                     "col-"+size_cls[0]+"-"+size_cls[1]
7188                 );
7189             }
7190             
7191             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7192         }
7193     }
7194 });
7195
7196  
7197
7198  /*
7199  * - LGPL
7200  *
7201  * table cell
7202  * 
7203  */
7204
7205 /**
7206  * @class Roo.bootstrap.TableCell
7207  * @extends Roo.bootstrap.Component
7208  * Bootstrap TableCell class
7209  * @cfg {String} html cell contain text
7210  * @cfg {String} cls cell class
7211  * @cfg {String} tag cell tag (td|th) default td
7212  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7213  * @cfg {String} align Aligns the content in a cell
7214  * @cfg {String} axis Categorizes cells
7215  * @cfg {String} bgcolor Specifies the background color of a cell
7216  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7217  * @cfg {Number} colspan Specifies the number of columns a cell should span
7218  * @cfg {String} headers Specifies one or more header cells a cell is related to
7219  * @cfg {Number} height Sets the height of a cell
7220  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7221  * @cfg {Number} rowspan Sets the number of rows a cell should span
7222  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7223  * @cfg {String} valign Vertical aligns the content in a cell
7224  * @cfg {Number} width Specifies the width of a cell
7225  * 
7226  * @constructor
7227  * Create a new TableCell
7228  * @param {Object} config The config object
7229  */
7230
7231 Roo.bootstrap.TableCell = function(config){
7232     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7233 };
7234
7235 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7236     
7237     html: false,
7238     cls: false,
7239     tag: false,
7240     abbr: false,
7241     align: false,
7242     axis: false,
7243     bgcolor: false,
7244     charoff: false,
7245     colspan: false,
7246     headers: false,
7247     height: false,
7248     nowrap: false,
7249     rowspan: false,
7250     scope: false,
7251     valign: false,
7252     width: false,
7253     
7254     
7255     getAutoCreate : function(){
7256         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7257         
7258         cfg = {
7259             tag: 'td'
7260         };
7261         
7262         if(this.tag){
7263             cfg.tag = this.tag;
7264         }
7265         
7266         if (this.html) {
7267             cfg.html=this.html
7268         }
7269         if (this.cls) {
7270             cfg.cls=this.cls
7271         }
7272         if (this.abbr) {
7273             cfg.abbr=this.abbr
7274         }
7275         if (this.align) {
7276             cfg.align=this.align
7277         }
7278         if (this.axis) {
7279             cfg.axis=this.axis
7280         }
7281         if (this.bgcolor) {
7282             cfg.bgcolor=this.bgcolor
7283         }
7284         if (this.charoff) {
7285             cfg.charoff=this.charoff
7286         }
7287         if (this.colspan) {
7288             cfg.colspan=this.colspan
7289         }
7290         if (this.headers) {
7291             cfg.headers=this.headers
7292         }
7293         if (this.height) {
7294             cfg.height=this.height
7295         }
7296         if (this.nowrap) {
7297             cfg.nowrap=this.nowrap
7298         }
7299         if (this.rowspan) {
7300             cfg.rowspan=this.rowspan
7301         }
7302         if (this.scope) {
7303             cfg.scope=this.scope
7304         }
7305         if (this.valign) {
7306             cfg.valign=this.valign
7307         }
7308         if (this.width) {
7309             cfg.width=this.width
7310         }
7311         
7312         
7313         return cfg;
7314     }
7315    
7316 });
7317
7318  
7319
7320  /*
7321  * - LGPL
7322  *
7323  * table row
7324  * 
7325  */
7326
7327 /**
7328  * @class Roo.bootstrap.TableRow
7329  * @extends Roo.bootstrap.Component
7330  * Bootstrap TableRow class
7331  * @cfg {String} cls row class
7332  * @cfg {String} align Aligns the content in a table row
7333  * @cfg {String} bgcolor Specifies a background color for a table row
7334  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7335  * @cfg {String} valign Vertical aligns the content in a table row
7336  * 
7337  * @constructor
7338  * Create a new TableRow
7339  * @param {Object} config The config object
7340  */
7341
7342 Roo.bootstrap.TableRow = function(config){
7343     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7344 };
7345
7346 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7347     
7348     cls: false,
7349     align: false,
7350     bgcolor: false,
7351     charoff: false,
7352     valign: false,
7353     
7354     getAutoCreate : function(){
7355         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7356         
7357         cfg = {
7358             tag: 'tr'
7359         };
7360             
7361         if(this.cls){
7362             cfg.cls = this.cls;
7363         }
7364         if(this.align){
7365             cfg.align = this.align;
7366         }
7367         if(this.bgcolor){
7368             cfg.bgcolor = this.bgcolor;
7369         }
7370         if(this.charoff){
7371             cfg.charoff = this.charoff;
7372         }
7373         if(this.valign){
7374             cfg.valign = this.valign;
7375         }
7376         
7377         return cfg;
7378     }
7379    
7380 });
7381
7382  
7383
7384  /*
7385  * - LGPL
7386  *
7387  * table body
7388  * 
7389  */
7390
7391 /**
7392  * @class Roo.bootstrap.TableBody
7393  * @extends Roo.bootstrap.Component
7394  * Bootstrap TableBody class
7395  * @cfg {String} cls element class
7396  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7397  * @cfg {String} align Aligns the content inside the element
7398  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7399  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7400  * 
7401  * @constructor
7402  * Create a new TableBody
7403  * @param {Object} config The config object
7404  */
7405
7406 Roo.bootstrap.TableBody = function(config){
7407     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7408 };
7409
7410 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7411     
7412     cls: false,
7413     tag: false,
7414     align: false,
7415     charoff: false,
7416     valign: false,
7417     
7418     getAutoCreate : function(){
7419         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7420         
7421         cfg = {
7422             tag: 'tbody'
7423         };
7424             
7425         if (this.cls) {
7426             cfg.cls=this.cls
7427         }
7428         if(this.tag){
7429             cfg.tag = this.tag;
7430         }
7431         
7432         if(this.align){
7433             cfg.align = this.align;
7434         }
7435         if(this.charoff){
7436             cfg.charoff = this.charoff;
7437         }
7438         if(this.valign){
7439             cfg.valign = this.valign;
7440         }
7441         
7442         return cfg;
7443     }
7444     
7445     
7446 //    initEvents : function()
7447 //    {
7448 //        
7449 //        if(!this.store){
7450 //            return;
7451 //        }
7452 //        
7453 //        this.store = Roo.factory(this.store, Roo.data);
7454 //        this.store.on('load', this.onLoad, this);
7455 //        
7456 //        this.store.load();
7457 //        
7458 //    },
7459 //    
7460 //    onLoad: function () 
7461 //    {   
7462 //        this.fireEvent('load', this);
7463 //    }
7464 //    
7465 //   
7466 });
7467
7468  
7469
7470  /*
7471  * Based on:
7472  * Ext JS Library 1.1.1
7473  * Copyright(c) 2006-2007, Ext JS, LLC.
7474  *
7475  * Originally Released Under LGPL - original licence link has changed is not relivant.
7476  *
7477  * Fork - LGPL
7478  * <script type="text/javascript">
7479  */
7480
7481 // as we use this in bootstrap.
7482 Roo.namespace('Roo.form');
7483  /**
7484  * @class Roo.form.Action
7485  * Internal Class used to handle form actions
7486  * @constructor
7487  * @param {Roo.form.BasicForm} el The form element or its id
7488  * @param {Object} config Configuration options
7489  */
7490
7491  
7492  
7493 // define the action interface
7494 Roo.form.Action = function(form, options){
7495     this.form = form;
7496     this.options = options || {};
7497 };
7498 /**
7499  * Client Validation Failed
7500  * @const 
7501  */
7502 Roo.form.Action.CLIENT_INVALID = 'client';
7503 /**
7504  * Server Validation Failed
7505  * @const 
7506  */
7507 Roo.form.Action.SERVER_INVALID = 'server';
7508  /**
7509  * Connect to Server Failed
7510  * @const 
7511  */
7512 Roo.form.Action.CONNECT_FAILURE = 'connect';
7513 /**
7514  * Reading Data from Server Failed
7515  * @const 
7516  */
7517 Roo.form.Action.LOAD_FAILURE = 'load';
7518
7519 Roo.form.Action.prototype = {
7520     type : 'default',
7521     failureType : undefined,
7522     response : undefined,
7523     result : undefined,
7524
7525     // interface method
7526     run : function(options){
7527
7528     },
7529
7530     // interface method
7531     success : function(response){
7532
7533     },
7534
7535     // interface method
7536     handleResponse : function(response){
7537
7538     },
7539
7540     // default connection failure
7541     failure : function(response){
7542         
7543         this.response = response;
7544         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7545         this.form.afterAction(this, false);
7546     },
7547
7548     processResponse : function(response){
7549         this.response = response;
7550         if(!response.responseText){
7551             return true;
7552         }
7553         this.result = this.handleResponse(response);
7554         return this.result;
7555     },
7556
7557     // utility functions used internally
7558     getUrl : function(appendParams){
7559         var url = this.options.url || this.form.url || this.form.el.dom.action;
7560         if(appendParams){
7561             var p = this.getParams();
7562             if(p){
7563                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7564             }
7565         }
7566         return url;
7567     },
7568
7569     getMethod : function(){
7570         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7571     },
7572
7573     getParams : function(){
7574         var bp = this.form.baseParams;
7575         var p = this.options.params;
7576         if(p){
7577             if(typeof p == "object"){
7578                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7579             }else if(typeof p == 'string' && bp){
7580                 p += '&' + Roo.urlEncode(bp);
7581             }
7582         }else if(bp){
7583             p = Roo.urlEncode(bp);
7584         }
7585         return p;
7586     },
7587
7588     createCallback : function(){
7589         return {
7590             success: this.success,
7591             failure: this.failure,
7592             scope: this,
7593             timeout: (this.form.timeout*1000),
7594             upload: this.form.fileUpload ? this.success : undefined
7595         };
7596     }
7597 };
7598
7599 Roo.form.Action.Submit = function(form, options){
7600     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7601 };
7602
7603 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7604     type : 'submit',
7605
7606     haveProgress : false,
7607     uploadComplete : false,
7608     
7609     // uploadProgress indicator.
7610     uploadProgress : function()
7611     {
7612         if (!this.form.progressUrl) {
7613             return;
7614         }
7615         
7616         if (!this.haveProgress) {
7617             Roo.MessageBox.progress("Uploading", "Uploading");
7618         }
7619         if (this.uploadComplete) {
7620            Roo.MessageBox.hide();
7621            return;
7622         }
7623         
7624         this.haveProgress = true;
7625    
7626         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7627         
7628         var c = new Roo.data.Connection();
7629         c.request({
7630             url : this.form.progressUrl,
7631             params: {
7632                 id : uid
7633             },
7634             method: 'GET',
7635             success : function(req){
7636                //console.log(data);
7637                 var rdata = false;
7638                 var edata;
7639                 try  {
7640                    rdata = Roo.decode(req.responseText)
7641                 } catch (e) {
7642                     Roo.log("Invalid data from server..");
7643                     Roo.log(edata);
7644                     return;
7645                 }
7646                 if (!rdata || !rdata.success) {
7647                     Roo.log(rdata);
7648                     Roo.MessageBox.alert(Roo.encode(rdata));
7649                     return;
7650                 }
7651                 var data = rdata.data;
7652                 
7653                 if (this.uploadComplete) {
7654                    Roo.MessageBox.hide();
7655                    return;
7656                 }
7657                    
7658                 if (data){
7659                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7660                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7661                     );
7662                 }
7663                 this.uploadProgress.defer(2000,this);
7664             },
7665        
7666             failure: function(data) {
7667                 Roo.log('progress url failed ');
7668                 Roo.log(data);
7669             },
7670             scope : this
7671         });
7672            
7673     },
7674     
7675     
7676     run : function()
7677     {
7678         // run get Values on the form, so it syncs any secondary forms.
7679         this.form.getValues();
7680         
7681         var o = this.options;
7682         var method = this.getMethod();
7683         var isPost = method == 'POST';
7684         if(o.clientValidation === false || this.form.isValid()){
7685             
7686             if (this.form.progressUrl) {
7687                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7688                     (new Date() * 1) + '' + Math.random());
7689                     
7690             } 
7691             
7692             
7693             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7694                 form:this.form.el.dom,
7695                 url:this.getUrl(!isPost),
7696                 method: method,
7697                 params:isPost ? this.getParams() : null,
7698                 isUpload: this.form.fileUpload
7699             }));
7700             
7701             this.uploadProgress();
7702
7703         }else if (o.clientValidation !== false){ // client validation failed
7704             this.failureType = Roo.form.Action.CLIENT_INVALID;
7705             this.form.afterAction(this, false);
7706         }
7707     },
7708
7709     success : function(response)
7710     {
7711         this.uploadComplete= true;
7712         if (this.haveProgress) {
7713             Roo.MessageBox.hide();
7714         }
7715         
7716         
7717         var result = this.processResponse(response);
7718         if(result === true || result.success){
7719             this.form.afterAction(this, true);
7720             return;
7721         }
7722         if(result.errors){
7723             this.form.markInvalid(result.errors);
7724             this.failureType = Roo.form.Action.SERVER_INVALID;
7725         }
7726         this.form.afterAction(this, false);
7727     },
7728     failure : function(response)
7729     {
7730         this.uploadComplete= true;
7731         if (this.haveProgress) {
7732             Roo.MessageBox.hide();
7733         }
7734         
7735         this.response = response;
7736         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7737         this.form.afterAction(this, false);
7738     },
7739     
7740     handleResponse : function(response){
7741         if(this.form.errorReader){
7742             var rs = this.form.errorReader.read(response);
7743             var errors = [];
7744             if(rs.records){
7745                 for(var i = 0, len = rs.records.length; i < len; i++) {
7746                     var r = rs.records[i];
7747                     errors[i] = r.data;
7748                 }
7749             }
7750             if(errors.length < 1){
7751                 errors = null;
7752             }
7753             return {
7754                 success : rs.success,
7755                 errors : errors
7756             };
7757         }
7758         var ret = false;
7759         try {
7760             ret = Roo.decode(response.responseText);
7761         } catch (e) {
7762             ret = {
7763                 success: false,
7764                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7765                 errors : []
7766             };
7767         }
7768         return ret;
7769         
7770     }
7771 });
7772
7773
7774 Roo.form.Action.Load = function(form, options){
7775     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7776     this.reader = this.form.reader;
7777 };
7778
7779 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7780     type : 'load',
7781
7782     run : function(){
7783         
7784         Roo.Ajax.request(Roo.apply(
7785                 this.createCallback(), {
7786                     method:this.getMethod(),
7787                     url:this.getUrl(false),
7788                     params:this.getParams()
7789         }));
7790     },
7791
7792     success : function(response){
7793         
7794         var result = this.processResponse(response);
7795         if(result === true || !result.success || !result.data){
7796             this.failureType = Roo.form.Action.LOAD_FAILURE;
7797             this.form.afterAction(this, false);
7798             return;
7799         }
7800         this.form.clearInvalid();
7801         this.form.setValues(result.data);
7802         this.form.afterAction(this, true);
7803     },
7804
7805     handleResponse : function(response){
7806         if(this.form.reader){
7807             var rs = this.form.reader.read(response);
7808             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7809             return {
7810                 success : rs.success,
7811                 data : data
7812             };
7813         }
7814         return Roo.decode(response.responseText);
7815     }
7816 });
7817
7818 Roo.form.Action.ACTION_TYPES = {
7819     'load' : Roo.form.Action.Load,
7820     'submit' : Roo.form.Action.Submit
7821 };/*
7822  * - LGPL
7823  *
7824  * form
7825  *
7826  */
7827
7828 /**
7829  * @class Roo.bootstrap.Form
7830  * @extends Roo.bootstrap.Component
7831  * Bootstrap Form class
7832  * @cfg {String} method  GET | POST (default POST)
7833  * @cfg {String} labelAlign top | left (default top)
7834  * @cfg {String} align left  | right - for navbars
7835  * @cfg {Boolean} loadMask load mask when submit (default true)
7836
7837  *
7838  * @constructor
7839  * Create a new Form
7840  * @param {Object} config The config object
7841  */
7842
7843
7844 Roo.bootstrap.Form = function(config){
7845     
7846     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7847     
7848     Roo.bootstrap.Form.popover.apply();
7849     
7850     this.addEvents({
7851         /**
7852          * @event clientvalidation
7853          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7854          * @param {Form} this
7855          * @param {Boolean} valid true if the form has passed client-side validation
7856          */
7857         clientvalidation: true,
7858         /**
7859          * @event beforeaction
7860          * Fires before any action is performed. Return false to cancel the action.
7861          * @param {Form} this
7862          * @param {Action} action The action to be performed
7863          */
7864         beforeaction: true,
7865         /**
7866          * @event actionfailed
7867          * Fires when an action fails.
7868          * @param {Form} this
7869          * @param {Action} action The action that failed
7870          */
7871         actionfailed : true,
7872         /**
7873          * @event actioncomplete
7874          * Fires when an action is completed.
7875          * @param {Form} this
7876          * @param {Action} action The action that completed
7877          */
7878         actioncomplete : true
7879     });
7880 };
7881
7882 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7883
7884      /**
7885      * @cfg {String} method
7886      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7887      */
7888     method : 'POST',
7889     /**
7890      * @cfg {String} url
7891      * The URL to use for form actions if one isn't supplied in the action options.
7892      */
7893     /**
7894      * @cfg {Boolean} fileUpload
7895      * Set to true if this form is a file upload.
7896      */
7897
7898     /**
7899      * @cfg {Object} baseParams
7900      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7901      */
7902
7903     /**
7904      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7905      */
7906     timeout: 30,
7907     /**
7908      * @cfg {Sting} align (left|right) for navbar forms
7909      */
7910     align : 'left',
7911
7912     // private
7913     activeAction : null,
7914
7915     /**
7916      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7917      * element by passing it or its id or mask the form itself by passing in true.
7918      * @type Mixed
7919      */
7920     waitMsgTarget : false,
7921
7922     loadMask : true,
7923     
7924     /**
7925      * @cfg {Boolean} errorMask (true|false) default false
7926      */
7927     errorMask : false,
7928     
7929     /**
7930      * @cfg {Number} maskOffset Default 100
7931      */
7932     maskOffset : 100,
7933     
7934     /**
7935      * @cfg {Boolean} maskBody
7936      */
7937     maskBody : false,
7938
7939     getAutoCreate : function(){
7940
7941         var cfg = {
7942             tag: 'form',
7943             method : this.method || 'POST',
7944             id : this.id || Roo.id(),
7945             cls : ''
7946         };
7947         if (this.parent().xtype.match(/^Nav/)) {
7948             cfg.cls = 'navbar-form navbar-' + this.align;
7949
7950         }
7951
7952         if (this.labelAlign == 'left' ) {
7953             cfg.cls += ' form-horizontal';
7954         }
7955
7956
7957         return cfg;
7958     },
7959     initEvents : function()
7960     {
7961         this.el.on('submit', this.onSubmit, this);
7962         // this was added as random key presses on the form where triggering form submit.
7963         this.el.on('keypress', function(e) {
7964             if (e.getCharCode() != 13) {
7965                 return true;
7966             }
7967             // we might need to allow it for textareas.. and some other items.
7968             // check e.getTarget().
7969
7970             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7971                 return true;
7972             }
7973
7974             Roo.log("keypress blocked");
7975
7976             e.preventDefault();
7977             return false;
7978         });
7979         
7980     },
7981     // private
7982     onSubmit : function(e){
7983         e.stopEvent();
7984     },
7985
7986      /**
7987      * Returns true if client-side validation on the form is successful.
7988      * @return Boolean
7989      */
7990     isValid : function(){
7991         var items = this.getItems();
7992         var valid = true;
7993         var target = false;
7994         
7995         items.each(function(f){
7996             
7997             if(f.validate()){
7998                 return;
7999             }
8000             
8001             Roo.log('invalid field: ' + f.name);
8002             
8003             valid = false;
8004
8005             if(!target && f.el.isVisible(true)){
8006                 target = f;
8007             }
8008            
8009         });
8010         
8011         if(this.errorMask && !valid){
8012             Roo.bootstrap.Form.popover.mask(this, target);
8013         }
8014         
8015         return valid;
8016     },
8017     
8018     /**
8019      * Returns true if any fields in this form have changed since their original load.
8020      * @return Boolean
8021      */
8022     isDirty : function(){
8023         var dirty = false;
8024         var items = this.getItems();
8025         items.each(function(f){
8026            if(f.isDirty()){
8027                dirty = true;
8028                return false;
8029            }
8030            return true;
8031         });
8032         return dirty;
8033     },
8034      /**
8035      * Performs a predefined action (submit or load) or custom actions you define on this form.
8036      * @param {String} actionName The name of the action type
8037      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8038      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8039      * accept other config options):
8040      * <pre>
8041 Property          Type             Description
8042 ----------------  ---------------  ----------------------------------------------------------------------------------
8043 url               String           The url for the action (defaults to the form's url)
8044 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8045 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8046 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8047                                    validate the form on the client (defaults to false)
8048      * </pre>
8049      * @return {BasicForm} this
8050      */
8051     doAction : function(action, options){
8052         if(typeof action == 'string'){
8053             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8054         }
8055         if(this.fireEvent('beforeaction', this, action) !== false){
8056             this.beforeAction(action);
8057             action.run.defer(100, action);
8058         }
8059         return this;
8060     },
8061
8062     // private
8063     beforeAction : function(action){
8064         var o = action.options;
8065         
8066         if(this.loadMask){
8067             
8068             if(this.maskBody){
8069                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8070             } else {
8071                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8072             }
8073         }
8074         // not really supported yet.. ??
8075
8076         //if(this.waitMsgTarget === true){
8077         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8078         //}else if(this.waitMsgTarget){
8079         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8080         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8081         //}else {
8082         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8083        // }
8084
8085     },
8086
8087     // private
8088     afterAction : function(action, success){
8089         this.activeAction = null;
8090         var o = action.options;
8091
8092         if(this.loadMask){
8093             
8094             if(this.maskBody){
8095                 Roo.get(document.body).unmask();
8096             } else {
8097                 this.el.unmask();
8098             }
8099         }
8100         
8101         //if(this.waitMsgTarget === true){
8102 //            this.el.unmask();
8103         //}else if(this.waitMsgTarget){
8104         //    this.waitMsgTarget.unmask();
8105         //}else{
8106         //    Roo.MessageBox.updateProgress(1);
8107         //    Roo.MessageBox.hide();
8108        // }
8109         //
8110         if(success){
8111             if(o.reset){
8112                 this.reset();
8113             }
8114             Roo.callback(o.success, o.scope, [this, action]);
8115             this.fireEvent('actioncomplete', this, action);
8116
8117         }else{
8118
8119             // failure condition..
8120             // we have a scenario where updates need confirming.
8121             // eg. if a locking scenario exists..
8122             // we look for { errors : { needs_confirm : true }} in the response.
8123             if (
8124                 (typeof(action.result) != 'undefined')  &&
8125                 (typeof(action.result.errors) != 'undefined')  &&
8126                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8127            ){
8128                 var _t = this;
8129                 Roo.log("not supported yet");
8130                  /*
8131
8132                 Roo.MessageBox.confirm(
8133                     "Change requires confirmation",
8134                     action.result.errorMsg,
8135                     function(r) {
8136                         if (r != 'yes') {
8137                             return;
8138                         }
8139                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8140                     }
8141
8142                 );
8143                 */
8144
8145
8146                 return;
8147             }
8148
8149             Roo.callback(o.failure, o.scope, [this, action]);
8150             // show an error message if no failed handler is set..
8151             if (!this.hasListener('actionfailed')) {
8152                 Roo.log("need to add dialog support");
8153                 /*
8154                 Roo.MessageBox.alert("Error",
8155                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8156                         action.result.errorMsg :
8157                         "Saving Failed, please check your entries or try again"
8158                 );
8159                 */
8160             }
8161
8162             this.fireEvent('actionfailed', this, action);
8163         }
8164
8165     },
8166     /**
8167      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8168      * @param {String} id The value to search for
8169      * @return Field
8170      */
8171     findField : function(id){
8172         var items = this.getItems();
8173         var field = items.get(id);
8174         if(!field){
8175              items.each(function(f){
8176                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8177                     field = f;
8178                     return false;
8179                 }
8180                 return true;
8181             });
8182         }
8183         return field || null;
8184     },
8185      /**
8186      * Mark fields in this form invalid in bulk.
8187      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8188      * @return {BasicForm} this
8189      */
8190     markInvalid : function(errors){
8191         if(errors instanceof Array){
8192             for(var i = 0, len = errors.length; i < len; i++){
8193                 var fieldError = errors[i];
8194                 var f = this.findField(fieldError.id);
8195                 if(f){
8196                     f.markInvalid(fieldError.msg);
8197                 }
8198             }
8199         }else{
8200             var field, id;
8201             for(id in errors){
8202                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8203                     field.markInvalid(errors[id]);
8204                 }
8205             }
8206         }
8207         //Roo.each(this.childForms || [], function (f) {
8208         //    f.markInvalid(errors);
8209         //});
8210
8211         return this;
8212     },
8213
8214     /**
8215      * Set values for fields in this form in bulk.
8216      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8217      * @return {BasicForm} this
8218      */
8219     setValues : function(values){
8220         if(values instanceof Array){ // array of objects
8221             for(var i = 0, len = values.length; i < len; i++){
8222                 var v = values[i];
8223                 var f = this.findField(v.id);
8224                 if(f){
8225                     f.setValue(v.value);
8226                     if(this.trackResetOnLoad){
8227                         f.originalValue = f.getValue();
8228                     }
8229                 }
8230             }
8231         }else{ // object hash
8232             var field, id;
8233             for(id in values){
8234                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8235
8236                     if (field.setFromData &&
8237                         field.valueField &&
8238                         field.displayField &&
8239                         // combos' with local stores can
8240                         // be queried via setValue()
8241                         // to set their value..
8242                         (field.store && !field.store.isLocal)
8243                         ) {
8244                         // it's a combo
8245                         var sd = { };
8246                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8247                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8248                         field.setFromData(sd);
8249
8250                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8251                         
8252                         field.setFromData(values);
8253                         
8254                     } else {
8255                         field.setValue(values[id]);
8256                     }
8257
8258
8259                     if(this.trackResetOnLoad){
8260                         field.originalValue = field.getValue();
8261                     }
8262                 }
8263             }
8264         }
8265
8266         //Roo.each(this.childForms || [], function (f) {
8267         //    f.setValues(values);
8268         //});
8269
8270         return this;
8271     },
8272
8273     /**
8274      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8275      * they are returned as an array.
8276      * @param {Boolean} asString
8277      * @return {Object}
8278      */
8279     getValues : function(asString){
8280         //if (this.childForms) {
8281             // copy values from the child forms
8282         //    Roo.each(this.childForms, function (f) {
8283         //        this.setValues(f.getValues());
8284         //    }, this);
8285         //}
8286
8287
8288
8289         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8290         if(asString === true){
8291             return fs;
8292         }
8293         return Roo.urlDecode(fs);
8294     },
8295
8296     /**
8297      * Returns the fields in this form as an object with key/value pairs.
8298      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8299      * @return {Object}
8300      */
8301     getFieldValues : function(with_hidden)
8302     {
8303         var items = this.getItems();
8304         var ret = {};
8305         items.each(function(f){
8306             
8307             if (!f.getName()) {
8308                 return;
8309             }
8310             
8311             var v = f.getValue();
8312             
8313             if (f.inputType =='radio') {
8314                 if (typeof(ret[f.getName()]) == 'undefined') {
8315                     ret[f.getName()] = ''; // empty..
8316                 }
8317
8318                 if (!f.el.dom.checked) {
8319                     return;
8320
8321                 }
8322                 v = f.el.dom.value;
8323
8324             }
8325             
8326             if(f.xtype == 'MoneyField'){
8327                 ret[f.currencyName] = f.getCurrency();
8328             }
8329
8330             // not sure if this supported any more..
8331             if ((typeof(v) == 'object') && f.getRawValue) {
8332                 v = f.getRawValue() ; // dates..
8333             }
8334             // combo boxes where name != hiddenName...
8335             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8336                 ret[f.name] = f.getRawValue();
8337             }
8338             ret[f.getName()] = v;
8339         });
8340
8341         return ret;
8342     },
8343
8344     /**
8345      * Clears all invalid messages in this form.
8346      * @return {BasicForm} this
8347      */
8348     clearInvalid : function(){
8349         var items = this.getItems();
8350
8351         items.each(function(f){
8352            f.clearInvalid();
8353         });
8354
8355         return this;
8356     },
8357
8358     /**
8359      * Resets this form.
8360      * @return {BasicForm} this
8361      */
8362     reset : function(){
8363         var items = this.getItems();
8364         items.each(function(f){
8365             f.reset();
8366         });
8367
8368         Roo.each(this.childForms || [], function (f) {
8369             f.reset();
8370         });
8371
8372
8373         return this;
8374     },
8375     
8376     getItems : function()
8377     {
8378         var r=new Roo.util.MixedCollection(false, function(o){
8379             return o.id || (o.id = Roo.id());
8380         });
8381         var iter = function(el) {
8382             if (el.inputEl) {
8383                 r.add(el);
8384             }
8385             if (!el.items) {
8386                 return;
8387             }
8388             Roo.each(el.items,function(e) {
8389                 iter(e);
8390             });
8391         };
8392
8393         iter(this);
8394         return r;
8395     },
8396     
8397     hideFields : function(items)
8398     {
8399         Roo.each(items, function(i){
8400             
8401             var f = this.findField(i);
8402             
8403             if(!f){
8404                 return;
8405             }
8406             
8407             f.hide();
8408             
8409         }, this);
8410     },
8411     
8412     showFields : function(items)
8413     {
8414         Roo.each(items, function(i){
8415             
8416             var f = this.findField(i);
8417             
8418             if(!f){
8419                 return;
8420             }
8421             
8422             f.show();
8423             
8424         }, this);
8425     }
8426
8427 });
8428
8429 Roo.apply(Roo.bootstrap.Form, {
8430     
8431     popover : {
8432         
8433         padding : 5,
8434         
8435         isApplied : false,
8436         
8437         isMasked : false,
8438         
8439         form : false,
8440         
8441         target : false,
8442         
8443         toolTip : false,
8444         
8445         intervalID : false,
8446         
8447         maskEl : false,
8448         
8449         apply : function()
8450         {
8451             if(this.isApplied){
8452                 return;
8453             }
8454             
8455             this.maskEl = {
8456                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8457                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8458                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8459                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8460             };
8461             
8462             this.maskEl.top.enableDisplayMode("block");
8463             this.maskEl.left.enableDisplayMode("block");
8464             this.maskEl.bottom.enableDisplayMode("block");
8465             this.maskEl.right.enableDisplayMode("block");
8466             
8467             this.toolTip = new Roo.bootstrap.Tooltip({
8468                 cls : 'roo-form-error-popover',
8469                 alignment : {
8470                     'left' : ['r-l', [-2,0], 'right'],
8471                     'right' : ['l-r', [2,0], 'left'],
8472                     'bottom' : ['tl-bl', [0,2], 'top'],
8473                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8474                 }
8475             });
8476             
8477             this.toolTip.render(Roo.get(document.body));
8478
8479             this.toolTip.el.enableDisplayMode("block");
8480             
8481             Roo.get(document.body).on('click', function(){
8482                 this.unmask();
8483             }, this);
8484             
8485             Roo.get(document.body).on('touchstart', function(){
8486                 this.unmask();
8487             }, this);
8488             
8489             this.isApplied = true
8490         },
8491         
8492         mask : function(form, target)
8493         {
8494             this.form = form;
8495             
8496             this.target = target;
8497             
8498             if(!this.form.errorMask || !target.el){
8499                 return;
8500             }
8501             
8502             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8503             
8504             Roo.log(scrollable);
8505             
8506             var ot = this.target.el.calcOffsetsTo(scrollable);
8507             
8508             var scrollTo = ot[1] - this.form.maskOffset;
8509             
8510             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8511             
8512             scrollable.scrollTo('top', scrollTo);
8513             
8514             var box = this.target.el.getBox();
8515             Roo.log(box);
8516             var zIndex = Roo.bootstrap.Modal.zIndex++;
8517
8518             
8519             this.maskEl.top.setStyle('position', 'absolute');
8520             this.maskEl.top.setStyle('z-index', zIndex);
8521             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8522             this.maskEl.top.setLeft(0);
8523             this.maskEl.top.setTop(0);
8524             this.maskEl.top.show();
8525             
8526             this.maskEl.left.setStyle('position', 'absolute');
8527             this.maskEl.left.setStyle('z-index', zIndex);
8528             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8529             this.maskEl.left.setLeft(0);
8530             this.maskEl.left.setTop(box.y - this.padding);
8531             this.maskEl.left.show();
8532
8533             this.maskEl.bottom.setStyle('position', 'absolute');
8534             this.maskEl.bottom.setStyle('z-index', zIndex);
8535             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8536             this.maskEl.bottom.setLeft(0);
8537             this.maskEl.bottom.setTop(box.bottom + this.padding);
8538             this.maskEl.bottom.show();
8539
8540             this.maskEl.right.setStyle('position', 'absolute');
8541             this.maskEl.right.setStyle('z-index', zIndex);
8542             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8543             this.maskEl.right.setLeft(box.right + this.padding);
8544             this.maskEl.right.setTop(box.y - this.padding);
8545             this.maskEl.right.show();
8546
8547             this.toolTip.bindEl = this.target.el;
8548
8549             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8550
8551             var tip = this.target.blankText;
8552
8553             if(this.target.getValue() !== '' ) {
8554                 
8555                 if (this.target.invalidText.length) {
8556                     tip = this.target.invalidText;
8557                 } else if (this.target.regexText.length){
8558                     tip = this.target.regexText;
8559                 }
8560             }
8561
8562             this.toolTip.show(tip);
8563
8564             this.intervalID = window.setInterval(function() {
8565                 Roo.bootstrap.Form.popover.unmask();
8566             }, 10000);
8567
8568             window.onwheel = function(){ return false;};
8569             
8570             (function(){ this.isMasked = true; }).defer(500, this);
8571             
8572         },
8573         
8574         unmask : function()
8575         {
8576             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8577                 return;
8578             }
8579             
8580             this.maskEl.top.setStyle('position', 'absolute');
8581             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8582             this.maskEl.top.hide();
8583
8584             this.maskEl.left.setStyle('position', 'absolute');
8585             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8586             this.maskEl.left.hide();
8587
8588             this.maskEl.bottom.setStyle('position', 'absolute');
8589             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8590             this.maskEl.bottom.hide();
8591
8592             this.maskEl.right.setStyle('position', 'absolute');
8593             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8594             this.maskEl.right.hide();
8595             
8596             this.toolTip.hide();
8597             
8598             this.toolTip.el.hide();
8599             
8600             window.onwheel = function(){ return true;};
8601             
8602             if(this.intervalID){
8603                 window.clearInterval(this.intervalID);
8604                 this.intervalID = false;
8605             }
8606             
8607             this.isMasked = false;
8608             
8609         }
8610         
8611     }
8612     
8613 });
8614
8615 /*
8616  * Based on:
8617  * Ext JS Library 1.1.1
8618  * Copyright(c) 2006-2007, Ext JS, LLC.
8619  *
8620  * Originally Released Under LGPL - original licence link has changed is not relivant.
8621  *
8622  * Fork - LGPL
8623  * <script type="text/javascript">
8624  */
8625 /**
8626  * @class Roo.form.VTypes
8627  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8628  * @singleton
8629  */
8630 Roo.form.VTypes = function(){
8631     // closure these in so they are only created once.
8632     var alpha = /^[a-zA-Z_]+$/;
8633     var alphanum = /^[a-zA-Z0-9_]+$/;
8634     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8635     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8636
8637     // All these messages and functions are configurable
8638     return {
8639         /**
8640          * The function used to validate email addresses
8641          * @param {String} value The email address
8642          */
8643         'email' : function(v){
8644             return email.test(v);
8645         },
8646         /**
8647          * The error text to display when the email validation function returns false
8648          * @type String
8649          */
8650         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8651         /**
8652          * The keystroke filter mask to be applied on email input
8653          * @type RegExp
8654          */
8655         'emailMask' : /[a-z0-9_\.\-@]/i,
8656
8657         /**
8658          * The function used to validate URLs
8659          * @param {String} value The URL
8660          */
8661         'url' : function(v){
8662             return url.test(v);
8663         },
8664         /**
8665          * The error text to display when the url validation function returns false
8666          * @type String
8667          */
8668         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8669         
8670         /**
8671          * The function used to validate alpha values
8672          * @param {String} value The value
8673          */
8674         'alpha' : function(v){
8675             return alpha.test(v);
8676         },
8677         /**
8678          * The error text to display when the alpha validation function returns false
8679          * @type String
8680          */
8681         'alphaText' : 'This field should only contain letters and _',
8682         /**
8683          * The keystroke filter mask to be applied on alpha input
8684          * @type RegExp
8685          */
8686         'alphaMask' : /[a-z_]/i,
8687
8688         /**
8689          * The function used to validate alphanumeric values
8690          * @param {String} value The value
8691          */
8692         'alphanum' : function(v){
8693             return alphanum.test(v);
8694         },
8695         /**
8696          * The error text to display when the alphanumeric validation function returns false
8697          * @type String
8698          */
8699         'alphanumText' : 'This field should only contain letters, numbers and _',
8700         /**
8701          * The keystroke filter mask to be applied on alphanumeric input
8702          * @type RegExp
8703          */
8704         'alphanumMask' : /[a-z0-9_]/i
8705     };
8706 }();/*
8707  * - LGPL
8708  *
8709  * Input
8710  * 
8711  */
8712
8713 /**
8714  * @class Roo.bootstrap.Input
8715  * @extends Roo.bootstrap.Component
8716  * Bootstrap Input class
8717  * @cfg {Boolean} disabled is it disabled
8718  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8719  * @cfg {String} name name of the input
8720  * @cfg {string} fieldLabel - the label associated
8721  * @cfg {string} placeholder - placeholder to put in text.
8722  * @cfg {string}  before - input group add on before
8723  * @cfg {string} after - input group add on after
8724  * @cfg {string} size - (lg|sm) or leave empty..
8725  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8726  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8727  * @cfg {Number} md colspan out of 12 for computer-sized screens
8728  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8729  * @cfg {string} value default value of the input
8730  * @cfg {Number} labelWidth set the width of label 
8731  * @cfg {Number} labellg set the width of label (1-12)
8732  * @cfg {Number} labelmd set the width of label (1-12)
8733  * @cfg {Number} labelsm set the width of label (1-12)
8734  * @cfg {Number} labelxs set the width of label (1-12)
8735  * @cfg {String} labelAlign (top|left)
8736  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8737  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8738  * @cfg {String} indicatorpos (left|right) default left
8739  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8740  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8741
8742  * @cfg {String} align (left|center|right) Default left
8743  * @cfg {Boolean} forceFeedback (true|false) Default false
8744  * 
8745  * @constructor
8746  * Create a new Input
8747  * @param {Object} config The config object
8748  */
8749
8750 Roo.bootstrap.Input = function(config){
8751     
8752     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8753     
8754     this.addEvents({
8755         /**
8756          * @event focus
8757          * Fires when this field receives input focus.
8758          * @param {Roo.form.Field} this
8759          */
8760         focus : true,
8761         /**
8762          * @event blur
8763          * Fires when this field loses input focus.
8764          * @param {Roo.form.Field} this
8765          */
8766         blur : true,
8767         /**
8768          * @event specialkey
8769          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8770          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8771          * @param {Roo.form.Field} this
8772          * @param {Roo.EventObject} e The event object
8773          */
8774         specialkey : true,
8775         /**
8776          * @event change
8777          * Fires just before the field blurs if the field value has changed.
8778          * @param {Roo.form.Field} this
8779          * @param {Mixed} newValue The new value
8780          * @param {Mixed} oldValue The original value
8781          */
8782         change : true,
8783         /**
8784          * @event invalid
8785          * Fires after the field has been marked as invalid.
8786          * @param {Roo.form.Field} this
8787          * @param {String} msg The validation message
8788          */
8789         invalid : true,
8790         /**
8791          * @event valid
8792          * Fires after the field has been validated with no errors.
8793          * @param {Roo.form.Field} this
8794          */
8795         valid : true,
8796          /**
8797          * @event keyup
8798          * Fires after the key up
8799          * @param {Roo.form.Field} this
8800          * @param {Roo.EventObject}  e The event Object
8801          */
8802         keyup : true
8803     });
8804 };
8805
8806 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8807      /**
8808      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8809       automatic validation (defaults to "keyup").
8810      */
8811     validationEvent : "keyup",
8812      /**
8813      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8814      */
8815     validateOnBlur : true,
8816     /**
8817      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8818      */
8819     validationDelay : 250,
8820      /**
8821      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8822      */
8823     focusClass : "x-form-focus",  // not needed???
8824     
8825        
8826     /**
8827      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8828      */
8829     invalidClass : "has-warning",
8830     
8831     /**
8832      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8833      */
8834     validClass : "has-success",
8835     
8836     /**
8837      * @cfg {Boolean} hasFeedback (true|false) default true
8838      */
8839     hasFeedback : true,
8840     
8841     /**
8842      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8843      */
8844     invalidFeedbackClass : "glyphicon-warning-sign",
8845     
8846     /**
8847      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8848      */
8849     validFeedbackClass : "glyphicon-ok",
8850     
8851     /**
8852      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8853      */
8854     selectOnFocus : false,
8855     
8856      /**
8857      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8858      */
8859     maskRe : null,
8860        /**
8861      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8862      */
8863     vtype : null,
8864     
8865       /**
8866      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8867      */
8868     disableKeyFilter : false,
8869     
8870        /**
8871      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8872      */
8873     disabled : false,
8874      /**
8875      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8876      */
8877     allowBlank : true,
8878     /**
8879      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8880      */
8881     blankText : "Please complete this mandatory field",
8882     
8883      /**
8884      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8885      */
8886     minLength : 0,
8887     /**
8888      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8889      */
8890     maxLength : Number.MAX_VALUE,
8891     /**
8892      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8893      */
8894     minLengthText : "The minimum length for this field is {0}",
8895     /**
8896      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8897      */
8898     maxLengthText : "The maximum length for this field is {0}",
8899   
8900     
8901     /**
8902      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8903      * If available, this function will be called only after the basic validators all return true, and will be passed the
8904      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8905      */
8906     validator : null,
8907     /**
8908      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8909      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8910      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8911      */
8912     regex : null,
8913     /**
8914      * @cfg {String} regexText -- Depricated - use Invalid Text
8915      */
8916     regexText : "",
8917     
8918     /**
8919      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8920      */
8921     invalidText : "",
8922     
8923     
8924     
8925     autocomplete: false,
8926     
8927     
8928     fieldLabel : '',
8929     inputType : 'text',
8930     
8931     name : false,
8932     placeholder: false,
8933     before : false,
8934     after : false,
8935     size : false,
8936     hasFocus : false,
8937     preventMark: false,
8938     isFormField : true,
8939     value : '',
8940     labelWidth : 2,
8941     labelAlign : false,
8942     readOnly : false,
8943     align : false,
8944     formatedValue : false,
8945     forceFeedback : false,
8946     
8947     indicatorpos : 'left',
8948     
8949     labellg : 0,
8950     labelmd : 0,
8951     labelsm : 0,
8952     labelxs : 0,
8953     
8954     capture : '',
8955     accept : '',
8956     
8957     parentLabelAlign : function()
8958     {
8959         var parent = this;
8960         while (parent.parent()) {
8961             parent = parent.parent();
8962             if (typeof(parent.labelAlign) !='undefined') {
8963                 return parent.labelAlign;
8964             }
8965         }
8966         return 'left';
8967         
8968     },
8969     
8970     getAutoCreate : function()
8971     {
8972         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8973         
8974         var id = Roo.id();
8975         
8976         var cfg = {};
8977         
8978         if(this.inputType != 'hidden'){
8979             cfg.cls = 'form-group' //input-group
8980         }
8981         
8982         var input =  {
8983             tag: 'input',
8984             id : id,
8985             type : this.inputType,
8986             value : this.value,
8987             cls : 'form-control',
8988             placeholder : this.placeholder || '',
8989             autocomplete : this.autocomplete || 'new-password'
8990         };
8991         
8992         if(this.capture.length){
8993             input.capture = this.capture;
8994         }
8995         
8996         if(this.accept.length){
8997             input.accept = this.accept + "/*";
8998         }
8999         
9000         if(this.align){
9001             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9002         }
9003         
9004         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9005             input.maxLength = this.maxLength;
9006         }
9007         
9008         if (this.disabled) {
9009             input.disabled=true;
9010         }
9011         
9012         if (this.readOnly) {
9013             input.readonly=true;
9014         }
9015         
9016         if (this.name) {
9017             input.name = this.name;
9018         }
9019         
9020         if (this.size) {
9021             input.cls += ' input-' + this.size;
9022         }
9023         
9024         var settings=this;
9025         ['xs','sm','md','lg'].map(function(size){
9026             if (settings[size]) {
9027                 cfg.cls += ' col-' + size + '-' + settings[size];
9028             }
9029         });
9030         
9031         var inputblock = input;
9032         
9033         var feedback = {
9034             tag: 'span',
9035             cls: 'glyphicon form-control-feedback'
9036         };
9037             
9038         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9039             
9040             inputblock = {
9041                 cls : 'has-feedback',
9042                 cn :  [
9043                     input,
9044                     feedback
9045                 ] 
9046             };  
9047         }
9048         
9049         if (this.before || this.after) {
9050             
9051             inputblock = {
9052                 cls : 'input-group',
9053                 cn :  [] 
9054             };
9055             
9056             if (this.before && typeof(this.before) == 'string') {
9057                 
9058                 inputblock.cn.push({
9059                     tag :'span',
9060                     cls : 'roo-input-before input-group-addon',
9061                     html : this.before
9062                 });
9063             }
9064             if (this.before && typeof(this.before) == 'object') {
9065                 this.before = Roo.factory(this.before);
9066                 
9067                 inputblock.cn.push({
9068                     tag :'span',
9069                     cls : 'roo-input-before input-group-' +
9070                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9071                 });
9072             }
9073             
9074             inputblock.cn.push(input);
9075             
9076             if (this.after && typeof(this.after) == 'string') {
9077                 inputblock.cn.push({
9078                     tag :'span',
9079                     cls : 'roo-input-after input-group-addon',
9080                     html : this.after
9081                 });
9082             }
9083             if (this.after && typeof(this.after) == 'object') {
9084                 this.after = Roo.factory(this.after);
9085                 
9086                 inputblock.cn.push({
9087                     tag :'span',
9088                     cls : 'roo-input-after input-group-' +
9089                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9090                 });
9091             }
9092             
9093             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9094                 inputblock.cls += ' has-feedback';
9095                 inputblock.cn.push(feedback);
9096             }
9097         };
9098         
9099         if (align ==='left' && this.fieldLabel.length) {
9100             
9101             cfg.cls += ' roo-form-group-label-left';
9102             
9103             cfg.cn = [
9104                 {
9105                     tag : 'i',
9106                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9107                     tooltip : 'This field is required'
9108                 },
9109                 {
9110                     tag: 'label',
9111                     'for' :  id,
9112                     cls : 'control-label',
9113                     html : this.fieldLabel
9114
9115                 },
9116                 {
9117                     cls : "", 
9118                     cn: [
9119                         inputblock
9120                     ]
9121                 }
9122             ];
9123             
9124             var labelCfg = cfg.cn[1];
9125             var contentCfg = cfg.cn[2];
9126             
9127             if(this.indicatorpos == 'right'){
9128                 cfg.cn = [
9129                     {
9130                         tag: 'label',
9131                         'for' :  id,
9132                         cls : 'control-label',
9133                         cn : [
9134                             {
9135                                 tag : 'span',
9136                                 html : this.fieldLabel
9137                             },
9138                             {
9139                                 tag : 'i',
9140                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9141                                 tooltip : 'This field is required'
9142                             }
9143                         ]
9144                     },
9145                     {
9146                         cls : "",
9147                         cn: [
9148                             inputblock
9149                         ]
9150                     }
9151
9152                 ];
9153                 
9154                 labelCfg = cfg.cn[0];
9155                 contentCfg = cfg.cn[1];
9156             
9157             }
9158             
9159             if(this.labelWidth > 12){
9160                 labelCfg.style = "width: " + this.labelWidth + 'px';
9161             }
9162             
9163             if(this.labelWidth < 13 && this.labelmd == 0){
9164                 this.labelmd = this.labelWidth;
9165             }
9166             
9167             if(this.labellg > 0){
9168                 labelCfg.cls += ' col-lg-' + this.labellg;
9169                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9170             }
9171             
9172             if(this.labelmd > 0){
9173                 labelCfg.cls += ' col-md-' + this.labelmd;
9174                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9175             }
9176             
9177             if(this.labelsm > 0){
9178                 labelCfg.cls += ' col-sm-' + this.labelsm;
9179                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9180             }
9181             
9182             if(this.labelxs > 0){
9183                 labelCfg.cls += ' col-xs-' + this.labelxs;
9184                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9185             }
9186             
9187             
9188         } else if ( this.fieldLabel.length) {
9189                 
9190             cfg.cn = [
9191                 {
9192                     tag : 'i',
9193                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9194                     tooltip : 'This field is required'
9195                 },
9196                 {
9197                     tag: 'label',
9198                    //cls : 'input-group-addon',
9199                     html : this.fieldLabel
9200
9201                 },
9202
9203                inputblock
9204
9205            ];
9206            
9207            if(this.indicatorpos == 'right'){
9208                 
9209                 cfg.cn = [
9210                     {
9211                         tag: 'label',
9212                        //cls : 'input-group-addon',
9213                         html : this.fieldLabel
9214
9215                     },
9216                     {
9217                         tag : 'i',
9218                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9219                         tooltip : 'This field is required'
9220                     },
9221
9222                    inputblock
9223
9224                ];
9225
9226             }
9227
9228         } else {
9229             
9230             cfg.cn = [
9231
9232                     inputblock
9233
9234             ];
9235                 
9236                 
9237         };
9238         
9239         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9240            cfg.cls += ' navbar-form';
9241         }
9242         
9243         if (this.parentType === 'NavGroup') {
9244            cfg.cls += ' navbar-form';
9245            cfg.tag = 'li';
9246         }
9247         
9248         return cfg;
9249         
9250     },
9251     /**
9252      * return the real input element.
9253      */
9254     inputEl: function ()
9255     {
9256         return this.el.select('input.form-control',true).first();
9257     },
9258     
9259     tooltipEl : function()
9260     {
9261         return this.inputEl();
9262     },
9263     
9264     indicatorEl : function()
9265     {
9266         var indicator = this.el.select('i.roo-required-indicator',true).first();
9267         
9268         if(!indicator){
9269             return false;
9270         }
9271         
9272         return indicator;
9273         
9274     },
9275     
9276     setDisabled : function(v)
9277     {
9278         var i  = this.inputEl().dom;
9279         if (!v) {
9280             i.removeAttribute('disabled');
9281             return;
9282             
9283         }
9284         i.setAttribute('disabled','true');
9285     },
9286     initEvents : function()
9287     {
9288           
9289         this.inputEl().on("keydown" , this.fireKey,  this);
9290         this.inputEl().on("focus", this.onFocus,  this);
9291         this.inputEl().on("blur", this.onBlur,  this);
9292         
9293         this.inputEl().relayEvent('keyup', this);
9294         
9295         this.indicator = this.indicatorEl();
9296         
9297         if(this.indicator){
9298             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9299         }
9300  
9301         // reference to original value for reset
9302         this.originalValue = this.getValue();
9303         //Roo.form.TextField.superclass.initEvents.call(this);
9304         if(this.validationEvent == 'keyup'){
9305             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9306             this.inputEl().on('keyup', this.filterValidation, this);
9307         }
9308         else if(this.validationEvent !== false){
9309             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9310         }
9311         
9312         if(this.selectOnFocus){
9313             this.on("focus", this.preFocus, this);
9314             
9315         }
9316         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9317             this.inputEl().on("keypress", this.filterKeys, this);
9318         } else {
9319             this.inputEl().relayEvent('keypress', this);
9320         }
9321        /* if(this.grow){
9322             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9323             this.el.on("click", this.autoSize,  this);
9324         }
9325         */
9326         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9327             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9328         }
9329         
9330         if (typeof(this.before) == 'object') {
9331             this.before.render(this.el.select('.roo-input-before',true).first());
9332         }
9333         if (typeof(this.after) == 'object') {
9334             this.after.render(this.el.select('.roo-input-after',true).first());
9335         }
9336         
9337         this.inputEl().on('change', this.onChange, this);
9338         
9339     },
9340     filterValidation : function(e){
9341         if(!e.isNavKeyPress()){
9342             this.validationTask.delay(this.validationDelay);
9343         }
9344     },
9345      /**
9346      * Validates the field value
9347      * @return {Boolean} True if the value is valid, else false
9348      */
9349     validate : function(){
9350         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9351         if(this.disabled || this.validateValue(this.getRawValue())){
9352             this.markValid();
9353             return true;
9354         }
9355         
9356         this.markInvalid();
9357         return false;
9358     },
9359     
9360     
9361     /**
9362      * Validates a value according to the field's validation rules and marks the field as invalid
9363      * if the validation fails
9364      * @param {Mixed} value The value to validate
9365      * @return {Boolean} True if the value is valid, else false
9366      */
9367     validateValue : function(value)
9368     {
9369         if(this.getVisibilityEl().hasClass('hidden')){
9370             return true;
9371         }
9372         
9373         if(value.length < 1)  { // if it's blank
9374             if(this.allowBlank){
9375                 return true;
9376             }
9377             return false;
9378         }
9379         
9380         if(value.length < this.minLength){
9381             return false;
9382         }
9383         if(value.length > this.maxLength){
9384             return false;
9385         }
9386         if(this.vtype){
9387             var vt = Roo.form.VTypes;
9388             if(!vt[this.vtype](value, this)){
9389                 return false;
9390             }
9391         }
9392         if(typeof this.validator == "function"){
9393             var msg = this.validator(value);
9394             if(msg !== true){
9395                 return false;
9396             }
9397             if (typeof(msg) == 'string') {
9398                 this.invalidText = msg;
9399             }
9400         }
9401         
9402         if(this.regex && !this.regex.test(value)){
9403             return false;
9404         }
9405         
9406         return true;
9407     },
9408     
9409      // private
9410     fireKey : function(e){
9411         //Roo.log('field ' + e.getKey());
9412         if(e.isNavKeyPress()){
9413             this.fireEvent("specialkey", this, e);
9414         }
9415     },
9416     focus : function (selectText){
9417         if(this.rendered){
9418             this.inputEl().focus();
9419             if(selectText === true){
9420                 this.inputEl().dom.select();
9421             }
9422         }
9423         return this;
9424     } ,
9425     
9426     onFocus : function(){
9427         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9428            // this.el.addClass(this.focusClass);
9429         }
9430         if(!this.hasFocus){
9431             this.hasFocus = true;
9432             this.startValue = this.getValue();
9433             this.fireEvent("focus", this);
9434         }
9435     },
9436     
9437     beforeBlur : Roo.emptyFn,
9438
9439     
9440     // private
9441     onBlur : function(){
9442         this.beforeBlur();
9443         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9444             //this.el.removeClass(this.focusClass);
9445         }
9446         this.hasFocus = false;
9447         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9448             this.validate();
9449         }
9450         var v = this.getValue();
9451         if(String(v) !== String(this.startValue)){
9452             this.fireEvent('change', this, v, this.startValue);
9453         }
9454         this.fireEvent("blur", this);
9455     },
9456     
9457     onChange : function(e)
9458     {
9459         var v = this.getValue();
9460         if(String(v) !== String(this.startValue)){
9461             this.fireEvent('change', this, v, this.startValue);
9462         }
9463         
9464     },
9465     
9466     /**
9467      * Resets the current field value to the originally loaded value and clears any validation messages
9468      */
9469     reset : function(){
9470         this.setValue(this.originalValue);
9471         this.validate();
9472     },
9473      /**
9474      * Returns the name of the field
9475      * @return {Mixed} name The name field
9476      */
9477     getName: function(){
9478         return this.name;
9479     },
9480      /**
9481      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9482      * @return {Mixed} value The field value
9483      */
9484     getValue : function(){
9485         
9486         var v = this.inputEl().getValue();
9487         
9488         return v;
9489     },
9490     /**
9491      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9492      * @return {Mixed} value The field value
9493      */
9494     getRawValue : function(){
9495         var v = this.inputEl().getValue();
9496         
9497         return v;
9498     },
9499     
9500     /**
9501      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9502      * @param {Mixed} value The value to set
9503      */
9504     setRawValue : function(v){
9505         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9506     },
9507     
9508     selectText : function(start, end){
9509         var v = this.getRawValue();
9510         if(v.length > 0){
9511             start = start === undefined ? 0 : start;
9512             end = end === undefined ? v.length : end;
9513             var d = this.inputEl().dom;
9514             if(d.setSelectionRange){
9515                 d.setSelectionRange(start, end);
9516             }else if(d.createTextRange){
9517                 var range = d.createTextRange();
9518                 range.moveStart("character", start);
9519                 range.moveEnd("character", v.length-end);
9520                 range.select();
9521             }
9522         }
9523     },
9524     
9525     /**
9526      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9527      * @param {Mixed} value The value to set
9528      */
9529     setValue : function(v){
9530         this.value = v;
9531         if(this.rendered){
9532             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9533             this.validate();
9534         }
9535     },
9536     
9537     /*
9538     processValue : function(value){
9539         if(this.stripCharsRe){
9540             var newValue = value.replace(this.stripCharsRe, '');
9541             if(newValue !== value){
9542                 this.setRawValue(newValue);
9543                 return newValue;
9544             }
9545         }
9546         return value;
9547     },
9548   */
9549     preFocus : function(){
9550         
9551         if(this.selectOnFocus){
9552             this.inputEl().dom.select();
9553         }
9554     },
9555     filterKeys : function(e){
9556         var k = e.getKey();
9557         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9558             return;
9559         }
9560         var c = e.getCharCode(), cc = String.fromCharCode(c);
9561         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9562             return;
9563         }
9564         if(!this.maskRe.test(cc)){
9565             e.stopEvent();
9566         }
9567     },
9568      /**
9569      * Clear any invalid styles/messages for this field
9570      */
9571     clearInvalid : function(){
9572         
9573         if(!this.el || this.preventMark){ // not rendered
9574             return;
9575         }
9576         
9577      
9578         this.el.removeClass(this.invalidClass);
9579         
9580         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9581             
9582             var feedback = this.el.select('.form-control-feedback', true).first();
9583             
9584             if(feedback){
9585                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9586             }
9587             
9588         }
9589         
9590         if(this.indicator){
9591             this.indicator.removeClass('visible');
9592             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9593         }
9594         
9595         this.fireEvent('valid', this);
9596     },
9597     
9598      /**
9599      * Mark this field as valid
9600      */
9601     markValid : function()
9602     {
9603         if(!this.el  || this.preventMark){ // not rendered...
9604             return;
9605         }
9606         
9607         this.el.removeClass([this.invalidClass, this.validClass]);
9608         
9609         var feedback = this.el.select('.form-control-feedback', true).first();
9610             
9611         if(feedback){
9612             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9613         }
9614         
9615         if(this.indicator){
9616             this.indicator.removeClass('visible');
9617             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9618         }
9619         
9620         if(this.disabled){
9621             return;
9622         }
9623         
9624         if(this.allowBlank && !this.getRawValue().length){
9625             return;
9626         }
9627         
9628         this.el.addClass(this.validClass);
9629         
9630         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9631             
9632             var feedback = this.el.select('.form-control-feedback', true).first();
9633             
9634             if(feedback){
9635                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9636                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9637             }
9638             
9639         }
9640         
9641         this.fireEvent('valid', this);
9642     },
9643     
9644      /**
9645      * Mark this field as invalid
9646      * @param {String} msg The validation message
9647      */
9648     markInvalid : function(msg)
9649     {
9650         if(!this.el  || this.preventMark){ // not rendered
9651             return;
9652         }
9653         
9654         this.el.removeClass([this.invalidClass, this.validClass]);
9655         
9656         var feedback = this.el.select('.form-control-feedback', true).first();
9657             
9658         if(feedback){
9659             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9660         }
9661
9662         if(this.disabled){
9663             return;
9664         }
9665         
9666         if(this.allowBlank && !this.getRawValue().length){
9667             return;
9668         }
9669         
9670         if(this.indicator){
9671             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9672             this.indicator.addClass('visible');
9673         }
9674         
9675         this.el.addClass(this.invalidClass);
9676         
9677         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9678             
9679             var feedback = this.el.select('.form-control-feedback', true).first();
9680             
9681             if(feedback){
9682                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9683                 
9684                 if(this.getValue().length || this.forceFeedback){
9685                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9686                 }
9687                 
9688             }
9689             
9690         }
9691         
9692         this.fireEvent('invalid', this, msg);
9693     },
9694     // private
9695     SafariOnKeyDown : function(event)
9696     {
9697         // this is a workaround for a password hang bug on chrome/ webkit.
9698         if (this.inputEl().dom.type != 'password') {
9699             return;
9700         }
9701         
9702         var isSelectAll = false;
9703         
9704         if(this.inputEl().dom.selectionEnd > 0){
9705             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9706         }
9707         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9708             event.preventDefault();
9709             this.setValue('');
9710             return;
9711         }
9712         
9713         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9714             
9715             event.preventDefault();
9716             // this is very hacky as keydown always get's upper case.
9717             //
9718             var cc = String.fromCharCode(event.getCharCode());
9719             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9720             
9721         }
9722     },
9723     adjustWidth : function(tag, w){
9724         tag = tag.toLowerCase();
9725         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9726             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9727                 if(tag == 'input'){
9728                     return w + 2;
9729                 }
9730                 if(tag == 'textarea'){
9731                     return w-2;
9732                 }
9733             }else if(Roo.isOpera){
9734                 if(tag == 'input'){
9735                     return w + 2;
9736                 }
9737                 if(tag == 'textarea'){
9738                     return w-2;
9739                 }
9740             }
9741         }
9742         return w;
9743     },
9744     
9745     setFieldLabel : function(v)
9746     {
9747         if(!this.rendered){
9748             return;
9749         }
9750         
9751         if(this.indicator){
9752             var ar = this.el.select('label > span',true);
9753             
9754             if (ar.elements.length) {
9755                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9756                 this.fieldLabel = v;
9757                 return;
9758             }
9759             
9760             var br = this.el.select('label',true);
9761             
9762             if(br.elements.length) {
9763                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9764                 this.fieldLabel = v;
9765                 return;
9766             }
9767             
9768             Roo.log('Cannot Found any of label > span || label in input');
9769             return;
9770         }
9771         
9772         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9773         this.fieldLabel = v;
9774         
9775         
9776     }
9777 });
9778
9779  
9780 /*
9781  * - LGPL
9782  *
9783  * Input
9784  * 
9785  */
9786
9787 /**
9788  * @class Roo.bootstrap.TextArea
9789  * @extends Roo.bootstrap.Input
9790  * Bootstrap TextArea class
9791  * @cfg {Number} cols Specifies the visible width of a text area
9792  * @cfg {Number} rows Specifies the visible number of lines in a text area
9793  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9794  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9795  * @cfg {string} html text
9796  * 
9797  * @constructor
9798  * Create a new TextArea
9799  * @param {Object} config The config object
9800  */
9801
9802 Roo.bootstrap.TextArea = function(config){
9803     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9804    
9805 };
9806
9807 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9808      
9809     cols : false,
9810     rows : 5,
9811     readOnly : false,
9812     warp : 'soft',
9813     resize : false,
9814     value: false,
9815     html: false,
9816     
9817     getAutoCreate : function(){
9818         
9819         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9820         
9821         var id = Roo.id();
9822         
9823         var cfg = {};
9824         
9825         if(this.inputType != 'hidden'){
9826             cfg.cls = 'form-group' //input-group
9827         }
9828         
9829         var input =  {
9830             tag: 'textarea',
9831             id : id,
9832             warp : this.warp,
9833             rows : this.rows,
9834             value : this.value || '',
9835             html: this.html || '',
9836             cls : 'form-control',
9837             placeholder : this.placeholder || '' 
9838             
9839         };
9840         
9841         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9842             input.maxLength = this.maxLength;
9843         }
9844         
9845         if(this.resize){
9846             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9847         }
9848         
9849         if(this.cols){
9850             input.cols = this.cols;
9851         }
9852         
9853         if (this.readOnly) {
9854             input.readonly = true;
9855         }
9856         
9857         if (this.name) {
9858             input.name = this.name;
9859         }
9860         
9861         if (this.size) {
9862             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9863         }
9864         
9865         var settings=this;
9866         ['xs','sm','md','lg'].map(function(size){
9867             if (settings[size]) {
9868                 cfg.cls += ' col-' + size + '-' + settings[size];
9869             }
9870         });
9871         
9872         var inputblock = input;
9873         
9874         if(this.hasFeedback && !this.allowBlank){
9875             
9876             var feedback = {
9877                 tag: 'span',
9878                 cls: 'glyphicon form-control-feedback'
9879             };
9880
9881             inputblock = {
9882                 cls : 'has-feedback',
9883                 cn :  [
9884                     input,
9885                     feedback
9886                 ] 
9887             };  
9888         }
9889         
9890         
9891         if (this.before || this.after) {
9892             
9893             inputblock = {
9894                 cls : 'input-group',
9895                 cn :  [] 
9896             };
9897             if (this.before) {
9898                 inputblock.cn.push({
9899                     tag :'span',
9900                     cls : 'input-group-addon',
9901                     html : this.before
9902                 });
9903             }
9904             
9905             inputblock.cn.push(input);
9906             
9907             if(this.hasFeedback && !this.allowBlank){
9908                 inputblock.cls += ' has-feedback';
9909                 inputblock.cn.push(feedback);
9910             }
9911             
9912             if (this.after) {
9913                 inputblock.cn.push({
9914                     tag :'span',
9915                     cls : 'input-group-addon',
9916                     html : this.after
9917                 });
9918             }
9919             
9920         }
9921         
9922         if (align ==='left' && this.fieldLabel.length) {
9923             cfg.cn = [
9924                 {
9925                     tag: 'label',
9926                     'for' :  id,
9927                     cls : 'control-label',
9928                     html : this.fieldLabel
9929                 },
9930                 {
9931                     cls : "",
9932                     cn: [
9933                         inputblock
9934                     ]
9935                 }
9936
9937             ];
9938             
9939             if(this.labelWidth > 12){
9940                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9941             }
9942
9943             if(this.labelWidth < 13 && this.labelmd == 0){
9944                 this.labelmd = this.labelWidth;
9945             }
9946
9947             if(this.labellg > 0){
9948                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9949                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9950             }
9951
9952             if(this.labelmd > 0){
9953                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9954                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9955             }
9956
9957             if(this.labelsm > 0){
9958                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9959                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9960             }
9961
9962             if(this.labelxs > 0){
9963                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9964                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9965             }
9966             
9967         } else if ( this.fieldLabel.length) {
9968             cfg.cn = [
9969
9970                {
9971                    tag: 'label',
9972                    //cls : 'input-group-addon',
9973                    html : this.fieldLabel
9974
9975                },
9976
9977                inputblock
9978
9979            ];
9980
9981         } else {
9982
9983             cfg.cn = [
9984
9985                 inputblock
9986
9987             ];
9988                 
9989         }
9990         
9991         if (this.disabled) {
9992             input.disabled=true;
9993         }
9994         
9995         return cfg;
9996         
9997     },
9998     /**
9999      * return the real textarea element.
10000      */
10001     inputEl: function ()
10002     {
10003         return this.el.select('textarea.form-control',true).first();
10004     },
10005     
10006     /**
10007      * Clear any invalid styles/messages for this field
10008      */
10009     clearInvalid : function()
10010     {
10011         
10012         if(!this.el || this.preventMark){ // not rendered
10013             return;
10014         }
10015         
10016         var label = this.el.select('label', true).first();
10017         var icon = this.el.select('i.fa-star', true).first();
10018         
10019         if(label && icon){
10020             icon.remove();
10021         }
10022         
10023         this.el.removeClass(this.invalidClass);
10024         
10025         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10026             
10027             var feedback = this.el.select('.form-control-feedback', true).first();
10028             
10029             if(feedback){
10030                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10031             }
10032             
10033         }
10034         
10035         this.fireEvent('valid', this);
10036     },
10037     
10038      /**
10039      * Mark this field as valid
10040      */
10041     markValid : function()
10042     {
10043         if(!this.el  || this.preventMark){ // not rendered
10044             return;
10045         }
10046         
10047         this.el.removeClass([this.invalidClass, this.validClass]);
10048         
10049         var feedback = this.el.select('.form-control-feedback', true).first();
10050             
10051         if(feedback){
10052             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10053         }
10054
10055         if(this.disabled || this.allowBlank){
10056             return;
10057         }
10058         
10059         var label = this.el.select('label', true).first();
10060         var icon = this.el.select('i.fa-star', true).first();
10061         
10062         if(label && icon){
10063             icon.remove();
10064         }
10065         
10066         this.el.addClass(this.validClass);
10067         
10068         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10069             
10070             var feedback = this.el.select('.form-control-feedback', true).first();
10071             
10072             if(feedback){
10073                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10074                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10075             }
10076             
10077         }
10078         
10079         this.fireEvent('valid', this);
10080     },
10081     
10082      /**
10083      * Mark this field as invalid
10084      * @param {String} msg The validation message
10085      */
10086     markInvalid : function(msg)
10087     {
10088         if(!this.el  || this.preventMark){ // not rendered
10089             return;
10090         }
10091         
10092         this.el.removeClass([this.invalidClass, this.validClass]);
10093         
10094         var feedback = this.el.select('.form-control-feedback', true).first();
10095             
10096         if(feedback){
10097             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10098         }
10099
10100         if(this.disabled || this.allowBlank){
10101             return;
10102         }
10103         
10104         var label = this.el.select('label', true).first();
10105         var icon = this.el.select('i.fa-star', true).first();
10106         
10107         if(!this.getValue().length && label && !icon){
10108             this.el.createChild({
10109                 tag : 'i',
10110                 cls : 'text-danger fa fa-lg fa-star',
10111                 tooltip : 'This field is required',
10112                 style : 'margin-right:5px;'
10113             }, label, true);
10114         }
10115
10116         this.el.addClass(this.invalidClass);
10117         
10118         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10119             
10120             var feedback = this.el.select('.form-control-feedback', true).first();
10121             
10122             if(feedback){
10123                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10124                 
10125                 if(this.getValue().length || this.forceFeedback){
10126                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10127                 }
10128                 
10129             }
10130             
10131         }
10132         
10133         this.fireEvent('invalid', this, msg);
10134     }
10135 });
10136
10137  
10138 /*
10139  * - LGPL
10140  *
10141  * trigger field - base class for combo..
10142  * 
10143  */
10144  
10145 /**
10146  * @class Roo.bootstrap.TriggerField
10147  * @extends Roo.bootstrap.Input
10148  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10149  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10150  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10151  * for which you can provide a custom implementation.  For example:
10152  * <pre><code>
10153 var trigger = new Roo.bootstrap.TriggerField();
10154 trigger.onTriggerClick = myTriggerFn;
10155 trigger.applyTo('my-field');
10156 </code></pre>
10157  *
10158  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10159  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10160  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10161  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10162  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10163
10164  * @constructor
10165  * Create a new TriggerField.
10166  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10167  * to the base TextField)
10168  */
10169 Roo.bootstrap.TriggerField = function(config){
10170     this.mimicing = false;
10171     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10172 };
10173
10174 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10175     /**
10176      * @cfg {String} triggerClass A CSS class to apply to the trigger
10177      */
10178      /**
10179      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10180      */
10181     hideTrigger:false,
10182
10183     /**
10184      * @cfg {Boolean} removable (true|false) special filter default false
10185      */
10186     removable : false,
10187     
10188     /** @cfg {Boolean} grow @hide */
10189     /** @cfg {Number} growMin @hide */
10190     /** @cfg {Number} growMax @hide */
10191
10192     /**
10193      * @hide 
10194      * @method
10195      */
10196     autoSize: Roo.emptyFn,
10197     // private
10198     monitorTab : true,
10199     // private
10200     deferHeight : true,
10201
10202     
10203     actionMode : 'wrap',
10204     
10205     caret : false,
10206     
10207     
10208     getAutoCreate : function(){
10209        
10210         var align = this.labelAlign || this.parentLabelAlign();
10211         
10212         var id = Roo.id();
10213         
10214         var cfg = {
10215             cls: 'form-group' //input-group
10216         };
10217         
10218         
10219         var input =  {
10220             tag: 'input',
10221             id : id,
10222             type : this.inputType,
10223             cls : 'form-control',
10224             autocomplete: 'new-password',
10225             placeholder : this.placeholder || '' 
10226             
10227         };
10228         if (this.name) {
10229             input.name = this.name;
10230         }
10231         if (this.size) {
10232             input.cls += ' input-' + this.size;
10233         }
10234         
10235         if (this.disabled) {
10236             input.disabled=true;
10237         }
10238         
10239         var inputblock = input;
10240         
10241         if(this.hasFeedback && !this.allowBlank){
10242             
10243             var feedback = {
10244                 tag: 'span',
10245                 cls: 'glyphicon form-control-feedback'
10246             };
10247             
10248             if(this.removable && !this.editable && !this.tickable){
10249                 inputblock = {
10250                     cls : 'has-feedback',
10251                     cn :  [
10252                         inputblock,
10253                         {
10254                             tag: 'button',
10255                             html : 'x',
10256                             cls : 'roo-combo-removable-btn close'
10257                         },
10258                         feedback
10259                     ] 
10260                 };
10261             } else {
10262                 inputblock = {
10263                     cls : 'has-feedback',
10264                     cn :  [
10265                         inputblock,
10266                         feedback
10267                     ] 
10268                 };
10269             }
10270
10271         } else {
10272             if(this.removable && !this.editable && !this.tickable){
10273                 inputblock = {
10274                     cls : 'roo-removable',
10275                     cn :  [
10276                         inputblock,
10277                         {
10278                             tag: 'button',
10279                             html : 'x',
10280                             cls : 'roo-combo-removable-btn close'
10281                         }
10282                     ] 
10283                 };
10284             }
10285         }
10286         
10287         if (this.before || this.after) {
10288             
10289             inputblock = {
10290                 cls : 'input-group',
10291                 cn :  [] 
10292             };
10293             if (this.before) {
10294                 inputblock.cn.push({
10295                     tag :'span',
10296                     cls : 'input-group-addon',
10297                     html : this.before
10298                 });
10299             }
10300             
10301             inputblock.cn.push(input);
10302             
10303             if(this.hasFeedback && !this.allowBlank){
10304                 inputblock.cls += ' has-feedback';
10305                 inputblock.cn.push(feedback);
10306             }
10307             
10308             if (this.after) {
10309                 inputblock.cn.push({
10310                     tag :'span',
10311                     cls : 'input-group-addon',
10312                     html : this.after
10313                 });
10314             }
10315             
10316         };
10317         
10318         var box = {
10319             tag: 'div',
10320             cn: [
10321                 {
10322                     tag: 'input',
10323                     type : 'hidden',
10324                     cls: 'form-hidden-field'
10325                 },
10326                 inputblock
10327             ]
10328             
10329         };
10330         
10331         if(this.multiple){
10332             box = {
10333                 tag: 'div',
10334                 cn: [
10335                     {
10336                         tag: 'input',
10337                         type : 'hidden',
10338                         cls: 'form-hidden-field'
10339                     },
10340                     {
10341                         tag: 'ul',
10342                         cls: 'roo-select2-choices',
10343                         cn:[
10344                             {
10345                                 tag: 'li',
10346                                 cls: 'roo-select2-search-field',
10347                                 cn: [
10348
10349                                     inputblock
10350                                 ]
10351                             }
10352                         ]
10353                     }
10354                 ]
10355             }
10356         };
10357         
10358         var combobox = {
10359             cls: 'roo-select2-container input-group',
10360             cn: [
10361                 box
10362 //                {
10363 //                    tag: 'ul',
10364 //                    cls: 'typeahead typeahead-long dropdown-menu',
10365 //                    style: 'display:none'
10366 //                }
10367             ]
10368         };
10369         
10370         if(!this.multiple && this.showToggleBtn){
10371             
10372             var caret = {
10373                         tag: 'span',
10374                         cls: 'caret'
10375              };
10376             if (this.caret != false) {
10377                 caret = {
10378                      tag: 'i',
10379                      cls: 'fa fa-' + this.caret
10380                 };
10381                 
10382             }
10383             
10384             combobox.cn.push({
10385                 tag :'span',
10386                 cls : 'input-group-addon btn dropdown-toggle',
10387                 cn : [
10388                     caret,
10389                     {
10390                         tag: 'span',
10391                         cls: 'combobox-clear',
10392                         cn  : [
10393                             {
10394                                 tag : 'i',
10395                                 cls: 'icon-remove'
10396                             }
10397                         ]
10398                     }
10399                 ]
10400
10401             })
10402         }
10403         
10404         if(this.multiple){
10405             combobox.cls += ' roo-select2-container-multi';
10406         }
10407         
10408         if (align ==='left' && this.fieldLabel.length) {
10409             
10410             cfg.cls += ' roo-form-group-label-left';
10411
10412             cfg.cn = [
10413                 {
10414                     tag : 'i',
10415                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10416                     tooltip : 'This field is required'
10417                 },
10418                 {
10419                     tag: 'label',
10420                     'for' :  id,
10421                     cls : 'control-label',
10422                     html : this.fieldLabel
10423
10424                 },
10425                 {
10426                     cls : "", 
10427                     cn: [
10428                         combobox
10429                     ]
10430                 }
10431
10432             ];
10433             
10434             var labelCfg = cfg.cn[1];
10435             var contentCfg = cfg.cn[2];
10436             
10437             if(this.indicatorpos == 'right'){
10438                 cfg.cn = [
10439                     {
10440                         tag: 'label',
10441                         'for' :  id,
10442                         cls : 'control-label',
10443                         cn : [
10444                             {
10445                                 tag : 'span',
10446                                 html : this.fieldLabel
10447                             },
10448                             {
10449                                 tag : 'i',
10450                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10451                                 tooltip : 'This field is required'
10452                             }
10453                         ]
10454                     },
10455                     {
10456                         cls : "", 
10457                         cn: [
10458                             combobox
10459                         ]
10460                     }
10461
10462                 ];
10463                 
10464                 labelCfg = cfg.cn[0];
10465                 contentCfg = cfg.cn[1];
10466             }
10467             
10468             if(this.labelWidth > 12){
10469                 labelCfg.style = "width: " + this.labelWidth + 'px';
10470             }
10471             
10472             if(this.labelWidth < 13 && this.labelmd == 0){
10473                 this.labelmd = this.labelWidth;
10474             }
10475             
10476             if(this.labellg > 0){
10477                 labelCfg.cls += ' col-lg-' + this.labellg;
10478                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10479             }
10480             
10481             if(this.labelmd > 0){
10482                 labelCfg.cls += ' col-md-' + this.labelmd;
10483                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10484             }
10485             
10486             if(this.labelsm > 0){
10487                 labelCfg.cls += ' col-sm-' + this.labelsm;
10488                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10489             }
10490             
10491             if(this.labelxs > 0){
10492                 labelCfg.cls += ' col-xs-' + this.labelxs;
10493                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10494             }
10495             
10496         } else if ( this.fieldLabel.length) {
10497 //                Roo.log(" label");
10498             cfg.cn = [
10499                 {
10500                    tag : 'i',
10501                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10502                    tooltip : 'This field is required'
10503                },
10504                {
10505                    tag: 'label',
10506                    //cls : 'input-group-addon',
10507                    html : this.fieldLabel
10508
10509                },
10510
10511                combobox
10512
10513             ];
10514             
10515             if(this.indicatorpos == 'right'){
10516                 
10517                 cfg.cn = [
10518                     {
10519                        tag: 'label',
10520                        cn : [
10521                            {
10522                                tag : 'span',
10523                                html : this.fieldLabel
10524                            },
10525                            {
10526                               tag : 'i',
10527                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10528                               tooltip : 'This field is required'
10529                            }
10530                        ]
10531
10532                     },
10533                     combobox
10534
10535                 ];
10536
10537             }
10538
10539         } else {
10540             
10541 //                Roo.log(" no label && no align");
10542                 cfg = combobox
10543                      
10544                 
10545         }
10546         
10547         var settings=this;
10548         ['xs','sm','md','lg'].map(function(size){
10549             if (settings[size]) {
10550                 cfg.cls += ' col-' + size + '-' + settings[size];
10551             }
10552         });
10553         
10554         return cfg;
10555         
10556     },
10557     
10558     
10559     
10560     // private
10561     onResize : function(w, h){
10562 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10563 //        if(typeof w == 'number'){
10564 //            var x = w - this.trigger.getWidth();
10565 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10566 //            this.trigger.setStyle('left', x+'px');
10567 //        }
10568     },
10569
10570     // private
10571     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10572
10573     // private
10574     getResizeEl : function(){
10575         return this.inputEl();
10576     },
10577
10578     // private
10579     getPositionEl : function(){
10580         return this.inputEl();
10581     },
10582
10583     // private
10584     alignErrorIcon : function(){
10585         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10586     },
10587
10588     // private
10589     initEvents : function(){
10590         
10591         this.createList();
10592         
10593         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10594         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10595         if(!this.multiple && this.showToggleBtn){
10596             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10597             if(this.hideTrigger){
10598                 this.trigger.setDisplayed(false);
10599             }
10600             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10601         }
10602         
10603         if(this.multiple){
10604             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10605         }
10606         
10607         if(this.removable && !this.editable && !this.tickable){
10608             var close = this.closeTriggerEl();
10609             
10610             if(close){
10611                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10612                 close.on('click', this.removeBtnClick, this, close);
10613             }
10614         }
10615         
10616         //this.trigger.addClassOnOver('x-form-trigger-over');
10617         //this.trigger.addClassOnClick('x-form-trigger-click');
10618         
10619         //if(!this.width){
10620         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10621         //}
10622     },
10623     
10624     closeTriggerEl : function()
10625     {
10626         var close = this.el.select('.roo-combo-removable-btn', true).first();
10627         return close ? close : false;
10628     },
10629     
10630     removeBtnClick : function(e, h, el)
10631     {
10632         e.preventDefault();
10633         
10634         if(this.fireEvent("remove", this) !== false){
10635             this.reset();
10636             this.fireEvent("afterremove", this)
10637         }
10638     },
10639     
10640     createList : function()
10641     {
10642         this.list = Roo.get(document.body).createChild({
10643             tag: 'ul',
10644             cls: 'typeahead typeahead-long dropdown-menu',
10645             style: 'display:none'
10646         });
10647         
10648         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10649         
10650     },
10651
10652     // private
10653     initTrigger : function(){
10654        
10655     },
10656
10657     // private
10658     onDestroy : function(){
10659         if(this.trigger){
10660             this.trigger.removeAllListeners();
10661           //  this.trigger.remove();
10662         }
10663         //if(this.wrap){
10664         //    this.wrap.remove();
10665         //}
10666         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10667     },
10668
10669     // private
10670     onFocus : function(){
10671         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10672         /*
10673         if(!this.mimicing){
10674             this.wrap.addClass('x-trigger-wrap-focus');
10675             this.mimicing = true;
10676             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10677             if(this.monitorTab){
10678                 this.el.on("keydown", this.checkTab, this);
10679             }
10680         }
10681         */
10682     },
10683
10684     // private
10685     checkTab : function(e){
10686         if(e.getKey() == e.TAB){
10687             this.triggerBlur();
10688         }
10689     },
10690
10691     // private
10692     onBlur : function(){
10693         // do nothing
10694     },
10695
10696     // private
10697     mimicBlur : function(e, t){
10698         /*
10699         if(!this.wrap.contains(t) && this.validateBlur()){
10700             this.triggerBlur();
10701         }
10702         */
10703     },
10704
10705     // private
10706     triggerBlur : function(){
10707         this.mimicing = false;
10708         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10709         if(this.monitorTab){
10710             this.el.un("keydown", this.checkTab, this);
10711         }
10712         //this.wrap.removeClass('x-trigger-wrap-focus');
10713         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10714     },
10715
10716     // private
10717     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10718     validateBlur : function(e, t){
10719         return true;
10720     },
10721
10722     // private
10723     onDisable : function(){
10724         this.inputEl().dom.disabled = true;
10725         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10726         //if(this.wrap){
10727         //    this.wrap.addClass('x-item-disabled');
10728         //}
10729     },
10730
10731     // private
10732     onEnable : function(){
10733         this.inputEl().dom.disabled = false;
10734         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10735         //if(this.wrap){
10736         //    this.el.removeClass('x-item-disabled');
10737         //}
10738     },
10739
10740     // private
10741     onShow : function(){
10742         var ae = this.getActionEl();
10743         
10744         if(ae){
10745             ae.dom.style.display = '';
10746             ae.dom.style.visibility = 'visible';
10747         }
10748     },
10749
10750     // private
10751     
10752     onHide : function(){
10753         var ae = this.getActionEl();
10754         ae.dom.style.display = 'none';
10755     },
10756
10757     /**
10758      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10759      * by an implementing function.
10760      * @method
10761      * @param {EventObject} e
10762      */
10763     onTriggerClick : Roo.emptyFn
10764 });
10765  /*
10766  * Based on:
10767  * Ext JS Library 1.1.1
10768  * Copyright(c) 2006-2007, Ext JS, LLC.
10769  *
10770  * Originally Released Under LGPL - original licence link has changed is not relivant.
10771  *
10772  * Fork - LGPL
10773  * <script type="text/javascript">
10774  */
10775
10776
10777 /**
10778  * @class Roo.data.SortTypes
10779  * @singleton
10780  * Defines the default sorting (casting?) comparison functions used when sorting data.
10781  */
10782 Roo.data.SortTypes = {
10783     /**
10784      * Default sort that does nothing
10785      * @param {Mixed} s The value being converted
10786      * @return {Mixed} The comparison value
10787      */
10788     none : function(s){
10789         return s;
10790     },
10791     
10792     /**
10793      * The regular expression used to strip tags
10794      * @type {RegExp}
10795      * @property
10796      */
10797     stripTagsRE : /<\/?[^>]+>/gi,
10798     
10799     /**
10800      * Strips all HTML tags to sort on text only
10801      * @param {Mixed} s The value being converted
10802      * @return {String} The comparison value
10803      */
10804     asText : function(s){
10805         return String(s).replace(this.stripTagsRE, "");
10806     },
10807     
10808     /**
10809      * Strips all HTML tags to sort on text only - Case insensitive
10810      * @param {Mixed} s The value being converted
10811      * @return {String} The comparison value
10812      */
10813     asUCText : function(s){
10814         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10815     },
10816     
10817     /**
10818      * Case insensitive string
10819      * @param {Mixed} s The value being converted
10820      * @return {String} The comparison value
10821      */
10822     asUCString : function(s) {
10823         return String(s).toUpperCase();
10824     },
10825     
10826     /**
10827      * Date sorting
10828      * @param {Mixed} s The value being converted
10829      * @return {Number} The comparison value
10830      */
10831     asDate : function(s) {
10832         if(!s){
10833             return 0;
10834         }
10835         if(s instanceof Date){
10836             return s.getTime();
10837         }
10838         return Date.parse(String(s));
10839     },
10840     
10841     /**
10842      * Float sorting
10843      * @param {Mixed} s The value being converted
10844      * @return {Float} The comparison value
10845      */
10846     asFloat : function(s) {
10847         var val = parseFloat(String(s).replace(/,/g, ""));
10848         if(isNaN(val)) {
10849             val = 0;
10850         }
10851         return val;
10852     },
10853     
10854     /**
10855      * Integer sorting
10856      * @param {Mixed} s The value being converted
10857      * @return {Number} The comparison value
10858      */
10859     asInt : function(s) {
10860         var val = parseInt(String(s).replace(/,/g, ""));
10861         if(isNaN(val)) {
10862             val = 0;
10863         }
10864         return val;
10865     }
10866 };/*
10867  * Based on:
10868  * Ext JS Library 1.1.1
10869  * Copyright(c) 2006-2007, Ext JS, LLC.
10870  *
10871  * Originally Released Under LGPL - original licence link has changed is not relivant.
10872  *
10873  * Fork - LGPL
10874  * <script type="text/javascript">
10875  */
10876
10877 /**
10878 * @class Roo.data.Record
10879  * Instances of this class encapsulate both record <em>definition</em> information, and record
10880  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10881  * to access Records cached in an {@link Roo.data.Store} object.<br>
10882  * <p>
10883  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10884  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10885  * objects.<br>
10886  * <p>
10887  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10888  * @constructor
10889  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10890  * {@link #create}. The parameters are the same.
10891  * @param {Array} data An associative Array of data values keyed by the field name.
10892  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10893  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10894  * not specified an integer id is generated.
10895  */
10896 Roo.data.Record = function(data, id){
10897     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10898     this.data = data;
10899 };
10900
10901 /**
10902  * Generate a constructor for a specific record layout.
10903  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10904  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10905  * Each field definition object may contain the following properties: <ul>
10906  * <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,
10907  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10908  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10909  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10910  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10911  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10912  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10913  * this may be omitted.</p></li>
10914  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10915  * <ul><li>auto (Default, implies no conversion)</li>
10916  * <li>string</li>
10917  * <li>int</li>
10918  * <li>float</li>
10919  * <li>boolean</li>
10920  * <li>date</li></ul></p></li>
10921  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10922  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10923  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10924  * by the Reader into an object that will be stored in the Record. It is passed the
10925  * following parameters:<ul>
10926  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10927  * </ul></p></li>
10928  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10929  * </ul>
10930  * <br>usage:<br><pre><code>
10931 var TopicRecord = Roo.data.Record.create(
10932     {name: 'title', mapping: 'topic_title'},
10933     {name: 'author', mapping: 'username'},
10934     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10935     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10936     {name: 'lastPoster', mapping: 'user2'},
10937     {name: 'excerpt', mapping: 'post_text'}
10938 );
10939
10940 var myNewRecord = new TopicRecord({
10941     title: 'Do my job please',
10942     author: 'noobie',
10943     totalPosts: 1,
10944     lastPost: new Date(),
10945     lastPoster: 'Animal',
10946     excerpt: 'No way dude!'
10947 });
10948 myStore.add(myNewRecord);
10949 </code></pre>
10950  * @method create
10951  * @static
10952  */
10953 Roo.data.Record.create = function(o){
10954     var f = function(){
10955         f.superclass.constructor.apply(this, arguments);
10956     };
10957     Roo.extend(f, Roo.data.Record);
10958     var p = f.prototype;
10959     p.fields = new Roo.util.MixedCollection(false, function(field){
10960         return field.name;
10961     });
10962     for(var i = 0, len = o.length; i < len; i++){
10963         p.fields.add(new Roo.data.Field(o[i]));
10964     }
10965     f.getField = function(name){
10966         return p.fields.get(name);  
10967     };
10968     return f;
10969 };
10970
10971 Roo.data.Record.AUTO_ID = 1000;
10972 Roo.data.Record.EDIT = 'edit';
10973 Roo.data.Record.REJECT = 'reject';
10974 Roo.data.Record.COMMIT = 'commit';
10975
10976 Roo.data.Record.prototype = {
10977     /**
10978      * Readonly flag - true if this record has been modified.
10979      * @type Boolean
10980      */
10981     dirty : false,
10982     editing : false,
10983     error: null,
10984     modified: null,
10985
10986     // private
10987     join : function(store){
10988         this.store = store;
10989     },
10990
10991     /**
10992      * Set the named field to the specified value.
10993      * @param {String} name The name of the field to set.
10994      * @param {Object} value The value to set the field to.
10995      */
10996     set : function(name, value){
10997         if(this.data[name] == value){
10998             return;
10999         }
11000         this.dirty = true;
11001         if(!this.modified){
11002             this.modified = {};
11003         }
11004         if(typeof this.modified[name] == 'undefined'){
11005             this.modified[name] = this.data[name];
11006         }
11007         this.data[name] = value;
11008         if(!this.editing && this.store){
11009             this.store.afterEdit(this);
11010         }       
11011     },
11012
11013     /**
11014      * Get the value of the named field.
11015      * @param {String} name The name of the field to get the value of.
11016      * @return {Object} The value of the field.
11017      */
11018     get : function(name){
11019         return this.data[name]; 
11020     },
11021
11022     // private
11023     beginEdit : function(){
11024         this.editing = true;
11025         this.modified = {}; 
11026     },
11027
11028     // private
11029     cancelEdit : function(){
11030         this.editing = false;
11031         delete this.modified;
11032     },
11033
11034     // private
11035     endEdit : function(){
11036         this.editing = false;
11037         if(this.dirty && this.store){
11038             this.store.afterEdit(this);
11039         }
11040     },
11041
11042     /**
11043      * Usually called by the {@link Roo.data.Store} which owns the Record.
11044      * Rejects all changes made to the Record since either creation, or the last commit operation.
11045      * Modified fields are reverted to their original values.
11046      * <p>
11047      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11048      * of reject operations.
11049      */
11050     reject : function(){
11051         var m = this.modified;
11052         for(var n in m){
11053             if(typeof m[n] != "function"){
11054                 this.data[n] = m[n];
11055             }
11056         }
11057         this.dirty = false;
11058         delete this.modified;
11059         this.editing = false;
11060         if(this.store){
11061             this.store.afterReject(this);
11062         }
11063     },
11064
11065     /**
11066      * Usually called by the {@link Roo.data.Store} which owns the Record.
11067      * Commits all changes made to the Record since either creation, or the last commit operation.
11068      * <p>
11069      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11070      * of commit operations.
11071      */
11072     commit : function(){
11073         this.dirty = false;
11074         delete this.modified;
11075         this.editing = false;
11076         if(this.store){
11077             this.store.afterCommit(this);
11078         }
11079     },
11080
11081     // private
11082     hasError : function(){
11083         return this.error != null;
11084     },
11085
11086     // private
11087     clearError : function(){
11088         this.error = null;
11089     },
11090
11091     /**
11092      * Creates a copy of this record.
11093      * @param {String} id (optional) A new record id if you don't want to use this record's id
11094      * @return {Record}
11095      */
11096     copy : function(newId) {
11097         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11098     }
11099 };/*
11100  * Based on:
11101  * Ext JS Library 1.1.1
11102  * Copyright(c) 2006-2007, Ext JS, LLC.
11103  *
11104  * Originally Released Under LGPL - original licence link has changed is not relivant.
11105  *
11106  * Fork - LGPL
11107  * <script type="text/javascript">
11108  */
11109
11110
11111
11112 /**
11113  * @class Roo.data.Store
11114  * @extends Roo.util.Observable
11115  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11116  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11117  * <p>
11118  * 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
11119  * has no knowledge of the format of the data returned by the Proxy.<br>
11120  * <p>
11121  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11122  * instances from the data object. These records are cached and made available through accessor functions.
11123  * @constructor
11124  * Creates a new Store.
11125  * @param {Object} config A config object containing the objects needed for the Store to access data,
11126  * and read the data into Records.
11127  */
11128 Roo.data.Store = function(config){
11129     this.data = new Roo.util.MixedCollection(false);
11130     this.data.getKey = function(o){
11131         return o.id;
11132     };
11133     this.baseParams = {};
11134     // private
11135     this.paramNames = {
11136         "start" : "start",
11137         "limit" : "limit",
11138         "sort" : "sort",
11139         "dir" : "dir",
11140         "multisort" : "_multisort"
11141     };
11142
11143     if(config && config.data){
11144         this.inlineData = config.data;
11145         delete config.data;
11146     }
11147
11148     Roo.apply(this, config);
11149     
11150     if(this.reader){ // reader passed
11151         this.reader = Roo.factory(this.reader, Roo.data);
11152         this.reader.xmodule = this.xmodule || false;
11153         if(!this.recordType){
11154             this.recordType = this.reader.recordType;
11155         }
11156         if(this.reader.onMetaChange){
11157             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11158         }
11159     }
11160
11161     if(this.recordType){
11162         this.fields = this.recordType.prototype.fields;
11163     }
11164     this.modified = [];
11165
11166     this.addEvents({
11167         /**
11168          * @event datachanged
11169          * Fires when the data cache has changed, and a widget which is using this Store
11170          * as a Record cache should refresh its view.
11171          * @param {Store} this
11172          */
11173         datachanged : true,
11174         /**
11175          * @event metachange
11176          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11177          * @param {Store} this
11178          * @param {Object} meta The JSON metadata
11179          */
11180         metachange : true,
11181         /**
11182          * @event add
11183          * Fires when Records have been added to the Store
11184          * @param {Store} this
11185          * @param {Roo.data.Record[]} records The array of Records added
11186          * @param {Number} index The index at which the record(s) were added
11187          */
11188         add : true,
11189         /**
11190          * @event remove
11191          * Fires when a Record has been removed from the Store
11192          * @param {Store} this
11193          * @param {Roo.data.Record} record The Record that was removed
11194          * @param {Number} index The index at which the record was removed
11195          */
11196         remove : true,
11197         /**
11198          * @event update
11199          * Fires when a Record has been updated
11200          * @param {Store} this
11201          * @param {Roo.data.Record} record The Record that was updated
11202          * @param {String} operation The update operation being performed.  Value may be one of:
11203          * <pre><code>
11204  Roo.data.Record.EDIT
11205  Roo.data.Record.REJECT
11206  Roo.data.Record.COMMIT
11207          * </code></pre>
11208          */
11209         update : true,
11210         /**
11211          * @event clear
11212          * Fires when the data cache has been cleared.
11213          * @param {Store} this
11214          */
11215         clear : true,
11216         /**
11217          * @event beforeload
11218          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11219          * the load action will be canceled.
11220          * @param {Store} this
11221          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11222          */
11223         beforeload : true,
11224         /**
11225          * @event beforeloadadd
11226          * Fires after a new set of Records has been loaded.
11227          * @param {Store} this
11228          * @param {Roo.data.Record[]} records The Records that were loaded
11229          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11230          */
11231         beforeloadadd : true,
11232         /**
11233          * @event load
11234          * Fires after a new set of Records has been loaded, before they are added to the store.
11235          * @param {Store} this
11236          * @param {Roo.data.Record[]} records The Records that were loaded
11237          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11238          * @params {Object} return from reader
11239          */
11240         load : true,
11241         /**
11242          * @event loadexception
11243          * Fires if an exception occurs in the Proxy during loading.
11244          * Called with the signature of the Proxy's "loadexception" event.
11245          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11246          * 
11247          * @param {Proxy} 
11248          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11249          * @param {Object} load options 
11250          * @param {Object} jsonData from your request (normally this contains the Exception)
11251          */
11252         loadexception : true
11253     });
11254     
11255     if(this.proxy){
11256         this.proxy = Roo.factory(this.proxy, Roo.data);
11257         this.proxy.xmodule = this.xmodule || false;
11258         this.relayEvents(this.proxy,  ["loadexception"]);
11259     }
11260     this.sortToggle = {};
11261     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11262
11263     Roo.data.Store.superclass.constructor.call(this);
11264
11265     if(this.inlineData){
11266         this.loadData(this.inlineData);
11267         delete this.inlineData;
11268     }
11269 };
11270
11271 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11272      /**
11273     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11274     * without a remote query - used by combo/forms at present.
11275     */
11276     
11277     /**
11278     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11279     */
11280     /**
11281     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11282     */
11283     /**
11284     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11285     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11286     */
11287     /**
11288     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11289     * on any HTTP request
11290     */
11291     /**
11292     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11293     */
11294     /**
11295     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11296     */
11297     multiSort: false,
11298     /**
11299     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11300     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11301     */
11302     remoteSort : false,
11303
11304     /**
11305     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11306      * loaded or when a record is removed. (defaults to false).
11307     */
11308     pruneModifiedRecords : false,
11309
11310     // private
11311     lastOptions : null,
11312
11313     /**
11314      * Add Records to the Store and fires the add event.
11315      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11316      */
11317     add : function(records){
11318         records = [].concat(records);
11319         for(var i = 0, len = records.length; i < len; i++){
11320             records[i].join(this);
11321         }
11322         var index = this.data.length;
11323         this.data.addAll(records);
11324         this.fireEvent("add", this, records, index);
11325     },
11326
11327     /**
11328      * Remove a Record from the Store and fires the remove event.
11329      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11330      */
11331     remove : function(record){
11332         var index = this.data.indexOf(record);
11333         this.data.removeAt(index);
11334  
11335         if(this.pruneModifiedRecords){
11336             this.modified.remove(record);
11337         }
11338         this.fireEvent("remove", this, record, index);
11339     },
11340
11341     /**
11342      * Remove all Records from the Store and fires the clear event.
11343      */
11344     removeAll : function(){
11345         this.data.clear();
11346         if(this.pruneModifiedRecords){
11347             this.modified = [];
11348         }
11349         this.fireEvent("clear", this);
11350     },
11351
11352     /**
11353      * Inserts Records to the Store at the given index and fires the add event.
11354      * @param {Number} index The start index at which to insert the passed Records.
11355      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11356      */
11357     insert : function(index, records){
11358         records = [].concat(records);
11359         for(var i = 0, len = records.length; i < len; i++){
11360             this.data.insert(index, records[i]);
11361             records[i].join(this);
11362         }
11363         this.fireEvent("add", this, records, index);
11364     },
11365
11366     /**
11367      * Get the index within the cache of the passed Record.
11368      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11369      * @return {Number} The index of the passed Record. Returns -1 if not found.
11370      */
11371     indexOf : function(record){
11372         return this.data.indexOf(record);
11373     },
11374
11375     /**
11376      * Get the index within the cache of the Record with the passed id.
11377      * @param {String} id The id of the Record to find.
11378      * @return {Number} The index of the Record. Returns -1 if not found.
11379      */
11380     indexOfId : function(id){
11381         return this.data.indexOfKey(id);
11382     },
11383
11384     /**
11385      * Get the Record with the specified id.
11386      * @param {String} id The id of the Record to find.
11387      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11388      */
11389     getById : function(id){
11390         return this.data.key(id);
11391     },
11392
11393     /**
11394      * Get the Record at the specified index.
11395      * @param {Number} index The index of the Record to find.
11396      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11397      */
11398     getAt : function(index){
11399         return this.data.itemAt(index);
11400     },
11401
11402     /**
11403      * Returns a range of Records between specified indices.
11404      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11405      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11406      * @return {Roo.data.Record[]} An array of Records
11407      */
11408     getRange : function(start, end){
11409         return this.data.getRange(start, end);
11410     },
11411
11412     // private
11413     storeOptions : function(o){
11414         o = Roo.apply({}, o);
11415         delete o.callback;
11416         delete o.scope;
11417         this.lastOptions = o;
11418     },
11419
11420     /**
11421      * Loads the Record cache from the configured Proxy using the configured Reader.
11422      * <p>
11423      * If using remote paging, then the first load call must specify the <em>start</em>
11424      * and <em>limit</em> properties in the options.params property to establish the initial
11425      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11426      * <p>
11427      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11428      * and this call will return before the new data has been loaded. Perform any post-processing
11429      * in a callback function, or in a "load" event handler.</strong>
11430      * <p>
11431      * @param {Object} options An object containing properties which control loading options:<ul>
11432      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11433      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11434      * passed the following arguments:<ul>
11435      * <li>r : Roo.data.Record[]</li>
11436      * <li>options: Options object from the load call</li>
11437      * <li>success: Boolean success indicator</li></ul></li>
11438      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11439      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11440      * </ul>
11441      */
11442     load : function(options){
11443         options = options || {};
11444         if(this.fireEvent("beforeload", this, options) !== false){
11445             this.storeOptions(options);
11446             var p = Roo.apply(options.params || {}, this.baseParams);
11447             // if meta was not loaded from remote source.. try requesting it.
11448             if (!this.reader.metaFromRemote) {
11449                 p._requestMeta = 1;
11450             }
11451             if(this.sortInfo && this.remoteSort){
11452                 var pn = this.paramNames;
11453                 p[pn["sort"]] = this.sortInfo.field;
11454                 p[pn["dir"]] = this.sortInfo.direction;
11455             }
11456             if (this.multiSort) {
11457                 var pn = this.paramNames;
11458                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11459             }
11460             
11461             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11462         }
11463     },
11464
11465     /**
11466      * Reloads the Record cache from the configured Proxy using the configured Reader and
11467      * the options from the last load operation performed.
11468      * @param {Object} options (optional) An object containing properties which may override the options
11469      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11470      * the most recently used options are reused).
11471      */
11472     reload : function(options){
11473         this.load(Roo.applyIf(options||{}, this.lastOptions));
11474     },
11475
11476     // private
11477     // Called as a callback by the Reader during a load operation.
11478     loadRecords : function(o, options, success){
11479         if(!o || success === false){
11480             if(success !== false){
11481                 this.fireEvent("load", this, [], options, o);
11482             }
11483             if(options.callback){
11484                 options.callback.call(options.scope || this, [], options, false);
11485             }
11486             return;
11487         }
11488         // if data returned failure - throw an exception.
11489         if (o.success === false) {
11490             // show a message if no listener is registered.
11491             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11492                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11493             }
11494             // loadmask wil be hooked into this..
11495             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11496             return;
11497         }
11498         var r = o.records, t = o.totalRecords || r.length;
11499         
11500         this.fireEvent("beforeloadadd", this, r, options, o);
11501         
11502         if(!options || options.add !== true){
11503             if(this.pruneModifiedRecords){
11504                 this.modified = [];
11505             }
11506             for(var i = 0, len = r.length; i < len; i++){
11507                 r[i].join(this);
11508             }
11509             if(this.snapshot){
11510                 this.data = this.snapshot;
11511                 delete this.snapshot;
11512             }
11513             this.data.clear();
11514             this.data.addAll(r);
11515             this.totalLength = t;
11516             this.applySort();
11517             this.fireEvent("datachanged", this);
11518         }else{
11519             this.totalLength = Math.max(t, this.data.length+r.length);
11520             this.add(r);
11521         }
11522         
11523         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11524                 
11525             var e = new Roo.data.Record({});
11526
11527             e.set(this.parent.displayField, this.parent.emptyTitle);
11528             e.set(this.parent.valueField, '');
11529
11530             this.insert(0, e);
11531         }
11532             
11533         this.fireEvent("load", this, r, options, o);
11534         if(options.callback){
11535             options.callback.call(options.scope || this, r, options, true);
11536         }
11537     },
11538
11539
11540     /**
11541      * Loads data from a passed data block. A Reader which understands the format of the data
11542      * must have been configured in the constructor.
11543      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11544      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11545      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11546      */
11547     loadData : function(o, append){
11548         var r = this.reader.readRecords(o);
11549         this.loadRecords(r, {add: append}, true);
11550     },
11551
11552     /**
11553      * Gets the number of cached records.
11554      * <p>
11555      * <em>If using paging, this may not be the total size of the dataset. If the data object
11556      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11557      * the data set size</em>
11558      */
11559     getCount : function(){
11560         return this.data.length || 0;
11561     },
11562
11563     /**
11564      * Gets the total number of records in the dataset as returned by the server.
11565      * <p>
11566      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11567      * the dataset size</em>
11568      */
11569     getTotalCount : function(){
11570         return this.totalLength || 0;
11571     },
11572
11573     /**
11574      * Returns the sort state of the Store as an object with two properties:
11575      * <pre><code>
11576  field {String} The name of the field by which the Records are sorted
11577  direction {String} The sort order, "ASC" or "DESC"
11578      * </code></pre>
11579      */
11580     getSortState : function(){
11581         return this.sortInfo;
11582     },
11583
11584     // private
11585     applySort : function(){
11586         if(this.sortInfo && !this.remoteSort){
11587             var s = this.sortInfo, f = s.field;
11588             var st = this.fields.get(f).sortType;
11589             var fn = function(r1, r2){
11590                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11591                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11592             };
11593             this.data.sort(s.direction, fn);
11594             if(this.snapshot && this.snapshot != this.data){
11595                 this.snapshot.sort(s.direction, fn);
11596             }
11597         }
11598     },
11599
11600     /**
11601      * Sets the default sort column and order to be used by the next load operation.
11602      * @param {String} fieldName The name of the field to sort by.
11603      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11604      */
11605     setDefaultSort : function(field, dir){
11606         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11607     },
11608
11609     /**
11610      * Sort the Records.
11611      * If remote sorting is used, the sort is performed on the server, and the cache is
11612      * reloaded. If local sorting is used, the cache is sorted internally.
11613      * @param {String} fieldName The name of the field to sort by.
11614      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11615      */
11616     sort : function(fieldName, dir){
11617         var f = this.fields.get(fieldName);
11618         if(!dir){
11619             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11620             
11621             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11622                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11623             }else{
11624                 dir = f.sortDir;
11625             }
11626         }
11627         this.sortToggle[f.name] = dir;
11628         this.sortInfo = {field: f.name, direction: dir};
11629         if(!this.remoteSort){
11630             this.applySort();
11631             this.fireEvent("datachanged", this);
11632         }else{
11633             this.load(this.lastOptions);
11634         }
11635     },
11636
11637     /**
11638      * Calls the specified function for each of the Records in the cache.
11639      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11640      * Returning <em>false</em> aborts and exits the iteration.
11641      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11642      */
11643     each : function(fn, scope){
11644         this.data.each(fn, scope);
11645     },
11646
11647     /**
11648      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11649      * (e.g., during paging).
11650      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11651      */
11652     getModifiedRecords : function(){
11653         return this.modified;
11654     },
11655
11656     // private
11657     createFilterFn : function(property, value, anyMatch){
11658         if(!value.exec){ // not a regex
11659             value = String(value);
11660             if(value.length == 0){
11661                 return false;
11662             }
11663             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11664         }
11665         return function(r){
11666             return value.test(r.data[property]);
11667         };
11668     },
11669
11670     /**
11671      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11672      * @param {String} property A field on your records
11673      * @param {Number} start The record index to start at (defaults to 0)
11674      * @param {Number} end The last record index to include (defaults to length - 1)
11675      * @return {Number} The sum
11676      */
11677     sum : function(property, start, end){
11678         var rs = this.data.items, v = 0;
11679         start = start || 0;
11680         end = (end || end === 0) ? end : rs.length-1;
11681
11682         for(var i = start; i <= end; i++){
11683             v += (rs[i].data[property] || 0);
11684         }
11685         return v;
11686     },
11687
11688     /**
11689      * Filter the records by a specified property.
11690      * @param {String} field A field on your records
11691      * @param {String/RegExp} value Either a string that the field
11692      * should start with or a RegExp to test against the field
11693      * @param {Boolean} anyMatch True to match any part not just the beginning
11694      */
11695     filter : function(property, value, anyMatch){
11696         var fn = this.createFilterFn(property, value, anyMatch);
11697         return fn ? this.filterBy(fn) : this.clearFilter();
11698     },
11699
11700     /**
11701      * Filter by a function. The specified function will be called with each
11702      * record in this data source. If the function returns true the record is included,
11703      * otherwise it is filtered.
11704      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11705      * @param {Object} scope (optional) The scope of the function (defaults to this)
11706      */
11707     filterBy : function(fn, scope){
11708         this.snapshot = this.snapshot || this.data;
11709         this.data = this.queryBy(fn, scope||this);
11710         this.fireEvent("datachanged", this);
11711     },
11712
11713     /**
11714      * Query the records by a specified property.
11715      * @param {String} field A field on your records
11716      * @param {String/RegExp} value Either a string that the field
11717      * should start with or a RegExp to test against the field
11718      * @param {Boolean} anyMatch True to match any part not just the beginning
11719      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11720      */
11721     query : function(property, value, anyMatch){
11722         var fn = this.createFilterFn(property, value, anyMatch);
11723         return fn ? this.queryBy(fn) : this.data.clone();
11724     },
11725
11726     /**
11727      * Query by a function. The specified function will be called with each
11728      * record in this data source. If the function returns true the record is included
11729      * in the results.
11730      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11731      * @param {Object} scope (optional) The scope of the function (defaults to this)
11732       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11733      **/
11734     queryBy : function(fn, scope){
11735         var data = this.snapshot || this.data;
11736         return data.filterBy(fn, scope||this);
11737     },
11738
11739     /**
11740      * Collects unique values for a particular dataIndex from this store.
11741      * @param {String} dataIndex The property to collect
11742      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11743      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11744      * @return {Array} An array of the unique values
11745      **/
11746     collect : function(dataIndex, allowNull, bypassFilter){
11747         var d = (bypassFilter === true && this.snapshot) ?
11748                 this.snapshot.items : this.data.items;
11749         var v, sv, r = [], l = {};
11750         for(var i = 0, len = d.length; i < len; i++){
11751             v = d[i].data[dataIndex];
11752             sv = String(v);
11753             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11754                 l[sv] = true;
11755                 r[r.length] = v;
11756             }
11757         }
11758         return r;
11759     },
11760
11761     /**
11762      * Revert to a view of the Record cache with no filtering applied.
11763      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11764      */
11765     clearFilter : function(suppressEvent){
11766         if(this.snapshot && this.snapshot != this.data){
11767             this.data = this.snapshot;
11768             delete this.snapshot;
11769             if(suppressEvent !== true){
11770                 this.fireEvent("datachanged", this);
11771             }
11772         }
11773     },
11774
11775     // private
11776     afterEdit : function(record){
11777         if(this.modified.indexOf(record) == -1){
11778             this.modified.push(record);
11779         }
11780         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11781     },
11782     
11783     // private
11784     afterReject : function(record){
11785         this.modified.remove(record);
11786         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11787     },
11788
11789     // private
11790     afterCommit : function(record){
11791         this.modified.remove(record);
11792         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11793     },
11794
11795     /**
11796      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11797      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11798      */
11799     commitChanges : function(){
11800         var m = this.modified.slice(0);
11801         this.modified = [];
11802         for(var i = 0, len = m.length; i < len; i++){
11803             m[i].commit();
11804         }
11805     },
11806
11807     /**
11808      * Cancel outstanding changes on all changed records.
11809      */
11810     rejectChanges : function(){
11811         var m = this.modified.slice(0);
11812         this.modified = [];
11813         for(var i = 0, len = m.length; i < len; i++){
11814             m[i].reject();
11815         }
11816     },
11817
11818     onMetaChange : function(meta, rtype, o){
11819         this.recordType = rtype;
11820         this.fields = rtype.prototype.fields;
11821         delete this.snapshot;
11822         this.sortInfo = meta.sortInfo || this.sortInfo;
11823         this.modified = [];
11824         this.fireEvent('metachange', this, this.reader.meta);
11825     },
11826     
11827     moveIndex : function(data, type)
11828     {
11829         var index = this.indexOf(data);
11830         
11831         var newIndex = index + type;
11832         
11833         this.remove(data);
11834         
11835         this.insert(newIndex, data);
11836         
11837     }
11838 });/*
11839  * Based on:
11840  * Ext JS Library 1.1.1
11841  * Copyright(c) 2006-2007, Ext JS, LLC.
11842  *
11843  * Originally Released Under LGPL - original licence link has changed is not relivant.
11844  *
11845  * Fork - LGPL
11846  * <script type="text/javascript">
11847  */
11848
11849 /**
11850  * @class Roo.data.SimpleStore
11851  * @extends Roo.data.Store
11852  * Small helper class to make creating Stores from Array data easier.
11853  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11854  * @cfg {Array} fields An array of field definition objects, or field name strings.
11855  * @cfg {Array} data The multi-dimensional array of data
11856  * @constructor
11857  * @param {Object} config
11858  */
11859 Roo.data.SimpleStore = function(config){
11860     Roo.data.SimpleStore.superclass.constructor.call(this, {
11861         isLocal : true,
11862         reader: new Roo.data.ArrayReader({
11863                 id: config.id
11864             },
11865             Roo.data.Record.create(config.fields)
11866         ),
11867         proxy : new Roo.data.MemoryProxy(config.data)
11868     });
11869     this.load();
11870 };
11871 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11872  * Based on:
11873  * Ext JS Library 1.1.1
11874  * Copyright(c) 2006-2007, Ext JS, LLC.
11875  *
11876  * Originally Released Under LGPL - original licence link has changed is not relivant.
11877  *
11878  * Fork - LGPL
11879  * <script type="text/javascript">
11880  */
11881
11882 /**
11883 /**
11884  * @extends Roo.data.Store
11885  * @class Roo.data.JsonStore
11886  * Small helper class to make creating Stores for JSON data easier. <br/>
11887 <pre><code>
11888 var store = new Roo.data.JsonStore({
11889     url: 'get-images.php',
11890     root: 'images',
11891     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11892 });
11893 </code></pre>
11894  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11895  * JsonReader and HttpProxy (unless inline data is provided).</b>
11896  * @cfg {Array} fields An array of field definition objects, or field name strings.
11897  * @constructor
11898  * @param {Object} config
11899  */
11900 Roo.data.JsonStore = function(c){
11901     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11902         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11903         reader: new Roo.data.JsonReader(c, c.fields)
11904     }));
11905 };
11906 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
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 Roo.data.Field = function(config){
11919     if(typeof config == "string"){
11920         config = {name: config};
11921     }
11922     Roo.apply(this, config);
11923     
11924     if(!this.type){
11925         this.type = "auto";
11926     }
11927     
11928     var st = Roo.data.SortTypes;
11929     // named sortTypes are supported, here we look them up
11930     if(typeof this.sortType == "string"){
11931         this.sortType = st[this.sortType];
11932     }
11933     
11934     // set default sortType for strings and dates
11935     if(!this.sortType){
11936         switch(this.type){
11937             case "string":
11938                 this.sortType = st.asUCString;
11939                 break;
11940             case "date":
11941                 this.sortType = st.asDate;
11942                 break;
11943             default:
11944                 this.sortType = st.none;
11945         }
11946     }
11947
11948     // define once
11949     var stripRe = /[\$,%]/g;
11950
11951     // prebuilt conversion function for this field, instead of
11952     // switching every time we're reading a value
11953     if(!this.convert){
11954         var cv, dateFormat = this.dateFormat;
11955         switch(this.type){
11956             case "":
11957             case "auto":
11958             case undefined:
11959                 cv = function(v){ return v; };
11960                 break;
11961             case "string":
11962                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11963                 break;
11964             case "int":
11965                 cv = function(v){
11966                     return v !== undefined && v !== null && v !== '' ?
11967                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11968                     };
11969                 break;
11970             case "float":
11971                 cv = function(v){
11972                     return v !== undefined && v !== null && v !== '' ?
11973                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11974                     };
11975                 break;
11976             case "bool":
11977             case "boolean":
11978                 cv = function(v){ return v === true || v === "true" || v == 1; };
11979                 break;
11980             case "date":
11981                 cv = function(v){
11982                     if(!v){
11983                         return '';
11984                     }
11985                     if(v instanceof Date){
11986                         return v;
11987                     }
11988                     if(dateFormat){
11989                         if(dateFormat == "timestamp"){
11990                             return new Date(v*1000);
11991                         }
11992                         return Date.parseDate(v, dateFormat);
11993                     }
11994                     var parsed = Date.parse(v);
11995                     return parsed ? new Date(parsed) : null;
11996                 };
11997              break;
11998             
11999         }
12000         this.convert = cv;
12001     }
12002 };
12003
12004 Roo.data.Field.prototype = {
12005     dateFormat: null,
12006     defaultValue: "",
12007     mapping: null,
12008     sortType : null,
12009     sortDir : "ASC"
12010 };/*
12011  * Based on:
12012  * Ext JS Library 1.1.1
12013  * Copyright(c) 2006-2007, Ext JS, LLC.
12014  *
12015  * Originally Released Under LGPL - original licence link has changed is not relivant.
12016  *
12017  * Fork - LGPL
12018  * <script type="text/javascript">
12019  */
12020  
12021 // Base class for reading structured data from a data source.  This class is intended to be
12022 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12023
12024 /**
12025  * @class Roo.data.DataReader
12026  * Base class for reading structured data from a data source.  This class is intended to be
12027  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12028  */
12029
12030 Roo.data.DataReader = function(meta, recordType){
12031     
12032     this.meta = meta;
12033     
12034     this.recordType = recordType instanceof Array ? 
12035         Roo.data.Record.create(recordType) : recordType;
12036 };
12037
12038 Roo.data.DataReader.prototype = {
12039      /**
12040      * Create an empty record
12041      * @param {Object} data (optional) - overlay some values
12042      * @return {Roo.data.Record} record created.
12043      */
12044     newRow :  function(d) {
12045         var da =  {};
12046         this.recordType.prototype.fields.each(function(c) {
12047             switch( c.type) {
12048                 case 'int' : da[c.name] = 0; break;
12049                 case 'date' : da[c.name] = new Date(); break;
12050                 case 'float' : da[c.name] = 0.0; break;
12051                 case 'boolean' : da[c.name] = false; break;
12052                 default : da[c.name] = ""; break;
12053             }
12054             
12055         });
12056         return new this.recordType(Roo.apply(da, d));
12057     }
12058     
12059 };/*
12060  * Based on:
12061  * Ext JS Library 1.1.1
12062  * Copyright(c) 2006-2007, Ext JS, LLC.
12063  *
12064  * Originally Released Under LGPL - original licence link has changed is not relivant.
12065  *
12066  * Fork - LGPL
12067  * <script type="text/javascript">
12068  */
12069
12070 /**
12071  * @class Roo.data.DataProxy
12072  * @extends Roo.data.Observable
12073  * This class is an abstract base class for implementations which provide retrieval of
12074  * unformatted data objects.<br>
12075  * <p>
12076  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12077  * (of the appropriate type which knows how to parse the data object) to provide a block of
12078  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12079  * <p>
12080  * Custom implementations must implement the load method as described in
12081  * {@link Roo.data.HttpProxy#load}.
12082  */
12083 Roo.data.DataProxy = function(){
12084     this.addEvents({
12085         /**
12086          * @event beforeload
12087          * Fires before a network request is made to retrieve a data object.
12088          * @param {Object} This DataProxy object.
12089          * @param {Object} params The params parameter to the load function.
12090          */
12091         beforeload : true,
12092         /**
12093          * @event load
12094          * Fires before the load method's callback is called.
12095          * @param {Object} This DataProxy object.
12096          * @param {Object} o The data object.
12097          * @param {Object} arg The callback argument object passed to the load function.
12098          */
12099         load : true,
12100         /**
12101          * @event loadexception
12102          * Fires if an Exception occurs during data retrieval.
12103          * @param {Object} This DataProxy object.
12104          * @param {Object} o The data object.
12105          * @param {Object} arg The callback argument object passed to the load function.
12106          * @param {Object} e The Exception.
12107          */
12108         loadexception : true
12109     });
12110     Roo.data.DataProxy.superclass.constructor.call(this);
12111 };
12112
12113 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12114
12115     /**
12116      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12117      */
12118 /*
12119  * Based on:
12120  * Ext JS Library 1.1.1
12121  * Copyright(c) 2006-2007, Ext JS, LLC.
12122  *
12123  * Originally Released Under LGPL - original licence link has changed is not relivant.
12124  *
12125  * Fork - LGPL
12126  * <script type="text/javascript">
12127  */
12128 /**
12129  * @class Roo.data.MemoryProxy
12130  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12131  * to the Reader when its load method is called.
12132  * @constructor
12133  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12134  */
12135 Roo.data.MemoryProxy = function(data){
12136     if (data.data) {
12137         data = data.data;
12138     }
12139     Roo.data.MemoryProxy.superclass.constructor.call(this);
12140     this.data = data;
12141 };
12142
12143 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12144     
12145     /**
12146      * Load data from the requested source (in this case an in-memory
12147      * data object passed to the constructor), read the data object into
12148      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12149      * process that block using the passed callback.
12150      * @param {Object} params This parameter is not used by the MemoryProxy class.
12151      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12152      * object into a block of Roo.data.Records.
12153      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12154      * The function must be passed <ul>
12155      * <li>The Record block object</li>
12156      * <li>The "arg" argument from the load function</li>
12157      * <li>A boolean success indicator</li>
12158      * </ul>
12159      * @param {Object} scope The scope in which to call the callback
12160      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12161      */
12162     load : function(params, reader, callback, scope, arg){
12163         params = params || {};
12164         var result;
12165         try {
12166             result = reader.readRecords(this.data);
12167         }catch(e){
12168             this.fireEvent("loadexception", this, arg, null, e);
12169             callback.call(scope, null, arg, false);
12170             return;
12171         }
12172         callback.call(scope, result, arg, true);
12173     },
12174     
12175     // private
12176     update : function(params, records){
12177         
12178     }
12179 });/*
12180  * Based on:
12181  * Ext JS Library 1.1.1
12182  * Copyright(c) 2006-2007, Ext JS, LLC.
12183  *
12184  * Originally Released Under LGPL - original licence link has changed is not relivant.
12185  *
12186  * Fork - LGPL
12187  * <script type="text/javascript">
12188  */
12189 /**
12190  * @class Roo.data.HttpProxy
12191  * @extends Roo.data.DataProxy
12192  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12193  * configured to reference a certain URL.<br><br>
12194  * <p>
12195  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12196  * from which the running page was served.<br><br>
12197  * <p>
12198  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12199  * <p>
12200  * Be aware that to enable the browser to parse an XML document, the server must set
12201  * the Content-Type header in the HTTP response to "text/xml".
12202  * @constructor
12203  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12204  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12205  * will be used to make the request.
12206  */
12207 Roo.data.HttpProxy = function(conn){
12208     Roo.data.HttpProxy.superclass.constructor.call(this);
12209     // is conn a conn config or a real conn?
12210     this.conn = conn;
12211     this.useAjax = !conn || !conn.events;
12212   
12213 };
12214
12215 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12216     // thse are take from connection...
12217     
12218     /**
12219      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12220      */
12221     /**
12222      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12223      * extra parameters to each request made by this object. (defaults to undefined)
12224      */
12225     /**
12226      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12227      *  to each request made by this object. (defaults to undefined)
12228      */
12229     /**
12230      * @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)
12231      */
12232     /**
12233      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12234      */
12235      /**
12236      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12237      * @type Boolean
12238      */
12239   
12240
12241     /**
12242      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12243      * @type Boolean
12244      */
12245     /**
12246      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12247      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12248      * a finer-grained basis than the DataProxy events.
12249      */
12250     getConnection : function(){
12251         return this.useAjax ? Roo.Ajax : this.conn;
12252     },
12253
12254     /**
12255      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12256      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12257      * process that block using the passed callback.
12258      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12259      * for the request to the remote server.
12260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12261      * object into a block of Roo.data.Records.
12262      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12263      * The function must be passed <ul>
12264      * <li>The Record block object</li>
12265      * <li>The "arg" argument from the load function</li>
12266      * <li>A boolean success indicator</li>
12267      * </ul>
12268      * @param {Object} scope The scope in which to call the callback
12269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12270      */
12271     load : function(params, reader, callback, scope, arg){
12272         if(this.fireEvent("beforeload", this, params) !== false){
12273             var  o = {
12274                 params : params || {},
12275                 request: {
12276                     callback : callback,
12277                     scope : scope,
12278                     arg : arg
12279                 },
12280                 reader: reader,
12281                 callback : this.loadResponse,
12282                 scope: this
12283             };
12284             if(this.useAjax){
12285                 Roo.applyIf(o, this.conn);
12286                 if(this.activeRequest){
12287                     Roo.Ajax.abort(this.activeRequest);
12288                 }
12289                 this.activeRequest = Roo.Ajax.request(o);
12290             }else{
12291                 this.conn.request(o);
12292             }
12293         }else{
12294             callback.call(scope||this, null, arg, false);
12295         }
12296     },
12297
12298     // private
12299     loadResponse : function(o, success, response){
12300         delete this.activeRequest;
12301         if(!success){
12302             this.fireEvent("loadexception", this, o, response);
12303             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12304             return;
12305         }
12306         var result;
12307         try {
12308             result = o.reader.read(response);
12309         }catch(e){
12310             this.fireEvent("loadexception", this, o, response, e);
12311             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12312             return;
12313         }
12314         
12315         this.fireEvent("load", this, o, o.request.arg);
12316         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12317     },
12318
12319     // private
12320     update : function(dataSet){
12321
12322     },
12323
12324     // private
12325     updateResponse : function(dataSet){
12326
12327     }
12328 });/*
12329  * Based on:
12330  * Ext JS Library 1.1.1
12331  * Copyright(c) 2006-2007, Ext JS, LLC.
12332  *
12333  * Originally Released Under LGPL - original licence link has changed is not relivant.
12334  *
12335  * Fork - LGPL
12336  * <script type="text/javascript">
12337  */
12338
12339 /**
12340  * @class Roo.data.ScriptTagProxy
12341  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12342  * other than the originating domain of the running page.<br><br>
12343  * <p>
12344  * <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
12345  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12346  * <p>
12347  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12348  * source code that is used as the source inside a &lt;script> tag.<br><br>
12349  * <p>
12350  * In order for the browser to process the returned data, the server must wrap the data object
12351  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12352  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12353  * depending on whether the callback name was passed:
12354  * <p>
12355  * <pre><code>
12356 boolean scriptTag = false;
12357 String cb = request.getParameter("callback");
12358 if (cb != null) {
12359     scriptTag = true;
12360     response.setContentType("text/javascript");
12361 } else {
12362     response.setContentType("application/x-json");
12363 }
12364 Writer out = response.getWriter();
12365 if (scriptTag) {
12366     out.write(cb + "(");
12367 }
12368 out.print(dataBlock.toJsonString());
12369 if (scriptTag) {
12370     out.write(");");
12371 }
12372 </pre></code>
12373  *
12374  * @constructor
12375  * @param {Object} config A configuration object.
12376  */
12377 Roo.data.ScriptTagProxy = function(config){
12378     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12379     Roo.apply(this, config);
12380     this.head = document.getElementsByTagName("head")[0];
12381 };
12382
12383 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12384
12385 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12386     /**
12387      * @cfg {String} url The URL from which to request the data object.
12388      */
12389     /**
12390      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12391      */
12392     timeout : 30000,
12393     /**
12394      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12395      * the server the name of the callback function set up by the load call to process the returned data object.
12396      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12397      * javascript output which calls this named function passing the data object as its only parameter.
12398      */
12399     callbackParam : "callback",
12400     /**
12401      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12402      * name to the request.
12403      */
12404     nocache : true,
12405
12406     /**
12407      * Load data from the configured URL, read the data object into
12408      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12409      * process that block using the passed callback.
12410      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12411      * for the request to the remote server.
12412      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12413      * object into a block of Roo.data.Records.
12414      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12415      * The function must be passed <ul>
12416      * <li>The Record block object</li>
12417      * <li>The "arg" argument from the load function</li>
12418      * <li>A boolean success indicator</li>
12419      * </ul>
12420      * @param {Object} scope The scope in which to call the callback
12421      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12422      */
12423     load : function(params, reader, callback, scope, arg){
12424         if(this.fireEvent("beforeload", this, params) !== false){
12425
12426             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12427
12428             var url = this.url;
12429             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12430             if(this.nocache){
12431                 url += "&_dc=" + (new Date().getTime());
12432             }
12433             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12434             var trans = {
12435                 id : transId,
12436                 cb : "stcCallback"+transId,
12437                 scriptId : "stcScript"+transId,
12438                 params : params,
12439                 arg : arg,
12440                 url : url,
12441                 callback : callback,
12442                 scope : scope,
12443                 reader : reader
12444             };
12445             var conn = this;
12446
12447             window[trans.cb] = function(o){
12448                 conn.handleResponse(o, trans);
12449             };
12450
12451             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12452
12453             if(this.autoAbort !== false){
12454                 this.abort();
12455             }
12456
12457             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12458
12459             var script = document.createElement("script");
12460             script.setAttribute("src", url);
12461             script.setAttribute("type", "text/javascript");
12462             script.setAttribute("id", trans.scriptId);
12463             this.head.appendChild(script);
12464
12465             this.trans = trans;
12466         }else{
12467             callback.call(scope||this, null, arg, false);
12468         }
12469     },
12470
12471     // private
12472     isLoading : function(){
12473         return this.trans ? true : false;
12474     },
12475
12476     /**
12477      * Abort the current server request.
12478      */
12479     abort : function(){
12480         if(this.isLoading()){
12481             this.destroyTrans(this.trans);
12482         }
12483     },
12484
12485     // private
12486     destroyTrans : function(trans, isLoaded){
12487         this.head.removeChild(document.getElementById(trans.scriptId));
12488         clearTimeout(trans.timeoutId);
12489         if(isLoaded){
12490             window[trans.cb] = undefined;
12491             try{
12492                 delete window[trans.cb];
12493             }catch(e){}
12494         }else{
12495             // if hasn't been loaded, wait for load to remove it to prevent script error
12496             window[trans.cb] = function(){
12497                 window[trans.cb] = undefined;
12498                 try{
12499                     delete window[trans.cb];
12500                 }catch(e){}
12501             };
12502         }
12503     },
12504
12505     // private
12506     handleResponse : function(o, trans){
12507         this.trans = false;
12508         this.destroyTrans(trans, true);
12509         var result;
12510         try {
12511             result = trans.reader.readRecords(o);
12512         }catch(e){
12513             this.fireEvent("loadexception", this, o, trans.arg, e);
12514             trans.callback.call(trans.scope||window, null, trans.arg, false);
12515             return;
12516         }
12517         this.fireEvent("load", this, o, trans.arg);
12518         trans.callback.call(trans.scope||window, result, trans.arg, true);
12519     },
12520
12521     // private
12522     handleFailure : function(trans){
12523         this.trans = false;
12524         this.destroyTrans(trans, false);
12525         this.fireEvent("loadexception", this, null, trans.arg);
12526         trans.callback.call(trans.scope||window, null, trans.arg, false);
12527     }
12528 });/*
12529  * Based on:
12530  * Ext JS Library 1.1.1
12531  * Copyright(c) 2006-2007, Ext JS, LLC.
12532  *
12533  * Originally Released Under LGPL - original licence link has changed is not relivant.
12534  *
12535  * Fork - LGPL
12536  * <script type="text/javascript">
12537  */
12538
12539 /**
12540  * @class Roo.data.JsonReader
12541  * @extends Roo.data.DataReader
12542  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12543  * based on mappings in a provided Roo.data.Record constructor.
12544  * 
12545  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12546  * in the reply previously. 
12547  * 
12548  * <p>
12549  * Example code:
12550  * <pre><code>
12551 var RecordDef = Roo.data.Record.create([
12552     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12553     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12554 ]);
12555 var myReader = new Roo.data.JsonReader({
12556     totalProperty: "results",    // The property which contains the total dataset size (optional)
12557     root: "rows",                // The property which contains an Array of row objects
12558     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12559 }, RecordDef);
12560 </code></pre>
12561  * <p>
12562  * This would consume a JSON file like this:
12563  * <pre><code>
12564 { 'results': 2, 'rows': [
12565     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12566     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12567 }
12568 </code></pre>
12569  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12570  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12571  * paged from the remote server.
12572  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12573  * @cfg {String} root name of the property which contains the Array of row objects.
12574  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12575  * @cfg {Array} fields Array of field definition objects
12576  * @constructor
12577  * Create a new JsonReader
12578  * @param {Object} meta Metadata configuration options
12579  * @param {Object} recordType Either an Array of field definition objects,
12580  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12581  */
12582 Roo.data.JsonReader = function(meta, recordType){
12583     
12584     meta = meta || {};
12585     // set some defaults:
12586     Roo.applyIf(meta, {
12587         totalProperty: 'total',
12588         successProperty : 'success',
12589         root : 'data',
12590         id : 'id'
12591     });
12592     
12593     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12594 };
12595 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12596     
12597     /**
12598      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12599      * Used by Store query builder to append _requestMeta to params.
12600      * 
12601      */
12602     metaFromRemote : false,
12603     /**
12604      * This method is only used by a DataProxy which has retrieved data from a remote server.
12605      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12606      * @return {Object} data A data block which is used by an Roo.data.Store object as
12607      * a cache of Roo.data.Records.
12608      */
12609     read : function(response){
12610         var json = response.responseText;
12611        
12612         var o = /* eval:var:o */ eval("("+json+")");
12613         if(!o) {
12614             throw {message: "JsonReader.read: Json object not found"};
12615         }
12616         
12617         if(o.metaData){
12618             
12619             delete this.ef;
12620             this.metaFromRemote = true;
12621             this.meta = o.metaData;
12622             this.recordType = Roo.data.Record.create(o.metaData.fields);
12623             this.onMetaChange(this.meta, this.recordType, o);
12624         }
12625         return this.readRecords(o);
12626     },
12627
12628     // private function a store will implement
12629     onMetaChange : function(meta, recordType, o){
12630
12631     },
12632
12633     /**
12634          * @ignore
12635          */
12636     simpleAccess: function(obj, subsc) {
12637         return obj[subsc];
12638     },
12639
12640         /**
12641          * @ignore
12642          */
12643     getJsonAccessor: function(){
12644         var re = /[\[\.]/;
12645         return function(expr) {
12646             try {
12647                 return(re.test(expr))
12648                     ? new Function("obj", "return obj." + expr)
12649                     : function(obj){
12650                         return obj[expr];
12651                     };
12652             } catch(e){}
12653             return Roo.emptyFn;
12654         };
12655     }(),
12656
12657     /**
12658      * Create a data block containing Roo.data.Records from an XML document.
12659      * @param {Object} o An object which contains an Array of row objects in the property specified
12660      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12661      * which contains the total size of the dataset.
12662      * @return {Object} data A data block which is used by an Roo.data.Store object as
12663      * a cache of Roo.data.Records.
12664      */
12665     readRecords : function(o){
12666         /**
12667          * After any data loads, the raw JSON data is available for further custom processing.
12668          * @type Object
12669          */
12670         this.o = o;
12671         var s = this.meta, Record = this.recordType,
12672             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12673
12674 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12675         if (!this.ef) {
12676             if(s.totalProperty) {
12677                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12678                 }
12679                 if(s.successProperty) {
12680                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12681                 }
12682                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12683                 if (s.id) {
12684                         var g = this.getJsonAccessor(s.id);
12685                         this.getId = function(rec) {
12686                                 var r = g(rec);  
12687                                 return (r === undefined || r === "") ? null : r;
12688                         };
12689                 } else {
12690                         this.getId = function(){return null;};
12691                 }
12692             this.ef = [];
12693             for(var jj = 0; jj < fl; jj++){
12694                 f = fi[jj];
12695                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12696                 this.ef[jj] = this.getJsonAccessor(map);
12697             }
12698         }
12699
12700         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12701         if(s.totalProperty){
12702             var vt = parseInt(this.getTotal(o), 10);
12703             if(!isNaN(vt)){
12704                 totalRecords = vt;
12705             }
12706         }
12707         if(s.successProperty){
12708             var vs = this.getSuccess(o);
12709             if(vs === false || vs === 'false'){
12710                 success = false;
12711             }
12712         }
12713         var records = [];
12714         for(var i = 0; i < c; i++){
12715                 var n = root[i];
12716             var values = {};
12717             var id = this.getId(n);
12718             for(var j = 0; j < fl; j++){
12719                 f = fi[j];
12720             var v = this.ef[j](n);
12721             if (!f.convert) {
12722                 Roo.log('missing convert for ' + f.name);
12723                 Roo.log(f);
12724                 continue;
12725             }
12726             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12727             }
12728             var record = new Record(values, id);
12729             record.json = n;
12730             records[i] = record;
12731         }
12732         return {
12733             raw : o,
12734             success : success,
12735             records : records,
12736             totalRecords : totalRecords
12737         };
12738     }
12739 });/*
12740  * Based on:
12741  * Ext JS Library 1.1.1
12742  * Copyright(c) 2006-2007, Ext JS, LLC.
12743  *
12744  * Originally Released Under LGPL - original licence link has changed is not relivant.
12745  *
12746  * Fork - LGPL
12747  * <script type="text/javascript">
12748  */
12749
12750 /**
12751  * @class Roo.data.ArrayReader
12752  * @extends Roo.data.DataReader
12753  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12754  * Each element of that Array represents a row of data fields. The
12755  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12756  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12757  * <p>
12758  * Example code:.
12759  * <pre><code>
12760 var RecordDef = Roo.data.Record.create([
12761     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12762     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12763 ]);
12764 var myReader = new Roo.data.ArrayReader({
12765     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12766 }, RecordDef);
12767 </code></pre>
12768  * <p>
12769  * This would consume an Array like this:
12770  * <pre><code>
12771 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12772   </code></pre>
12773  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12774  * @constructor
12775  * Create a new JsonReader
12776  * @param {Object} meta Metadata configuration options.
12777  * @param {Object} recordType Either an Array of field definition objects
12778  * as specified to {@link Roo.data.Record#create},
12779  * or an {@link Roo.data.Record} object
12780  * created using {@link Roo.data.Record#create}.
12781  */
12782 Roo.data.ArrayReader = function(meta, recordType){
12783     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12784 };
12785
12786 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12787     /**
12788      * Create a data block containing Roo.data.Records from an XML document.
12789      * @param {Object} o An Array of row objects which represents the dataset.
12790      * @return {Object} data A data block which is used by an Roo.data.Store object as
12791      * a cache of Roo.data.Records.
12792      */
12793     readRecords : function(o){
12794         var sid = this.meta ? this.meta.id : null;
12795         var recordType = this.recordType, fields = recordType.prototype.fields;
12796         var records = [];
12797         var root = o;
12798             for(var i = 0; i < root.length; i++){
12799                     var n = root[i];
12800                 var values = {};
12801                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12802                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12803                 var f = fields.items[j];
12804                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12805                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12806                 v = f.convert(v);
12807                 values[f.name] = v;
12808             }
12809                 var record = new recordType(values, id);
12810                 record.json = n;
12811                 records[records.length] = record;
12812             }
12813             return {
12814                 records : records,
12815                 totalRecords : records.length
12816             };
12817     }
12818 });/*
12819  * - LGPL
12820  * * 
12821  */
12822
12823 /**
12824  * @class Roo.bootstrap.ComboBox
12825  * @extends Roo.bootstrap.TriggerField
12826  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12827  * @cfg {Boolean} append (true|false) default false
12828  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12829  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12830  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12831  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12832  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12833  * @cfg {Boolean} animate default true
12834  * @cfg {Boolean} emptyResultText only for touch device
12835  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12836  * @cfg {String} emptyTitle default ''
12837  * @constructor
12838  * Create a new ComboBox.
12839  * @param {Object} config Configuration options
12840  */
12841 Roo.bootstrap.ComboBox = function(config){
12842     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12843     this.addEvents({
12844         /**
12845          * @event expand
12846          * Fires when the dropdown list is expanded
12847         * @param {Roo.bootstrap.ComboBox} combo This combo box
12848         */
12849         'expand' : true,
12850         /**
12851          * @event collapse
12852          * Fires when the dropdown list is collapsed
12853         * @param {Roo.bootstrap.ComboBox} combo This combo box
12854         */
12855         'collapse' : true,
12856         /**
12857          * @event beforeselect
12858          * Fires before a list item is selected. Return false to cancel the selection.
12859         * @param {Roo.bootstrap.ComboBox} combo This combo box
12860         * @param {Roo.data.Record} record The data record returned from the underlying store
12861         * @param {Number} index The index of the selected item in the dropdown list
12862         */
12863         'beforeselect' : true,
12864         /**
12865          * @event select
12866          * Fires when a list item is selected
12867         * @param {Roo.bootstrap.ComboBox} combo This combo box
12868         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12869         * @param {Number} index The index of the selected item in the dropdown list
12870         */
12871         'select' : true,
12872         /**
12873          * @event beforequery
12874          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12875          * The event object passed has these properties:
12876         * @param {Roo.bootstrap.ComboBox} combo This combo box
12877         * @param {String} query The query
12878         * @param {Boolean} forceAll true to force "all" query
12879         * @param {Boolean} cancel true to cancel the query
12880         * @param {Object} e The query event object
12881         */
12882         'beforequery': true,
12883          /**
12884          * @event add
12885          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12886         * @param {Roo.bootstrap.ComboBox} combo This combo box
12887         */
12888         'add' : true,
12889         /**
12890          * @event edit
12891          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12892         * @param {Roo.bootstrap.ComboBox} combo This combo box
12893         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12894         */
12895         'edit' : true,
12896         /**
12897          * @event remove
12898          * Fires when the remove value from the combobox array
12899         * @param {Roo.bootstrap.ComboBox} combo This combo box
12900         */
12901         'remove' : true,
12902         /**
12903          * @event afterremove
12904          * Fires when the remove value from the combobox array
12905         * @param {Roo.bootstrap.ComboBox} combo This combo box
12906         */
12907         'afterremove' : true,
12908         /**
12909          * @event specialfilter
12910          * Fires when specialfilter
12911             * @param {Roo.bootstrap.ComboBox} combo This combo box
12912             */
12913         'specialfilter' : true,
12914         /**
12915          * @event tick
12916          * Fires when tick the element
12917             * @param {Roo.bootstrap.ComboBox} combo This combo box
12918             */
12919         'tick' : true,
12920         /**
12921          * @event touchviewdisplay
12922          * Fires when touch view require special display (default is using displayField)
12923             * @param {Roo.bootstrap.ComboBox} combo This combo box
12924             * @param {Object} cfg set html .
12925             */
12926         'touchviewdisplay' : true
12927         
12928     });
12929     
12930     this.item = [];
12931     this.tickItems = [];
12932     
12933     this.selectedIndex = -1;
12934     if(this.mode == 'local'){
12935         if(config.queryDelay === undefined){
12936             this.queryDelay = 10;
12937         }
12938         if(config.minChars === undefined){
12939             this.minChars = 0;
12940         }
12941     }
12942 };
12943
12944 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12945      
12946     /**
12947      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12948      * rendering into an Roo.Editor, defaults to false)
12949      */
12950     /**
12951      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12952      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12953      */
12954     /**
12955      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12956      */
12957     /**
12958      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12959      * the dropdown list (defaults to undefined, with no header element)
12960      */
12961
12962      /**
12963      * @cfg {String/Roo.Template} tpl The template to use to render the output
12964      */
12965      
12966      /**
12967      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12968      */
12969     listWidth: undefined,
12970     /**
12971      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12972      * mode = 'remote' or 'text' if mode = 'local')
12973      */
12974     displayField: undefined,
12975     
12976     /**
12977      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12978      * mode = 'remote' or 'value' if mode = 'local'). 
12979      * Note: use of a valueField requires the user make a selection
12980      * in order for a value to be mapped.
12981      */
12982     valueField: undefined,
12983     /**
12984      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12985      */
12986     modalTitle : '',
12987     
12988     /**
12989      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12990      * field's data value (defaults to the underlying DOM element's name)
12991      */
12992     hiddenName: undefined,
12993     /**
12994      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12995      */
12996     listClass: '',
12997     /**
12998      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12999      */
13000     selectedClass: 'active',
13001     
13002     /**
13003      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13004      */
13005     shadow:'sides',
13006     /**
13007      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13008      * anchor positions (defaults to 'tl-bl')
13009      */
13010     listAlign: 'tl-bl?',
13011     /**
13012      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13013      */
13014     maxHeight: 300,
13015     /**
13016      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13017      * query specified by the allQuery config option (defaults to 'query')
13018      */
13019     triggerAction: 'query',
13020     /**
13021      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13022      * (defaults to 4, does not apply if editable = false)
13023      */
13024     minChars : 4,
13025     /**
13026      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13027      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13028      */
13029     typeAhead: false,
13030     /**
13031      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13032      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13033      */
13034     queryDelay: 500,
13035     /**
13036      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13037      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13038      */
13039     pageSize: 0,
13040     /**
13041      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13042      * when editable = true (defaults to false)
13043      */
13044     selectOnFocus:false,
13045     /**
13046      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13047      */
13048     queryParam: 'query',
13049     /**
13050      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13051      * when mode = 'remote' (defaults to 'Loading...')
13052      */
13053     loadingText: 'Loading...',
13054     /**
13055      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13056      */
13057     resizable: false,
13058     /**
13059      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13060      */
13061     handleHeight : 8,
13062     /**
13063      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13064      * traditional select (defaults to true)
13065      */
13066     editable: true,
13067     /**
13068      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13069      */
13070     allQuery: '',
13071     /**
13072      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13073      */
13074     mode: 'remote',
13075     /**
13076      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13077      * listWidth has a higher value)
13078      */
13079     minListWidth : 70,
13080     /**
13081      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13082      * allow the user to set arbitrary text into the field (defaults to false)
13083      */
13084     forceSelection:false,
13085     /**
13086      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13087      * if typeAhead = true (defaults to 250)
13088      */
13089     typeAheadDelay : 250,
13090     /**
13091      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13092      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13093      */
13094     valueNotFoundText : undefined,
13095     /**
13096      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13097      */
13098     blockFocus : false,
13099     
13100     /**
13101      * @cfg {Boolean} disableClear Disable showing of clear button.
13102      */
13103     disableClear : false,
13104     /**
13105      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13106      */
13107     alwaysQuery : false,
13108     
13109     /**
13110      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13111      */
13112     multiple : false,
13113     
13114     /**
13115      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13116      */
13117     invalidClass : "has-warning",
13118     
13119     /**
13120      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13121      */
13122     validClass : "has-success",
13123     
13124     /**
13125      * @cfg {Boolean} specialFilter (true|false) special filter default false
13126      */
13127     specialFilter : false,
13128     
13129     /**
13130      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13131      */
13132     mobileTouchView : true,
13133     
13134     /**
13135      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13136      */
13137     useNativeIOS : false,
13138     
13139     /**
13140      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13141      */
13142     mobile_restrict_height : false,
13143     
13144     ios_options : false,
13145     
13146     //private
13147     addicon : false,
13148     editicon: false,
13149     
13150     page: 0,
13151     hasQuery: false,
13152     append: false,
13153     loadNext: false,
13154     autoFocus : true,
13155     tickable : false,
13156     btnPosition : 'right',
13157     triggerList : true,
13158     showToggleBtn : true,
13159     animate : true,
13160     emptyResultText: 'Empty',
13161     triggerText : 'Select',
13162     emptyTitle : '',
13163     
13164     // element that contains real text value.. (when hidden is used..)
13165     
13166     getAutoCreate : function()
13167     {   
13168         var cfg = false;
13169         //render
13170         /*
13171          * Render classic select for iso
13172          */
13173         
13174         if(Roo.isIOS && this.useNativeIOS){
13175             cfg = this.getAutoCreateNativeIOS();
13176             return cfg;
13177         }
13178         
13179         /*
13180          * Touch Devices
13181          */
13182         
13183         if(Roo.isTouch && this.mobileTouchView){
13184             cfg = this.getAutoCreateTouchView();
13185             return cfg;;
13186         }
13187         
13188         /*
13189          *  Normal ComboBox
13190          */
13191         if(!this.tickable){
13192             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13193             return cfg;
13194         }
13195         
13196         /*
13197          *  ComboBox with tickable selections
13198          */
13199              
13200         var align = this.labelAlign || this.parentLabelAlign();
13201         
13202         cfg = {
13203             cls : 'form-group roo-combobox-tickable' //input-group
13204         };
13205         
13206         var btn_text_select = '';
13207         var btn_text_done = '';
13208         var btn_text_cancel = '';
13209         
13210         if (this.btn_text_show) {
13211             btn_text_select = 'Select';
13212             btn_text_done = 'Done';
13213             btn_text_cancel = 'Cancel'; 
13214         }
13215         
13216         var buttons = {
13217             tag : 'div',
13218             cls : 'tickable-buttons',
13219             cn : [
13220                 {
13221                     tag : 'button',
13222                     type : 'button',
13223                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13224                     //html : this.triggerText
13225                     html: btn_text_select
13226                 },
13227                 {
13228                     tag : 'button',
13229                     type : 'button',
13230                     name : 'ok',
13231                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13232                     //html : 'Done'
13233                     html: btn_text_done
13234                 },
13235                 {
13236                     tag : 'button',
13237                     type : 'button',
13238                     name : 'cancel',
13239                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13240                     //html : 'Cancel'
13241                     html: btn_text_cancel
13242                 }
13243             ]
13244         };
13245         
13246         if(this.editable){
13247             buttons.cn.unshift({
13248                 tag: 'input',
13249                 cls: 'roo-select2-search-field-input'
13250             });
13251         }
13252         
13253         var _this = this;
13254         
13255         Roo.each(buttons.cn, function(c){
13256             if (_this.size) {
13257                 c.cls += ' btn-' + _this.size;
13258             }
13259
13260             if (_this.disabled) {
13261                 c.disabled = true;
13262             }
13263         });
13264         
13265         var box = {
13266             tag: 'div',
13267             cn: [
13268                 {
13269                     tag: 'input',
13270                     type : 'hidden',
13271                     cls: 'form-hidden-field'
13272                 },
13273                 {
13274                     tag: 'ul',
13275                     cls: 'roo-select2-choices',
13276                     cn:[
13277                         {
13278                             tag: 'li',
13279                             cls: 'roo-select2-search-field',
13280                             cn: [
13281                                 buttons
13282                             ]
13283                         }
13284                     ]
13285                 }
13286             ]
13287         };
13288         
13289         var combobox = {
13290             cls: 'roo-select2-container input-group roo-select2-container-multi',
13291             cn: [
13292                 box
13293 //                {
13294 //                    tag: 'ul',
13295 //                    cls: 'typeahead typeahead-long dropdown-menu',
13296 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13297 //                }
13298             ]
13299         };
13300         
13301         if(this.hasFeedback && !this.allowBlank){
13302             
13303             var feedback = {
13304                 tag: 'span',
13305                 cls: 'glyphicon form-control-feedback'
13306             };
13307
13308             combobox.cn.push(feedback);
13309         }
13310         
13311         
13312         if (align ==='left' && this.fieldLabel.length) {
13313             
13314             cfg.cls += ' roo-form-group-label-left';
13315             
13316             cfg.cn = [
13317                 {
13318                     tag : 'i',
13319                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13320                     tooltip : 'This field is required'
13321                 },
13322                 {
13323                     tag: 'label',
13324                     'for' :  id,
13325                     cls : 'control-label',
13326                     html : this.fieldLabel
13327
13328                 },
13329                 {
13330                     cls : "", 
13331                     cn: [
13332                         combobox
13333                     ]
13334                 }
13335
13336             ];
13337             
13338             var labelCfg = cfg.cn[1];
13339             var contentCfg = cfg.cn[2];
13340             
13341
13342             if(this.indicatorpos == 'right'){
13343                 
13344                 cfg.cn = [
13345                     {
13346                         tag: 'label',
13347                         'for' :  id,
13348                         cls : 'control-label',
13349                         cn : [
13350                             {
13351                                 tag : 'span',
13352                                 html : this.fieldLabel
13353                             },
13354                             {
13355                                 tag : 'i',
13356                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13357                                 tooltip : 'This field is required'
13358                             }
13359                         ]
13360                     },
13361                     {
13362                         cls : "",
13363                         cn: [
13364                             combobox
13365                         ]
13366                     }
13367
13368                 ];
13369                 
13370                 
13371                 
13372                 labelCfg = cfg.cn[0];
13373                 contentCfg = cfg.cn[1];
13374             
13375             }
13376             
13377             if(this.labelWidth > 12){
13378                 labelCfg.style = "width: " + this.labelWidth + 'px';
13379             }
13380             
13381             if(this.labelWidth < 13 && this.labelmd == 0){
13382                 this.labelmd = this.labelWidth;
13383             }
13384             
13385             if(this.labellg > 0){
13386                 labelCfg.cls += ' col-lg-' + this.labellg;
13387                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13388             }
13389             
13390             if(this.labelmd > 0){
13391                 labelCfg.cls += ' col-md-' + this.labelmd;
13392                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13393             }
13394             
13395             if(this.labelsm > 0){
13396                 labelCfg.cls += ' col-sm-' + this.labelsm;
13397                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13398             }
13399             
13400             if(this.labelxs > 0){
13401                 labelCfg.cls += ' col-xs-' + this.labelxs;
13402                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13403             }
13404                 
13405                 
13406         } else if ( this.fieldLabel.length) {
13407 //                Roo.log(" label");
13408                  cfg.cn = [
13409                     {
13410                         tag : 'i',
13411                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13412                         tooltip : 'This field is required'
13413                     },
13414                     {
13415                         tag: 'label',
13416                         //cls : 'input-group-addon',
13417                         html : this.fieldLabel
13418                     },
13419                     combobox
13420                 ];
13421                 
13422                 if(this.indicatorpos == 'right'){
13423                     cfg.cn = [
13424                         {
13425                             tag: 'label',
13426                             //cls : 'input-group-addon',
13427                             html : this.fieldLabel
13428                         },
13429                         {
13430                             tag : 'i',
13431                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13432                             tooltip : 'This field is required'
13433                         },
13434                         combobox
13435                     ];
13436                     
13437                 }
13438
13439         } else {
13440             
13441 //                Roo.log(" no label && no align");
13442                 cfg = combobox
13443                      
13444                 
13445         }
13446          
13447         var settings=this;
13448         ['xs','sm','md','lg'].map(function(size){
13449             if (settings[size]) {
13450                 cfg.cls += ' col-' + size + '-' + settings[size];
13451             }
13452         });
13453         
13454         return cfg;
13455         
13456     },
13457     
13458     _initEventsCalled : false,
13459     
13460     // private
13461     initEvents: function()
13462     {   
13463         if (this._initEventsCalled) { // as we call render... prevent looping...
13464             return;
13465         }
13466         this._initEventsCalled = true;
13467         
13468         if (!this.store) {
13469             throw "can not find store for combo";
13470         }
13471         
13472         this.indicator = this.indicatorEl();
13473         
13474         this.store = Roo.factory(this.store, Roo.data);
13475         this.store.parent = this;
13476         
13477         // if we are building from html. then this element is so complex, that we can not really
13478         // use the rendered HTML.
13479         // so we have to trash and replace the previous code.
13480         if (Roo.XComponent.build_from_html) {
13481             // remove this element....
13482             var e = this.el.dom, k=0;
13483             while (e ) { e = e.previousSibling;  ++k;}
13484
13485             this.el.remove();
13486             
13487             this.el=false;
13488             this.rendered = false;
13489             
13490             this.render(this.parent().getChildContainer(true), k);
13491         }
13492         
13493         if(Roo.isIOS && this.useNativeIOS){
13494             this.initIOSView();
13495             return;
13496         }
13497         
13498         /*
13499          * Touch Devices
13500          */
13501         
13502         if(Roo.isTouch && this.mobileTouchView){
13503             this.initTouchView();
13504             return;
13505         }
13506         
13507         if(this.tickable){
13508             this.initTickableEvents();
13509             return;
13510         }
13511         
13512         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13513         
13514         if(this.hiddenName){
13515             
13516             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13517             
13518             this.hiddenField.dom.value =
13519                 this.hiddenValue !== undefined ? this.hiddenValue :
13520                 this.value !== undefined ? this.value : '';
13521
13522             // prevent input submission
13523             this.el.dom.removeAttribute('name');
13524             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13525              
13526              
13527         }
13528         //if(Roo.isGecko){
13529         //    this.el.dom.setAttribute('autocomplete', 'off');
13530         //}
13531         
13532         var cls = 'x-combo-list';
13533         
13534         //this.list = new Roo.Layer({
13535         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13536         //});
13537         
13538         var _this = this;
13539         
13540         (function(){
13541             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13542             _this.list.setWidth(lw);
13543         }).defer(100);
13544         
13545         this.list.on('mouseover', this.onViewOver, this);
13546         this.list.on('mousemove', this.onViewMove, this);
13547         this.list.on('scroll', this.onViewScroll, this);
13548         
13549         /*
13550         this.list.swallowEvent('mousewheel');
13551         this.assetHeight = 0;
13552
13553         if(this.title){
13554             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13555             this.assetHeight += this.header.getHeight();
13556         }
13557
13558         this.innerList = this.list.createChild({cls:cls+'-inner'});
13559         this.innerList.on('mouseover', this.onViewOver, this);
13560         this.innerList.on('mousemove', this.onViewMove, this);
13561         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13562         
13563         if(this.allowBlank && !this.pageSize && !this.disableClear){
13564             this.footer = this.list.createChild({cls:cls+'-ft'});
13565             this.pageTb = new Roo.Toolbar(this.footer);
13566            
13567         }
13568         if(this.pageSize){
13569             this.footer = this.list.createChild({cls:cls+'-ft'});
13570             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13571                     {pageSize: this.pageSize});
13572             
13573         }
13574         
13575         if (this.pageTb && this.allowBlank && !this.disableClear) {
13576             var _this = this;
13577             this.pageTb.add(new Roo.Toolbar.Fill(), {
13578                 cls: 'x-btn-icon x-btn-clear',
13579                 text: '&#160;',
13580                 handler: function()
13581                 {
13582                     _this.collapse();
13583                     _this.clearValue();
13584                     _this.onSelect(false, -1);
13585                 }
13586             });
13587         }
13588         if (this.footer) {
13589             this.assetHeight += this.footer.getHeight();
13590         }
13591         */
13592             
13593         if(!this.tpl){
13594             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13595         }
13596
13597         this.view = new Roo.View(this.list, this.tpl, {
13598             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13599         });
13600         //this.view.wrapEl.setDisplayed(false);
13601         this.view.on('click', this.onViewClick, this);
13602         
13603         
13604         this.store.on('beforeload', this.onBeforeLoad, this);
13605         this.store.on('load', this.onLoad, this);
13606         this.store.on('loadexception', this.onLoadException, this);
13607         /*
13608         if(this.resizable){
13609             this.resizer = new Roo.Resizable(this.list,  {
13610                pinned:true, handles:'se'
13611             });
13612             this.resizer.on('resize', function(r, w, h){
13613                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13614                 this.listWidth = w;
13615                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13616                 this.restrictHeight();
13617             }, this);
13618             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13619         }
13620         */
13621         if(!this.editable){
13622             this.editable = true;
13623             this.setEditable(false);
13624         }
13625         
13626         /*
13627         
13628         if (typeof(this.events.add.listeners) != 'undefined') {
13629             
13630             this.addicon = this.wrap.createChild(
13631                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13632        
13633             this.addicon.on('click', function(e) {
13634                 this.fireEvent('add', this);
13635             }, this);
13636         }
13637         if (typeof(this.events.edit.listeners) != 'undefined') {
13638             
13639             this.editicon = this.wrap.createChild(
13640                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13641             if (this.addicon) {
13642                 this.editicon.setStyle('margin-left', '40px');
13643             }
13644             this.editicon.on('click', function(e) {
13645                 
13646                 // we fire even  if inothing is selected..
13647                 this.fireEvent('edit', this, this.lastData );
13648                 
13649             }, this);
13650         }
13651         */
13652         
13653         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13654             "up" : function(e){
13655                 this.inKeyMode = true;
13656                 this.selectPrev();
13657             },
13658
13659             "down" : function(e){
13660                 if(!this.isExpanded()){
13661                     this.onTriggerClick();
13662                 }else{
13663                     this.inKeyMode = true;
13664                     this.selectNext();
13665                 }
13666             },
13667
13668             "enter" : function(e){
13669 //                this.onViewClick();
13670                 //return true;
13671                 this.collapse();
13672                 
13673                 if(this.fireEvent("specialkey", this, e)){
13674                     this.onViewClick(false);
13675                 }
13676                 
13677                 return true;
13678             },
13679
13680             "esc" : function(e){
13681                 this.collapse();
13682             },
13683
13684             "tab" : function(e){
13685                 this.collapse();
13686                 
13687                 if(this.fireEvent("specialkey", this, e)){
13688                     this.onViewClick(false);
13689                 }
13690                 
13691                 return true;
13692             },
13693
13694             scope : this,
13695
13696             doRelay : function(foo, bar, hname){
13697                 if(hname == 'down' || this.scope.isExpanded()){
13698                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13699                 }
13700                 return true;
13701             },
13702
13703             forceKeyDown: true
13704         });
13705         
13706         
13707         this.queryDelay = Math.max(this.queryDelay || 10,
13708                 this.mode == 'local' ? 10 : 250);
13709         
13710         
13711         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13712         
13713         if(this.typeAhead){
13714             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13715         }
13716         if(this.editable !== false){
13717             this.inputEl().on("keyup", this.onKeyUp, this);
13718         }
13719         if(this.forceSelection){
13720             this.inputEl().on('blur', this.doForce, this);
13721         }
13722         
13723         if(this.multiple){
13724             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13725             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13726         }
13727     },
13728     
13729     initTickableEvents: function()
13730     {   
13731         this.createList();
13732         
13733         if(this.hiddenName){
13734             
13735             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13736             
13737             this.hiddenField.dom.value =
13738                 this.hiddenValue !== undefined ? this.hiddenValue :
13739                 this.value !== undefined ? this.value : '';
13740
13741             // prevent input submission
13742             this.el.dom.removeAttribute('name');
13743             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13744              
13745              
13746         }
13747         
13748 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13749         
13750         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13751         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13752         if(this.triggerList){
13753             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13754         }
13755          
13756         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13757         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13758         
13759         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13760         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13761         
13762         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13763         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13764         
13765         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13766         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13767         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13768         
13769         this.okBtn.hide();
13770         this.cancelBtn.hide();
13771         
13772         var _this = this;
13773         
13774         (function(){
13775             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13776             _this.list.setWidth(lw);
13777         }).defer(100);
13778         
13779         this.list.on('mouseover', this.onViewOver, this);
13780         this.list.on('mousemove', this.onViewMove, this);
13781         
13782         this.list.on('scroll', this.onViewScroll, this);
13783         
13784         if(!this.tpl){
13785             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13786                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13787         }
13788
13789         this.view = new Roo.View(this.list, this.tpl, {
13790             singleSelect:true,
13791             tickable:true,
13792             parent:this,
13793             store: this.store,
13794             selectedClass: this.selectedClass
13795         });
13796         
13797         //this.view.wrapEl.setDisplayed(false);
13798         this.view.on('click', this.onViewClick, this);
13799         
13800         
13801         
13802         this.store.on('beforeload', this.onBeforeLoad, this);
13803         this.store.on('load', this.onLoad, this);
13804         this.store.on('loadexception', this.onLoadException, this);
13805         
13806         if(this.editable){
13807             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13808                 "up" : function(e){
13809                     this.inKeyMode = true;
13810                     this.selectPrev();
13811                 },
13812
13813                 "down" : function(e){
13814                     this.inKeyMode = true;
13815                     this.selectNext();
13816                 },
13817
13818                 "enter" : function(e){
13819                     if(this.fireEvent("specialkey", this, e)){
13820                         this.onViewClick(false);
13821                     }
13822                     
13823                     return true;
13824                 },
13825
13826                 "esc" : function(e){
13827                     this.onTickableFooterButtonClick(e, false, false);
13828                 },
13829
13830                 "tab" : function(e){
13831                     this.fireEvent("specialkey", this, e);
13832                     
13833                     this.onTickableFooterButtonClick(e, false, false);
13834                     
13835                     return true;
13836                 },
13837
13838                 scope : this,
13839
13840                 doRelay : function(e, fn, key){
13841                     if(this.scope.isExpanded()){
13842                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13843                     }
13844                     return true;
13845                 },
13846
13847                 forceKeyDown: true
13848             });
13849         }
13850         
13851         this.queryDelay = Math.max(this.queryDelay || 10,
13852                 this.mode == 'local' ? 10 : 250);
13853         
13854         
13855         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13856         
13857         if(this.typeAhead){
13858             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13859         }
13860         
13861         if(this.editable !== false){
13862             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13863         }
13864         
13865         this.indicator = this.indicatorEl();
13866         
13867         if(this.indicator){
13868             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13869             this.indicator.hide();
13870         }
13871         
13872     },
13873
13874     onDestroy : function(){
13875         if(this.view){
13876             this.view.setStore(null);
13877             this.view.el.removeAllListeners();
13878             this.view.el.remove();
13879             this.view.purgeListeners();
13880         }
13881         if(this.list){
13882             this.list.dom.innerHTML  = '';
13883         }
13884         
13885         if(this.store){
13886             this.store.un('beforeload', this.onBeforeLoad, this);
13887             this.store.un('load', this.onLoad, this);
13888             this.store.un('loadexception', this.onLoadException, this);
13889         }
13890         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13891     },
13892
13893     // private
13894     fireKey : function(e){
13895         if(e.isNavKeyPress() && !this.list.isVisible()){
13896             this.fireEvent("specialkey", this, e);
13897         }
13898     },
13899
13900     // private
13901     onResize: function(w, h){
13902 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13903 //        
13904 //        if(typeof w != 'number'){
13905 //            // we do not handle it!?!?
13906 //            return;
13907 //        }
13908 //        var tw = this.trigger.getWidth();
13909 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13910 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13911 //        var x = w - tw;
13912 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13913 //            
13914 //        //this.trigger.setStyle('left', x+'px');
13915 //        
13916 //        if(this.list && this.listWidth === undefined){
13917 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13918 //            this.list.setWidth(lw);
13919 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13920 //        }
13921         
13922     
13923         
13924     },
13925
13926     /**
13927      * Allow or prevent the user from directly editing the field text.  If false is passed,
13928      * the user will only be able to select from the items defined in the dropdown list.  This method
13929      * is the runtime equivalent of setting the 'editable' config option at config time.
13930      * @param {Boolean} value True to allow the user to directly edit the field text
13931      */
13932     setEditable : function(value){
13933         if(value == this.editable){
13934             return;
13935         }
13936         this.editable = value;
13937         if(!value){
13938             this.inputEl().dom.setAttribute('readOnly', true);
13939             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13940             this.inputEl().addClass('x-combo-noedit');
13941         }else{
13942             this.inputEl().dom.setAttribute('readOnly', false);
13943             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13944             this.inputEl().removeClass('x-combo-noedit');
13945         }
13946     },
13947
13948     // private
13949     
13950     onBeforeLoad : function(combo,opts){
13951         if(!this.hasFocus){
13952             return;
13953         }
13954          if (!opts.add) {
13955             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13956          }
13957         this.restrictHeight();
13958         this.selectedIndex = -1;
13959     },
13960
13961     // private
13962     onLoad : function(){
13963         
13964         this.hasQuery = false;
13965         
13966         if(!this.hasFocus){
13967             return;
13968         }
13969         
13970         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13971             this.loading.hide();
13972         }
13973         
13974         if(this.store.getCount() > 0){
13975             
13976             this.expand();
13977             this.restrictHeight();
13978             if(this.lastQuery == this.allQuery){
13979                 if(this.editable && !this.tickable){
13980                     this.inputEl().dom.select();
13981                 }
13982                 
13983                 if(
13984                     !this.selectByValue(this.value, true) &&
13985                     this.autoFocus && 
13986                     (
13987                         !this.store.lastOptions ||
13988                         typeof(this.store.lastOptions.add) == 'undefined' || 
13989                         this.store.lastOptions.add != true
13990                     )
13991                 ){
13992                     this.select(0, true);
13993                 }
13994             }else{
13995                 if(this.autoFocus){
13996                     this.selectNext();
13997                 }
13998                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13999                     this.taTask.delay(this.typeAheadDelay);
14000                 }
14001             }
14002         }else{
14003             this.onEmptyResults();
14004         }
14005         
14006         //this.el.focus();
14007     },
14008     // private
14009     onLoadException : function()
14010     {
14011         this.hasQuery = false;
14012         
14013         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14014             this.loading.hide();
14015         }
14016         
14017         if(this.tickable && this.editable){
14018             return;
14019         }
14020         
14021         this.collapse();
14022         // only causes errors at present
14023         //Roo.log(this.store.reader.jsonData);
14024         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14025             // fixme
14026             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14027         //}
14028         
14029         
14030     },
14031     // private
14032     onTypeAhead : function(){
14033         if(this.store.getCount() > 0){
14034             var r = this.store.getAt(0);
14035             var newValue = r.data[this.displayField];
14036             var len = newValue.length;
14037             var selStart = this.getRawValue().length;
14038             
14039             if(selStart != len){
14040                 this.setRawValue(newValue);
14041                 this.selectText(selStart, newValue.length);
14042             }
14043         }
14044     },
14045
14046     // private
14047     onSelect : function(record, index){
14048         
14049         if(this.fireEvent('beforeselect', this, record, index) !== false){
14050         
14051             this.setFromData(index > -1 ? record.data : false);
14052             
14053             this.collapse();
14054             this.fireEvent('select', this, record, index);
14055         }
14056     },
14057
14058     /**
14059      * Returns the currently selected field value or empty string if no value is set.
14060      * @return {String} value The selected value
14061      */
14062     getValue : function()
14063     {
14064         if(Roo.isIOS && this.useNativeIOS){
14065             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14066         }
14067         
14068         if(this.multiple){
14069             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14070         }
14071         
14072         if(this.valueField){
14073             return typeof this.value != 'undefined' ? this.value : '';
14074         }else{
14075             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14076         }
14077     },
14078     
14079     getRawValue : function()
14080     {
14081         if(Roo.isIOS && this.useNativeIOS){
14082             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14083         }
14084         
14085         var v = this.inputEl().getValue();
14086         
14087         return v;
14088     },
14089
14090     /**
14091      * Clears any text/value currently set in the field
14092      */
14093     clearValue : function(){
14094         
14095         if(this.hiddenField){
14096             this.hiddenField.dom.value = '';
14097         }
14098         this.value = '';
14099         this.setRawValue('');
14100         this.lastSelectionText = '';
14101         this.lastData = false;
14102         
14103         var close = this.closeTriggerEl();
14104         
14105         if(close){
14106             close.hide();
14107         }
14108         
14109         this.validate();
14110         
14111     },
14112
14113     /**
14114      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14115      * will be displayed in the field.  If the value does not match the data value of an existing item,
14116      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14117      * Otherwise the field will be blank (although the value will still be set).
14118      * @param {String} value The value to match
14119      */
14120     setValue : function(v)
14121     {
14122         if(Roo.isIOS && this.useNativeIOS){
14123             this.setIOSValue(v);
14124             return;
14125         }
14126         
14127         if(this.multiple){
14128             this.syncValue();
14129             return;
14130         }
14131         
14132         var text = v;
14133         if(this.valueField){
14134             var r = this.findRecord(this.valueField, v);
14135             if(r){
14136                 text = r.data[this.displayField];
14137             }else if(this.valueNotFoundText !== undefined){
14138                 text = this.valueNotFoundText;
14139             }
14140         }
14141         this.lastSelectionText = text;
14142         if(this.hiddenField){
14143             this.hiddenField.dom.value = v;
14144         }
14145         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14146         this.value = v;
14147         
14148         var close = this.closeTriggerEl();
14149         
14150         if(close){
14151             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14152         }
14153         
14154         this.validate();
14155     },
14156     /**
14157      * @property {Object} the last set data for the element
14158      */
14159     
14160     lastData : false,
14161     /**
14162      * Sets the value of the field based on a object which is related to the record format for the store.
14163      * @param {Object} value the value to set as. or false on reset?
14164      */
14165     setFromData : function(o){
14166         
14167         if(this.multiple){
14168             this.addItem(o);
14169             return;
14170         }
14171             
14172         var dv = ''; // display value
14173         var vv = ''; // value value..
14174         this.lastData = o;
14175         if (this.displayField) {
14176             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14177         } else {
14178             // this is an error condition!!!
14179             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14180         }
14181         
14182         if(this.valueField){
14183             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14184         }
14185         
14186         var close = this.closeTriggerEl();
14187         
14188         if(close){
14189             if(dv.length || vv * 1 > 0){
14190                 close.show() ;
14191                 this.blockFocus=true;
14192             } else {
14193                 close.hide();
14194             }             
14195         }
14196         
14197         if(this.hiddenField){
14198             this.hiddenField.dom.value = vv;
14199             
14200             this.lastSelectionText = dv;
14201             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14202             this.value = vv;
14203             return;
14204         }
14205         // no hidden field.. - we store the value in 'value', but still display
14206         // display field!!!!
14207         this.lastSelectionText = dv;
14208         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14209         this.value = vv;
14210         
14211         
14212         
14213     },
14214     // private
14215     reset : function(){
14216         // overridden so that last data is reset..
14217         
14218         if(this.multiple){
14219             this.clearItem();
14220             return;
14221         }
14222         
14223         this.setValue(this.originalValue);
14224         //this.clearInvalid();
14225         this.lastData = false;
14226         if (this.view) {
14227             this.view.clearSelections();
14228         }
14229         
14230         this.validate();
14231     },
14232     // private
14233     findRecord : function(prop, value){
14234         var record;
14235         if(this.store.getCount() > 0){
14236             this.store.each(function(r){
14237                 if(r.data[prop] == value){
14238                     record = r;
14239                     return false;
14240                 }
14241                 return true;
14242             });
14243         }
14244         return record;
14245     },
14246     
14247     getName: function()
14248     {
14249         // returns hidden if it's set..
14250         if (!this.rendered) {return ''};
14251         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14252         
14253     },
14254     // private
14255     onViewMove : function(e, t){
14256         this.inKeyMode = false;
14257     },
14258
14259     // private
14260     onViewOver : function(e, t){
14261         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14262             return;
14263         }
14264         var item = this.view.findItemFromChild(t);
14265         
14266         if(item){
14267             var index = this.view.indexOf(item);
14268             this.select(index, false);
14269         }
14270     },
14271
14272     // private
14273     onViewClick : function(view, doFocus, el, e)
14274     {
14275         var index = this.view.getSelectedIndexes()[0];
14276         
14277         var r = this.store.getAt(index);
14278         
14279         if(this.tickable){
14280             
14281             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14282                 return;
14283             }
14284             
14285             var rm = false;
14286             var _this = this;
14287             
14288             Roo.each(this.tickItems, function(v,k){
14289                 
14290                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14291                     Roo.log(v);
14292                     _this.tickItems.splice(k, 1);
14293                     
14294                     if(typeof(e) == 'undefined' && view == false){
14295                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14296                     }
14297                     
14298                     rm = true;
14299                     return;
14300                 }
14301             });
14302             
14303             if(rm){
14304                 return;
14305             }
14306             
14307             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14308                 this.tickItems.push(r.data);
14309             }
14310             
14311             if(typeof(e) == 'undefined' && view == false){
14312                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14313             }
14314                     
14315             return;
14316         }
14317         
14318         if(r){
14319             this.onSelect(r, index);
14320         }
14321         if(doFocus !== false && !this.blockFocus){
14322             this.inputEl().focus();
14323         }
14324     },
14325
14326     // private
14327     restrictHeight : function(){
14328         //this.innerList.dom.style.height = '';
14329         //var inner = this.innerList.dom;
14330         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14331         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14332         //this.list.beginUpdate();
14333         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14334         this.list.alignTo(this.inputEl(), this.listAlign);
14335         this.list.alignTo(this.inputEl(), this.listAlign);
14336         //this.list.endUpdate();
14337     },
14338
14339     // private
14340     onEmptyResults : function(){
14341         
14342         if(this.tickable && this.editable){
14343             this.hasFocus = false;
14344             this.restrictHeight();
14345             return;
14346         }
14347         
14348         this.collapse();
14349     },
14350
14351     /**
14352      * Returns true if the dropdown list is expanded, else false.
14353      */
14354     isExpanded : function(){
14355         return this.list.isVisible();
14356     },
14357
14358     /**
14359      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14360      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14361      * @param {String} value The data value of the item to select
14362      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14363      * selected item if it is not currently in view (defaults to true)
14364      * @return {Boolean} True if the value matched an item in the list, else false
14365      */
14366     selectByValue : function(v, scrollIntoView){
14367         if(v !== undefined && v !== null){
14368             var r = this.findRecord(this.valueField || this.displayField, v);
14369             if(r){
14370                 this.select(this.store.indexOf(r), scrollIntoView);
14371                 return true;
14372             }
14373         }
14374         return false;
14375     },
14376
14377     /**
14378      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14379      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14380      * @param {Number} index The zero-based index of the list item to select
14381      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14382      * selected item if it is not currently in view (defaults to true)
14383      */
14384     select : function(index, scrollIntoView){
14385         this.selectedIndex = index;
14386         this.view.select(index);
14387         if(scrollIntoView !== false){
14388             var el = this.view.getNode(index);
14389             /*
14390              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14391              */
14392             if(el){
14393                 this.list.scrollChildIntoView(el, false);
14394             }
14395         }
14396     },
14397
14398     // private
14399     selectNext : function(){
14400         var ct = this.store.getCount();
14401         if(ct > 0){
14402             if(this.selectedIndex == -1){
14403                 this.select(0);
14404             }else if(this.selectedIndex < ct-1){
14405                 this.select(this.selectedIndex+1);
14406             }
14407         }
14408     },
14409
14410     // private
14411     selectPrev : function(){
14412         var ct = this.store.getCount();
14413         if(ct > 0){
14414             if(this.selectedIndex == -1){
14415                 this.select(0);
14416             }else if(this.selectedIndex != 0){
14417                 this.select(this.selectedIndex-1);
14418             }
14419         }
14420     },
14421
14422     // private
14423     onKeyUp : function(e){
14424         if(this.editable !== false && !e.isSpecialKey()){
14425             this.lastKey = e.getKey();
14426             this.dqTask.delay(this.queryDelay);
14427         }
14428     },
14429
14430     // private
14431     validateBlur : function(){
14432         return !this.list || !this.list.isVisible();   
14433     },
14434
14435     // private
14436     initQuery : function(){
14437         
14438         var v = this.getRawValue();
14439         
14440         if(this.tickable && this.editable){
14441             v = this.tickableInputEl().getValue();
14442         }
14443         
14444         this.doQuery(v);
14445     },
14446
14447     // private
14448     doForce : function(){
14449         if(this.inputEl().dom.value.length > 0){
14450             this.inputEl().dom.value =
14451                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14452              
14453         }
14454     },
14455
14456     /**
14457      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14458      * query allowing the query action to be canceled if needed.
14459      * @param {String} query The SQL query to execute
14460      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14461      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14462      * saved in the current store (defaults to false)
14463      */
14464     doQuery : function(q, forceAll){
14465         
14466         if(q === undefined || q === null){
14467             q = '';
14468         }
14469         var qe = {
14470             query: q,
14471             forceAll: forceAll,
14472             combo: this,
14473             cancel:false
14474         };
14475         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14476             return false;
14477         }
14478         q = qe.query;
14479         
14480         forceAll = qe.forceAll;
14481         if(forceAll === true || (q.length >= this.minChars)){
14482             
14483             this.hasQuery = true;
14484             
14485             if(this.lastQuery != q || this.alwaysQuery){
14486                 this.lastQuery = q;
14487                 if(this.mode == 'local'){
14488                     this.selectedIndex = -1;
14489                     if(forceAll){
14490                         this.store.clearFilter();
14491                     }else{
14492                         
14493                         if(this.specialFilter){
14494                             this.fireEvent('specialfilter', this);
14495                             this.onLoad();
14496                             return;
14497                         }
14498                         
14499                         this.store.filter(this.displayField, q);
14500                     }
14501                     
14502                     this.store.fireEvent("datachanged", this.store);
14503                     
14504                     this.onLoad();
14505                     
14506                     
14507                 }else{
14508                     
14509                     this.store.baseParams[this.queryParam] = q;
14510                     
14511                     var options = {params : this.getParams(q)};
14512                     
14513                     if(this.loadNext){
14514                         options.add = true;
14515                         options.params.start = this.page * this.pageSize;
14516                     }
14517                     
14518                     this.store.load(options);
14519                     
14520                     /*
14521                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14522                      *  we should expand the list on onLoad
14523                      *  so command out it
14524                      */
14525 //                    this.expand();
14526                 }
14527             }else{
14528                 this.selectedIndex = -1;
14529                 this.onLoad();   
14530             }
14531         }
14532         
14533         this.loadNext = false;
14534     },
14535     
14536     // private
14537     getParams : function(q){
14538         var p = {};
14539         //p[this.queryParam] = q;
14540         
14541         if(this.pageSize){
14542             p.start = 0;
14543             p.limit = this.pageSize;
14544         }
14545         return p;
14546     },
14547
14548     /**
14549      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14550      */
14551     collapse : function(){
14552         if(!this.isExpanded()){
14553             return;
14554         }
14555         
14556         this.list.hide();
14557         
14558         this.hasFocus = false;
14559         
14560         if(this.tickable){
14561             this.okBtn.hide();
14562             this.cancelBtn.hide();
14563             this.trigger.show();
14564             
14565             if(this.editable){
14566                 this.tickableInputEl().dom.value = '';
14567                 this.tickableInputEl().blur();
14568             }
14569             
14570         }
14571         
14572         Roo.get(document).un('mousedown', this.collapseIf, this);
14573         Roo.get(document).un('mousewheel', this.collapseIf, this);
14574         if (!this.editable) {
14575             Roo.get(document).un('keydown', this.listKeyPress, this);
14576         }
14577         this.fireEvent('collapse', this);
14578         
14579         this.validate();
14580     },
14581
14582     // private
14583     collapseIf : function(e){
14584         var in_combo  = e.within(this.el);
14585         var in_list =  e.within(this.list);
14586         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14587         
14588         if (in_combo || in_list || is_list) {
14589             //e.stopPropagation();
14590             return;
14591         }
14592         
14593         if(this.tickable){
14594             this.onTickableFooterButtonClick(e, false, false);
14595         }
14596
14597         this.collapse();
14598         
14599     },
14600
14601     /**
14602      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14603      */
14604     expand : function(){
14605        
14606         if(this.isExpanded() || !this.hasFocus){
14607             return;
14608         }
14609         
14610         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14611         this.list.setWidth(lw);
14612         
14613         Roo.log('expand');
14614         
14615         this.list.show();
14616         
14617         this.restrictHeight();
14618         
14619         if(this.tickable){
14620             
14621             this.tickItems = Roo.apply([], this.item);
14622             
14623             this.okBtn.show();
14624             this.cancelBtn.show();
14625             this.trigger.hide();
14626             
14627             if(this.editable){
14628                 this.tickableInputEl().focus();
14629             }
14630             
14631         }
14632         
14633         Roo.get(document).on('mousedown', this.collapseIf, this);
14634         Roo.get(document).on('mousewheel', this.collapseIf, this);
14635         if (!this.editable) {
14636             Roo.get(document).on('keydown', this.listKeyPress, this);
14637         }
14638         
14639         this.fireEvent('expand', this);
14640     },
14641
14642     // private
14643     // Implements the default empty TriggerField.onTriggerClick function
14644     onTriggerClick : function(e)
14645     {
14646         Roo.log('trigger click');
14647         
14648         if(this.disabled || !this.triggerList){
14649             return;
14650         }
14651         
14652         this.page = 0;
14653         this.loadNext = false;
14654         
14655         if(this.isExpanded()){
14656             this.collapse();
14657             if (!this.blockFocus) {
14658                 this.inputEl().focus();
14659             }
14660             
14661         }else {
14662             this.hasFocus = true;
14663             if(this.triggerAction == 'all') {
14664                 this.doQuery(this.allQuery, true);
14665             } else {
14666                 this.doQuery(this.getRawValue());
14667             }
14668             if (!this.blockFocus) {
14669                 this.inputEl().focus();
14670             }
14671         }
14672     },
14673     
14674     onTickableTriggerClick : function(e)
14675     {
14676         if(this.disabled){
14677             return;
14678         }
14679         
14680         this.page = 0;
14681         this.loadNext = false;
14682         this.hasFocus = true;
14683         
14684         if(this.triggerAction == 'all') {
14685             this.doQuery(this.allQuery, true);
14686         } else {
14687             this.doQuery(this.getRawValue());
14688         }
14689     },
14690     
14691     onSearchFieldClick : function(e)
14692     {
14693         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14694             this.onTickableFooterButtonClick(e, false, false);
14695             return;
14696         }
14697         
14698         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14699             return;
14700         }
14701         
14702         this.page = 0;
14703         this.loadNext = false;
14704         this.hasFocus = true;
14705         
14706         if(this.triggerAction == 'all') {
14707             this.doQuery(this.allQuery, true);
14708         } else {
14709             this.doQuery(this.getRawValue());
14710         }
14711     },
14712     
14713     listKeyPress : function(e)
14714     {
14715         //Roo.log('listkeypress');
14716         // scroll to first matching element based on key pres..
14717         if (e.isSpecialKey()) {
14718             return false;
14719         }
14720         var k = String.fromCharCode(e.getKey()).toUpperCase();
14721         //Roo.log(k);
14722         var match  = false;
14723         var csel = this.view.getSelectedNodes();
14724         var cselitem = false;
14725         if (csel.length) {
14726             var ix = this.view.indexOf(csel[0]);
14727             cselitem  = this.store.getAt(ix);
14728             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14729                 cselitem = false;
14730             }
14731             
14732         }
14733         
14734         this.store.each(function(v) { 
14735             if (cselitem) {
14736                 // start at existing selection.
14737                 if (cselitem.id == v.id) {
14738                     cselitem = false;
14739                 }
14740                 return true;
14741             }
14742                 
14743             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14744                 match = this.store.indexOf(v);
14745                 return false;
14746             }
14747             return true;
14748         }, this);
14749         
14750         if (match === false) {
14751             return true; // no more action?
14752         }
14753         // scroll to?
14754         this.view.select(match);
14755         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14756         sn.scrollIntoView(sn.dom.parentNode, false);
14757     },
14758     
14759     onViewScroll : function(e, t){
14760         
14761         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){
14762             return;
14763         }
14764         
14765         this.hasQuery = true;
14766         
14767         this.loading = this.list.select('.loading', true).first();
14768         
14769         if(this.loading === null){
14770             this.list.createChild({
14771                 tag: 'div',
14772                 cls: 'loading roo-select2-more-results roo-select2-active',
14773                 html: 'Loading more results...'
14774             });
14775             
14776             this.loading = this.list.select('.loading', true).first();
14777             
14778             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14779             
14780             this.loading.hide();
14781         }
14782         
14783         this.loading.show();
14784         
14785         var _combo = this;
14786         
14787         this.page++;
14788         this.loadNext = true;
14789         
14790         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14791         
14792         return;
14793     },
14794     
14795     addItem : function(o)
14796     {   
14797         var dv = ''; // display value
14798         
14799         if (this.displayField) {
14800             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14801         } else {
14802             // this is an error condition!!!
14803             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14804         }
14805         
14806         if(!dv.length){
14807             return;
14808         }
14809         
14810         var choice = this.choices.createChild({
14811             tag: 'li',
14812             cls: 'roo-select2-search-choice',
14813             cn: [
14814                 {
14815                     tag: 'div',
14816                     html: dv
14817                 },
14818                 {
14819                     tag: 'a',
14820                     href: '#',
14821                     cls: 'roo-select2-search-choice-close fa fa-times',
14822                     tabindex: '-1'
14823                 }
14824             ]
14825             
14826         }, this.searchField);
14827         
14828         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14829         
14830         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14831         
14832         this.item.push(o);
14833         
14834         this.lastData = o;
14835         
14836         this.syncValue();
14837         
14838         this.inputEl().dom.value = '';
14839         
14840         this.validate();
14841     },
14842     
14843     onRemoveItem : function(e, _self, o)
14844     {
14845         e.preventDefault();
14846         
14847         this.lastItem = Roo.apply([], this.item);
14848         
14849         var index = this.item.indexOf(o.data) * 1;
14850         
14851         if( index < 0){
14852             Roo.log('not this item?!');
14853             return;
14854         }
14855         
14856         this.item.splice(index, 1);
14857         o.item.remove();
14858         
14859         this.syncValue();
14860         
14861         this.fireEvent('remove', this, e);
14862         
14863         this.validate();
14864         
14865     },
14866     
14867     syncValue : function()
14868     {
14869         if(!this.item.length){
14870             this.clearValue();
14871             return;
14872         }
14873             
14874         var value = [];
14875         var _this = this;
14876         Roo.each(this.item, function(i){
14877             if(_this.valueField){
14878                 value.push(i[_this.valueField]);
14879                 return;
14880             }
14881
14882             value.push(i);
14883         });
14884
14885         this.value = value.join(',');
14886
14887         if(this.hiddenField){
14888             this.hiddenField.dom.value = this.value;
14889         }
14890         
14891         this.store.fireEvent("datachanged", this.store);
14892         
14893         this.validate();
14894     },
14895     
14896     clearItem : function()
14897     {
14898         if(!this.multiple){
14899             return;
14900         }
14901         
14902         this.item = [];
14903         
14904         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14905            c.remove();
14906         });
14907         
14908         this.syncValue();
14909         
14910         this.validate();
14911         
14912         if(this.tickable && !Roo.isTouch){
14913             this.view.refresh();
14914         }
14915     },
14916     
14917     inputEl: function ()
14918     {
14919         if(Roo.isIOS && this.useNativeIOS){
14920             return this.el.select('select.roo-ios-select', true).first();
14921         }
14922         
14923         if(Roo.isTouch && this.mobileTouchView){
14924             return this.el.select('input.form-control',true).first();
14925         }
14926         
14927         if(this.tickable){
14928             return this.searchField;
14929         }
14930         
14931         return this.el.select('input.form-control',true).first();
14932     },
14933     
14934     onTickableFooterButtonClick : function(e, btn, el)
14935     {
14936         e.preventDefault();
14937         
14938         this.lastItem = Roo.apply([], this.item);
14939         
14940         if(btn && btn.name == 'cancel'){
14941             this.tickItems = Roo.apply([], this.item);
14942             this.collapse();
14943             return;
14944         }
14945         
14946         this.clearItem();
14947         
14948         var _this = this;
14949         
14950         Roo.each(this.tickItems, function(o){
14951             _this.addItem(o);
14952         });
14953         
14954         this.collapse();
14955         
14956     },
14957     
14958     validate : function()
14959     {
14960         if(this.getVisibilityEl().hasClass('hidden')){
14961             return true;
14962         }
14963         
14964         var v = this.getRawValue();
14965         
14966         if(this.multiple){
14967             v = this.getValue();
14968         }
14969         
14970         if(this.disabled || this.allowBlank || v.length){
14971             this.markValid();
14972             return true;
14973         }
14974         
14975         this.markInvalid();
14976         return false;
14977     },
14978     
14979     tickableInputEl : function()
14980     {
14981         if(!this.tickable || !this.editable){
14982             return this.inputEl();
14983         }
14984         
14985         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14986     },
14987     
14988     
14989     getAutoCreateTouchView : function()
14990     {
14991         var id = Roo.id();
14992         
14993         var cfg = {
14994             cls: 'form-group' //input-group
14995         };
14996         
14997         var input =  {
14998             tag: 'input',
14999             id : id,
15000             type : this.inputType,
15001             cls : 'form-control x-combo-noedit',
15002             autocomplete: 'new-password',
15003             placeholder : this.placeholder || '',
15004             readonly : true
15005         };
15006         
15007         if (this.name) {
15008             input.name = this.name;
15009         }
15010         
15011         if (this.size) {
15012             input.cls += ' input-' + this.size;
15013         }
15014         
15015         if (this.disabled) {
15016             input.disabled = true;
15017         }
15018         
15019         var inputblock = {
15020             cls : '',
15021             cn : [
15022                 input
15023             ]
15024         };
15025         
15026         if(this.before){
15027             inputblock.cls += ' input-group';
15028             
15029             inputblock.cn.unshift({
15030                 tag :'span',
15031                 cls : 'input-group-addon',
15032                 html : this.before
15033             });
15034         }
15035         
15036         if(this.removable && !this.multiple){
15037             inputblock.cls += ' roo-removable';
15038             
15039             inputblock.cn.push({
15040                 tag: 'button',
15041                 html : 'x',
15042                 cls : 'roo-combo-removable-btn close'
15043             });
15044         }
15045
15046         if(this.hasFeedback && !this.allowBlank){
15047             
15048             inputblock.cls += ' has-feedback';
15049             
15050             inputblock.cn.push({
15051                 tag: 'span',
15052                 cls: 'glyphicon form-control-feedback'
15053             });
15054             
15055         }
15056         
15057         if (this.after) {
15058             
15059             inputblock.cls += (this.before) ? '' : ' input-group';
15060             
15061             inputblock.cn.push({
15062                 tag :'span',
15063                 cls : 'input-group-addon',
15064                 html : this.after
15065             });
15066         }
15067
15068         var box = {
15069             tag: 'div',
15070             cn: [
15071                 {
15072                     tag: 'input',
15073                     type : 'hidden',
15074                     cls: 'form-hidden-field'
15075                 },
15076                 inputblock
15077             ]
15078             
15079         };
15080         
15081         if(this.multiple){
15082             box = {
15083                 tag: 'div',
15084                 cn: [
15085                     {
15086                         tag: 'input',
15087                         type : 'hidden',
15088                         cls: 'form-hidden-field'
15089                     },
15090                     {
15091                         tag: 'ul',
15092                         cls: 'roo-select2-choices',
15093                         cn:[
15094                             {
15095                                 tag: 'li',
15096                                 cls: 'roo-select2-search-field',
15097                                 cn: [
15098
15099                                     inputblock
15100                                 ]
15101                             }
15102                         ]
15103                     }
15104                 ]
15105             }
15106         };
15107         
15108         var combobox = {
15109             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15110             cn: [
15111                 box
15112             ]
15113         };
15114         
15115         if(!this.multiple && this.showToggleBtn){
15116             
15117             var caret = {
15118                         tag: 'span',
15119                         cls: 'caret'
15120             };
15121             
15122             if (this.caret != false) {
15123                 caret = {
15124                      tag: 'i',
15125                      cls: 'fa fa-' + this.caret
15126                 };
15127                 
15128             }
15129             
15130             combobox.cn.push({
15131                 tag :'span',
15132                 cls : 'input-group-addon btn dropdown-toggle',
15133                 cn : [
15134                     caret,
15135                     {
15136                         tag: 'span',
15137                         cls: 'combobox-clear',
15138                         cn  : [
15139                             {
15140                                 tag : 'i',
15141                                 cls: 'icon-remove'
15142                             }
15143                         ]
15144                     }
15145                 ]
15146
15147             })
15148         }
15149         
15150         if(this.multiple){
15151             combobox.cls += ' roo-select2-container-multi';
15152         }
15153         
15154         var align = this.labelAlign || this.parentLabelAlign();
15155         
15156         if (align ==='left' && this.fieldLabel.length) {
15157
15158             cfg.cn = [
15159                 {
15160                    tag : 'i',
15161                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15162                    tooltip : 'This field is required'
15163                 },
15164                 {
15165                     tag: 'label',
15166                     cls : 'control-label',
15167                     html : this.fieldLabel
15168
15169                 },
15170                 {
15171                     cls : '', 
15172                     cn: [
15173                         combobox
15174                     ]
15175                 }
15176             ];
15177             
15178             var labelCfg = cfg.cn[1];
15179             var contentCfg = cfg.cn[2];
15180             
15181
15182             if(this.indicatorpos == 'right'){
15183                 cfg.cn = [
15184                     {
15185                         tag: 'label',
15186                         'for' :  id,
15187                         cls : 'control-label',
15188                         cn : [
15189                             {
15190                                 tag : 'span',
15191                                 html : this.fieldLabel
15192                             },
15193                             {
15194                                 tag : 'i',
15195                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15196                                 tooltip : 'This field is required'
15197                             }
15198                         ]
15199                     },
15200                     {
15201                         cls : "",
15202                         cn: [
15203                             combobox
15204                         ]
15205                     }
15206
15207                 ];
15208                 
15209                 labelCfg = cfg.cn[0];
15210                 contentCfg = cfg.cn[1];
15211             }
15212             
15213            
15214             
15215             if(this.labelWidth > 12){
15216                 labelCfg.style = "width: " + this.labelWidth + 'px';
15217             }
15218             
15219             if(this.labelWidth < 13 && this.labelmd == 0){
15220                 this.labelmd = this.labelWidth;
15221             }
15222             
15223             if(this.labellg > 0){
15224                 labelCfg.cls += ' col-lg-' + this.labellg;
15225                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15226             }
15227             
15228             if(this.labelmd > 0){
15229                 labelCfg.cls += ' col-md-' + this.labelmd;
15230                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15231             }
15232             
15233             if(this.labelsm > 0){
15234                 labelCfg.cls += ' col-sm-' + this.labelsm;
15235                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15236             }
15237             
15238             if(this.labelxs > 0){
15239                 labelCfg.cls += ' col-xs-' + this.labelxs;
15240                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15241             }
15242                 
15243                 
15244         } else if ( this.fieldLabel.length) {
15245             cfg.cn = [
15246                 {
15247                    tag : 'i',
15248                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15249                    tooltip : 'This field is required'
15250                 },
15251                 {
15252                     tag: 'label',
15253                     cls : 'control-label',
15254                     html : this.fieldLabel
15255
15256                 },
15257                 {
15258                     cls : '', 
15259                     cn: [
15260                         combobox
15261                     ]
15262                 }
15263             ];
15264             
15265             if(this.indicatorpos == 'right'){
15266                 cfg.cn = [
15267                     {
15268                         tag: 'label',
15269                         cls : 'control-label',
15270                         html : this.fieldLabel,
15271                         cn : [
15272                             {
15273                                tag : 'i',
15274                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15275                                tooltip : 'This field is required'
15276                             }
15277                         ]
15278                     },
15279                     {
15280                         cls : '', 
15281                         cn: [
15282                             combobox
15283                         ]
15284                     }
15285                 ];
15286             }
15287         } else {
15288             cfg.cn = combobox;    
15289         }
15290         
15291         
15292         var settings = this;
15293         
15294         ['xs','sm','md','lg'].map(function(size){
15295             if (settings[size]) {
15296                 cfg.cls += ' col-' + size + '-' + settings[size];
15297             }
15298         });
15299         
15300         return cfg;
15301     },
15302     
15303     initTouchView : function()
15304     {
15305         this.renderTouchView();
15306         
15307         this.touchViewEl.on('scroll', function(){
15308             this.el.dom.scrollTop = 0;
15309         }, this);
15310         
15311         this.originalValue = this.getValue();
15312         
15313         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15314         
15315         this.inputEl().on("click", this.showTouchView, this);
15316         if (this.triggerEl) {
15317             this.triggerEl.on("click", this.showTouchView, this);
15318         }
15319         
15320         
15321         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15322         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15323         
15324         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15325         
15326         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15327         this.store.on('load', this.onTouchViewLoad, this);
15328         this.store.on('loadexception', this.onTouchViewLoadException, this);
15329         
15330         if(this.hiddenName){
15331             
15332             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15333             
15334             this.hiddenField.dom.value =
15335                 this.hiddenValue !== undefined ? this.hiddenValue :
15336                 this.value !== undefined ? this.value : '';
15337         
15338             this.el.dom.removeAttribute('name');
15339             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15340         }
15341         
15342         if(this.multiple){
15343             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15344             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15345         }
15346         
15347         if(this.removable && !this.multiple){
15348             var close = this.closeTriggerEl();
15349             if(close){
15350                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15351                 close.on('click', this.removeBtnClick, this, close);
15352             }
15353         }
15354         /*
15355          * fix the bug in Safari iOS8
15356          */
15357         this.inputEl().on("focus", function(e){
15358             document.activeElement.blur();
15359         }, this);
15360         
15361         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15362         
15363         return;
15364         
15365         
15366     },
15367     
15368     renderTouchView : function()
15369     {
15370         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15371         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15372         
15373         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15374         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15375         
15376         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15377         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15378         this.touchViewBodyEl.setStyle('overflow', 'auto');
15379         
15380         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15381         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15382         
15383         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15384         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15385         
15386     },
15387     
15388     showTouchView : function()
15389     {
15390         if(this.disabled){
15391             return;
15392         }
15393         
15394         this.touchViewHeaderEl.hide();
15395
15396         if(this.modalTitle.length){
15397             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15398             this.touchViewHeaderEl.show();
15399         }
15400
15401         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15402         this.touchViewEl.show();
15403
15404         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15405         
15406         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15407         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15408
15409         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15410
15411         if(this.modalTitle.length){
15412             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15413         }
15414         
15415         this.touchViewBodyEl.setHeight(bodyHeight);
15416
15417         if(this.animate){
15418             var _this = this;
15419             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15420         }else{
15421             this.touchViewEl.addClass('in');
15422         }
15423         
15424         if(this._touchViewMask){
15425             Roo.get(document.body).addClass("x-body-masked");
15426             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15427             this._touchViewMask.setStyle('z-index', 10000);
15428             this._touchViewMask.addClass('show');
15429         }
15430         
15431         this.doTouchViewQuery();
15432         
15433     },
15434     
15435     hideTouchView : function()
15436     {
15437         this.touchViewEl.removeClass('in');
15438
15439         if(this.animate){
15440             var _this = this;
15441             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15442         }else{
15443             this.touchViewEl.setStyle('display', 'none');
15444         }
15445         
15446         if(this._touchViewMask){
15447             this._touchViewMask.removeClass('show');
15448             Roo.get(document.body).removeClass("x-body-masked");
15449         }
15450     },
15451     
15452     setTouchViewValue : function()
15453     {
15454         if(this.multiple){
15455             this.clearItem();
15456         
15457             var _this = this;
15458
15459             Roo.each(this.tickItems, function(o){
15460                 this.addItem(o);
15461             }, this);
15462         }
15463         
15464         this.hideTouchView();
15465     },
15466     
15467     doTouchViewQuery : function()
15468     {
15469         var qe = {
15470             query: '',
15471             forceAll: true,
15472             combo: this,
15473             cancel:false
15474         };
15475         
15476         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15477             return false;
15478         }
15479         
15480         if(!this.alwaysQuery || this.mode == 'local'){
15481             this.onTouchViewLoad();
15482             return;
15483         }
15484         
15485         this.store.load();
15486     },
15487     
15488     onTouchViewBeforeLoad : function(combo,opts)
15489     {
15490         return;
15491     },
15492
15493     // private
15494     onTouchViewLoad : function()
15495     {
15496         if(this.store.getCount() < 1){
15497             this.onTouchViewEmptyResults();
15498             return;
15499         }
15500         
15501         this.clearTouchView();
15502         
15503         var rawValue = this.getRawValue();
15504         
15505         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15506         
15507         this.tickItems = [];
15508         
15509         this.store.data.each(function(d, rowIndex){
15510             var row = this.touchViewListGroup.createChild(template);
15511             
15512             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15513                 row.addClass(d.data.cls);
15514             }
15515             
15516             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15517                 var cfg = {
15518                     data : d.data,
15519                     html : d.data[this.displayField]
15520                 };
15521                 
15522                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15523                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15524                 }
15525             }
15526             row.removeClass('selected');
15527             if(!this.multiple && this.valueField &&
15528                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15529             {
15530                 // radio buttons..
15531                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15532                 row.addClass('selected');
15533             }
15534             
15535             if(this.multiple && this.valueField &&
15536                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15537             {
15538                 
15539                 // checkboxes...
15540                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15541                 this.tickItems.push(d.data);
15542             }
15543             
15544             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15545             
15546         }, this);
15547         
15548         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15549         
15550         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15551
15552         if(this.modalTitle.length){
15553             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15554         }
15555
15556         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15557         
15558         if(this.mobile_restrict_height && listHeight < bodyHeight){
15559             this.touchViewBodyEl.setHeight(listHeight);
15560         }
15561         
15562         var _this = this;
15563         
15564         if(firstChecked && listHeight > bodyHeight){
15565             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15566         }
15567         
15568     },
15569     
15570     onTouchViewLoadException : function()
15571     {
15572         this.hideTouchView();
15573     },
15574     
15575     onTouchViewEmptyResults : function()
15576     {
15577         this.clearTouchView();
15578         
15579         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15580         
15581         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15582         
15583     },
15584     
15585     clearTouchView : function()
15586     {
15587         this.touchViewListGroup.dom.innerHTML = '';
15588     },
15589     
15590     onTouchViewClick : function(e, el, o)
15591     {
15592         e.preventDefault();
15593         
15594         var row = o.row;
15595         var rowIndex = o.rowIndex;
15596         
15597         var r = this.store.getAt(rowIndex);
15598         
15599         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15600             
15601             if(!this.multiple){
15602                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15603                     c.dom.removeAttribute('checked');
15604                 }, this);
15605
15606                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15607
15608                 this.setFromData(r.data);
15609
15610                 var close = this.closeTriggerEl();
15611
15612                 if(close){
15613                     close.show();
15614                 }
15615
15616                 this.hideTouchView();
15617
15618                 this.fireEvent('select', this, r, rowIndex);
15619
15620                 return;
15621             }
15622
15623             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15624                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15625                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15626                 return;
15627             }
15628
15629             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15630             this.addItem(r.data);
15631             this.tickItems.push(r.data);
15632         }
15633     },
15634     
15635     getAutoCreateNativeIOS : function()
15636     {
15637         var cfg = {
15638             cls: 'form-group' //input-group,
15639         };
15640         
15641         var combobox =  {
15642             tag: 'select',
15643             cls : 'roo-ios-select'
15644         };
15645         
15646         if (this.name) {
15647             combobox.name = this.name;
15648         }
15649         
15650         if (this.disabled) {
15651             combobox.disabled = true;
15652         }
15653         
15654         var settings = this;
15655         
15656         ['xs','sm','md','lg'].map(function(size){
15657             if (settings[size]) {
15658                 cfg.cls += ' col-' + size + '-' + settings[size];
15659             }
15660         });
15661         
15662         cfg.cn = combobox;
15663         
15664         return cfg;
15665         
15666     },
15667     
15668     initIOSView : function()
15669     {
15670         this.store.on('load', this.onIOSViewLoad, this);
15671         
15672         return;
15673     },
15674     
15675     onIOSViewLoad : function()
15676     {
15677         if(this.store.getCount() < 1){
15678             return;
15679         }
15680         
15681         this.clearIOSView();
15682         
15683         if(this.allowBlank) {
15684             
15685             var default_text = '-- SELECT --';
15686             
15687             if(this.placeholder.length){
15688                 default_text = this.placeholder;
15689             }
15690             
15691             if(this.emptyTitle.length){
15692                 default_text += ' - ' + this.emptyTitle + ' -';
15693             }
15694             
15695             var opt = this.inputEl().createChild({
15696                 tag: 'option',
15697                 value : 0,
15698                 html : default_text
15699             });
15700             
15701             var o = {};
15702             o[this.valueField] = 0;
15703             o[this.displayField] = default_text;
15704             
15705             this.ios_options.push({
15706                 data : o,
15707                 el : opt
15708             });
15709             
15710         }
15711         
15712         this.store.data.each(function(d, rowIndex){
15713             
15714             var html = '';
15715             
15716             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15717                 html = d.data[this.displayField];
15718             }
15719             
15720             var value = '';
15721             
15722             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15723                 value = d.data[this.valueField];
15724             }
15725             
15726             var option = {
15727                 tag: 'option',
15728                 value : value,
15729                 html : html
15730             };
15731             
15732             if(this.value == d.data[this.valueField]){
15733                 option['selected'] = true;
15734             }
15735             
15736             var opt = this.inputEl().createChild(option);
15737             
15738             this.ios_options.push({
15739                 data : d.data,
15740                 el : opt
15741             });
15742             
15743         }, this);
15744         
15745         this.inputEl().on('change', function(){
15746            this.fireEvent('select', this);
15747         }, this);
15748         
15749     },
15750     
15751     clearIOSView: function()
15752     {
15753         this.inputEl().dom.innerHTML = '';
15754         
15755         this.ios_options = [];
15756     },
15757     
15758     setIOSValue: function(v)
15759     {
15760         this.value = v;
15761         
15762         if(!this.ios_options){
15763             return;
15764         }
15765         
15766         Roo.each(this.ios_options, function(opts){
15767            
15768            opts.el.dom.removeAttribute('selected');
15769            
15770            if(opts.data[this.valueField] != v){
15771                return;
15772            }
15773            
15774            opts.el.dom.setAttribute('selected', true);
15775            
15776         }, this);
15777     }
15778
15779     /** 
15780     * @cfg {Boolean} grow 
15781     * @hide 
15782     */
15783     /** 
15784     * @cfg {Number} growMin 
15785     * @hide 
15786     */
15787     /** 
15788     * @cfg {Number} growMax 
15789     * @hide 
15790     */
15791     /**
15792      * @hide
15793      * @method autoSize
15794      */
15795 });
15796
15797 Roo.apply(Roo.bootstrap.ComboBox,  {
15798     
15799     header : {
15800         tag: 'div',
15801         cls: 'modal-header',
15802         cn: [
15803             {
15804                 tag: 'h4',
15805                 cls: 'modal-title'
15806             }
15807         ]
15808     },
15809     
15810     body : {
15811         tag: 'div',
15812         cls: 'modal-body',
15813         cn: [
15814             {
15815                 tag: 'ul',
15816                 cls: 'list-group'
15817             }
15818         ]
15819     },
15820     
15821     listItemRadio : {
15822         tag: 'li',
15823         cls: 'list-group-item',
15824         cn: [
15825             {
15826                 tag: 'span',
15827                 cls: 'roo-combobox-list-group-item-value'
15828             },
15829             {
15830                 tag: 'div',
15831                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15832                 cn: [
15833                     {
15834                         tag: 'input',
15835                         type: 'radio'
15836                     },
15837                     {
15838                         tag: 'label'
15839                     }
15840                 ]
15841             }
15842         ]
15843     },
15844     
15845     listItemCheckbox : {
15846         tag: 'li',
15847         cls: 'list-group-item',
15848         cn: [
15849             {
15850                 tag: 'span',
15851                 cls: 'roo-combobox-list-group-item-value'
15852             },
15853             {
15854                 tag: 'div',
15855                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15856                 cn: [
15857                     {
15858                         tag: 'input',
15859                         type: 'checkbox'
15860                     },
15861                     {
15862                         tag: 'label'
15863                     }
15864                 ]
15865             }
15866         ]
15867     },
15868     
15869     emptyResult : {
15870         tag: 'div',
15871         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15872     },
15873     
15874     footer : {
15875         tag: 'div',
15876         cls: 'modal-footer',
15877         cn: [
15878             {
15879                 tag: 'div',
15880                 cls: 'row',
15881                 cn: [
15882                     {
15883                         tag: 'div',
15884                         cls: 'col-xs-6 text-left',
15885                         cn: {
15886                             tag: 'button',
15887                             cls: 'btn btn-danger roo-touch-view-cancel',
15888                             html: 'Cancel'
15889                         }
15890                     },
15891                     {
15892                         tag: 'div',
15893                         cls: 'col-xs-6 text-right',
15894                         cn: {
15895                             tag: 'button',
15896                             cls: 'btn btn-success roo-touch-view-ok',
15897                             html: 'OK'
15898                         }
15899                     }
15900                 ]
15901             }
15902         ]
15903         
15904     }
15905 });
15906
15907 Roo.apply(Roo.bootstrap.ComboBox,  {
15908     
15909     touchViewTemplate : {
15910         tag: 'div',
15911         cls: 'modal fade roo-combobox-touch-view',
15912         cn: [
15913             {
15914                 tag: 'div',
15915                 cls: 'modal-dialog',
15916                 style : 'position:fixed', // we have to fix position....
15917                 cn: [
15918                     {
15919                         tag: 'div',
15920                         cls: 'modal-content',
15921                         cn: [
15922                             Roo.bootstrap.ComboBox.header,
15923                             Roo.bootstrap.ComboBox.body,
15924                             Roo.bootstrap.ComboBox.footer
15925                         ]
15926                     }
15927                 ]
15928             }
15929         ]
15930     }
15931 });/*
15932  * Based on:
15933  * Ext JS Library 1.1.1
15934  * Copyright(c) 2006-2007, Ext JS, LLC.
15935  *
15936  * Originally Released Under LGPL - original licence link has changed is not relivant.
15937  *
15938  * Fork - LGPL
15939  * <script type="text/javascript">
15940  */
15941
15942 /**
15943  * @class Roo.View
15944  * @extends Roo.util.Observable
15945  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15946  * This class also supports single and multi selection modes. <br>
15947  * Create a data model bound view:
15948  <pre><code>
15949  var store = new Roo.data.Store(...);
15950
15951  var view = new Roo.View({
15952     el : "my-element",
15953     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15954  
15955     singleSelect: true,
15956     selectedClass: "ydataview-selected",
15957     store: store
15958  });
15959
15960  // listen for node click?
15961  view.on("click", function(vw, index, node, e){
15962  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15963  });
15964
15965  // load XML data
15966  dataModel.load("foobar.xml");
15967  </code></pre>
15968  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15969  * <br><br>
15970  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15971  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15972  * 
15973  * Note: old style constructor is still suported (container, template, config)
15974  * 
15975  * @constructor
15976  * Create a new View
15977  * @param {Object} config The config object
15978  * 
15979  */
15980 Roo.View = function(config, depreciated_tpl, depreciated_config){
15981     
15982     this.parent = false;
15983     
15984     if (typeof(depreciated_tpl) == 'undefined') {
15985         // new way.. - universal constructor.
15986         Roo.apply(this, config);
15987         this.el  = Roo.get(this.el);
15988     } else {
15989         // old format..
15990         this.el  = Roo.get(config);
15991         this.tpl = depreciated_tpl;
15992         Roo.apply(this, depreciated_config);
15993     }
15994     this.wrapEl  = this.el.wrap().wrap();
15995     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15996     
15997     
15998     if(typeof(this.tpl) == "string"){
15999         this.tpl = new Roo.Template(this.tpl);
16000     } else {
16001         // support xtype ctors..
16002         this.tpl = new Roo.factory(this.tpl, Roo);
16003     }
16004     
16005     
16006     this.tpl.compile();
16007     
16008     /** @private */
16009     this.addEvents({
16010         /**
16011          * @event beforeclick
16012          * Fires before a click is processed. Returns false to cancel the default action.
16013          * @param {Roo.View} this
16014          * @param {Number} index The index of the target node
16015          * @param {HTMLElement} node The target node
16016          * @param {Roo.EventObject} e The raw event object
16017          */
16018             "beforeclick" : true,
16019         /**
16020          * @event click
16021          * Fires when a template node is clicked.
16022          * @param {Roo.View} this
16023          * @param {Number} index The index of the target node
16024          * @param {HTMLElement} node The target node
16025          * @param {Roo.EventObject} e The raw event object
16026          */
16027             "click" : true,
16028         /**
16029          * @event dblclick
16030          * Fires when a template node is double clicked.
16031          * @param {Roo.View} this
16032          * @param {Number} index The index of the target node
16033          * @param {HTMLElement} node The target node
16034          * @param {Roo.EventObject} e The raw event object
16035          */
16036             "dblclick" : true,
16037         /**
16038          * @event contextmenu
16039          * Fires when a template node is right clicked.
16040          * @param {Roo.View} this
16041          * @param {Number} index The index of the target node
16042          * @param {HTMLElement} node The target node
16043          * @param {Roo.EventObject} e The raw event object
16044          */
16045             "contextmenu" : true,
16046         /**
16047          * @event selectionchange
16048          * Fires when the selected nodes change.
16049          * @param {Roo.View} this
16050          * @param {Array} selections Array of the selected nodes
16051          */
16052             "selectionchange" : true,
16053     
16054         /**
16055          * @event beforeselect
16056          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16057          * @param {Roo.View} this
16058          * @param {HTMLElement} node The node to be selected
16059          * @param {Array} selections Array of currently selected nodes
16060          */
16061             "beforeselect" : true,
16062         /**
16063          * @event preparedata
16064          * Fires on every row to render, to allow you to change the data.
16065          * @param {Roo.View} this
16066          * @param {Object} data to be rendered (change this)
16067          */
16068           "preparedata" : true
16069           
16070           
16071         });
16072
16073
16074
16075     this.el.on({
16076         "click": this.onClick,
16077         "dblclick": this.onDblClick,
16078         "contextmenu": this.onContextMenu,
16079         scope:this
16080     });
16081
16082     this.selections = [];
16083     this.nodes = [];
16084     this.cmp = new Roo.CompositeElementLite([]);
16085     if(this.store){
16086         this.store = Roo.factory(this.store, Roo.data);
16087         this.setStore(this.store, true);
16088     }
16089     
16090     if ( this.footer && this.footer.xtype) {
16091            
16092          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16093         
16094         this.footer.dataSource = this.store;
16095         this.footer.container = fctr;
16096         this.footer = Roo.factory(this.footer, Roo);
16097         fctr.insertFirst(this.el);
16098         
16099         // this is a bit insane - as the paging toolbar seems to detach the el..
16100 //        dom.parentNode.parentNode.parentNode
16101          // they get detached?
16102     }
16103     
16104     
16105     Roo.View.superclass.constructor.call(this);
16106     
16107     
16108 };
16109
16110 Roo.extend(Roo.View, Roo.util.Observable, {
16111     
16112      /**
16113      * @cfg {Roo.data.Store} store Data store to load data from.
16114      */
16115     store : false,
16116     
16117     /**
16118      * @cfg {String|Roo.Element} el The container element.
16119      */
16120     el : '',
16121     
16122     /**
16123      * @cfg {String|Roo.Template} tpl The template used by this View 
16124      */
16125     tpl : false,
16126     /**
16127      * @cfg {String} dataName the named area of the template to use as the data area
16128      *                          Works with domtemplates roo-name="name"
16129      */
16130     dataName: false,
16131     /**
16132      * @cfg {String} selectedClass The css class to add to selected nodes
16133      */
16134     selectedClass : "x-view-selected",
16135      /**
16136      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16137      */
16138     emptyText : "",
16139     
16140     /**
16141      * @cfg {String} text to display on mask (default Loading)
16142      */
16143     mask : false,
16144     /**
16145      * @cfg {Boolean} multiSelect Allow multiple selection
16146      */
16147     multiSelect : false,
16148     /**
16149      * @cfg {Boolean} singleSelect Allow single selection
16150      */
16151     singleSelect:  false,
16152     
16153     /**
16154      * @cfg {Boolean} toggleSelect - selecting 
16155      */
16156     toggleSelect : false,
16157     
16158     /**
16159      * @cfg {Boolean} tickable - selecting 
16160      */
16161     tickable : false,
16162     
16163     /**
16164      * Returns the element this view is bound to.
16165      * @return {Roo.Element}
16166      */
16167     getEl : function(){
16168         return this.wrapEl;
16169     },
16170     
16171     
16172
16173     /**
16174      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16175      */
16176     refresh : function(){
16177         //Roo.log('refresh');
16178         var t = this.tpl;
16179         
16180         // if we are using something like 'domtemplate', then
16181         // the what gets used is:
16182         // t.applySubtemplate(NAME, data, wrapping data..)
16183         // the outer template then get' applied with
16184         //     the store 'extra data'
16185         // and the body get's added to the
16186         //      roo-name="data" node?
16187         //      <span class='roo-tpl-{name}'></span> ?????
16188         
16189         
16190         
16191         this.clearSelections();
16192         this.el.update("");
16193         var html = [];
16194         var records = this.store.getRange();
16195         if(records.length < 1) {
16196             
16197             // is this valid??  = should it render a template??
16198             
16199             this.el.update(this.emptyText);
16200             return;
16201         }
16202         var el = this.el;
16203         if (this.dataName) {
16204             this.el.update(t.apply(this.store.meta)); //????
16205             el = this.el.child('.roo-tpl-' + this.dataName);
16206         }
16207         
16208         for(var i = 0, len = records.length; i < len; i++){
16209             var data = this.prepareData(records[i].data, i, records[i]);
16210             this.fireEvent("preparedata", this, data, i, records[i]);
16211             
16212             var d = Roo.apply({}, data);
16213             
16214             if(this.tickable){
16215                 Roo.apply(d, {'roo-id' : Roo.id()});
16216                 
16217                 var _this = this;
16218             
16219                 Roo.each(this.parent.item, function(item){
16220                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16221                         return;
16222                     }
16223                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16224                 });
16225             }
16226             
16227             html[html.length] = Roo.util.Format.trim(
16228                 this.dataName ?
16229                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16230                     t.apply(d)
16231             );
16232         }
16233         
16234         
16235         
16236         el.update(html.join(""));
16237         this.nodes = el.dom.childNodes;
16238         this.updateIndexes(0);
16239     },
16240     
16241
16242     /**
16243      * Function to override to reformat the data that is sent to
16244      * the template for each node.
16245      * DEPRICATED - use the preparedata event handler.
16246      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16247      * a JSON object for an UpdateManager bound view).
16248      */
16249     prepareData : function(data, index, record)
16250     {
16251         this.fireEvent("preparedata", this, data, index, record);
16252         return data;
16253     },
16254
16255     onUpdate : function(ds, record){
16256         // Roo.log('on update');   
16257         this.clearSelections();
16258         var index = this.store.indexOf(record);
16259         var n = this.nodes[index];
16260         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16261         n.parentNode.removeChild(n);
16262         this.updateIndexes(index, index);
16263     },
16264
16265     
16266     
16267 // --------- FIXME     
16268     onAdd : function(ds, records, index)
16269     {
16270         //Roo.log(['on Add', ds, records, index] );        
16271         this.clearSelections();
16272         if(this.nodes.length == 0){
16273             this.refresh();
16274             return;
16275         }
16276         var n = this.nodes[index];
16277         for(var i = 0, len = records.length; i < len; i++){
16278             var d = this.prepareData(records[i].data, i, records[i]);
16279             if(n){
16280                 this.tpl.insertBefore(n, d);
16281             }else{
16282                 
16283                 this.tpl.append(this.el, d);
16284             }
16285         }
16286         this.updateIndexes(index);
16287     },
16288
16289     onRemove : function(ds, record, index){
16290        // Roo.log('onRemove');
16291         this.clearSelections();
16292         var el = this.dataName  ?
16293             this.el.child('.roo-tpl-' + this.dataName) :
16294             this.el; 
16295         
16296         el.dom.removeChild(this.nodes[index]);
16297         this.updateIndexes(index);
16298     },
16299
16300     /**
16301      * Refresh an individual node.
16302      * @param {Number} index
16303      */
16304     refreshNode : function(index){
16305         this.onUpdate(this.store, this.store.getAt(index));
16306     },
16307
16308     updateIndexes : function(startIndex, endIndex){
16309         var ns = this.nodes;
16310         startIndex = startIndex || 0;
16311         endIndex = endIndex || ns.length - 1;
16312         for(var i = startIndex; i <= endIndex; i++){
16313             ns[i].nodeIndex = i;
16314         }
16315     },
16316
16317     /**
16318      * Changes the data store this view uses and refresh the view.
16319      * @param {Store} store
16320      */
16321     setStore : function(store, initial){
16322         if(!initial && this.store){
16323             this.store.un("datachanged", this.refresh);
16324             this.store.un("add", this.onAdd);
16325             this.store.un("remove", this.onRemove);
16326             this.store.un("update", this.onUpdate);
16327             this.store.un("clear", this.refresh);
16328             this.store.un("beforeload", this.onBeforeLoad);
16329             this.store.un("load", this.onLoad);
16330             this.store.un("loadexception", this.onLoad);
16331         }
16332         if(store){
16333           
16334             store.on("datachanged", this.refresh, this);
16335             store.on("add", this.onAdd, this);
16336             store.on("remove", this.onRemove, this);
16337             store.on("update", this.onUpdate, this);
16338             store.on("clear", this.refresh, this);
16339             store.on("beforeload", this.onBeforeLoad, this);
16340             store.on("load", this.onLoad, this);
16341             store.on("loadexception", this.onLoad, this);
16342         }
16343         
16344         if(store){
16345             this.refresh();
16346         }
16347     },
16348     /**
16349      * onbeforeLoad - masks the loading area.
16350      *
16351      */
16352     onBeforeLoad : function(store,opts)
16353     {
16354          //Roo.log('onBeforeLoad');   
16355         if (!opts.add) {
16356             this.el.update("");
16357         }
16358         this.el.mask(this.mask ? this.mask : "Loading" ); 
16359     },
16360     onLoad : function ()
16361     {
16362         this.el.unmask();
16363     },
16364     
16365
16366     /**
16367      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16368      * @param {HTMLElement} node
16369      * @return {HTMLElement} The template node
16370      */
16371     findItemFromChild : function(node){
16372         var el = this.dataName  ?
16373             this.el.child('.roo-tpl-' + this.dataName,true) :
16374             this.el.dom; 
16375         
16376         if(!node || node.parentNode == el){
16377                     return node;
16378             }
16379             var p = node.parentNode;
16380             while(p && p != el){
16381             if(p.parentNode == el){
16382                 return p;
16383             }
16384             p = p.parentNode;
16385         }
16386             return null;
16387     },
16388
16389     /** @ignore */
16390     onClick : function(e){
16391         var item = this.findItemFromChild(e.getTarget());
16392         if(item){
16393             var index = this.indexOf(item);
16394             if(this.onItemClick(item, index, e) !== false){
16395                 this.fireEvent("click", this, index, item, e);
16396             }
16397         }else{
16398             this.clearSelections();
16399         }
16400     },
16401
16402     /** @ignore */
16403     onContextMenu : function(e){
16404         var item = this.findItemFromChild(e.getTarget());
16405         if(item){
16406             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16407         }
16408     },
16409
16410     /** @ignore */
16411     onDblClick : function(e){
16412         var item = this.findItemFromChild(e.getTarget());
16413         if(item){
16414             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16415         }
16416     },
16417
16418     onItemClick : function(item, index, e)
16419     {
16420         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16421             return false;
16422         }
16423         if (this.toggleSelect) {
16424             var m = this.isSelected(item) ? 'unselect' : 'select';
16425             //Roo.log(m);
16426             var _t = this;
16427             _t[m](item, true, false);
16428             return true;
16429         }
16430         if(this.multiSelect || this.singleSelect){
16431             if(this.multiSelect && e.shiftKey && this.lastSelection){
16432                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16433             }else{
16434                 this.select(item, this.multiSelect && e.ctrlKey);
16435                 this.lastSelection = item;
16436             }
16437             
16438             if(!this.tickable){
16439                 e.preventDefault();
16440             }
16441             
16442         }
16443         return true;
16444     },
16445
16446     /**
16447      * Get the number of selected nodes.
16448      * @return {Number}
16449      */
16450     getSelectionCount : function(){
16451         return this.selections.length;
16452     },
16453
16454     /**
16455      * Get the currently selected nodes.
16456      * @return {Array} An array of HTMLElements
16457      */
16458     getSelectedNodes : function(){
16459         return this.selections;
16460     },
16461
16462     /**
16463      * Get the indexes of the selected nodes.
16464      * @return {Array}
16465      */
16466     getSelectedIndexes : function(){
16467         var indexes = [], s = this.selections;
16468         for(var i = 0, len = s.length; i < len; i++){
16469             indexes.push(s[i].nodeIndex);
16470         }
16471         return indexes;
16472     },
16473
16474     /**
16475      * Clear all selections
16476      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16477      */
16478     clearSelections : function(suppressEvent){
16479         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16480             this.cmp.elements = this.selections;
16481             this.cmp.removeClass(this.selectedClass);
16482             this.selections = [];
16483             if(!suppressEvent){
16484                 this.fireEvent("selectionchange", this, this.selections);
16485             }
16486         }
16487     },
16488
16489     /**
16490      * Returns true if the passed node is selected
16491      * @param {HTMLElement/Number} node The node or node index
16492      * @return {Boolean}
16493      */
16494     isSelected : function(node){
16495         var s = this.selections;
16496         if(s.length < 1){
16497             return false;
16498         }
16499         node = this.getNode(node);
16500         return s.indexOf(node) !== -1;
16501     },
16502
16503     /**
16504      * Selects nodes.
16505      * @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
16506      * @param {Boolean} keepExisting (optional) true to keep existing selections
16507      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16508      */
16509     select : function(nodeInfo, keepExisting, suppressEvent){
16510         if(nodeInfo instanceof Array){
16511             if(!keepExisting){
16512                 this.clearSelections(true);
16513             }
16514             for(var i = 0, len = nodeInfo.length; i < len; i++){
16515                 this.select(nodeInfo[i], true, true);
16516             }
16517             return;
16518         } 
16519         var node = this.getNode(nodeInfo);
16520         if(!node || this.isSelected(node)){
16521             return; // already selected.
16522         }
16523         if(!keepExisting){
16524             this.clearSelections(true);
16525         }
16526         
16527         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16528             Roo.fly(node).addClass(this.selectedClass);
16529             this.selections.push(node);
16530             if(!suppressEvent){
16531                 this.fireEvent("selectionchange", this, this.selections);
16532             }
16533         }
16534         
16535         
16536     },
16537       /**
16538      * Unselects nodes.
16539      * @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
16540      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16541      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16542      */
16543     unselect : function(nodeInfo, keepExisting, suppressEvent)
16544     {
16545         if(nodeInfo instanceof Array){
16546             Roo.each(this.selections, function(s) {
16547                 this.unselect(s, nodeInfo);
16548             }, this);
16549             return;
16550         }
16551         var node = this.getNode(nodeInfo);
16552         if(!node || !this.isSelected(node)){
16553             //Roo.log("not selected");
16554             return; // not selected.
16555         }
16556         // fireevent???
16557         var ns = [];
16558         Roo.each(this.selections, function(s) {
16559             if (s == node ) {
16560                 Roo.fly(node).removeClass(this.selectedClass);
16561
16562                 return;
16563             }
16564             ns.push(s);
16565         },this);
16566         
16567         this.selections= ns;
16568         this.fireEvent("selectionchange", this, this.selections);
16569     },
16570
16571     /**
16572      * Gets a template node.
16573      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16574      * @return {HTMLElement} The node or null if it wasn't found
16575      */
16576     getNode : function(nodeInfo){
16577         if(typeof nodeInfo == "string"){
16578             return document.getElementById(nodeInfo);
16579         }else if(typeof nodeInfo == "number"){
16580             return this.nodes[nodeInfo];
16581         }
16582         return nodeInfo;
16583     },
16584
16585     /**
16586      * Gets a range template nodes.
16587      * @param {Number} startIndex
16588      * @param {Number} endIndex
16589      * @return {Array} An array of nodes
16590      */
16591     getNodes : function(start, end){
16592         var ns = this.nodes;
16593         start = start || 0;
16594         end = typeof end == "undefined" ? ns.length - 1 : end;
16595         var nodes = [];
16596         if(start <= end){
16597             for(var i = start; i <= end; i++){
16598                 nodes.push(ns[i]);
16599             }
16600         } else{
16601             for(var i = start; i >= end; i--){
16602                 nodes.push(ns[i]);
16603             }
16604         }
16605         return nodes;
16606     },
16607
16608     /**
16609      * Finds the index of the passed node
16610      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16611      * @return {Number} The index of the node or -1
16612      */
16613     indexOf : function(node){
16614         node = this.getNode(node);
16615         if(typeof node.nodeIndex == "number"){
16616             return node.nodeIndex;
16617         }
16618         var ns = this.nodes;
16619         for(var i = 0, len = ns.length; i < len; i++){
16620             if(ns[i] == node){
16621                 return i;
16622             }
16623         }
16624         return -1;
16625     }
16626 });
16627 /*
16628  * - LGPL
16629  *
16630  * based on jquery fullcalendar
16631  * 
16632  */
16633
16634 Roo.bootstrap = Roo.bootstrap || {};
16635 /**
16636  * @class Roo.bootstrap.Calendar
16637  * @extends Roo.bootstrap.Component
16638  * Bootstrap Calendar class
16639  * @cfg {Boolean} loadMask (true|false) default false
16640  * @cfg {Object} header generate the user specific header of the calendar, default false
16641
16642  * @constructor
16643  * Create a new Container
16644  * @param {Object} config The config object
16645  */
16646
16647
16648
16649 Roo.bootstrap.Calendar = function(config){
16650     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16651      this.addEvents({
16652         /**
16653              * @event select
16654              * Fires when a date is selected
16655              * @param {DatePicker} this
16656              * @param {Date} date The selected date
16657              */
16658         'select': true,
16659         /**
16660              * @event monthchange
16661              * Fires when the displayed month changes 
16662              * @param {DatePicker} this
16663              * @param {Date} date The selected month
16664              */
16665         'monthchange': true,
16666         /**
16667              * @event evententer
16668              * Fires when mouse over an event
16669              * @param {Calendar} this
16670              * @param {event} Event
16671              */
16672         'evententer': true,
16673         /**
16674              * @event eventleave
16675              * Fires when the mouse leaves an
16676              * @param {Calendar} this
16677              * @param {event}
16678              */
16679         'eventleave': true,
16680         /**
16681              * @event eventclick
16682              * Fires when the mouse click an
16683              * @param {Calendar} this
16684              * @param {event}
16685              */
16686         'eventclick': true
16687         
16688     });
16689
16690 };
16691
16692 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16693     
16694      /**
16695      * @cfg {Number} startDay
16696      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16697      */
16698     startDay : 0,
16699     
16700     loadMask : false,
16701     
16702     header : false,
16703       
16704     getAutoCreate : function(){
16705         
16706         
16707         var fc_button = function(name, corner, style, content ) {
16708             return Roo.apply({},{
16709                 tag : 'span',
16710                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16711                          (corner.length ?
16712                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16713                             ''
16714                         ),
16715                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16716                 unselectable: 'on'
16717             });
16718         };
16719         
16720         var header = {};
16721         
16722         if(!this.header){
16723             header = {
16724                 tag : 'table',
16725                 cls : 'fc-header',
16726                 style : 'width:100%',
16727                 cn : [
16728                     {
16729                         tag: 'tr',
16730                         cn : [
16731                             {
16732                                 tag : 'td',
16733                                 cls : 'fc-header-left',
16734                                 cn : [
16735                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16736                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16737                                     { tag: 'span', cls: 'fc-header-space' },
16738                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16739
16740
16741                                 ]
16742                             },
16743
16744                             {
16745                                 tag : 'td',
16746                                 cls : 'fc-header-center',
16747                                 cn : [
16748                                     {
16749                                         tag: 'span',
16750                                         cls: 'fc-header-title',
16751                                         cn : {
16752                                             tag: 'H2',
16753                                             html : 'month / year'
16754                                         }
16755                                     }
16756
16757                                 ]
16758                             },
16759                             {
16760                                 tag : 'td',
16761                                 cls : 'fc-header-right',
16762                                 cn : [
16763                               /*      fc_button('month', 'left', '', 'month' ),
16764                                     fc_button('week', '', '', 'week' ),
16765                                     fc_button('day', 'right', '', 'day' )
16766                                 */    
16767
16768                                 ]
16769                             }
16770
16771                         ]
16772                     }
16773                 ]
16774             };
16775         }
16776         
16777         header = this.header;
16778         
16779        
16780         var cal_heads = function() {
16781             var ret = [];
16782             // fixme - handle this.
16783             
16784             for (var i =0; i < Date.dayNames.length; i++) {
16785                 var d = Date.dayNames[i];
16786                 ret.push({
16787                     tag: 'th',
16788                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16789                     html : d.substring(0,3)
16790                 });
16791                 
16792             }
16793             ret[0].cls += ' fc-first';
16794             ret[6].cls += ' fc-last';
16795             return ret;
16796         };
16797         var cal_cell = function(n) {
16798             return  {
16799                 tag: 'td',
16800                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16801                 cn : [
16802                     {
16803                         cn : [
16804                             {
16805                                 cls: 'fc-day-number',
16806                                 html: 'D'
16807                             },
16808                             {
16809                                 cls: 'fc-day-content',
16810                              
16811                                 cn : [
16812                                      {
16813                                         style: 'position: relative;' // height: 17px;
16814                                     }
16815                                 ]
16816                             }
16817                             
16818                             
16819                         ]
16820                     }
16821                 ]
16822                 
16823             }
16824         };
16825         var cal_rows = function() {
16826             
16827             var ret = [];
16828             for (var r = 0; r < 6; r++) {
16829                 var row= {
16830                     tag : 'tr',
16831                     cls : 'fc-week',
16832                     cn : []
16833                 };
16834                 
16835                 for (var i =0; i < Date.dayNames.length; i++) {
16836                     var d = Date.dayNames[i];
16837                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16838
16839                 }
16840                 row.cn[0].cls+=' fc-first';
16841                 row.cn[0].cn[0].style = 'min-height:90px';
16842                 row.cn[6].cls+=' fc-last';
16843                 ret.push(row);
16844                 
16845             }
16846             ret[0].cls += ' fc-first';
16847             ret[4].cls += ' fc-prev-last';
16848             ret[5].cls += ' fc-last';
16849             return ret;
16850             
16851         };
16852         
16853         var cal_table = {
16854             tag: 'table',
16855             cls: 'fc-border-separate',
16856             style : 'width:100%',
16857             cellspacing  : 0,
16858             cn : [
16859                 { 
16860                     tag: 'thead',
16861                     cn : [
16862                         { 
16863                             tag: 'tr',
16864                             cls : 'fc-first fc-last',
16865                             cn : cal_heads()
16866                         }
16867                     ]
16868                 },
16869                 { 
16870                     tag: 'tbody',
16871                     cn : cal_rows()
16872                 }
16873                   
16874             ]
16875         };
16876          
16877          var cfg = {
16878             cls : 'fc fc-ltr',
16879             cn : [
16880                 header,
16881                 {
16882                     cls : 'fc-content',
16883                     style : "position: relative;",
16884                     cn : [
16885                         {
16886                             cls : 'fc-view fc-view-month fc-grid',
16887                             style : 'position: relative',
16888                             unselectable : 'on',
16889                             cn : [
16890                                 {
16891                                     cls : 'fc-event-container',
16892                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16893                                 },
16894                                 cal_table
16895                             ]
16896                         }
16897                     ]
16898     
16899                 }
16900            ] 
16901             
16902         };
16903         
16904          
16905         
16906         return cfg;
16907     },
16908     
16909     
16910     initEvents : function()
16911     {
16912         if(!this.store){
16913             throw "can not find store for calendar";
16914         }
16915         
16916         var mark = {
16917             tag: "div",
16918             cls:"x-dlg-mask",
16919             style: "text-align:center",
16920             cn: [
16921                 {
16922                     tag: "div",
16923                     style: "background-color:white;width:50%;margin:250 auto",
16924                     cn: [
16925                         {
16926                             tag: "img",
16927                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16928                         },
16929                         {
16930                             tag: "span",
16931                             html: "Loading"
16932                         }
16933                         
16934                     ]
16935                 }
16936             ]
16937         };
16938         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16939         
16940         var size = this.el.select('.fc-content', true).first().getSize();
16941         this.maskEl.setSize(size.width, size.height);
16942         this.maskEl.enableDisplayMode("block");
16943         if(!this.loadMask){
16944             this.maskEl.hide();
16945         }
16946         
16947         this.store = Roo.factory(this.store, Roo.data);
16948         this.store.on('load', this.onLoad, this);
16949         this.store.on('beforeload', this.onBeforeLoad, this);
16950         
16951         this.resize();
16952         
16953         this.cells = this.el.select('.fc-day',true);
16954         //Roo.log(this.cells);
16955         this.textNodes = this.el.query('.fc-day-number');
16956         this.cells.addClassOnOver('fc-state-hover');
16957         
16958         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16959         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16960         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16961         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16962         
16963         this.on('monthchange', this.onMonthChange, this);
16964         
16965         this.update(new Date().clearTime());
16966     },
16967     
16968     resize : function() {
16969         var sz  = this.el.getSize();
16970         
16971         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16972         this.el.select('.fc-day-content div',true).setHeight(34);
16973     },
16974     
16975     
16976     // private
16977     showPrevMonth : function(e){
16978         this.update(this.activeDate.add("mo", -1));
16979     },
16980     showToday : function(e){
16981         this.update(new Date().clearTime());
16982     },
16983     // private
16984     showNextMonth : function(e){
16985         this.update(this.activeDate.add("mo", 1));
16986     },
16987
16988     // private
16989     showPrevYear : function(){
16990         this.update(this.activeDate.add("y", -1));
16991     },
16992
16993     // private
16994     showNextYear : function(){
16995         this.update(this.activeDate.add("y", 1));
16996     },
16997
16998     
16999    // private
17000     update : function(date)
17001     {
17002         var vd = this.activeDate;
17003         this.activeDate = date;
17004 //        if(vd && this.el){
17005 //            var t = date.getTime();
17006 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17007 //                Roo.log('using add remove');
17008 //                
17009 //                this.fireEvent('monthchange', this, date);
17010 //                
17011 //                this.cells.removeClass("fc-state-highlight");
17012 //                this.cells.each(function(c){
17013 //                   if(c.dateValue == t){
17014 //                       c.addClass("fc-state-highlight");
17015 //                       setTimeout(function(){
17016 //                            try{c.dom.firstChild.focus();}catch(e){}
17017 //                       }, 50);
17018 //                       return false;
17019 //                   }
17020 //                   return true;
17021 //                });
17022 //                return;
17023 //            }
17024 //        }
17025         
17026         var days = date.getDaysInMonth();
17027         
17028         var firstOfMonth = date.getFirstDateOfMonth();
17029         var startingPos = firstOfMonth.getDay()-this.startDay;
17030         
17031         if(startingPos < this.startDay){
17032             startingPos += 7;
17033         }
17034         
17035         var pm = date.add(Date.MONTH, -1);
17036         var prevStart = pm.getDaysInMonth()-startingPos;
17037 //        
17038         this.cells = this.el.select('.fc-day',true);
17039         this.textNodes = this.el.query('.fc-day-number');
17040         this.cells.addClassOnOver('fc-state-hover');
17041         
17042         var cells = this.cells.elements;
17043         var textEls = this.textNodes;
17044         
17045         Roo.each(cells, function(cell){
17046             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17047         });
17048         
17049         days += startingPos;
17050
17051         // convert everything to numbers so it's fast
17052         var day = 86400000;
17053         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17054         //Roo.log(d);
17055         //Roo.log(pm);
17056         //Roo.log(prevStart);
17057         
17058         var today = new Date().clearTime().getTime();
17059         var sel = date.clearTime().getTime();
17060         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17061         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17062         var ddMatch = this.disabledDatesRE;
17063         var ddText = this.disabledDatesText;
17064         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17065         var ddaysText = this.disabledDaysText;
17066         var format = this.format;
17067         
17068         var setCellClass = function(cal, cell){
17069             cell.row = 0;
17070             cell.events = [];
17071             cell.more = [];
17072             //Roo.log('set Cell Class');
17073             cell.title = "";
17074             var t = d.getTime();
17075             
17076             //Roo.log(d);
17077             
17078             cell.dateValue = t;
17079             if(t == today){
17080                 cell.className += " fc-today";
17081                 cell.className += " fc-state-highlight";
17082                 cell.title = cal.todayText;
17083             }
17084             if(t == sel){
17085                 // disable highlight in other month..
17086                 //cell.className += " fc-state-highlight";
17087                 
17088             }
17089             // disabling
17090             if(t < min) {
17091                 cell.className = " fc-state-disabled";
17092                 cell.title = cal.minText;
17093                 return;
17094             }
17095             if(t > max) {
17096                 cell.className = " fc-state-disabled";
17097                 cell.title = cal.maxText;
17098                 return;
17099             }
17100             if(ddays){
17101                 if(ddays.indexOf(d.getDay()) != -1){
17102                     cell.title = ddaysText;
17103                     cell.className = " fc-state-disabled";
17104                 }
17105             }
17106             if(ddMatch && format){
17107                 var fvalue = d.dateFormat(format);
17108                 if(ddMatch.test(fvalue)){
17109                     cell.title = ddText.replace("%0", fvalue);
17110                     cell.className = " fc-state-disabled";
17111                 }
17112             }
17113             
17114             if (!cell.initialClassName) {
17115                 cell.initialClassName = cell.dom.className;
17116             }
17117             
17118             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17119         };
17120
17121         var i = 0;
17122         
17123         for(; i < startingPos; i++) {
17124             textEls[i].innerHTML = (++prevStart);
17125             d.setDate(d.getDate()+1);
17126             
17127             cells[i].className = "fc-past fc-other-month";
17128             setCellClass(this, cells[i]);
17129         }
17130         
17131         var intDay = 0;
17132         
17133         for(; i < days; i++){
17134             intDay = i - startingPos + 1;
17135             textEls[i].innerHTML = (intDay);
17136             d.setDate(d.getDate()+1);
17137             
17138             cells[i].className = ''; // "x-date-active";
17139             setCellClass(this, cells[i]);
17140         }
17141         var extraDays = 0;
17142         
17143         for(; i < 42; i++) {
17144             textEls[i].innerHTML = (++extraDays);
17145             d.setDate(d.getDate()+1);
17146             
17147             cells[i].className = "fc-future fc-other-month";
17148             setCellClass(this, cells[i]);
17149         }
17150         
17151         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17152         
17153         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17154         
17155         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17156         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17157         
17158         if(totalRows != 6){
17159             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17160             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17161         }
17162         
17163         this.fireEvent('monthchange', this, date);
17164         
17165         
17166         /*
17167         if(!this.internalRender){
17168             var main = this.el.dom.firstChild;
17169             var w = main.offsetWidth;
17170             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17171             Roo.fly(main).setWidth(w);
17172             this.internalRender = true;
17173             // opera does not respect the auto grow header center column
17174             // then, after it gets a width opera refuses to recalculate
17175             // without a second pass
17176             if(Roo.isOpera && !this.secondPass){
17177                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17178                 this.secondPass = true;
17179                 this.update.defer(10, this, [date]);
17180             }
17181         }
17182         */
17183         
17184     },
17185     
17186     findCell : function(dt) {
17187         dt = dt.clearTime().getTime();
17188         var ret = false;
17189         this.cells.each(function(c){
17190             //Roo.log("check " +c.dateValue + '?=' + dt);
17191             if(c.dateValue == dt){
17192                 ret = c;
17193                 return false;
17194             }
17195             return true;
17196         });
17197         
17198         return ret;
17199     },
17200     
17201     findCells : function(ev) {
17202         var s = ev.start.clone().clearTime().getTime();
17203        // Roo.log(s);
17204         var e= ev.end.clone().clearTime().getTime();
17205        // Roo.log(e);
17206         var ret = [];
17207         this.cells.each(function(c){
17208              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17209             
17210             if(c.dateValue > e){
17211                 return ;
17212             }
17213             if(c.dateValue < s){
17214                 return ;
17215             }
17216             ret.push(c);
17217         });
17218         
17219         return ret;    
17220     },
17221     
17222 //    findBestRow: function(cells)
17223 //    {
17224 //        var ret = 0;
17225 //        
17226 //        for (var i =0 ; i < cells.length;i++) {
17227 //            ret  = Math.max(cells[i].rows || 0,ret);
17228 //        }
17229 //        return ret;
17230 //        
17231 //    },
17232     
17233     
17234     addItem : function(ev)
17235     {
17236         // look for vertical location slot in
17237         var cells = this.findCells(ev);
17238         
17239 //        ev.row = this.findBestRow(cells);
17240         
17241         // work out the location.
17242         
17243         var crow = false;
17244         var rows = [];
17245         for(var i =0; i < cells.length; i++) {
17246             
17247             cells[i].row = cells[0].row;
17248             
17249             if(i == 0){
17250                 cells[i].row = cells[i].row + 1;
17251             }
17252             
17253             if (!crow) {
17254                 crow = {
17255                     start : cells[i],
17256                     end :  cells[i]
17257                 };
17258                 continue;
17259             }
17260             if (crow.start.getY() == cells[i].getY()) {
17261                 // on same row.
17262                 crow.end = cells[i];
17263                 continue;
17264             }
17265             // different row.
17266             rows.push(crow);
17267             crow = {
17268                 start: cells[i],
17269                 end : cells[i]
17270             };
17271             
17272         }
17273         
17274         rows.push(crow);
17275         ev.els = [];
17276         ev.rows = rows;
17277         ev.cells = cells;
17278         
17279         cells[0].events.push(ev);
17280         
17281         this.calevents.push(ev);
17282     },
17283     
17284     clearEvents: function() {
17285         
17286         if(!this.calevents){
17287             return;
17288         }
17289         
17290         Roo.each(this.cells.elements, function(c){
17291             c.row = 0;
17292             c.events = [];
17293             c.more = [];
17294         });
17295         
17296         Roo.each(this.calevents, function(e) {
17297             Roo.each(e.els, function(el) {
17298                 el.un('mouseenter' ,this.onEventEnter, this);
17299                 el.un('mouseleave' ,this.onEventLeave, this);
17300                 el.remove();
17301             },this);
17302         },this);
17303         
17304         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17305             e.remove();
17306         });
17307         
17308     },
17309     
17310     renderEvents: function()
17311     {   
17312         var _this = this;
17313         
17314         this.cells.each(function(c) {
17315             
17316             if(c.row < 5){
17317                 return;
17318             }
17319             
17320             var ev = c.events;
17321             
17322             var r = 4;
17323             if(c.row != c.events.length){
17324                 r = 4 - (4 - (c.row - c.events.length));
17325             }
17326             
17327             c.events = ev.slice(0, r);
17328             c.more = ev.slice(r);
17329             
17330             if(c.more.length && c.more.length == 1){
17331                 c.events.push(c.more.pop());
17332             }
17333             
17334             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17335             
17336         });
17337             
17338         this.cells.each(function(c) {
17339             
17340             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17341             
17342             
17343             for (var e = 0; e < c.events.length; e++){
17344                 var ev = c.events[e];
17345                 var rows = ev.rows;
17346                 
17347                 for(var i = 0; i < rows.length; i++) {
17348                 
17349                     // how many rows should it span..
17350
17351                     var  cfg = {
17352                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17353                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17354
17355                         unselectable : "on",
17356                         cn : [
17357                             {
17358                                 cls: 'fc-event-inner',
17359                                 cn : [
17360     //                                {
17361     //                                  tag:'span',
17362     //                                  cls: 'fc-event-time',
17363     //                                  html : cells.length > 1 ? '' : ev.time
17364     //                                },
17365                                     {
17366                                       tag:'span',
17367                                       cls: 'fc-event-title',
17368                                       html : String.format('{0}', ev.title)
17369                                     }
17370
17371
17372                                 ]
17373                             },
17374                             {
17375                                 cls: 'ui-resizable-handle ui-resizable-e',
17376                                 html : '&nbsp;&nbsp;&nbsp'
17377                             }
17378
17379                         ]
17380                     };
17381
17382                     if (i == 0) {
17383                         cfg.cls += ' fc-event-start';
17384                     }
17385                     if ((i+1) == rows.length) {
17386                         cfg.cls += ' fc-event-end';
17387                     }
17388
17389                     var ctr = _this.el.select('.fc-event-container',true).first();
17390                     var cg = ctr.createChild(cfg);
17391
17392                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17393                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17394
17395                     var r = (c.more.length) ? 1 : 0;
17396                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17397                     cg.setWidth(ebox.right - sbox.x -2);
17398
17399                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17400                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17401                     cg.on('click', _this.onEventClick, _this, ev);
17402
17403                     ev.els.push(cg);
17404                     
17405                 }
17406                 
17407             }
17408             
17409             
17410             if(c.more.length){
17411                 var  cfg = {
17412                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17413                     style : 'position: absolute',
17414                     unselectable : "on",
17415                     cn : [
17416                         {
17417                             cls: 'fc-event-inner',
17418                             cn : [
17419                                 {
17420                                   tag:'span',
17421                                   cls: 'fc-event-title',
17422                                   html : 'More'
17423                                 }
17424
17425
17426                             ]
17427                         },
17428                         {
17429                             cls: 'ui-resizable-handle ui-resizable-e',
17430                             html : '&nbsp;&nbsp;&nbsp'
17431                         }
17432
17433                     ]
17434                 };
17435
17436                 var ctr = _this.el.select('.fc-event-container',true).first();
17437                 var cg = ctr.createChild(cfg);
17438
17439                 var sbox = c.select('.fc-day-content',true).first().getBox();
17440                 var ebox = c.select('.fc-day-content',true).first().getBox();
17441                 //Roo.log(cg);
17442                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17443                 cg.setWidth(ebox.right - sbox.x -2);
17444
17445                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17446                 
17447             }
17448             
17449         });
17450         
17451         
17452         
17453     },
17454     
17455     onEventEnter: function (e, el,event,d) {
17456         this.fireEvent('evententer', this, el, event);
17457     },
17458     
17459     onEventLeave: function (e, el,event,d) {
17460         this.fireEvent('eventleave', this, el, event);
17461     },
17462     
17463     onEventClick: function (e, el,event,d) {
17464         this.fireEvent('eventclick', this, el, event);
17465     },
17466     
17467     onMonthChange: function () {
17468         this.store.load();
17469     },
17470     
17471     onMoreEventClick: function(e, el, more)
17472     {
17473         var _this = this;
17474         
17475         this.calpopover.placement = 'right';
17476         this.calpopover.setTitle('More');
17477         
17478         this.calpopover.setContent('');
17479         
17480         var ctr = this.calpopover.el.select('.popover-content', true).first();
17481         
17482         Roo.each(more, function(m){
17483             var cfg = {
17484                 cls : 'fc-event-hori fc-event-draggable',
17485                 html : m.title
17486             };
17487             var cg = ctr.createChild(cfg);
17488             
17489             cg.on('click', _this.onEventClick, _this, m);
17490         });
17491         
17492         this.calpopover.show(el);
17493         
17494         
17495     },
17496     
17497     onLoad: function () 
17498     {   
17499         this.calevents = [];
17500         var cal = this;
17501         
17502         if(this.store.getCount() > 0){
17503             this.store.data.each(function(d){
17504                cal.addItem({
17505                     id : d.data.id,
17506                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17507                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17508                     time : d.data.start_time,
17509                     title : d.data.title,
17510                     description : d.data.description,
17511                     venue : d.data.venue
17512                 });
17513             });
17514         }
17515         
17516         this.renderEvents();
17517         
17518         if(this.calevents.length && this.loadMask){
17519             this.maskEl.hide();
17520         }
17521     },
17522     
17523     onBeforeLoad: function()
17524     {
17525         this.clearEvents();
17526         if(this.loadMask){
17527             this.maskEl.show();
17528         }
17529     }
17530 });
17531
17532  
17533  /*
17534  * - LGPL
17535  *
17536  * element
17537  * 
17538  */
17539
17540 /**
17541  * @class Roo.bootstrap.Popover
17542  * @extends Roo.bootstrap.Component
17543  * Bootstrap Popover class
17544  * @cfg {String} html contents of the popover   (or false to use children..)
17545  * @cfg {String} title of popover (or false to hide)
17546  * @cfg {String} placement how it is placed
17547  * @cfg {String} trigger click || hover (or false to trigger manually)
17548  * @cfg {String} over what (parent or false to trigger manually.)
17549  * @cfg {Number} delay - delay before showing
17550  
17551  * @constructor
17552  * Create a new Popover
17553  * @param {Object} config The config object
17554  */
17555
17556 Roo.bootstrap.Popover = function(config){
17557     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17558     
17559     this.addEvents({
17560         // raw events
17561          /**
17562          * @event show
17563          * After the popover show
17564          * 
17565          * @param {Roo.bootstrap.Popover} this
17566          */
17567         "show" : true,
17568         /**
17569          * @event hide
17570          * After the popover hide
17571          * 
17572          * @param {Roo.bootstrap.Popover} this
17573          */
17574         "hide" : true
17575     });
17576 };
17577
17578 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17579     
17580     title: 'Fill in a title',
17581     html: false,
17582     
17583     placement : 'right',
17584     trigger : 'hover', // hover
17585     
17586     delay : 0,
17587     
17588     over: 'parent',
17589     
17590     can_build_overlaid : false,
17591     
17592     getChildContainer : function()
17593     {
17594         return this.el.select('.popover-content',true).first();
17595     },
17596     
17597     getAutoCreate : function(){
17598          
17599         var cfg = {
17600            cls : 'popover roo-dynamic',
17601            style: 'display:block',
17602            cn : [
17603                 {
17604                     cls : 'arrow'
17605                 },
17606                 {
17607                     cls : 'popover-inner',
17608                     cn : [
17609                         {
17610                             tag: 'h3',
17611                             cls: 'popover-title',
17612                             html : this.title
17613                         },
17614                         {
17615                             cls : 'popover-content',
17616                             html : this.html
17617                         }
17618                     ]
17619                     
17620                 }
17621            ]
17622         };
17623         
17624         return cfg;
17625     },
17626     setTitle: function(str)
17627     {
17628         this.title = str;
17629         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17630     },
17631     setContent: function(str)
17632     {
17633         this.html = str;
17634         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17635     },
17636     // as it get's added to the bottom of the page.
17637     onRender : function(ct, position)
17638     {
17639         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17640         if(!this.el){
17641             var cfg = Roo.apply({},  this.getAutoCreate());
17642             cfg.id = Roo.id();
17643             
17644             if (this.cls) {
17645                 cfg.cls += ' ' + this.cls;
17646             }
17647             if (this.style) {
17648                 cfg.style = this.style;
17649             }
17650             //Roo.log("adding to ");
17651             this.el = Roo.get(document.body).createChild(cfg, position);
17652 //            Roo.log(this.el);
17653         }
17654         this.initEvents();
17655     },
17656     
17657     initEvents : function()
17658     {
17659         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17660         this.el.enableDisplayMode('block');
17661         this.el.hide();
17662         if (this.over === false) {
17663             return; 
17664         }
17665         if (this.triggers === false) {
17666             return;
17667         }
17668         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17669         var triggers = this.trigger ? this.trigger.split(' ') : [];
17670         Roo.each(triggers, function(trigger) {
17671         
17672             if (trigger == 'click') {
17673                 on_el.on('click', this.toggle, this);
17674             } else if (trigger != 'manual') {
17675                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17676                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17677       
17678                 on_el.on(eventIn  ,this.enter, this);
17679                 on_el.on(eventOut, this.leave, this);
17680             }
17681         }, this);
17682         
17683     },
17684     
17685     
17686     // private
17687     timeout : null,
17688     hoverState : null,
17689     
17690     toggle : function () {
17691         this.hoverState == 'in' ? this.leave() : this.enter();
17692     },
17693     
17694     enter : function () {
17695         
17696         clearTimeout(this.timeout);
17697     
17698         this.hoverState = 'in';
17699     
17700         if (!this.delay || !this.delay.show) {
17701             this.show();
17702             return;
17703         }
17704         var _t = this;
17705         this.timeout = setTimeout(function () {
17706             if (_t.hoverState == 'in') {
17707                 _t.show();
17708             }
17709         }, this.delay.show)
17710     },
17711     
17712     leave : function() {
17713         clearTimeout(this.timeout);
17714     
17715         this.hoverState = 'out';
17716     
17717         if (!this.delay || !this.delay.hide) {
17718             this.hide();
17719             return;
17720         }
17721         var _t = this;
17722         this.timeout = setTimeout(function () {
17723             if (_t.hoverState == 'out') {
17724                 _t.hide();
17725             }
17726         }, this.delay.hide)
17727     },
17728     
17729     show : function (on_el)
17730     {
17731         if (!on_el) {
17732             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17733         }
17734         
17735         // set content.
17736         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17737         if (this.html !== false) {
17738             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17739         }
17740         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17741         if (!this.title.length) {
17742             this.el.select('.popover-title',true).hide();
17743         }
17744         
17745         var placement = typeof this.placement == 'function' ?
17746             this.placement.call(this, this.el, on_el) :
17747             this.placement;
17748             
17749         var autoToken = /\s?auto?\s?/i;
17750         var autoPlace = autoToken.test(placement);
17751         if (autoPlace) {
17752             placement = placement.replace(autoToken, '') || 'top';
17753         }
17754         
17755         //this.el.detach()
17756         //this.el.setXY([0,0]);
17757         this.el.show();
17758         this.el.dom.style.display='block';
17759         this.el.addClass(placement);
17760         
17761         //this.el.appendTo(on_el);
17762         
17763         var p = this.getPosition();
17764         var box = this.el.getBox();
17765         
17766         if (autoPlace) {
17767             // fixme..
17768         }
17769         var align = Roo.bootstrap.Popover.alignment[placement];
17770         
17771 //        Roo.log(align);
17772         this.el.alignTo(on_el, align[0],align[1]);
17773         //var arrow = this.el.select('.arrow',true).first();
17774         //arrow.set(align[2], 
17775         
17776         this.el.addClass('in');
17777         
17778         
17779         if (this.el.hasClass('fade')) {
17780             // fade it?
17781         }
17782         
17783         this.hoverState = 'in';
17784         
17785         this.fireEvent('show', this);
17786         
17787     },
17788     hide : function()
17789     {
17790         this.el.setXY([0,0]);
17791         this.el.removeClass('in');
17792         this.el.hide();
17793         this.hoverState = null;
17794         
17795         this.fireEvent('hide', this);
17796     }
17797     
17798 });
17799
17800 Roo.bootstrap.Popover.alignment = {
17801     'left' : ['r-l', [-10,0], 'right'],
17802     'right' : ['l-r', [10,0], 'left'],
17803     'bottom' : ['t-b', [0,10], 'top'],
17804     'top' : [ 'b-t', [0,-10], 'bottom']
17805 };
17806
17807  /*
17808  * - LGPL
17809  *
17810  * Progress
17811  * 
17812  */
17813
17814 /**
17815  * @class Roo.bootstrap.Progress
17816  * @extends Roo.bootstrap.Component
17817  * Bootstrap Progress class
17818  * @cfg {Boolean} striped striped of the progress bar
17819  * @cfg {Boolean} active animated of the progress bar
17820  * 
17821  * 
17822  * @constructor
17823  * Create a new Progress
17824  * @param {Object} config The config object
17825  */
17826
17827 Roo.bootstrap.Progress = function(config){
17828     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17829 };
17830
17831 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17832     
17833     striped : false,
17834     active: false,
17835     
17836     getAutoCreate : function(){
17837         var cfg = {
17838             tag: 'div',
17839             cls: 'progress'
17840         };
17841         
17842         
17843         if(this.striped){
17844             cfg.cls += ' progress-striped';
17845         }
17846       
17847         if(this.active){
17848             cfg.cls += ' active';
17849         }
17850         
17851         
17852         return cfg;
17853     }
17854    
17855 });
17856
17857  
17858
17859  /*
17860  * - LGPL
17861  *
17862  * ProgressBar
17863  * 
17864  */
17865
17866 /**
17867  * @class Roo.bootstrap.ProgressBar
17868  * @extends Roo.bootstrap.Component
17869  * Bootstrap ProgressBar class
17870  * @cfg {Number} aria_valuenow aria-value now
17871  * @cfg {Number} aria_valuemin aria-value min
17872  * @cfg {Number} aria_valuemax aria-value max
17873  * @cfg {String} label label for the progress bar
17874  * @cfg {String} panel (success | info | warning | danger )
17875  * @cfg {String} role role of the progress bar
17876  * @cfg {String} sr_only text
17877  * 
17878  * 
17879  * @constructor
17880  * Create a new ProgressBar
17881  * @param {Object} config The config object
17882  */
17883
17884 Roo.bootstrap.ProgressBar = function(config){
17885     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17886 };
17887
17888 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17889     
17890     aria_valuenow : 0,
17891     aria_valuemin : 0,
17892     aria_valuemax : 100,
17893     label : false,
17894     panel : false,
17895     role : false,
17896     sr_only: false,
17897     
17898     getAutoCreate : function()
17899     {
17900         
17901         var cfg = {
17902             tag: 'div',
17903             cls: 'progress-bar',
17904             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17905         };
17906         
17907         if(this.sr_only){
17908             cfg.cn = {
17909                 tag: 'span',
17910                 cls: 'sr-only',
17911                 html: this.sr_only
17912             }
17913         }
17914         
17915         if(this.role){
17916             cfg.role = this.role;
17917         }
17918         
17919         if(this.aria_valuenow){
17920             cfg['aria-valuenow'] = this.aria_valuenow;
17921         }
17922         
17923         if(this.aria_valuemin){
17924             cfg['aria-valuemin'] = this.aria_valuemin;
17925         }
17926         
17927         if(this.aria_valuemax){
17928             cfg['aria-valuemax'] = this.aria_valuemax;
17929         }
17930         
17931         if(this.label && !this.sr_only){
17932             cfg.html = this.label;
17933         }
17934         
17935         if(this.panel){
17936             cfg.cls += ' progress-bar-' + this.panel;
17937         }
17938         
17939         return cfg;
17940     },
17941     
17942     update : function(aria_valuenow)
17943     {
17944         this.aria_valuenow = aria_valuenow;
17945         
17946         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17947     }
17948    
17949 });
17950
17951  
17952
17953  /*
17954  * - LGPL
17955  *
17956  * column
17957  * 
17958  */
17959
17960 /**
17961  * @class Roo.bootstrap.TabGroup
17962  * @extends Roo.bootstrap.Column
17963  * Bootstrap Column class
17964  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17965  * @cfg {Boolean} carousel true to make the group behave like a carousel
17966  * @cfg {Boolean} bullets show bullets for the panels
17967  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17968  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17969  * @cfg {Boolean} showarrow (true|false) show arrow default true
17970  * 
17971  * @constructor
17972  * Create a new TabGroup
17973  * @param {Object} config The config object
17974  */
17975
17976 Roo.bootstrap.TabGroup = function(config){
17977     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17978     if (!this.navId) {
17979         this.navId = Roo.id();
17980     }
17981     this.tabs = [];
17982     Roo.bootstrap.TabGroup.register(this);
17983     
17984 };
17985
17986 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17987     
17988     carousel : false,
17989     transition : false,
17990     bullets : 0,
17991     timer : 0,
17992     autoslide : false,
17993     slideFn : false,
17994     slideOnTouch : false,
17995     showarrow : true,
17996     
17997     getAutoCreate : function()
17998     {
17999         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18000         
18001         cfg.cls += ' tab-content';
18002         
18003         if (this.carousel) {
18004             cfg.cls += ' carousel slide';
18005             
18006             cfg.cn = [{
18007                cls : 'carousel-inner',
18008                cn : []
18009             }];
18010         
18011             if(this.bullets  && !Roo.isTouch){
18012                 
18013                 var bullets = {
18014                     cls : 'carousel-bullets',
18015                     cn : []
18016                 };
18017                
18018                 if(this.bullets_cls){
18019                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18020                 }
18021                 
18022                 bullets.cn.push({
18023                     cls : 'clear'
18024                 });
18025                 
18026                 cfg.cn[0].cn.push(bullets);
18027             }
18028             
18029             if(this.showarrow){
18030                 cfg.cn[0].cn.push({
18031                     tag : 'div',
18032                     class : 'carousel-arrow',
18033                     cn : [
18034                         {
18035                             tag : 'div',
18036                             class : 'carousel-prev',
18037                             cn : [
18038                                 {
18039                                     tag : 'i',
18040                                     class : 'fa fa-chevron-left'
18041                                 }
18042                             ]
18043                         },
18044                         {
18045                             tag : 'div',
18046                             class : 'carousel-next',
18047                             cn : [
18048                                 {
18049                                     tag : 'i',
18050                                     class : 'fa fa-chevron-right'
18051                                 }
18052                             ]
18053                         }
18054                     ]
18055                 });
18056             }
18057             
18058         }
18059         
18060         return cfg;
18061     },
18062     
18063     initEvents:  function()
18064     {
18065 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18066 //            this.el.on("touchstart", this.onTouchStart, this);
18067 //        }
18068         
18069         if(this.autoslide){
18070             var _this = this;
18071             
18072             this.slideFn = window.setInterval(function() {
18073                 _this.showPanelNext();
18074             }, this.timer);
18075         }
18076         
18077         if(this.showarrow){
18078             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18079             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18080         }
18081         
18082         
18083     },
18084     
18085 //    onTouchStart : function(e, el, o)
18086 //    {
18087 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18088 //            return;
18089 //        }
18090 //        
18091 //        this.showPanelNext();
18092 //    },
18093     
18094     
18095     getChildContainer : function()
18096     {
18097         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18098     },
18099     
18100     /**
18101     * register a Navigation item
18102     * @param {Roo.bootstrap.NavItem} the navitem to add
18103     */
18104     register : function(item)
18105     {
18106         this.tabs.push( item);
18107         item.navId = this.navId; // not really needed..
18108         this.addBullet();
18109     
18110     },
18111     
18112     getActivePanel : function()
18113     {
18114         var r = false;
18115         Roo.each(this.tabs, function(t) {
18116             if (t.active) {
18117                 r = t;
18118                 return false;
18119             }
18120             return null;
18121         });
18122         return r;
18123         
18124     },
18125     getPanelByName : function(n)
18126     {
18127         var r = false;
18128         Roo.each(this.tabs, function(t) {
18129             if (t.tabId == n) {
18130                 r = t;
18131                 return false;
18132             }
18133             return null;
18134         });
18135         return r;
18136     },
18137     indexOfPanel : function(p)
18138     {
18139         var r = false;
18140         Roo.each(this.tabs, function(t,i) {
18141             if (t.tabId == p.tabId) {
18142                 r = i;
18143                 return false;
18144             }
18145             return null;
18146         });
18147         return r;
18148     },
18149     /**
18150      * show a specific panel
18151      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18152      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18153      */
18154     showPanel : function (pan)
18155     {
18156         if(this.transition || typeof(pan) == 'undefined'){
18157             Roo.log("waiting for the transitionend");
18158             return;
18159         }
18160         
18161         if (typeof(pan) == 'number') {
18162             pan = this.tabs[pan];
18163         }
18164         
18165         if (typeof(pan) == 'string') {
18166             pan = this.getPanelByName(pan);
18167         }
18168         
18169         var cur = this.getActivePanel();
18170         
18171         if(!pan || !cur){
18172             Roo.log('pan or acitve pan is undefined');
18173             return false;
18174         }
18175         
18176         if (pan.tabId == this.getActivePanel().tabId) {
18177             return true;
18178         }
18179         
18180         if (false === cur.fireEvent('beforedeactivate')) {
18181             return false;
18182         }
18183         
18184         if(this.bullets > 0 && !Roo.isTouch){
18185             this.setActiveBullet(this.indexOfPanel(pan));
18186         }
18187         
18188         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18189             
18190             this.transition = true;
18191             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18192             var lr = dir == 'next' ? 'left' : 'right';
18193             pan.el.addClass(dir); // or prev
18194             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18195             cur.el.addClass(lr); // or right
18196             pan.el.addClass(lr);
18197             
18198             var _this = this;
18199             cur.el.on('transitionend', function() {
18200                 Roo.log("trans end?");
18201                 
18202                 pan.el.removeClass([lr,dir]);
18203                 pan.setActive(true);
18204                 
18205                 cur.el.removeClass([lr]);
18206                 cur.setActive(false);
18207                 
18208                 _this.transition = false;
18209                 
18210             }, this, { single:  true } );
18211             
18212             return true;
18213         }
18214         
18215         cur.setActive(false);
18216         pan.setActive(true);
18217         
18218         return true;
18219         
18220     },
18221     showPanelNext : function()
18222     {
18223         var i = this.indexOfPanel(this.getActivePanel());
18224         
18225         if (i >= this.tabs.length - 1 && !this.autoslide) {
18226             return;
18227         }
18228         
18229         if (i >= this.tabs.length - 1 && this.autoslide) {
18230             i = -1;
18231         }
18232         
18233         this.showPanel(this.tabs[i+1]);
18234     },
18235     
18236     showPanelPrev : function()
18237     {
18238         var i = this.indexOfPanel(this.getActivePanel());
18239         
18240         if (i  < 1 && !this.autoslide) {
18241             return;
18242         }
18243         
18244         if (i < 1 && this.autoslide) {
18245             i = this.tabs.length;
18246         }
18247         
18248         this.showPanel(this.tabs[i-1]);
18249     },
18250     
18251     
18252     addBullet: function()
18253     {
18254         if(!this.bullets || Roo.isTouch){
18255             return;
18256         }
18257         var ctr = this.el.select('.carousel-bullets',true).first();
18258         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18259         var bullet = ctr.createChild({
18260             cls : 'bullet bullet-' + i
18261         },ctr.dom.lastChild);
18262         
18263         
18264         var _this = this;
18265         
18266         bullet.on('click', (function(e, el, o, ii, t){
18267
18268             e.preventDefault();
18269
18270             this.showPanel(ii);
18271
18272             if(this.autoslide && this.slideFn){
18273                 clearInterval(this.slideFn);
18274                 this.slideFn = window.setInterval(function() {
18275                     _this.showPanelNext();
18276                 }, this.timer);
18277             }
18278
18279         }).createDelegate(this, [i, bullet], true));
18280                 
18281         
18282     },
18283      
18284     setActiveBullet : function(i)
18285     {
18286         if(Roo.isTouch){
18287             return;
18288         }
18289         
18290         Roo.each(this.el.select('.bullet', true).elements, function(el){
18291             el.removeClass('selected');
18292         });
18293
18294         var bullet = this.el.select('.bullet-' + i, true).first();
18295         
18296         if(!bullet){
18297             return;
18298         }
18299         
18300         bullet.addClass('selected');
18301     }
18302     
18303     
18304   
18305 });
18306
18307  
18308
18309  
18310  
18311 Roo.apply(Roo.bootstrap.TabGroup, {
18312     
18313     groups: {},
18314      /**
18315     * register a Navigation Group
18316     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18317     */
18318     register : function(navgrp)
18319     {
18320         this.groups[navgrp.navId] = navgrp;
18321         
18322     },
18323     /**
18324     * fetch a Navigation Group based on the navigation ID
18325     * if one does not exist , it will get created.
18326     * @param {string} the navgroup to add
18327     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18328     */
18329     get: function(navId) {
18330         if (typeof(this.groups[navId]) == 'undefined') {
18331             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18332         }
18333         return this.groups[navId] ;
18334     }
18335     
18336     
18337     
18338 });
18339
18340  /*
18341  * - LGPL
18342  *
18343  * TabPanel
18344  * 
18345  */
18346
18347 /**
18348  * @class Roo.bootstrap.TabPanel
18349  * @extends Roo.bootstrap.Component
18350  * Bootstrap TabPanel class
18351  * @cfg {Boolean} active panel active
18352  * @cfg {String} html panel content
18353  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18354  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18355  * @cfg {String} href click to link..
18356  * 
18357  * 
18358  * @constructor
18359  * Create a new TabPanel
18360  * @param {Object} config The config object
18361  */
18362
18363 Roo.bootstrap.TabPanel = function(config){
18364     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18365     this.addEvents({
18366         /**
18367              * @event changed
18368              * Fires when the active status changes
18369              * @param {Roo.bootstrap.TabPanel} this
18370              * @param {Boolean} state the new state
18371             
18372          */
18373         'changed': true,
18374         /**
18375              * @event beforedeactivate
18376              * Fires before a tab is de-activated - can be used to do validation on a form.
18377              * @param {Roo.bootstrap.TabPanel} this
18378              * @return {Boolean} false if there is an error
18379             
18380          */
18381         'beforedeactivate': true
18382      });
18383     
18384     this.tabId = this.tabId || Roo.id();
18385   
18386 };
18387
18388 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18389     
18390     active: false,
18391     html: false,
18392     tabId: false,
18393     navId : false,
18394     href : '',
18395     
18396     getAutoCreate : function(){
18397         var cfg = {
18398             tag: 'div',
18399             // item is needed for carousel - not sure if it has any effect otherwise
18400             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18401             html: this.html || ''
18402         };
18403         
18404         if(this.active){
18405             cfg.cls += ' active';
18406         }
18407         
18408         if(this.tabId){
18409             cfg.tabId = this.tabId;
18410         }
18411         
18412         
18413         return cfg;
18414     },
18415     
18416     initEvents:  function()
18417     {
18418         var p = this.parent();
18419         
18420         this.navId = this.navId || p.navId;
18421         
18422         if (typeof(this.navId) != 'undefined') {
18423             // not really needed.. but just in case.. parent should be a NavGroup.
18424             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18425             
18426             tg.register(this);
18427             
18428             var i = tg.tabs.length - 1;
18429             
18430             if(this.active && tg.bullets > 0 && i < tg.bullets){
18431                 tg.setActiveBullet(i);
18432             }
18433         }
18434         
18435         this.el.on('click', this.onClick, this);
18436         
18437         if(Roo.isTouch){
18438             this.el.on("touchstart", this.onTouchStart, this);
18439             this.el.on("touchmove", this.onTouchMove, this);
18440             this.el.on("touchend", this.onTouchEnd, this);
18441         }
18442         
18443     },
18444     
18445     onRender : function(ct, position)
18446     {
18447         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18448     },
18449     
18450     setActive : function(state)
18451     {
18452         Roo.log("panel - set active " + this.tabId + "=" + state);
18453         
18454         this.active = state;
18455         if (!state) {
18456             this.el.removeClass('active');
18457             
18458         } else  if (!this.el.hasClass('active')) {
18459             this.el.addClass('active');
18460         }
18461         
18462         this.fireEvent('changed', this, state);
18463     },
18464     
18465     onClick : function(e)
18466     {
18467         e.preventDefault();
18468         
18469         if(!this.href.length){
18470             return;
18471         }
18472         
18473         window.location.href = this.href;
18474     },
18475     
18476     startX : 0,
18477     startY : 0,
18478     endX : 0,
18479     endY : 0,
18480     swiping : false,
18481     
18482     onTouchStart : function(e)
18483     {
18484         this.swiping = false;
18485         
18486         this.startX = e.browserEvent.touches[0].clientX;
18487         this.startY = e.browserEvent.touches[0].clientY;
18488     },
18489     
18490     onTouchMove : function(e)
18491     {
18492         this.swiping = true;
18493         
18494         this.endX = e.browserEvent.touches[0].clientX;
18495         this.endY = e.browserEvent.touches[0].clientY;
18496     },
18497     
18498     onTouchEnd : function(e)
18499     {
18500         if(!this.swiping){
18501             this.onClick(e);
18502             return;
18503         }
18504         
18505         var tabGroup = this.parent();
18506         
18507         if(this.endX > this.startX){ // swiping right
18508             tabGroup.showPanelPrev();
18509             return;
18510         }
18511         
18512         if(this.startX > this.endX){ // swiping left
18513             tabGroup.showPanelNext();
18514             return;
18515         }
18516     }
18517     
18518     
18519 });
18520  
18521
18522  
18523
18524  /*
18525  * - LGPL
18526  *
18527  * DateField
18528  * 
18529  */
18530
18531 /**
18532  * @class Roo.bootstrap.DateField
18533  * @extends Roo.bootstrap.Input
18534  * Bootstrap DateField class
18535  * @cfg {Number} weekStart default 0
18536  * @cfg {String} viewMode default empty, (months|years)
18537  * @cfg {String} minViewMode default empty, (months|years)
18538  * @cfg {Number} startDate default -Infinity
18539  * @cfg {Number} endDate default Infinity
18540  * @cfg {Boolean} todayHighlight default false
18541  * @cfg {Boolean} todayBtn default false
18542  * @cfg {Boolean} calendarWeeks default false
18543  * @cfg {Object} daysOfWeekDisabled default empty
18544  * @cfg {Boolean} singleMode default false (true | false)
18545  * 
18546  * @cfg {Boolean} keyboardNavigation default true
18547  * @cfg {String} language default en
18548  * 
18549  * @constructor
18550  * Create a new DateField
18551  * @param {Object} config The config object
18552  */
18553
18554 Roo.bootstrap.DateField = function(config){
18555     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18556      this.addEvents({
18557             /**
18558              * @event show
18559              * Fires when this field show.
18560              * @param {Roo.bootstrap.DateField} this
18561              * @param {Mixed} date The date value
18562              */
18563             show : true,
18564             /**
18565              * @event show
18566              * Fires when this field hide.
18567              * @param {Roo.bootstrap.DateField} this
18568              * @param {Mixed} date The date value
18569              */
18570             hide : true,
18571             /**
18572              * @event select
18573              * Fires when select a date.
18574              * @param {Roo.bootstrap.DateField} this
18575              * @param {Mixed} date The date value
18576              */
18577             select : true,
18578             /**
18579              * @event beforeselect
18580              * Fires when before select a date.
18581              * @param {Roo.bootstrap.DateField} this
18582              * @param {Mixed} date The date value
18583              */
18584             beforeselect : true
18585         });
18586 };
18587
18588 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18589     
18590     /**
18591      * @cfg {String} format
18592      * The default date format string which can be overriden for localization support.  The format must be
18593      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18594      */
18595     format : "m/d/y",
18596     /**
18597      * @cfg {String} altFormats
18598      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18599      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18600      */
18601     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18602     
18603     weekStart : 0,
18604     
18605     viewMode : '',
18606     
18607     minViewMode : '',
18608     
18609     todayHighlight : false,
18610     
18611     todayBtn: false,
18612     
18613     language: 'en',
18614     
18615     keyboardNavigation: true,
18616     
18617     calendarWeeks: false,
18618     
18619     startDate: -Infinity,
18620     
18621     endDate: Infinity,
18622     
18623     daysOfWeekDisabled: [],
18624     
18625     _events: [],
18626     
18627     singleMode : false,
18628     
18629     UTCDate: function()
18630     {
18631         return new Date(Date.UTC.apply(Date, arguments));
18632     },
18633     
18634     UTCToday: function()
18635     {
18636         var today = new Date();
18637         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18638     },
18639     
18640     getDate: function() {
18641             var d = this.getUTCDate();
18642             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18643     },
18644     
18645     getUTCDate: function() {
18646             return this.date;
18647     },
18648     
18649     setDate: function(d) {
18650             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18651     },
18652     
18653     setUTCDate: function(d) {
18654             this.date = d;
18655             this.setValue(this.formatDate(this.date));
18656     },
18657         
18658     onRender: function(ct, position)
18659     {
18660         
18661         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18662         
18663         this.language = this.language || 'en';
18664         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18665         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18666         
18667         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18668         this.format = this.format || 'm/d/y';
18669         this.isInline = false;
18670         this.isInput = true;
18671         this.component = this.el.select('.add-on', true).first() || false;
18672         this.component = (this.component && this.component.length === 0) ? false : this.component;
18673         this.hasInput = this.component && this.inputEl().length;
18674         
18675         if (typeof(this.minViewMode === 'string')) {
18676             switch (this.minViewMode) {
18677                 case 'months':
18678                     this.minViewMode = 1;
18679                     break;
18680                 case 'years':
18681                     this.minViewMode = 2;
18682                     break;
18683                 default:
18684                     this.minViewMode = 0;
18685                     break;
18686             }
18687         }
18688         
18689         if (typeof(this.viewMode === 'string')) {
18690             switch (this.viewMode) {
18691                 case 'months':
18692                     this.viewMode = 1;
18693                     break;
18694                 case 'years':
18695                     this.viewMode = 2;
18696                     break;
18697                 default:
18698                     this.viewMode = 0;
18699                     break;
18700             }
18701         }
18702                 
18703         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18704         
18705 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18706         
18707         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18708         
18709         this.picker().on('mousedown', this.onMousedown, this);
18710         this.picker().on('click', this.onClick, this);
18711         
18712         this.picker().addClass('datepicker-dropdown');
18713         
18714         this.startViewMode = this.viewMode;
18715         
18716         if(this.singleMode){
18717             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18718                 v.setVisibilityMode(Roo.Element.DISPLAY);
18719                 v.hide();
18720             });
18721             
18722             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18723                 v.setStyle('width', '189px');
18724             });
18725         }
18726         
18727         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18728             if(!this.calendarWeeks){
18729                 v.remove();
18730                 return;
18731             }
18732             
18733             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18734             v.attr('colspan', function(i, val){
18735                 return parseInt(val) + 1;
18736             });
18737         });
18738                         
18739         
18740         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18741         
18742         this.setStartDate(this.startDate);
18743         this.setEndDate(this.endDate);
18744         
18745         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18746         
18747         this.fillDow();
18748         this.fillMonths();
18749         this.update();
18750         this.showMode();
18751         
18752         if(this.isInline) {
18753             this.showPopup();
18754         }
18755     },
18756     
18757     picker : function()
18758     {
18759         return this.pickerEl;
18760 //        return this.el.select('.datepicker', true).first();
18761     },
18762     
18763     fillDow: function()
18764     {
18765         var dowCnt = this.weekStart;
18766         
18767         var dow = {
18768             tag: 'tr',
18769             cn: [
18770                 
18771             ]
18772         };
18773         
18774         if(this.calendarWeeks){
18775             dow.cn.push({
18776                 tag: 'th',
18777                 cls: 'cw',
18778                 html: '&nbsp;'
18779             })
18780         }
18781         
18782         while (dowCnt < this.weekStart + 7) {
18783             dow.cn.push({
18784                 tag: 'th',
18785                 cls: 'dow',
18786                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18787             });
18788         }
18789         
18790         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18791     },
18792     
18793     fillMonths: function()
18794     {    
18795         var i = 0;
18796         var months = this.picker().select('>.datepicker-months td', true).first();
18797         
18798         months.dom.innerHTML = '';
18799         
18800         while (i < 12) {
18801             var month = {
18802                 tag: 'span',
18803                 cls: 'month',
18804                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18805             };
18806             
18807             months.createChild(month);
18808         }
18809         
18810     },
18811     
18812     update: function()
18813     {
18814         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;
18815         
18816         if (this.date < this.startDate) {
18817             this.viewDate = new Date(this.startDate);
18818         } else if (this.date > this.endDate) {
18819             this.viewDate = new Date(this.endDate);
18820         } else {
18821             this.viewDate = new Date(this.date);
18822         }
18823         
18824         this.fill();
18825     },
18826     
18827     fill: function() 
18828     {
18829         var d = new Date(this.viewDate),
18830                 year = d.getUTCFullYear(),
18831                 month = d.getUTCMonth(),
18832                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18833                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18834                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18835                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18836                 currentDate = this.date && this.date.valueOf(),
18837                 today = this.UTCToday();
18838         
18839         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18840         
18841 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18842         
18843 //        this.picker.select('>tfoot th.today').
18844 //                                              .text(dates[this.language].today)
18845 //                                              .toggle(this.todayBtn !== false);
18846     
18847         this.updateNavArrows();
18848         this.fillMonths();
18849                                                 
18850         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18851         
18852         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18853          
18854         prevMonth.setUTCDate(day);
18855         
18856         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18857         
18858         var nextMonth = new Date(prevMonth);
18859         
18860         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18861         
18862         nextMonth = nextMonth.valueOf();
18863         
18864         var fillMonths = false;
18865         
18866         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18867         
18868         while(prevMonth.valueOf() <= nextMonth) {
18869             var clsName = '';
18870             
18871             if (prevMonth.getUTCDay() === this.weekStart) {
18872                 if(fillMonths){
18873                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18874                 }
18875                     
18876                 fillMonths = {
18877                     tag: 'tr',
18878                     cn: []
18879                 };
18880                 
18881                 if(this.calendarWeeks){
18882                     // ISO 8601: First week contains first thursday.
18883                     // ISO also states week starts on Monday, but we can be more abstract here.
18884                     var
18885                     // Start of current week: based on weekstart/current date
18886                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18887                     // Thursday of this week
18888                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18889                     // First Thursday of year, year from thursday
18890                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18891                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18892                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18893                     
18894                     fillMonths.cn.push({
18895                         tag: 'td',
18896                         cls: 'cw',
18897                         html: calWeek
18898                     });
18899                 }
18900             }
18901             
18902             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18903                 clsName += ' old';
18904             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18905                 clsName += ' new';
18906             }
18907             if (this.todayHighlight &&
18908                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18909                 prevMonth.getUTCMonth() == today.getMonth() &&
18910                 prevMonth.getUTCDate() == today.getDate()) {
18911                 clsName += ' today';
18912             }
18913             
18914             if (currentDate && prevMonth.valueOf() === currentDate) {
18915                 clsName += ' active';
18916             }
18917             
18918             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18919                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18920                     clsName += ' disabled';
18921             }
18922             
18923             fillMonths.cn.push({
18924                 tag: 'td',
18925                 cls: 'day ' + clsName,
18926                 html: prevMonth.getDate()
18927             });
18928             
18929             prevMonth.setDate(prevMonth.getDate()+1);
18930         }
18931           
18932         var currentYear = this.date && this.date.getUTCFullYear();
18933         var currentMonth = this.date && this.date.getUTCMonth();
18934         
18935         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18936         
18937         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18938             v.removeClass('active');
18939             
18940             if(currentYear === year && k === currentMonth){
18941                 v.addClass('active');
18942             }
18943             
18944             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18945                 v.addClass('disabled');
18946             }
18947             
18948         });
18949         
18950         
18951         year = parseInt(year/10, 10) * 10;
18952         
18953         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18954         
18955         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18956         
18957         year -= 1;
18958         for (var i = -1; i < 11; i++) {
18959             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18960                 tag: 'span',
18961                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18962                 html: year
18963             });
18964             
18965             year += 1;
18966         }
18967     },
18968     
18969     showMode: function(dir) 
18970     {
18971         if (dir) {
18972             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18973         }
18974         
18975         Roo.each(this.picker().select('>div',true).elements, function(v){
18976             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18977             v.hide();
18978         });
18979         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18980     },
18981     
18982     place: function()
18983     {
18984         if(this.isInline) {
18985             return;
18986         }
18987         
18988         this.picker().removeClass(['bottom', 'top']);
18989         
18990         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18991             /*
18992              * place to the top of element!
18993              *
18994              */
18995             
18996             this.picker().addClass('top');
18997             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18998             
18999             return;
19000         }
19001         
19002         this.picker().addClass('bottom');
19003         
19004         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19005     },
19006     
19007     parseDate : function(value)
19008     {
19009         if(!value || value instanceof Date){
19010             return value;
19011         }
19012         var v = Date.parseDate(value, this.format);
19013         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19014             v = Date.parseDate(value, 'Y-m-d');
19015         }
19016         if(!v && this.altFormats){
19017             if(!this.altFormatsArray){
19018                 this.altFormatsArray = this.altFormats.split("|");
19019             }
19020             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19021                 v = Date.parseDate(value, this.altFormatsArray[i]);
19022             }
19023         }
19024         return v;
19025     },
19026     
19027     formatDate : function(date, fmt)
19028     {   
19029         return (!date || !(date instanceof Date)) ?
19030         date : date.dateFormat(fmt || this.format);
19031     },
19032     
19033     onFocus : function()
19034     {
19035         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19036         this.showPopup();
19037     },
19038     
19039     onBlur : function()
19040     {
19041         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19042         
19043         var d = this.inputEl().getValue();
19044         
19045         this.setValue(d);
19046                 
19047         this.hidePopup();
19048     },
19049     
19050     showPopup : function()
19051     {
19052         this.picker().show();
19053         this.update();
19054         this.place();
19055         
19056         this.fireEvent('showpopup', this, this.date);
19057     },
19058     
19059     hidePopup : function()
19060     {
19061         if(this.isInline) {
19062             return;
19063         }
19064         this.picker().hide();
19065         this.viewMode = this.startViewMode;
19066         this.showMode();
19067         
19068         this.fireEvent('hidepopup', this, this.date);
19069         
19070     },
19071     
19072     onMousedown: function(e)
19073     {
19074         e.stopPropagation();
19075         e.preventDefault();
19076     },
19077     
19078     keyup: function(e)
19079     {
19080         Roo.bootstrap.DateField.superclass.keyup.call(this);
19081         this.update();
19082     },
19083
19084     setValue: function(v)
19085     {
19086         if(this.fireEvent('beforeselect', this, v) !== false){
19087             var d = new Date(this.parseDate(v) ).clearTime();
19088         
19089             if(isNaN(d.getTime())){
19090                 this.date = this.viewDate = '';
19091                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19092                 return;
19093             }
19094
19095             v = this.formatDate(d);
19096
19097             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19098
19099             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19100
19101             this.update();
19102
19103             this.fireEvent('select', this, this.date);
19104         }
19105     },
19106     
19107     getValue: function()
19108     {
19109         return this.formatDate(this.date);
19110     },
19111     
19112     fireKey: function(e)
19113     {
19114         if (!this.picker().isVisible()){
19115             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19116                 this.showPopup();
19117             }
19118             return;
19119         }
19120         
19121         var dateChanged = false,
19122         dir, day, month,
19123         newDate, newViewDate;
19124         
19125         switch(e.keyCode){
19126             case 27: // escape
19127                 this.hidePopup();
19128                 e.preventDefault();
19129                 break;
19130             case 37: // left
19131             case 39: // right
19132                 if (!this.keyboardNavigation) {
19133                     break;
19134                 }
19135                 dir = e.keyCode == 37 ? -1 : 1;
19136                 
19137                 if (e.ctrlKey){
19138                     newDate = this.moveYear(this.date, dir);
19139                     newViewDate = this.moveYear(this.viewDate, dir);
19140                 } else if (e.shiftKey){
19141                     newDate = this.moveMonth(this.date, dir);
19142                     newViewDate = this.moveMonth(this.viewDate, dir);
19143                 } else {
19144                     newDate = new Date(this.date);
19145                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19146                     newViewDate = new Date(this.viewDate);
19147                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19148                 }
19149                 if (this.dateWithinRange(newDate)){
19150                     this.date = newDate;
19151                     this.viewDate = newViewDate;
19152                     this.setValue(this.formatDate(this.date));
19153 //                    this.update();
19154                     e.preventDefault();
19155                     dateChanged = true;
19156                 }
19157                 break;
19158             case 38: // up
19159             case 40: // down
19160                 if (!this.keyboardNavigation) {
19161                     break;
19162                 }
19163                 dir = e.keyCode == 38 ? -1 : 1;
19164                 if (e.ctrlKey){
19165                     newDate = this.moveYear(this.date, dir);
19166                     newViewDate = this.moveYear(this.viewDate, dir);
19167                 } else if (e.shiftKey){
19168                     newDate = this.moveMonth(this.date, dir);
19169                     newViewDate = this.moveMonth(this.viewDate, dir);
19170                 } else {
19171                     newDate = new Date(this.date);
19172                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19173                     newViewDate = new Date(this.viewDate);
19174                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19175                 }
19176                 if (this.dateWithinRange(newDate)){
19177                     this.date = newDate;
19178                     this.viewDate = newViewDate;
19179                     this.setValue(this.formatDate(this.date));
19180 //                    this.update();
19181                     e.preventDefault();
19182                     dateChanged = true;
19183                 }
19184                 break;
19185             case 13: // enter
19186                 this.setValue(this.formatDate(this.date));
19187                 this.hidePopup();
19188                 e.preventDefault();
19189                 break;
19190             case 9: // tab
19191                 this.setValue(this.formatDate(this.date));
19192                 this.hidePopup();
19193                 break;
19194             case 16: // shift
19195             case 17: // ctrl
19196             case 18: // alt
19197                 break;
19198             default :
19199                 this.hidePopup();
19200                 
19201         }
19202     },
19203     
19204     
19205     onClick: function(e) 
19206     {
19207         e.stopPropagation();
19208         e.preventDefault();
19209         
19210         var target = e.getTarget();
19211         
19212         if(target.nodeName.toLowerCase() === 'i'){
19213             target = Roo.get(target).dom.parentNode;
19214         }
19215         
19216         var nodeName = target.nodeName;
19217         var className = target.className;
19218         var html = target.innerHTML;
19219         //Roo.log(nodeName);
19220         
19221         switch(nodeName.toLowerCase()) {
19222             case 'th':
19223                 switch(className) {
19224                     case 'switch':
19225                         this.showMode(1);
19226                         break;
19227                     case 'prev':
19228                     case 'next':
19229                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19230                         switch(this.viewMode){
19231                                 case 0:
19232                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19233                                         break;
19234                                 case 1:
19235                                 case 2:
19236                                         this.viewDate = this.moveYear(this.viewDate, dir);
19237                                         break;
19238                         }
19239                         this.fill();
19240                         break;
19241                     case 'today':
19242                         var date = new Date();
19243                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19244 //                        this.fill()
19245                         this.setValue(this.formatDate(this.date));
19246                         
19247                         this.hidePopup();
19248                         break;
19249                 }
19250                 break;
19251             case 'span':
19252                 if (className.indexOf('disabled') < 0) {
19253                     this.viewDate.setUTCDate(1);
19254                     if (className.indexOf('month') > -1) {
19255                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19256                     } else {
19257                         var year = parseInt(html, 10) || 0;
19258                         this.viewDate.setUTCFullYear(year);
19259                         
19260                     }
19261                     
19262                     if(this.singleMode){
19263                         this.setValue(this.formatDate(this.viewDate));
19264                         this.hidePopup();
19265                         return;
19266                     }
19267                     
19268                     this.showMode(-1);
19269                     this.fill();
19270                 }
19271                 break;
19272                 
19273             case 'td':
19274                 //Roo.log(className);
19275                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19276                     var day = parseInt(html, 10) || 1;
19277                     var year = this.viewDate.getUTCFullYear(),
19278                         month = this.viewDate.getUTCMonth();
19279
19280                     if (className.indexOf('old') > -1) {
19281                         if(month === 0 ){
19282                             month = 11;
19283                             year -= 1;
19284                         }else{
19285                             month -= 1;
19286                         }
19287                     } else if (className.indexOf('new') > -1) {
19288                         if (month == 11) {
19289                             month = 0;
19290                             year += 1;
19291                         } else {
19292                             month += 1;
19293                         }
19294                     }
19295                     //Roo.log([year,month,day]);
19296                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19297                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19298 //                    this.fill();
19299                     //Roo.log(this.formatDate(this.date));
19300                     this.setValue(this.formatDate(this.date));
19301                     this.hidePopup();
19302                 }
19303                 break;
19304         }
19305     },
19306     
19307     setStartDate: function(startDate)
19308     {
19309         this.startDate = startDate || -Infinity;
19310         if (this.startDate !== -Infinity) {
19311             this.startDate = this.parseDate(this.startDate);
19312         }
19313         this.update();
19314         this.updateNavArrows();
19315     },
19316
19317     setEndDate: function(endDate)
19318     {
19319         this.endDate = endDate || Infinity;
19320         if (this.endDate !== Infinity) {
19321             this.endDate = this.parseDate(this.endDate);
19322         }
19323         this.update();
19324         this.updateNavArrows();
19325     },
19326     
19327     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19328     {
19329         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19330         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19331             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19332         }
19333         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19334             return parseInt(d, 10);
19335         });
19336         this.update();
19337         this.updateNavArrows();
19338     },
19339     
19340     updateNavArrows: function() 
19341     {
19342         if(this.singleMode){
19343             return;
19344         }
19345         
19346         var d = new Date(this.viewDate),
19347         year = d.getUTCFullYear(),
19348         month = d.getUTCMonth();
19349         
19350         Roo.each(this.picker().select('.prev', true).elements, function(v){
19351             v.show();
19352             switch (this.viewMode) {
19353                 case 0:
19354
19355                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19356                         v.hide();
19357                     }
19358                     break;
19359                 case 1:
19360                 case 2:
19361                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19362                         v.hide();
19363                     }
19364                     break;
19365             }
19366         });
19367         
19368         Roo.each(this.picker().select('.next', true).elements, function(v){
19369             v.show();
19370             switch (this.viewMode) {
19371                 case 0:
19372
19373                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19374                         v.hide();
19375                     }
19376                     break;
19377                 case 1:
19378                 case 2:
19379                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19380                         v.hide();
19381                     }
19382                     break;
19383             }
19384         })
19385     },
19386     
19387     moveMonth: function(date, dir)
19388     {
19389         if (!dir) {
19390             return date;
19391         }
19392         var new_date = new Date(date.valueOf()),
19393         day = new_date.getUTCDate(),
19394         month = new_date.getUTCMonth(),
19395         mag = Math.abs(dir),
19396         new_month, test;
19397         dir = dir > 0 ? 1 : -1;
19398         if (mag == 1){
19399             test = dir == -1
19400             // If going back one month, make sure month is not current month
19401             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19402             ? function(){
19403                 return new_date.getUTCMonth() == month;
19404             }
19405             // If going forward one month, make sure month is as expected
19406             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19407             : function(){
19408                 return new_date.getUTCMonth() != new_month;
19409             };
19410             new_month = month + dir;
19411             new_date.setUTCMonth(new_month);
19412             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19413             if (new_month < 0 || new_month > 11) {
19414                 new_month = (new_month + 12) % 12;
19415             }
19416         } else {
19417             // For magnitudes >1, move one month at a time...
19418             for (var i=0; i<mag; i++) {
19419                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19420                 new_date = this.moveMonth(new_date, dir);
19421             }
19422             // ...then reset the day, keeping it in the new month
19423             new_month = new_date.getUTCMonth();
19424             new_date.setUTCDate(day);
19425             test = function(){
19426                 return new_month != new_date.getUTCMonth();
19427             };
19428         }
19429         // Common date-resetting loop -- if date is beyond end of month, make it
19430         // end of month
19431         while (test()){
19432             new_date.setUTCDate(--day);
19433             new_date.setUTCMonth(new_month);
19434         }
19435         return new_date;
19436     },
19437
19438     moveYear: function(date, dir)
19439     {
19440         return this.moveMonth(date, dir*12);
19441     },
19442
19443     dateWithinRange: function(date)
19444     {
19445         return date >= this.startDate && date <= this.endDate;
19446     },
19447
19448     
19449     remove: function() 
19450     {
19451         this.picker().remove();
19452     },
19453     
19454     validateValue : function(value)
19455     {
19456         if(this.getVisibilityEl().hasClass('hidden')){
19457             return true;
19458         }
19459         
19460         if(value.length < 1)  {
19461             if(this.allowBlank){
19462                 return true;
19463             }
19464             return false;
19465         }
19466         
19467         if(value.length < this.minLength){
19468             return false;
19469         }
19470         if(value.length > this.maxLength){
19471             return false;
19472         }
19473         if(this.vtype){
19474             var vt = Roo.form.VTypes;
19475             if(!vt[this.vtype](value, this)){
19476                 return false;
19477             }
19478         }
19479         if(typeof this.validator == "function"){
19480             var msg = this.validator(value);
19481             if(msg !== true){
19482                 return false;
19483             }
19484         }
19485         
19486         if(this.regex && !this.regex.test(value)){
19487             return false;
19488         }
19489         
19490         if(typeof(this.parseDate(value)) == 'undefined'){
19491             return false;
19492         }
19493         
19494         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19495             return false;
19496         }      
19497         
19498         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19499             return false;
19500         } 
19501         
19502         
19503         return true;
19504     },
19505     
19506     reset : function()
19507     {
19508         this.date = this.viewDate = '';
19509         
19510         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19511     }
19512    
19513 });
19514
19515 Roo.apply(Roo.bootstrap.DateField,  {
19516     
19517     head : {
19518         tag: 'thead',
19519         cn: [
19520         {
19521             tag: 'tr',
19522             cn: [
19523             {
19524                 tag: 'th',
19525                 cls: 'prev',
19526                 html: '<i class="fa fa-arrow-left"/>'
19527             },
19528             {
19529                 tag: 'th',
19530                 cls: 'switch',
19531                 colspan: '5'
19532             },
19533             {
19534                 tag: 'th',
19535                 cls: 'next',
19536                 html: '<i class="fa fa-arrow-right"/>'
19537             }
19538
19539             ]
19540         }
19541         ]
19542     },
19543     
19544     content : {
19545         tag: 'tbody',
19546         cn: [
19547         {
19548             tag: 'tr',
19549             cn: [
19550             {
19551                 tag: 'td',
19552                 colspan: '7'
19553             }
19554             ]
19555         }
19556         ]
19557     },
19558     
19559     footer : {
19560         tag: 'tfoot',
19561         cn: [
19562         {
19563             tag: 'tr',
19564             cn: [
19565             {
19566                 tag: 'th',
19567                 colspan: '7',
19568                 cls: 'today'
19569             }
19570                     
19571             ]
19572         }
19573         ]
19574     },
19575     
19576     dates:{
19577         en: {
19578             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19579             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19580             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19581             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19582             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19583             today: "Today"
19584         }
19585     },
19586     
19587     modes: [
19588     {
19589         clsName: 'days',
19590         navFnc: 'Month',
19591         navStep: 1
19592     },
19593     {
19594         clsName: 'months',
19595         navFnc: 'FullYear',
19596         navStep: 1
19597     },
19598     {
19599         clsName: 'years',
19600         navFnc: 'FullYear',
19601         navStep: 10
19602     }]
19603 });
19604
19605 Roo.apply(Roo.bootstrap.DateField,  {
19606   
19607     template : {
19608         tag: 'div',
19609         cls: 'datepicker dropdown-menu roo-dynamic',
19610         cn: [
19611         {
19612             tag: 'div',
19613             cls: 'datepicker-days',
19614             cn: [
19615             {
19616                 tag: 'table',
19617                 cls: 'table-condensed',
19618                 cn:[
19619                 Roo.bootstrap.DateField.head,
19620                 {
19621                     tag: 'tbody'
19622                 },
19623                 Roo.bootstrap.DateField.footer
19624                 ]
19625             }
19626             ]
19627         },
19628         {
19629             tag: 'div',
19630             cls: 'datepicker-months',
19631             cn: [
19632             {
19633                 tag: 'table',
19634                 cls: 'table-condensed',
19635                 cn:[
19636                 Roo.bootstrap.DateField.head,
19637                 Roo.bootstrap.DateField.content,
19638                 Roo.bootstrap.DateField.footer
19639                 ]
19640             }
19641             ]
19642         },
19643         {
19644             tag: 'div',
19645             cls: 'datepicker-years',
19646             cn: [
19647             {
19648                 tag: 'table',
19649                 cls: 'table-condensed',
19650                 cn:[
19651                 Roo.bootstrap.DateField.head,
19652                 Roo.bootstrap.DateField.content,
19653                 Roo.bootstrap.DateField.footer
19654                 ]
19655             }
19656             ]
19657         }
19658         ]
19659     }
19660 });
19661
19662  
19663
19664  /*
19665  * - LGPL
19666  *
19667  * TimeField
19668  * 
19669  */
19670
19671 /**
19672  * @class Roo.bootstrap.TimeField
19673  * @extends Roo.bootstrap.Input
19674  * Bootstrap DateField class
19675  * 
19676  * 
19677  * @constructor
19678  * Create a new TimeField
19679  * @param {Object} config The config object
19680  */
19681
19682 Roo.bootstrap.TimeField = function(config){
19683     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19684     this.addEvents({
19685             /**
19686              * @event show
19687              * Fires when this field show.
19688              * @param {Roo.bootstrap.DateField} thisthis
19689              * @param {Mixed} date The date value
19690              */
19691             show : true,
19692             /**
19693              * @event show
19694              * Fires when this field hide.
19695              * @param {Roo.bootstrap.DateField} this
19696              * @param {Mixed} date The date value
19697              */
19698             hide : true,
19699             /**
19700              * @event select
19701              * Fires when select a date.
19702              * @param {Roo.bootstrap.DateField} this
19703              * @param {Mixed} date The date value
19704              */
19705             select : true
19706         });
19707 };
19708
19709 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19710     
19711     /**
19712      * @cfg {String} format
19713      * The default time format string which can be overriden for localization support.  The format must be
19714      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19715      */
19716     format : "H:i",
19717        
19718     onRender: function(ct, position)
19719     {
19720         
19721         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19722                 
19723         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19724         
19725         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19726         
19727         this.pop = this.picker().select('>.datepicker-time',true).first();
19728         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19729         
19730         this.picker().on('mousedown', this.onMousedown, this);
19731         this.picker().on('click', this.onClick, this);
19732         
19733         this.picker().addClass('datepicker-dropdown');
19734     
19735         this.fillTime();
19736         this.update();
19737             
19738         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19739         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19740         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19741         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19742         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19743         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19744
19745     },
19746     
19747     fireKey: function(e){
19748         if (!this.picker().isVisible()){
19749             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19750                 this.show();
19751             }
19752             return;
19753         }
19754
19755         e.preventDefault();
19756         
19757         switch(e.keyCode){
19758             case 27: // escape
19759                 this.hide();
19760                 break;
19761             case 37: // left
19762             case 39: // right
19763                 this.onTogglePeriod();
19764                 break;
19765             case 38: // up
19766                 this.onIncrementMinutes();
19767                 break;
19768             case 40: // down
19769                 this.onDecrementMinutes();
19770                 break;
19771             case 13: // enter
19772             case 9: // tab
19773                 this.setTime();
19774                 break;
19775         }
19776     },
19777     
19778     onClick: function(e) {
19779         e.stopPropagation();
19780         e.preventDefault();
19781     },
19782     
19783     picker : function()
19784     {
19785         return this.el.select('.datepicker', true).first();
19786     },
19787     
19788     fillTime: function()
19789     {    
19790         var time = this.pop.select('tbody', true).first();
19791         
19792         time.dom.innerHTML = '';
19793         
19794         time.createChild({
19795             tag: 'tr',
19796             cn: [
19797                 {
19798                     tag: 'td',
19799                     cn: [
19800                         {
19801                             tag: 'a',
19802                             href: '#',
19803                             cls: 'btn',
19804                             cn: [
19805                                 {
19806                                     tag: 'span',
19807                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19808                                 }
19809                             ]
19810                         } 
19811                     ]
19812                 },
19813                 {
19814                     tag: 'td',
19815                     cls: 'separator'
19816                 },
19817                 {
19818                     tag: 'td',
19819                     cn: [
19820                         {
19821                             tag: 'a',
19822                             href: '#',
19823                             cls: 'btn',
19824                             cn: [
19825                                 {
19826                                     tag: 'span',
19827                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19828                                 }
19829                             ]
19830                         }
19831                     ]
19832                 },
19833                 {
19834                     tag: 'td',
19835                     cls: 'separator'
19836                 }
19837             ]
19838         });
19839         
19840         time.createChild({
19841             tag: 'tr',
19842             cn: [
19843                 {
19844                     tag: 'td',
19845                     cn: [
19846                         {
19847                             tag: 'span',
19848                             cls: 'timepicker-hour',
19849                             html: '00'
19850                         }  
19851                     ]
19852                 },
19853                 {
19854                     tag: 'td',
19855                     cls: 'separator',
19856                     html: ':'
19857                 },
19858                 {
19859                     tag: 'td',
19860                     cn: [
19861                         {
19862                             tag: 'span',
19863                             cls: 'timepicker-minute',
19864                             html: '00'
19865                         }  
19866                     ]
19867                 },
19868                 {
19869                     tag: 'td',
19870                     cls: 'separator'
19871                 },
19872                 {
19873                     tag: 'td',
19874                     cn: [
19875                         {
19876                             tag: 'button',
19877                             type: 'button',
19878                             cls: 'btn btn-primary period',
19879                             html: 'AM'
19880                             
19881                         }
19882                     ]
19883                 }
19884             ]
19885         });
19886         
19887         time.createChild({
19888             tag: 'tr',
19889             cn: [
19890                 {
19891                     tag: 'td',
19892                     cn: [
19893                         {
19894                             tag: 'a',
19895                             href: '#',
19896                             cls: 'btn',
19897                             cn: [
19898                                 {
19899                                     tag: 'span',
19900                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19901                                 }
19902                             ]
19903                         }
19904                     ]
19905                 },
19906                 {
19907                     tag: 'td',
19908                     cls: 'separator'
19909                 },
19910                 {
19911                     tag: 'td',
19912                     cn: [
19913                         {
19914                             tag: 'a',
19915                             href: '#',
19916                             cls: 'btn',
19917                             cn: [
19918                                 {
19919                                     tag: 'span',
19920                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19921                                 }
19922                             ]
19923                         }
19924                     ]
19925                 },
19926                 {
19927                     tag: 'td',
19928                     cls: 'separator'
19929                 }
19930             ]
19931         });
19932         
19933     },
19934     
19935     update: function()
19936     {
19937         
19938         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19939         
19940         this.fill();
19941     },
19942     
19943     fill: function() 
19944     {
19945         var hours = this.time.getHours();
19946         var minutes = this.time.getMinutes();
19947         var period = 'AM';
19948         
19949         if(hours > 11){
19950             period = 'PM';
19951         }
19952         
19953         if(hours == 0){
19954             hours = 12;
19955         }
19956         
19957         
19958         if(hours > 12){
19959             hours = hours - 12;
19960         }
19961         
19962         if(hours < 10){
19963             hours = '0' + hours;
19964         }
19965         
19966         if(minutes < 10){
19967             minutes = '0' + minutes;
19968         }
19969         
19970         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19971         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19972         this.pop.select('button', true).first().dom.innerHTML = period;
19973         
19974     },
19975     
19976     place: function()
19977     {   
19978         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19979         
19980         var cls = ['bottom'];
19981         
19982         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19983             cls.pop();
19984             cls.push('top');
19985         }
19986         
19987         cls.push('right');
19988         
19989         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19990             cls.pop();
19991             cls.push('left');
19992         }
19993         
19994         this.picker().addClass(cls.join('-'));
19995         
19996         var _this = this;
19997         
19998         Roo.each(cls, function(c){
19999             if(c == 'bottom'){
20000                 _this.picker().setTop(_this.inputEl().getHeight());
20001                 return;
20002             }
20003             if(c == 'top'){
20004                 _this.picker().setTop(0 - _this.picker().getHeight());
20005                 return;
20006             }
20007             
20008             if(c == 'left'){
20009                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20010                 return;
20011             }
20012             if(c == 'right'){
20013                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20014                 return;
20015             }
20016         });
20017         
20018     },
20019   
20020     onFocus : function()
20021     {
20022         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20023         this.show();
20024     },
20025     
20026     onBlur : function()
20027     {
20028         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20029         this.hide();
20030     },
20031     
20032     show : function()
20033     {
20034         this.picker().show();
20035         this.pop.show();
20036         this.update();
20037         this.place();
20038         
20039         this.fireEvent('show', this, this.date);
20040     },
20041     
20042     hide : function()
20043     {
20044         this.picker().hide();
20045         this.pop.hide();
20046         
20047         this.fireEvent('hide', this, this.date);
20048     },
20049     
20050     setTime : function()
20051     {
20052         this.hide();
20053         this.setValue(this.time.format(this.format));
20054         
20055         this.fireEvent('select', this, this.date);
20056         
20057         
20058     },
20059     
20060     onMousedown: function(e){
20061         e.stopPropagation();
20062         e.preventDefault();
20063     },
20064     
20065     onIncrementHours: function()
20066     {
20067         Roo.log('onIncrementHours');
20068         this.time = this.time.add(Date.HOUR, 1);
20069         this.update();
20070         
20071     },
20072     
20073     onDecrementHours: function()
20074     {
20075         Roo.log('onDecrementHours');
20076         this.time = this.time.add(Date.HOUR, -1);
20077         this.update();
20078     },
20079     
20080     onIncrementMinutes: function()
20081     {
20082         Roo.log('onIncrementMinutes');
20083         this.time = this.time.add(Date.MINUTE, 1);
20084         this.update();
20085     },
20086     
20087     onDecrementMinutes: function()
20088     {
20089         Roo.log('onDecrementMinutes');
20090         this.time = this.time.add(Date.MINUTE, -1);
20091         this.update();
20092     },
20093     
20094     onTogglePeriod: function()
20095     {
20096         Roo.log('onTogglePeriod');
20097         this.time = this.time.add(Date.HOUR, 12);
20098         this.update();
20099     }
20100     
20101    
20102 });
20103
20104 Roo.apply(Roo.bootstrap.TimeField,  {
20105     
20106     content : {
20107         tag: 'tbody',
20108         cn: [
20109             {
20110                 tag: 'tr',
20111                 cn: [
20112                 {
20113                     tag: 'td',
20114                     colspan: '7'
20115                 }
20116                 ]
20117             }
20118         ]
20119     },
20120     
20121     footer : {
20122         tag: 'tfoot',
20123         cn: [
20124             {
20125                 tag: 'tr',
20126                 cn: [
20127                 {
20128                     tag: 'th',
20129                     colspan: '7',
20130                     cls: '',
20131                     cn: [
20132                         {
20133                             tag: 'button',
20134                             cls: 'btn btn-info ok',
20135                             html: 'OK'
20136                         }
20137                     ]
20138                 }
20139
20140                 ]
20141             }
20142         ]
20143     }
20144 });
20145
20146 Roo.apply(Roo.bootstrap.TimeField,  {
20147   
20148     template : {
20149         tag: 'div',
20150         cls: 'datepicker dropdown-menu',
20151         cn: [
20152             {
20153                 tag: 'div',
20154                 cls: 'datepicker-time',
20155                 cn: [
20156                 {
20157                     tag: 'table',
20158                     cls: 'table-condensed',
20159                     cn:[
20160                     Roo.bootstrap.TimeField.content,
20161                     Roo.bootstrap.TimeField.footer
20162                     ]
20163                 }
20164                 ]
20165             }
20166         ]
20167     }
20168 });
20169
20170  
20171
20172  /*
20173  * - LGPL
20174  *
20175  * MonthField
20176  * 
20177  */
20178
20179 /**
20180  * @class Roo.bootstrap.MonthField
20181  * @extends Roo.bootstrap.Input
20182  * Bootstrap MonthField class
20183  * 
20184  * @cfg {String} language default en
20185  * 
20186  * @constructor
20187  * Create a new MonthField
20188  * @param {Object} config The config object
20189  */
20190
20191 Roo.bootstrap.MonthField = function(config){
20192     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20193     
20194     this.addEvents({
20195         /**
20196          * @event show
20197          * Fires when this field show.
20198          * @param {Roo.bootstrap.MonthField} this
20199          * @param {Mixed} date The date value
20200          */
20201         show : true,
20202         /**
20203          * @event show
20204          * Fires when this field hide.
20205          * @param {Roo.bootstrap.MonthField} this
20206          * @param {Mixed} date The date value
20207          */
20208         hide : true,
20209         /**
20210          * @event select
20211          * Fires when select a date.
20212          * @param {Roo.bootstrap.MonthField} this
20213          * @param {String} oldvalue The old value
20214          * @param {String} newvalue The new value
20215          */
20216         select : true
20217     });
20218 };
20219
20220 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20221     
20222     onRender: function(ct, position)
20223     {
20224         
20225         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20226         
20227         this.language = this.language || 'en';
20228         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20229         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20230         
20231         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20232         this.isInline = false;
20233         this.isInput = true;
20234         this.component = this.el.select('.add-on', true).first() || false;
20235         this.component = (this.component && this.component.length === 0) ? false : this.component;
20236         this.hasInput = this.component && this.inputEL().length;
20237         
20238         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20239         
20240         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20241         
20242         this.picker().on('mousedown', this.onMousedown, this);
20243         this.picker().on('click', this.onClick, this);
20244         
20245         this.picker().addClass('datepicker-dropdown');
20246         
20247         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20248             v.setStyle('width', '189px');
20249         });
20250         
20251         this.fillMonths();
20252         
20253         this.update();
20254         
20255         if(this.isInline) {
20256             this.show();
20257         }
20258         
20259     },
20260     
20261     setValue: function(v, suppressEvent)
20262     {   
20263         var o = this.getValue();
20264         
20265         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20266         
20267         this.update();
20268
20269         if(suppressEvent !== true){
20270             this.fireEvent('select', this, o, v);
20271         }
20272         
20273     },
20274     
20275     getValue: function()
20276     {
20277         return this.value;
20278     },
20279     
20280     onClick: function(e) 
20281     {
20282         e.stopPropagation();
20283         e.preventDefault();
20284         
20285         var target = e.getTarget();
20286         
20287         if(target.nodeName.toLowerCase() === 'i'){
20288             target = Roo.get(target).dom.parentNode;
20289         }
20290         
20291         var nodeName = target.nodeName;
20292         var className = target.className;
20293         var html = target.innerHTML;
20294         
20295         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20296             return;
20297         }
20298         
20299         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20300         
20301         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20302         
20303         this.hide();
20304                         
20305     },
20306     
20307     picker : function()
20308     {
20309         return this.pickerEl;
20310     },
20311     
20312     fillMonths: function()
20313     {    
20314         var i = 0;
20315         var months = this.picker().select('>.datepicker-months td', true).first();
20316         
20317         months.dom.innerHTML = '';
20318         
20319         while (i < 12) {
20320             var month = {
20321                 tag: 'span',
20322                 cls: 'month',
20323                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20324             };
20325             
20326             months.createChild(month);
20327         }
20328         
20329     },
20330     
20331     update: function()
20332     {
20333         var _this = this;
20334         
20335         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20336             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20337         }
20338         
20339         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20340             e.removeClass('active');
20341             
20342             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20343                 e.addClass('active');
20344             }
20345         })
20346     },
20347     
20348     place: function()
20349     {
20350         if(this.isInline) {
20351             return;
20352         }
20353         
20354         this.picker().removeClass(['bottom', 'top']);
20355         
20356         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20357             /*
20358              * place to the top of element!
20359              *
20360              */
20361             
20362             this.picker().addClass('top');
20363             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20364             
20365             return;
20366         }
20367         
20368         this.picker().addClass('bottom');
20369         
20370         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20371     },
20372     
20373     onFocus : function()
20374     {
20375         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20376         this.show();
20377     },
20378     
20379     onBlur : function()
20380     {
20381         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20382         
20383         var d = this.inputEl().getValue();
20384         
20385         this.setValue(d);
20386                 
20387         this.hide();
20388     },
20389     
20390     show : function()
20391     {
20392         this.picker().show();
20393         this.picker().select('>.datepicker-months', true).first().show();
20394         this.update();
20395         this.place();
20396         
20397         this.fireEvent('show', this, this.date);
20398     },
20399     
20400     hide : function()
20401     {
20402         if(this.isInline) {
20403             return;
20404         }
20405         this.picker().hide();
20406         this.fireEvent('hide', this, this.date);
20407         
20408     },
20409     
20410     onMousedown: function(e)
20411     {
20412         e.stopPropagation();
20413         e.preventDefault();
20414     },
20415     
20416     keyup: function(e)
20417     {
20418         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20419         this.update();
20420     },
20421
20422     fireKey: function(e)
20423     {
20424         if (!this.picker().isVisible()){
20425             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20426                 this.show();
20427             }
20428             return;
20429         }
20430         
20431         var dir;
20432         
20433         switch(e.keyCode){
20434             case 27: // escape
20435                 this.hide();
20436                 e.preventDefault();
20437                 break;
20438             case 37: // left
20439             case 39: // right
20440                 dir = e.keyCode == 37 ? -1 : 1;
20441                 
20442                 this.vIndex = this.vIndex + dir;
20443                 
20444                 if(this.vIndex < 0){
20445                     this.vIndex = 0;
20446                 }
20447                 
20448                 if(this.vIndex > 11){
20449                     this.vIndex = 11;
20450                 }
20451                 
20452                 if(isNaN(this.vIndex)){
20453                     this.vIndex = 0;
20454                 }
20455                 
20456                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20457                 
20458                 break;
20459             case 38: // up
20460             case 40: // down
20461                 
20462                 dir = e.keyCode == 38 ? -1 : 1;
20463                 
20464                 this.vIndex = this.vIndex + dir * 4;
20465                 
20466                 if(this.vIndex < 0){
20467                     this.vIndex = 0;
20468                 }
20469                 
20470                 if(this.vIndex > 11){
20471                     this.vIndex = 11;
20472                 }
20473                 
20474                 if(isNaN(this.vIndex)){
20475                     this.vIndex = 0;
20476                 }
20477                 
20478                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20479                 break;
20480                 
20481             case 13: // enter
20482                 
20483                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20484                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20485                 }
20486                 
20487                 this.hide();
20488                 e.preventDefault();
20489                 break;
20490             case 9: // tab
20491                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20492                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20493                 }
20494                 this.hide();
20495                 break;
20496             case 16: // shift
20497             case 17: // ctrl
20498             case 18: // alt
20499                 break;
20500             default :
20501                 this.hide();
20502                 
20503         }
20504     },
20505     
20506     remove: function() 
20507     {
20508         this.picker().remove();
20509     }
20510    
20511 });
20512
20513 Roo.apply(Roo.bootstrap.MonthField,  {
20514     
20515     content : {
20516         tag: 'tbody',
20517         cn: [
20518         {
20519             tag: 'tr',
20520             cn: [
20521             {
20522                 tag: 'td',
20523                 colspan: '7'
20524             }
20525             ]
20526         }
20527         ]
20528     },
20529     
20530     dates:{
20531         en: {
20532             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20533             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20534         }
20535     }
20536 });
20537
20538 Roo.apply(Roo.bootstrap.MonthField,  {
20539   
20540     template : {
20541         tag: 'div',
20542         cls: 'datepicker dropdown-menu roo-dynamic',
20543         cn: [
20544             {
20545                 tag: 'div',
20546                 cls: 'datepicker-months',
20547                 cn: [
20548                 {
20549                     tag: 'table',
20550                     cls: 'table-condensed',
20551                     cn:[
20552                         Roo.bootstrap.DateField.content
20553                     ]
20554                 }
20555                 ]
20556             }
20557         ]
20558     }
20559 });
20560
20561  
20562
20563  
20564  /*
20565  * - LGPL
20566  *
20567  * CheckBox
20568  * 
20569  */
20570
20571 /**
20572  * @class Roo.bootstrap.CheckBox
20573  * @extends Roo.bootstrap.Input
20574  * Bootstrap CheckBox class
20575  * 
20576  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20577  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20578  * @cfg {String} boxLabel The text that appears beside the checkbox
20579  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20580  * @cfg {Boolean} checked initnal the element
20581  * @cfg {Boolean} inline inline the element (default false)
20582  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20583  * @cfg {String} tooltip label tooltip
20584  * 
20585  * @constructor
20586  * Create a new CheckBox
20587  * @param {Object} config The config object
20588  */
20589
20590 Roo.bootstrap.CheckBox = function(config){
20591     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20592    
20593     this.addEvents({
20594         /**
20595         * @event check
20596         * Fires when the element is checked or unchecked.
20597         * @param {Roo.bootstrap.CheckBox} this This input
20598         * @param {Boolean} checked The new checked value
20599         */
20600        check : true,
20601        /**
20602         * @event click
20603         * Fires when the element is click.
20604         * @param {Roo.bootstrap.CheckBox} this This input
20605         */
20606        click : true
20607     });
20608     
20609 };
20610
20611 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20612   
20613     inputType: 'checkbox',
20614     inputValue: 1,
20615     valueOff: 0,
20616     boxLabel: false,
20617     checked: false,
20618     weight : false,
20619     inline: false,
20620     tooltip : '',
20621     
20622     getAutoCreate : function()
20623     {
20624         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20625         
20626         var id = Roo.id();
20627         
20628         var cfg = {};
20629         
20630         cfg.cls = 'form-group ' + this.inputType; //input-group
20631         
20632         if(this.inline){
20633             cfg.cls += ' ' + this.inputType + '-inline';
20634         }
20635         
20636         var input =  {
20637             tag: 'input',
20638             id : id,
20639             type : this.inputType,
20640             value : this.inputValue,
20641             cls : 'roo-' + this.inputType, //'form-box',
20642             placeholder : this.placeholder || ''
20643             
20644         };
20645         
20646         if(this.inputType != 'radio'){
20647             var hidden =  {
20648                 tag: 'input',
20649                 type : 'hidden',
20650                 cls : 'roo-hidden-value',
20651                 value : this.checked ? this.inputValue : this.valueOff
20652             };
20653         }
20654         
20655             
20656         if (this.weight) { // Validity check?
20657             cfg.cls += " " + this.inputType + "-" + this.weight;
20658         }
20659         
20660         if (this.disabled) {
20661             input.disabled=true;
20662         }
20663         
20664         if(this.checked){
20665             input.checked = this.checked;
20666         }
20667         
20668         if (this.name) {
20669             
20670             input.name = this.name;
20671             
20672             if(this.inputType != 'radio'){
20673                 hidden.name = this.name;
20674                 input.name = '_hidden_' + this.name;
20675             }
20676         }
20677         
20678         if (this.size) {
20679             input.cls += ' input-' + this.size;
20680         }
20681         
20682         var settings=this;
20683         
20684         ['xs','sm','md','lg'].map(function(size){
20685             if (settings[size]) {
20686                 cfg.cls += ' col-' + size + '-' + settings[size];
20687             }
20688         });
20689         
20690         var inputblock = input;
20691          
20692         if (this.before || this.after) {
20693             
20694             inputblock = {
20695                 cls : 'input-group',
20696                 cn :  [] 
20697             };
20698             
20699             if (this.before) {
20700                 inputblock.cn.push({
20701                     tag :'span',
20702                     cls : 'input-group-addon',
20703                     html : this.before
20704                 });
20705             }
20706             
20707             inputblock.cn.push(input);
20708             
20709             if(this.inputType != 'radio'){
20710                 inputblock.cn.push(hidden);
20711             }
20712             
20713             if (this.after) {
20714                 inputblock.cn.push({
20715                     tag :'span',
20716                     cls : 'input-group-addon',
20717                     html : this.after
20718                 });
20719             }
20720             
20721         }
20722         
20723         if (align ==='left' && this.fieldLabel.length) {
20724 //                Roo.log("left and has label");
20725             cfg.cn = [
20726                 {
20727                     tag: 'label',
20728                     'for' :  id,
20729                     cls : 'control-label',
20730                     html : this.fieldLabel
20731                 },
20732                 {
20733                     cls : "", 
20734                     cn: [
20735                         inputblock
20736                     ]
20737                 }
20738             ];
20739             
20740             if(this.labelWidth > 12){
20741                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20742             }
20743             
20744             if(this.labelWidth < 13 && this.labelmd == 0){
20745                 this.labelmd = this.labelWidth;
20746             }
20747             
20748             if(this.labellg > 0){
20749                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20750                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20751             }
20752             
20753             if(this.labelmd > 0){
20754                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20755                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20756             }
20757             
20758             if(this.labelsm > 0){
20759                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20760                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20761             }
20762             
20763             if(this.labelxs > 0){
20764                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20765                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20766             }
20767             
20768         } else if ( this.fieldLabel.length) {
20769 //                Roo.log(" label");
20770                 cfg.cn = [
20771                    
20772                     {
20773                         tag: this.boxLabel ? 'span' : 'label',
20774                         'for': id,
20775                         cls: 'control-label box-input-label',
20776                         //cls : 'input-group-addon',
20777                         html : this.fieldLabel
20778                     },
20779                     
20780                     inputblock
20781                     
20782                 ];
20783
20784         } else {
20785             
20786 //                Roo.log(" no label && no align");
20787                 cfg.cn = [  inputblock ] ;
20788                 
20789                 
20790         }
20791         
20792         if(this.boxLabel){
20793              var boxLabelCfg = {
20794                 tag: 'label',
20795                 //'for': id, // box label is handled by onclick - so no for...
20796                 cls: 'box-label',
20797                 html: this.boxLabel
20798             };
20799             
20800             if(this.tooltip){
20801                 boxLabelCfg.tooltip = this.tooltip;
20802             }
20803              
20804             cfg.cn.push(boxLabelCfg);
20805         }
20806         
20807         if(this.inputType != 'radio'){
20808             cfg.cn.push(hidden);
20809         }
20810         
20811         return cfg;
20812         
20813     },
20814     
20815     /**
20816      * return the real input element.
20817      */
20818     inputEl: function ()
20819     {
20820         return this.el.select('input.roo-' + this.inputType,true).first();
20821     },
20822     hiddenEl: function ()
20823     {
20824         return this.el.select('input.roo-hidden-value',true).first();
20825     },
20826     
20827     labelEl: function()
20828     {
20829         return this.el.select('label.control-label',true).first();
20830     },
20831     /* depricated... */
20832     
20833     label: function()
20834     {
20835         return this.labelEl();
20836     },
20837     
20838     boxLabelEl: function()
20839     {
20840         return this.el.select('label.box-label',true).first();
20841     },
20842     
20843     initEvents : function()
20844     {
20845 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20846         
20847         this.inputEl().on('click', this.onClick,  this);
20848         
20849         if (this.boxLabel) { 
20850             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20851         }
20852         
20853         this.startValue = this.getValue();
20854         
20855         if(this.groupId){
20856             Roo.bootstrap.CheckBox.register(this);
20857         }
20858     },
20859     
20860     onClick : function(e)
20861     {   
20862         if(this.fireEvent('click', this, e) !== false){
20863             this.setChecked(!this.checked);
20864         }
20865         
20866     },
20867     
20868     setChecked : function(state,suppressEvent)
20869     {
20870         this.startValue = this.getValue();
20871
20872         if(this.inputType == 'radio'){
20873             
20874             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20875                 e.dom.checked = false;
20876             });
20877             
20878             this.inputEl().dom.checked = true;
20879             
20880             this.inputEl().dom.value = this.inputValue;
20881             
20882             if(suppressEvent !== true){
20883                 this.fireEvent('check', this, true);
20884             }
20885             
20886             this.validate();
20887             
20888             return;
20889         }
20890         
20891         this.checked = state;
20892         
20893         this.inputEl().dom.checked = state;
20894         
20895         
20896         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20897         
20898         if(suppressEvent !== true){
20899             this.fireEvent('check', this, state);
20900         }
20901         
20902         this.validate();
20903     },
20904     
20905     getValue : function()
20906     {
20907         if(this.inputType == 'radio'){
20908             return this.getGroupValue();
20909         }
20910         
20911         return this.hiddenEl().dom.value;
20912         
20913     },
20914     
20915     getGroupValue : function()
20916     {
20917         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20918             return '';
20919         }
20920         
20921         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20922     },
20923     
20924     setValue : function(v,suppressEvent)
20925     {
20926         if(this.inputType == 'radio'){
20927             this.setGroupValue(v, suppressEvent);
20928             return;
20929         }
20930         
20931         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20932         
20933         this.validate();
20934     },
20935     
20936     setGroupValue : function(v, suppressEvent)
20937     {
20938         this.startValue = this.getValue();
20939         
20940         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20941             e.dom.checked = false;
20942             
20943             if(e.dom.value == v){
20944                 e.dom.checked = true;
20945             }
20946         });
20947         
20948         if(suppressEvent !== true){
20949             this.fireEvent('check', this, true);
20950         }
20951
20952         this.validate();
20953         
20954         return;
20955     },
20956     
20957     validate : function()
20958     {
20959         if(this.getVisibilityEl().hasClass('hidden')){
20960             return true;
20961         }
20962         
20963         if(
20964                 this.disabled || 
20965                 (this.inputType == 'radio' && this.validateRadio()) ||
20966                 (this.inputType == 'checkbox' && this.validateCheckbox())
20967         ){
20968             this.markValid();
20969             return true;
20970         }
20971         
20972         this.markInvalid();
20973         return false;
20974     },
20975     
20976     validateRadio : function()
20977     {
20978         if(this.getVisibilityEl().hasClass('hidden')){
20979             return true;
20980         }
20981         
20982         if(this.allowBlank){
20983             return true;
20984         }
20985         
20986         var valid = false;
20987         
20988         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20989             if(!e.dom.checked){
20990                 return;
20991             }
20992             
20993             valid = true;
20994             
20995             return false;
20996         });
20997         
20998         return valid;
20999     },
21000     
21001     validateCheckbox : function()
21002     {
21003         if(!this.groupId){
21004             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21005             //return (this.getValue() == this.inputValue) ? true : false;
21006         }
21007         
21008         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21009         
21010         if(!group){
21011             return false;
21012         }
21013         
21014         var r = false;
21015         
21016         for(var i in group){
21017             if(group[i].el.isVisible(true)){
21018                 r = false;
21019                 break;
21020             }
21021             
21022             r = true;
21023         }
21024         
21025         for(var i in group){
21026             if(r){
21027                 break;
21028             }
21029             
21030             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21031         }
21032         
21033         return r;
21034     },
21035     
21036     /**
21037      * Mark this field as valid
21038      */
21039     markValid : function()
21040     {
21041         var _this = this;
21042         
21043         this.fireEvent('valid', this);
21044         
21045         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21046         
21047         if(this.groupId){
21048             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21049         }
21050         
21051         if(label){
21052             label.markValid();
21053         }
21054
21055         if(this.inputType == 'radio'){
21056             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21057                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21058                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21059             });
21060             
21061             return;
21062         }
21063
21064         if(!this.groupId){
21065             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21066             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21067             return;
21068         }
21069         
21070         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21071         
21072         if(!group){
21073             return;
21074         }
21075         
21076         for(var i in group){
21077             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21078             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21079         }
21080     },
21081     
21082      /**
21083      * Mark this field as invalid
21084      * @param {String} msg The validation message
21085      */
21086     markInvalid : function(msg)
21087     {
21088         if(this.allowBlank){
21089             return;
21090         }
21091         
21092         var _this = this;
21093         
21094         this.fireEvent('invalid', this, msg);
21095         
21096         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21097         
21098         if(this.groupId){
21099             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21100         }
21101         
21102         if(label){
21103             label.markInvalid();
21104         }
21105             
21106         if(this.inputType == 'radio'){
21107             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21108                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21109                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21110             });
21111             
21112             return;
21113         }
21114         
21115         if(!this.groupId){
21116             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21117             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21118             return;
21119         }
21120         
21121         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21122         
21123         if(!group){
21124             return;
21125         }
21126         
21127         for(var i in group){
21128             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21129             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21130         }
21131         
21132     },
21133     
21134     clearInvalid : function()
21135     {
21136         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21137         
21138         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21139         
21140         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21141         
21142         if (label && label.iconEl) {
21143             label.iconEl.removeClass(label.validClass);
21144             label.iconEl.removeClass(label.invalidClass);
21145         }
21146     },
21147     
21148     disable : function()
21149     {
21150         if(this.inputType != 'radio'){
21151             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21152             return;
21153         }
21154         
21155         var _this = this;
21156         
21157         if(this.rendered){
21158             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21159                 _this.getActionEl().addClass(this.disabledClass);
21160                 e.dom.disabled = true;
21161             });
21162         }
21163         
21164         this.disabled = true;
21165         this.fireEvent("disable", this);
21166         return this;
21167     },
21168
21169     enable : function()
21170     {
21171         if(this.inputType != 'radio'){
21172             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21173             return;
21174         }
21175         
21176         var _this = this;
21177         
21178         if(this.rendered){
21179             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21180                 _this.getActionEl().removeClass(this.disabledClass);
21181                 e.dom.disabled = false;
21182             });
21183         }
21184         
21185         this.disabled = false;
21186         this.fireEvent("enable", this);
21187         return this;
21188     },
21189     
21190     setBoxLabel : function(v)
21191     {
21192         this.boxLabel = v;
21193         
21194         if(this.rendered){
21195             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21196         }
21197     }
21198
21199 });
21200
21201 Roo.apply(Roo.bootstrap.CheckBox, {
21202     
21203     groups: {},
21204     
21205      /**
21206     * register a CheckBox Group
21207     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21208     */
21209     register : function(checkbox)
21210     {
21211         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21212             this.groups[checkbox.groupId] = {};
21213         }
21214         
21215         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21216             return;
21217         }
21218         
21219         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21220         
21221     },
21222     /**
21223     * fetch a CheckBox Group based on the group ID
21224     * @param {string} the group ID
21225     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21226     */
21227     get: function(groupId) {
21228         if (typeof(this.groups[groupId]) == 'undefined') {
21229             return false;
21230         }
21231         
21232         return this.groups[groupId] ;
21233     }
21234     
21235     
21236 });
21237 /*
21238  * - LGPL
21239  *
21240  * RadioItem
21241  * 
21242  */
21243
21244 /**
21245  * @class Roo.bootstrap.Radio
21246  * @extends Roo.bootstrap.Component
21247  * Bootstrap Radio class
21248  * @cfg {String} boxLabel - the label associated
21249  * @cfg {String} value - the value of radio
21250  * 
21251  * @constructor
21252  * Create a new Radio
21253  * @param {Object} config The config object
21254  */
21255 Roo.bootstrap.Radio = function(config){
21256     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21257     
21258 };
21259
21260 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21261     
21262     boxLabel : '',
21263     
21264     value : '',
21265     
21266     getAutoCreate : function()
21267     {
21268         var cfg = {
21269             tag : 'div',
21270             cls : 'form-group radio',
21271             cn : [
21272                 {
21273                     tag : 'label',
21274                     cls : 'box-label',
21275                     html : this.boxLabel
21276                 }
21277             ]
21278         };
21279         
21280         return cfg;
21281     },
21282     
21283     initEvents : function() 
21284     {
21285         this.parent().register(this);
21286         
21287         this.el.on('click', this.onClick, this);
21288         
21289     },
21290     
21291     onClick : function(e)
21292     {
21293         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21294             this.setChecked(true);
21295         }
21296     },
21297     
21298     setChecked : function(state, suppressEvent)
21299     {
21300         this.parent().setValue(this.value, suppressEvent);
21301         
21302     },
21303     
21304     setBoxLabel : function(v)
21305     {
21306         this.boxLabel = v;
21307         
21308         if(this.rendered){
21309             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21310         }
21311     }
21312     
21313 });
21314  
21315
21316  /*
21317  * - LGPL
21318  *
21319  * Input
21320  * 
21321  */
21322
21323 /**
21324  * @class Roo.bootstrap.SecurePass
21325  * @extends Roo.bootstrap.Input
21326  * Bootstrap SecurePass class
21327  *
21328  * 
21329  * @constructor
21330  * Create a new SecurePass
21331  * @param {Object} config The config object
21332  */
21333  
21334 Roo.bootstrap.SecurePass = function (config) {
21335     // these go here, so the translation tool can replace them..
21336     this.errors = {
21337         PwdEmpty: "Please type a password, and then retype it to confirm.",
21338         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21339         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21340         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21341         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21342         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21343         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21344         TooWeak: "Your password is Too Weak."
21345     },
21346     this.meterLabel = "Password strength:";
21347     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21348     this.meterClass = [
21349         "roo-password-meter-tooweak", 
21350         "roo-password-meter-weak", 
21351         "roo-password-meter-medium", 
21352         "roo-password-meter-strong", 
21353         "roo-password-meter-grey"
21354     ];
21355     
21356     this.errors = {};
21357     
21358     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21359 }
21360
21361 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21362     /**
21363      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21364      * {
21365      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21366      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21367      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21368      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21369      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21370      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21371      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21372      * })
21373      */
21374     // private
21375     
21376     meterWidth: 300,
21377     errorMsg :'',    
21378     errors: false,
21379     imageRoot: '/',
21380     /**
21381      * @cfg {String/Object} Label for the strength meter (defaults to
21382      * 'Password strength:')
21383      */
21384     // private
21385     meterLabel: '',
21386     /**
21387      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21388      * ['Weak', 'Medium', 'Strong'])
21389      */
21390     // private    
21391     pwdStrengths: false,    
21392     // private
21393     strength: 0,
21394     // private
21395     _lastPwd: null,
21396     // private
21397     kCapitalLetter: 0,
21398     kSmallLetter: 1,
21399     kDigit: 2,
21400     kPunctuation: 3,
21401     
21402     insecure: false,
21403     // private
21404     initEvents: function ()
21405     {
21406         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21407
21408         if (this.el.is('input[type=password]') && Roo.isSafari) {
21409             this.el.on('keydown', this.SafariOnKeyDown, this);
21410         }
21411
21412         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21413     },
21414     // private
21415     onRender: function (ct, position)
21416     {
21417         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21418         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21419         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21420
21421         this.trigger.createChild({
21422                    cn: [
21423                     {
21424                     //id: 'PwdMeter',
21425                     tag: 'div',
21426                     cls: 'roo-password-meter-grey col-xs-12',
21427                     style: {
21428                         //width: 0,
21429                         //width: this.meterWidth + 'px'                                                
21430                         }
21431                     },
21432                     {                            
21433                          cls: 'roo-password-meter-text'                          
21434                     }
21435                 ]            
21436         });
21437
21438          
21439         if (this.hideTrigger) {
21440             this.trigger.setDisplayed(false);
21441         }
21442         this.setSize(this.width || '', this.height || '');
21443     },
21444     // private
21445     onDestroy: function ()
21446     {
21447         if (this.trigger) {
21448             this.trigger.removeAllListeners();
21449             this.trigger.remove();
21450         }
21451         if (this.wrap) {
21452             this.wrap.remove();
21453         }
21454         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21455     },
21456     // private
21457     checkStrength: function ()
21458     {
21459         var pwd = this.inputEl().getValue();
21460         if (pwd == this._lastPwd) {
21461             return;
21462         }
21463
21464         var strength;
21465         if (this.ClientSideStrongPassword(pwd)) {
21466             strength = 3;
21467         } else if (this.ClientSideMediumPassword(pwd)) {
21468             strength = 2;
21469         } else if (this.ClientSideWeakPassword(pwd)) {
21470             strength = 1;
21471         } else {
21472             strength = 0;
21473         }
21474         
21475         Roo.log('strength1: ' + strength);
21476         
21477         //var pm = this.trigger.child('div/div/div').dom;
21478         var pm = this.trigger.child('div/div');
21479         pm.removeClass(this.meterClass);
21480         pm.addClass(this.meterClass[strength]);
21481                 
21482         
21483         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21484                 
21485         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21486         
21487         this._lastPwd = pwd;
21488     },
21489     reset: function ()
21490     {
21491         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21492         
21493         this._lastPwd = '';
21494         
21495         var pm = this.trigger.child('div/div');
21496         pm.removeClass(this.meterClass);
21497         pm.addClass('roo-password-meter-grey');        
21498         
21499         
21500         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21501         
21502         pt.innerHTML = '';
21503         this.inputEl().dom.type='password';
21504     },
21505     // private
21506     validateValue: function (value)
21507     {
21508         
21509         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21510             return false;
21511         }
21512         if (value.length == 0) {
21513             if (this.allowBlank) {
21514                 this.clearInvalid();
21515                 return true;
21516             }
21517
21518             this.markInvalid(this.errors.PwdEmpty);
21519             this.errorMsg = this.errors.PwdEmpty;
21520             return false;
21521         }
21522         
21523         if(this.insecure){
21524             return true;
21525         }
21526         
21527         if ('[\x21-\x7e]*'.match(value)) {
21528             this.markInvalid(this.errors.PwdBadChar);
21529             this.errorMsg = this.errors.PwdBadChar;
21530             return false;
21531         }
21532         if (value.length < 6) {
21533             this.markInvalid(this.errors.PwdShort);
21534             this.errorMsg = this.errors.PwdShort;
21535             return false;
21536         }
21537         if (value.length > 16) {
21538             this.markInvalid(this.errors.PwdLong);
21539             this.errorMsg = this.errors.PwdLong;
21540             return false;
21541         }
21542         var strength;
21543         if (this.ClientSideStrongPassword(value)) {
21544             strength = 3;
21545         } else if (this.ClientSideMediumPassword(value)) {
21546             strength = 2;
21547         } else if (this.ClientSideWeakPassword(value)) {
21548             strength = 1;
21549         } else {
21550             strength = 0;
21551         }
21552
21553         
21554         if (strength < 2) {
21555             //this.markInvalid(this.errors.TooWeak);
21556             this.errorMsg = this.errors.TooWeak;
21557             //return false;
21558         }
21559         
21560         
21561         console.log('strength2: ' + strength);
21562         
21563         //var pm = this.trigger.child('div/div/div').dom;
21564         
21565         var pm = this.trigger.child('div/div');
21566         pm.removeClass(this.meterClass);
21567         pm.addClass(this.meterClass[strength]);
21568                 
21569         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21570                 
21571         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21572         
21573         this.errorMsg = ''; 
21574         return true;
21575     },
21576     // private
21577     CharacterSetChecks: function (type)
21578     {
21579         this.type = type;
21580         this.fResult = false;
21581     },
21582     // private
21583     isctype: function (character, type)
21584     {
21585         switch (type) {  
21586             case this.kCapitalLetter:
21587                 if (character >= 'A' && character <= 'Z') {
21588                     return true;
21589                 }
21590                 break;
21591             
21592             case this.kSmallLetter:
21593                 if (character >= 'a' && character <= 'z') {
21594                     return true;
21595                 }
21596                 break;
21597             
21598             case this.kDigit:
21599                 if (character >= '0' && character <= '9') {
21600                     return true;
21601                 }
21602                 break;
21603             
21604             case this.kPunctuation:
21605                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21606                     return true;
21607                 }
21608                 break;
21609             
21610             default:
21611                 return false;
21612         }
21613
21614     },
21615     // private
21616     IsLongEnough: function (pwd, size)
21617     {
21618         return !(pwd == null || isNaN(size) || pwd.length < size);
21619     },
21620     // private
21621     SpansEnoughCharacterSets: function (word, nb)
21622     {
21623         if (!this.IsLongEnough(word, nb))
21624         {
21625             return false;
21626         }
21627
21628         var characterSetChecks = new Array(
21629             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21630             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21631         );
21632         
21633         for (var index = 0; index < word.length; ++index) {
21634             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21635                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21636                     characterSetChecks[nCharSet].fResult = true;
21637                     break;
21638                 }
21639             }
21640         }
21641
21642         var nCharSets = 0;
21643         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21644             if (characterSetChecks[nCharSet].fResult) {
21645                 ++nCharSets;
21646             }
21647         }
21648
21649         if (nCharSets < nb) {
21650             return false;
21651         }
21652         return true;
21653     },
21654     // private
21655     ClientSideStrongPassword: function (pwd)
21656     {
21657         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21658     },
21659     // private
21660     ClientSideMediumPassword: function (pwd)
21661     {
21662         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21663     },
21664     // private
21665     ClientSideWeakPassword: function (pwd)
21666     {
21667         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21668     }
21669           
21670 })//<script type="text/javascript">
21671
21672 /*
21673  * Based  Ext JS Library 1.1.1
21674  * Copyright(c) 2006-2007, Ext JS, LLC.
21675  * LGPL
21676  *
21677  */
21678  
21679 /**
21680  * @class Roo.HtmlEditorCore
21681  * @extends Roo.Component
21682  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21683  *
21684  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21685  */
21686
21687 Roo.HtmlEditorCore = function(config){
21688     
21689     
21690     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21691     
21692     
21693     this.addEvents({
21694         /**
21695          * @event initialize
21696          * Fires when the editor is fully initialized (including the iframe)
21697          * @param {Roo.HtmlEditorCore} this
21698          */
21699         initialize: true,
21700         /**
21701          * @event activate
21702          * Fires when the editor is first receives the focus. Any insertion must wait
21703          * until after this event.
21704          * @param {Roo.HtmlEditorCore} this
21705          */
21706         activate: true,
21707          /**
21708          * @event beforesync
21709          * Fires before the textarea is updated with content from the editor iframe. Return false
21710          * to cancel the sync.
21711          * @param {Roo.HtmlEditorCore} this
21712          * @param {String} html
21713          */
21714         beforesync: true,
21715          /**
21716          * @event beforepush
21717          * Fires before the iframe editor is updated with content from the textarea. Return false
21718          * to cancel the push.
21719          * @param {Roo.HtmlEditorCore} this
21720          * @param {String} html
21721          */
21722         beforepush: true,
21723          /**
21724          * @event sync
21725          * Fires when the textarea is updated with content from the editor iframe.
21726          * @param {Roo.HtmlEditorCore} this
21727          * @param {String} html
21728          */
21729         sync: true,
21730          /**
21731          * @event push
21732          * Fires when the iframe editor is updated with content from the textarea.
21733          * @param {Roo.HtmlEditorCore} this
21734          * @param {String} html
21735          */
21736         push: true,
21737         
21738         /**
21739          * @event editorevent
21740          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21741          * @param {Roo.HtmlEditorCore} this
21742          */
21743         editorevent: true
21744         
21745     });
21746     
21747     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21748     
21749     // defaults : white / black...
21750     this.applyBlacklists();
21751     
21752     
21753     
21754 };
21755
21756
21757 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21758
21759
21760      /**
21761      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21762      */
21763     
21764     owner : false,
21765     
21766      /**
21767      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21768      *                        Roo.resizable.
21769      */
21770     resizable : false,
21771      /**
21772      * @cfg {Number} height (in pixels)
21773      */   
21774     height: 300,
21775    /**
21776      * @cfg {Number} width (in pixels)
21777      */   
21778     width: 500,
21779     
21780     /**
21781      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21782      * 
21783      */
21784     stylesheets: false,
21785     
21786     // id of frame..
21787     frameId: false,
21788     
21789     // private properties
21790     validationEvent : false,
21791     deferHeight: true,
21792     initialized : false,
21793     activated : false,
21794     sourceEditMode : false,
21795     onFocus : Roo.emptyFn,
21796     iframePad:3,
21797     hideMode:'offsets',
21798     
21799     clearUp: true,
21800     
21801     // blacklist + whitelisted elements..
21802     black: false,
21803     white: false,
21804      
21805     bodyCls : '',
21806
21807     /**
21808      * Protected method that will not generally be called directly. It
21809      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21810      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21811      */
21812     getDocMarkup : function(){
21813         // body styles..
21814         var st = '';
21815         
21816         // inherit styels from page...?? 
21817         if (this.stylesheets === false) {
21818             
21819             Roo.get(document.head).select('style').each(function(node) {
21820                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21821             });
21822             
21823             Roo.get(document.head).select('link').each(function(node) { 
21824                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21825             });
21826             
21827         } else if (!this.stylesheets.length) {
21828                 // simple..
21829                 st = '<style type="text/css">' +
21830                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21831                    '</style>';
21832         } else { 
21833             st = '<style type="text/css">' +
21834                     this.stylesheets +
21835                 '</style>';
21836         }
21837         
21838         st +=  '<style type="text/css">' +
21839             'IMG { cursor: pointer } ' +
21840         '</style>';
21841
21842         var cls = 'roo-htmleditor-body';
21843         
21844         if(this.bodyCls.length){
21845             cls += ' ' + this.bodyCls;
21846         }
21847         
21848         return '<html><head>' + st  +
21849             //<style type="text/css">' +
21850             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21851             //'</style>' +
21852             ' </head><body class="' +  cls + '"></body></html>';
21853     },
21854
21855     // private
21856     onRender : function(ct, position)
21857     {
21858         var _t = this;
21859         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21860         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21861         
21862         
21863         this.el.dom.style.border = '0 none';
21864         this.el.dom.setAttribute('tabIndex', -1);
21865         this.el.addClass('x-hidden hide');
21866         
21867         
21868         
21869         if(Roo.isIE){ // fix IE 1px bogus margin
21870             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21871         }
21872        
21873         
21874         this.frameId = Roo.id();
21875         
21876          
21877         
21878         var iframe = this.owner.wrap.createChild({
21879             tag: 'iframe',
21880             cls: 'form-control', // bootstrap..
21881             id: this.frameId,
21882             name: this.frameId,
21883             frameBorder : 'no',
21884             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21885         }, this.el
21886         );
21887         
21888         
21889         this.iframe = iframe.dom;
21890
21891          this.assignDocWin();
21892         
21893         this.doc.designMode = 'on';
21894        
21895         this.doc.open();
21896         this.doc.write(this.getDocMarkup());
21897         this.doc.close();
21898
21899         
21900         var task = { // must defer to wait for browser to be ready
21901             run : function(){
21902                 //console.log("run task?" + this.doc.readyState);
21903                 this.assignDocWin();
21904                 if(this.doc.body || this.doc.readyState == 'complete'){
21905                     try {
21906                         this.doc.designMode="on";
21907                     } catch (e) {
21908                         return;
21909                     }
21910                     Roo.TaskMgr.stop(task);
21911                     this.initEditor.defer(10, this);
21912                 }
21913             },
21914             interval : 10,
21915             duration: 10000,
21916             scope: this
21917         };
21918         Roo.TaskMgr.start(task);
21919
21920     },
21921
21922     // private
21923     onResize : function(w, h)
21924     {
21925          Roo.log('resize: ' +w + ',' + h );
21926         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21927         if(!this.iframe){
21928             return;
21929         }
21930         if(typeof w == 'number'){
21931             
21932             this.iframe.style.width = w + 'px';
21933         }
21934         if(typeof h == 'number'){
21935             
21936             this.iframe.style.height = h + 'px';
21937             if(this.doc){
21938                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21939             }
21940         }
21941         
21942     },
21943
21944     /**
21945      * Toggles the editor between standard and source edit mode.
21946      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21947      */
21948     toggleSourceEdit : function(sourceEditMode){
21949         
21950         this.sourceEditMode = sourceEditMode === true;
21951         
21952         if(this.sourceEditMode){
21953  
21954             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21955             
21956         }else{
21957             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21958             //this.iframe.className = '';
21959             this.deferFocus();
21960         }
21961         //this.setSize(this.owner.wrap.getSize());
21962         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21963     },
21964
21965     
21966   
21967
21968     /**
21969      * Protected method that will not generally be called directly. If you need/want
21970      * custom HTML cleanup, this is the method you should override.
21971      * @param {String} html The HTML to be cleaned
21972      * return {String} The cleaned HTML
21973      */
21974     cleanHtml : function(html){
21975         html = String(html);
21976         if(html.length > 5){
21977             if(Roo.isSafari){ // strip safari nonsense
21978                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21979             }
21980         }
21981         if(html == '&nbsp;'){
21982             html = '';
21983         }
21984         return html;
21985     },
21986
21987     /**
21988      * HTML Editor -> Textarea
21989      * Protected method that will not generally be called directly. Syncs the contents
21990      * of the editor iframe with the textarea.
21991      */
21992     syncValue : function(){
21993         if(this.initialized){
21994             var bd = (this.doc.body || this.doc.documentElement);
21995             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21996             var html = bd.innerHTML;
21997             if(Roo.isSafari){
21998                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21999                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22000                 if(m && m[1]){
22001                     html = '<div style="'+m[0]+'">' + html + '</div>';
22002                 }
22003             }
22004             html = this.cleanHtml(html);
22005             // fix up the special chars.. normaly like back quotes in word...
22006             // however we do not want to do this with chinese..
22007             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22008                 var cc = b.charCodeAt();
22009                 if (
22010                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22011                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22012                     (cc >= 0xf900 && cc < 0xfb00 )
22013                 ) {
22014                         return b;
22015                 }
22016                 return "&#"+cc+";" 
22017             });
22018             if(this.owner.fireEvent('beforesync', this, html) !== false){
22019                 this.el.dom.value = html;
22020                 this.owner.fireEvent('sync', this, html);
22021             }
22022         }
22023     },
22024
22025     /**
22026      * Protected method that will not generally be called directly. Pushes the value of the textarea
22027      * into the iframe editor.
22028      */
22029     pushValue : function(){
22030         if(this.initialized){
22031             var v = this.el.dom.value.trim();
22032             
22033 //            if(v.length < 1){
22034 //                v = '&#160;';
22035 //            }
22036             
22037             if(this.owner.fireEvent('beforepush', this, v) !== false){
22038                 var d = (this.doc.body || this.doc.documentElement);
22039                 d.innerHTML = v;
22040                 this.cleanUpPaste();
22041                 this.el.dom.value = d.innerHTML;
22042                 this.owner.fireEvent('push', this, v);
22043             }
22044         }
22045     },
22046
22047     // private
22048     deferFocus : function(){
22049         this.focus.defer(10, this);
22050     },
22051
22052     // doc'ed in Field
22053     focus : function(){
22054         if(this.win && !this.sourceEditMode){
22055             this.win.focus();
22056         }else{
22057             this.el.focus();
22058         }
22059     },
22060     
22061     assignDocWin: function()
22062     {
22063         var iframe = this.iframe;
22064         
22065          if(Roo.isIE){
22066             this.doc = iframe.contentWindow.document;
22067             this.win = iframe.contentWindow;
22068         } else {
22069 //            if (!Roo.get(this.frameId)) {
22070 //                return;
22071 //            }
22072 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22073 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22074             
22075             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22076                 return;
22077             }
22078             
22079             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22080             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22081         }
22082     },
22083     
22084     // private
22085     initEditor : function(){
22086         //console.log("INIT EDITOR");
22087         this.assignDocWin();
22088         
22089         
22090         
22091         this.doc.designMode="on";
22092         this.doc.open();
22093         this.doc.write(this.getDocMarkup());
22094         this.doc.close();
22095         
22096         var dbody = (this.doc.body || this.doc.documentElement);
22097         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22098         // this copies styles from the containing element into thsi one..
22099         // not sure why we need all of this..
22100         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22101         
22102         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22103         //ss['background-attachment'] = 'fixed'; // w3c
22104         dbody.bgProperties = 'fixed'; // ie
22105         //Roo.DomHelper.applyStyles(dbody, ss);
22106         Roo.EventManager.on(this.doc, {
22107             //'mousedown': this.onEditorEvent,
22108             'mouseup': this.onEditorEvent,
22109             'dblclick': this.onEditorEvent,
22110             'click': this.onEditorEvent,
22111             'keyup': this.onEditorEvent,
22112             buffer:100,
22113             scope: this
22114         });
22115         if(Roo.isGecko){
22116             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22117         }
22118         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22119             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22120         }
22121         this.initialized = true;
22122
22123         this.owner.fireEvent('initialize', this);
22124         this.pushValue();
22125     },
22126
22127     // private
22128     onDestroy : function(){
22129         
22130         
22131         
22132         if(this.rendered){
22133             
22134             //for (var i =0; i < this.toolbars.length;i++) {
22135             //    // fixme - ask toolbars for heights?
22136             //    this.toolbars[i].onDestroy();
22137            // }
22138             
22139             //this.wrap.dom.innerHTML = '';
22140             //this.wrap.remove();
22141         }
22142     },
22143
22144     // private
22145     onFirstFocus : function(){
22146         
22147         this.assignDocWin();
22148         
22149         
22150         this.activated = true;
22151          
22152     
22153         if(Roo.isGecko){ // prevent silly gecko errors
22154             this.win.focus();
22155             var s = this.win.getSelection();
22156             if(!s.focusNode || s.focusNode.nodeType != 3){
22157                 var r = s.getRangeAt(0);
22158                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22159                 r.collapse(true);
22160                 this.deferFocus();
22161             }
22162             try{
22163                 this.execCmd('useCSS', true);
22164                 this.execCmd('styleWithCSS', false);
22165             }catch(e){}
22166         }
22167         this.owner.fireEvent('activate', this);
22168     },
22169
22170     // private
22171     adjustFont: function(btn){
22172         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22173         //if(Roo.isSafari){ // safari
22174         //    adjust *= 2;
22175        // }
22176         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22177         if(Roo.isSafari){ // safari
22178             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22179             v =  (v < 10) ? 10 : v;
22180             v =  (v > 48) ? 48 : v;
22181             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22182             
22183         }
22184         
22185         
22186         v = Math.max(1, v+adjust);
22187         
22188         this.execCmd('FontSize', v  );
22189     },
22190
22191     onEditorEvent : function(e)
22192     {
22193         this.owner.fireEvent('editorevent', this, e);
22194       //  this.updateToolbar();
22195         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22196     },
22197
22198     insertTag : function(tg)
22199     {
22200         // could be a bit smarter... -> wrap the current selected tRoo..
22201         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22202             
22203             range = this.createRange(this.getSelection());
22204             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22205             wrappingNode.appendChild(range.extractContents());
22206             range.insertNode(wrappingNode);
22207
22208             return;
22209             
22210             
22211             
22212         }
22213         this.execCmd("formatblock",   tg);
22214         
22215     },
22216     
22217     insertText : function(txt)
22218     {
22219         
22220         
22221         var range = this.createRange();
22222         range.deleteContents();
22223                //alert(Sender.getAttribute('label'));
22224                
22225         range.insertNode(this.doc.createTextNode(txt));
22226     } ,
22227     
22228      
22229
22230     /**
22231      * Executes a Midas editor command on the editor document and performs necessary focus and
22232      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22233      * @param {String} cmd The Midas command
22234      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22235      */
22236     relayCmd : function(cmd, value){
22237         this.win.focus();
22238         this.execCmd(cmd, value);
22239         this.owner.fireEvent('editorevent', this);
22240         //this.updateToolbar();
22241         this.owner.deferFocus();
22242     },
22243
22244     /**
22245      * Executes a Midas editor command directly on the editor document.
22246      * For visual commands, you should use {@link #relayCmd} instead.
22247      * <b>This should only be called after the editor is initialized.</b>
22248      * @param {String} cmd The Midas command
22249      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22250      */
22251     execCmd : function(cmd, value){
22252         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22253         this.syncValue();
22254     },
22255  
22256  
22257    
22258     /**
22259      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22260      * to insert tRoo.
22261      * @param {String} text | dom node.. 
22262      */
22263     insertAtCursor : function(text)
22264     {
22265         
22266         if(!this.activated){
22267             return;
22268         }
22269         /*
22270         if(Roo.isIE){
22271             this.win.focus();
22272             var r = this.doc.selection.createRange();
22273             if(r){
22274                 r.collapse(true);
22275                 r.pasteHTML(text);
22276                 this.syncValue();
22277                 this.deferFocus();
22278             
22279             }
22280             return;
22281         }
22282         */
22283         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22284             this.win.focus();
22285             
22286             
22287             // from jquery ui (MIT licenced)
22288             var range, node;
22289             var win = this.win;
22290             
22291             if (win.getSelection && win.getSelection().getRangeAt) {
22292                 range = win.getSelection().getRangeAt(0);
22293                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22294                 range.insertNode(node);
22295             } else if (win.document.selection && win.document.selection.createRange) {
22296                 // no firefox support
22297                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22298                 win.document.selection.createRange().pasteHTML(txt);
22299             } else {
22300                 // no firefox support
22301                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22302                 this.execCmd('InsertHTML', txt);
22303             } 
22304             
22305             this.syncValue();
22306             
22307             this.deferFocus();
22308         }
22309     },
22310  // private
22311     mozKeyPress : function(e){
22312         if(e.ctrlKey){
22313             var c = e.getCharCode(), cmd;
22314           
22315             if(c > 0){
22316                 c = String.fromCharCode(c).toLowerCase();
22317                 switch(c){
22318                     case 'b':
22319                         cmd = 'bold';
22320                         break;
22321                     case 'i':
22322                         cmd = 'italic';
22323                         break;
22324                     
22325                     case 'u':
22326                         cmd = 'underline';
22327                         break;
22328                     
22329                     case 'v':
22330                         this.cleanUpPaste.defer(100, this);
22331                         return;
22332                         
22333                 }
22334                 if(cmd){
22335                     this.win.focus();
22336                     this.execCmd(cmd);
22337                     this.deferFocus();
22338                     e.preventDefault();
22339                 }
22340                 
22341             }
22342         }
22343     },
22344
22345     // private
22346     fixKeys : function(){ // load time branching for fastest keydown performance
22347         if(Roo.isIE){
22348             return function(e){
22349                 var k = e.getKey(), r;
22350                 if(k == e.TAB){
22351                     e.stopEvent();
22352                     r = this.doc.selection.createRange();
22353                     if(r){
22354                         r.collapse(true);
22355                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22356                         this.deferFocus();
22357                     }
22358                     return;
22359                 }
22360                 
22361                 if(k == e.ENTER){
22362                     r = this.doc.selection.createRange();
22363                     if(r){
22364                         var target = r.parentElement();
22365                         if(!target || target.tagName.toLowerCase() != 'li'){
22366                             e.stopEvent();
22367                             r.pasteHTML('<br />');
22368                             r.collapse(false);
22369                             r.select();
22370                         }
22371                     }
22372                 }
22373                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22374                     this.cleanUpPaste.defer(100, this);
22375                     return;
22376                 }
22377                 
22378                 
22379             };
22380         }else if(Roo.isOpera){
22381             return function(e){
22382                 var k = e.getKey();
22383                 if(k == e.TAB){
22384                     e.stopEvent();
22385                     this.win.focus();
22386                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22387                     this.deferFocus();
22388                 }
22389                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22390                     this.cleanUpPaste.defer(100, this);
22391                     return;
22392                 }
22393                 
22394             };
22395         }else if(Roo.isSafari){
22396             return function(e){
22397                 var k = e.getKey();
22398                 
22399                 if(k == e.TAB){
22400                     e.stopEvent();
22401                     this.execCmd('InsertText','\t');
22402                     this.deferFocus();
22403                     return;
22404                 }
22405                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22406                     this.cleanUpPaste.defer(100, this);
22407                     return;
22408                 }
22409                 
22410              };
22411         }
22412     }(),
22413     
22414     getAllAncestors: function()
22415     {
22416         var p = this.getSelectedNode();
22417         var a = [];
22418         if (!p) {
22419             a.push(p); // push blank onto stack..
22420             p = this.getParentElement();
22421         }
22422         
22423         
22424         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22425             a.push(p);
22426             p = p.parentNode;
22427         }
22428         a.push(this.doc.body);
22429         return a;
22430     },
22431     lastSel : false,
22432     lastSelNode : false,
22433     
22434     
22435     getSelection : function() 
22436     {
22437         this.assignDocWin();
22438         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22439     },
22440     
22441     getSelectedNode: function() 
22442     {
22443         // this may only work on Gecko!!!
22444         
22445         // should we cache this!!!!
22446         
22447         
22448         
22449          
22450         var range = this.createRange(this.getSelection()).cloneRange();
22451         
22452         if (Roo.isIE) {
22453             var parent = range.parentElement();
22454             while (true) {
22455                 var testRange = range.duplicate();
22456                 testRange.moveToElementText(parent);
22457                 if (testRange.inRange(range)) {
22458                     break;
22459                 }
22460                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22461                     break;
22462                 }
22463                 parent = parent.parentElement;
22464             }
22465             return parent;
22466         }
22467         
22468         // is ancestor a text element.
22469         var ac =  range.commonAncestorContainer;
22470         if (ac.nodeType == 3) {
22471             ac = ac.parentNode;
22472         }
22473         
22474         var ar = ac.childNodes;
22475          
22476         var nodes = [];
22477         var other_nodes = [];
22478         var has_other_nodes = false;
22479         for (var i=0;i<ar.length;i++) {
22480             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22481                 continue;
22482             }
22483             // fullly contained node.
22484             
22485             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22486                 nodes.push(ar[i]);
22487                 continue;
22488             }
22489             
22490             // probably selected..
22491             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22492                 other_nodes.push(ar[i]);
22493                 continue;
22494             }
22495             // outer..
22496             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22497                 continue;
22498             }
22499             
22500             
22501             has_other_nodes = true;
22502         }
22503         if (!nodes.length && other_nodes.length) {
22504             nodes= other_nodes;
22505         }
22506         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22507             return false;
22508         }
22509         
22510         return nodes[0];
22511     },
22512     createRange: function(sel)
22513     {
22514         // this has strange effects when using with 
22515         // top toolbar - not sure if it's a great idea.
22516         //this.editor.contentWindow.focus();
22517         if (typeof sel != "undefined") {
22518             try {
22519                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22520             } catch(e) {
22521                 return this.doc.createRange();
22522             }
22523         } else {
22524             return this.doc.createRange();
22525         }
22526     },
22527     getParentElement: function()
22528     {
22529         
22530         this.assignDocWin();
22531         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22532         
22533         var range = this.createRange(sel);
22534          
22535         try {
22536             var p = range.commonAncestorContainer;
22537             while (p.nodeType == 3) { // text node
22538                 p = p.parentNode;
22539             }
22540             return p;
22541         } catch (e) {
22542             return null;
22543         }
22544     
22545     },
22546     /***
22547      *
22548      * Range intersection.. the hard stuff...
22549      *  '-1' = before
22550      *  '0' = hits..
22551      *  '1' = after.
22552      *         [ -- selected range --- ]
22553      *   [fail]                        [fail]
22554      *
22555      *    basically..
22556      *      if end is before start or  hits it. fail.
22557      *      if start is after end or hits it fail.
22558      *
22559      *   if either hits (but other is outside. - then it's not 
22560      *   
22561      *    
22562      **/
22563     
22564     
22565     // @see http://www.thismuchiknow.co.uk/?p=64.
22566     rangeIntersectsNode : function(range, node)
22567     {
22568         var nodeRange = node.ownerDocument.createRange();
22569         try {
22570             nodeRange.selectNode(node);
22571         } catch (e) {
22572             nodeRange.selectNodeContents(node);
22573         }
22574     
22575         var rangeStartRange = range.cloneRange();
22576         rangeStartRange.collapse(true);
22577     
22578         var rangeEndRange = range.cloneRange();
22579         rangeEndRange.collapse(false);
22580     
22581         var nodeStartRange = nodeRange.cloneRange();
22582         nodeStartRange.collapse(true);
22583     
22584         var nodeEndRange = nodeRange.cloneRange();
22585         nodeEndRange.collapse(false);
22586     
22587         return rangeStartRange.compareBoundaryPoints(
22588                  Range.START_TO_START, nodeEndRange) == -1 &&
22589                rangeEndRange.compareBoundaryPoints(
22590                  Range.START_TO_START, nodeStartRange) == 1;
22591         
22592          
22593     },
22594     rangeCompareNode : function(range, node)
22595     {
22596         var nodeRange = node.ownerDocument.createRange();
22597         try {
22598             nodeRange.selectNode(node);
22599         } catch (e) {
22600             nodeRange.selectNodeContents(node);
22601         }
22602         
22603         
22604         range.collapse(true);
22605     
22606         nodeRange.collapse(true);
22607      
22608         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22609         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22610          
22611         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22612         
22613         var nodeIsBefore   =  ss == 1;
22614         var nodeIsAfter    = ee == -1;
22615         
22616         if (nodeIsBefore && nodeIsAfter) {
22617             return 0; // outer
22618         }
22619         if (!nodeIsBefore && nodeIsAfter) {
22620             return 1; //right trailed.
22621         }
22622         
22623         if (nodeIsBefore && !nodeIsAfter) {
22624             return 2;  // left trailed.
22625         }
22626         // fully contined.
22627         return 3;
22628     },
22629
22630     // private? - in a new class?
22631     cleanUpPaste :  function()
22632     {
22633         // cleans up the whole document..
22634         Roo.log('cleanuppaste');
22635         
22636         this.cleanUpChildren(this.doc.body);
22637         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22638         if (clean != this.doc.body.innerHTML) {
22639             this.doc.body.innerHTML = clean;
22640         }
22641         
22642     },
22643     
22644     cleanWordChars : function(input) {// change the chars to hex code
22645         var he = Roo.HtmlEditorCore;
22646         
22647         var output = input;
22648         Roo.each(he.swapCodes, function(sw) { 
22649             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22650             
22651             output = output.replace(swapper, sw[1]);
22652         });
22653         
22654         return output;
22655     },
22656     
22657     
22658     cleanUpChildren : function (n)
22659     {
22660         if (!n.childNodes.length) {
22661             return;
22662         }
22663         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22664            this.cleanUpChild(n.childNodes[i]);
22665         }
22666     },
22667     
22668     
22669         
22670     
22671     cleanUpChild : function (node)
22672     {
22673         var ed = this;
22674         //console.log(node);
22675         if (node.nodeName == "#text") {
22676             // clean up silly Windows -- stuff?
22677             return; 
22678         }
22679         if (node.nodeName == "#comment") {
22680             node.parentNode.removeChild(node);
22681             // clean up silly Windows -- stuff?
22682             return; 
22683         }
22684         var lcname = node.tagName.toLowerCase();
22685         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22686         // whitelist of tags..
22687         
22688         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22689             // remove node.
22690             node.parentNode.removeChild(node);
22691             return;
22692             
22693         }
22694         
22695         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22696         
22697         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22698         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22699         
22700         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22701         //    remove_keep_children = true;
22702         //}
22703         
22704         if (remove_keep_children) {
22705             this.cleanUpChildren(node);
22706             // inserts everything just before this node...
22707             while (node.childNodes.length) {
22708                 var cn = node.childNodes[0];
22709                 node.removeChild(cn);
22710                 node.parentNode.insertBefore(cn, node);
22711             }
22712             node.parentNode.removeChild(node);
22713             return;
22714         }
22715         
22716         if (!node.attributes || !node.attributes.length) {
22717             this.cleanUpChildren(node);
22718             return;
22719         }
22720         
22721         function cleanAttr(n,v)
22722         {
22723             
22724             if (v.match(/^\./) || v.match(/^\//)) {
22725                 return;
22726             }
22727             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22728                 return;
22729             }
22730             if (v.match(/^#/)) {
22731                 return;
22732             }
22733 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22734             node.removeAttribute(n);
22735             
22736         }
22737         
22738         var cwhite = this.cwhite;
22739         var cblack = this.cblack;
22740             
22741         function cleanStyle(n,v)
22742         {
22743             if (v.match(/expression/)) { //XSS?? should we even bother..
22744                 node.removeAttribute(n);
22745                 return;
22746             }
22747             
22748             var parts = v.split(/;/);
22749             var clean = [];
22750             
22751             Roo.each(parts, function(p) {
22752                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22753                 if (!p.length) {
22754                     return true;
22755                 }
22756                 var l = p.split(':').shift().replace(/\s+/g,'');
22757                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22758                 
22759                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22760 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22761                     //node.removeAttribute(n);
22762                     return true;
22763                 }
22764                 //Roo.log()
22765                 // only allow 'c whitelisted system attributes'
22766                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22767 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22768                     //node.removeAttribute(n);
22769                     return true;
22770                 }
22771                 
22772                 
22773                  
22774                 
22775                 clean.push(p);
22776                 return true;
22777             });
22778             if (clean.length) { 
22779                 node.setAttribute(n, clean.join(';'));
22780             } else {
22781                 node.removeAttribute(n);
22782             }
22783             
22784         }
22785         
22786         
22787         for (var i = node.attributes.length-1; i > -1 ; i--) {
22788             var a = node.attributes[i];
22789             //console.log(a);
22790             
22791             if (a.name.toLowerCase().substr(0,2)=='on')  {
22792                 node.removeAttribute(a.name);
22793                 continue;
22794             }
22795             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22796                 node.removeAttribute(a.name);
22797                 continue;
22798             }
22799             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22800                 cleanAttr(a.name,a.value); // fixme..
22801                 continue;
22802             }
22803             if (a.name == 'style') {
22804                 cleanStyle(a.name,a.value);
22805                 continue;
22806             }
22807             /// clean up MS crap..
22808             // tecnically this should be a list of valid class'es..
22809             
22810             
22811             if (a.name == 'class') {
22812                 if (a.value.match(/^Mso/)) {
22813                     node.className = '';
22814                 }
22815                 
22816                 if (a.value.match(/^body$/)) {
22817                     node.className = '';
22818                 }
22819                 continue;
22820             }
22821             
22822             // style cleanup!?
22823             // class cleanup?
22824             
22825         }
22826         
22827         
22828         this.cleanUpChildren(node);
22829         
22830         
22831     },
22832     
22833     /**
22834      * Clean up MS wordisms...
22835      */
22836     cleanWord : function(node)
22837     {
22838         
22839         
22840         if (!node) {
22841             this.cleanWord(this.doc.body);
22842             return;
22843         }
22844         if (node.nodeName == "#text") {
22845             // clean up silly Windows -- stuff?
22846             return; 
22847         }
22848         if (node.nodeName == "#comment") {
22849             node.parentNode.removeChild(node);
22850             // clean up silly Windows -- stuff?
22851             return; 
22852         }
22853         
22854         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22855             node.parentNode.removeChild(node);
22856             return;
22857         }
22858         
22859         // remove - but keep children..
22860         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22861             while (node.childNodes.length) {
22862                 var cn = node.childNodes[0];
22863                 node.removeChild(cn);
22864                 node.parentNode.insertBefore(cn, node);
22865             }
22866             node.parentNode.removeChild(node);
22867             this.iterateChildren(node, this.cleanWord);
22868             return;
22869         }
22870         // clean styles
22871         if (node.className.length) {
22872             
22873             var cn = node.className.split(/\W+/);
22874             var cna = [];
22875             Roo.each(cn, function(cls) {
22876                 if (cls.match(/Mso[a-zA-Z]+/)) {
22877                     return;
22878                 }
22879                 cna.push(cls);
22880             });
22881             node.className = cna.length ? cna.join(' ') : '';
22882             if (!cna.length) {
22883                 node.removeAttribute("class");
22884             }
22885         }
22886         
22887         if (node.hasAttribute("lang")) {
22888             node.removeAttribute("lang");
22889         }
22890         
22891         if (node.hasAttribute("style")) {
22892             
22893             var styles = node.getAttribute("style").split(";");
22894             var nstyle = [];
22895             Roo.each(styles, function(s) {
22896                 if (!s.match(/:/)) {
22897                     return;
22898                 }
22899                 var kv = s.split(":");
22900                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22901                     return;
22902                 }
22903                 // what ever is left... we allow.
22904                 nstyle.push(s);
22905             });
22906             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22907             if (!nstyle.length) {
22908                 node.removeAttribute('style');
22909             }
22910         }
22911         this.iterateChildren(node, this.cleanWord);
22912         
22913         
22914         
22915     },
22916     /**
22917      * iterateChildren of a Node, calling fn each time, using this as the scole..
22918      * @param {DomNode} node node to iterate children of.
22919      * @param {Function} fn method of this class to call on each item.
22920      */
22921     iterateChildren : function(node, fn)
22922     {
22923         if (!node.childNodes.length) {
22924                 return;
22925         }
22926         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22927            fn.call(this, node.childNodes[i])
22928         }
22929     },
22930     
22931     
22932     /**
22933      * cleanTableWidths.
22934      *
22935      * Quite often pasting from word etc.. results in tables with column and widths.
22936      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22937      *
22938      */
22939     cleanTableWidths : function(node)
22940     {
22941          
22942          
22943         if (!node) {
22944             this.cleanTableWidths(this.doc.body);
22945             return;
22946         }
22947         
22948         // ignore list...
22949         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22950             return; 
22951         }
22952         Roo.log(node.tagName);
22953         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22954             this.iterateChildren(node, this.cleanTableWidths);
22955             return;
22956         }
22957         if (node.hasAttribute('width')) {
22958             node.removeAttribute('width');
22959         }
22960         
22961          
22962         if (node.hasAttribute("style")) {
22963             // pretty basic...
22964             
22965             var styles = node.getAttribute("style").split(";");
22966             var nstyle = [];
22967             Roo.each(styles, function(s) {
22968                 if (!s.match(/:/)) {
22969                     return;
22970                 }
22971                 var kv = s.split(":");
22972                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22973                     return;
22974                 }
22975                 // what ever is left... we allow.
22976                 nstyle.push(s);
22977             });
22978             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22979             if (!nstyle.length) {
22980                 node.removeAttribute('style');
22981             }
22982         }
22983         
22984         this.iterateChildren(node, this.cleanTableWidths);
22985         
22986         
22987     },
22988     
22989     
22990     
22991     
22992     domToHTML : function(currentElement, depth, nopadtext) {
22993         
22994         depth = depth || 0;
22995         nopadtext = nopadtext || false;
22996     
22997         if (!currentElement) {
22998             return this.domToHTML(this.doc.body);
22999         }
23000         
23001         //Roo.log(currentElement);
23002         var j;
23003         var allText = false;
23004         var nodeName = currentElement.nodeName;
23005         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23006         
23007         if  (nodeName == '#text') {
23008             
23009             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23010         }
23011         
23012         
23013         var ret = '';
23014         if (nodeName != 'BODY') {
23015              
23016             var i = 0;
23017             // Prints the node tagName, such as <A>, <IMG>, etc
23018             if (tagName) {
23019                 var attr = [];
23020                 for(i = 0; i < currentElement.attributes.length;i++) {
23021                     // quoting?
23022                     var aname = currentElement.attributes.item(i).name;
23023                     if (!currentElement.attributes.item(i).value.length) {
23024                         continue;
23025                     }
23026                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23027                 }
23028                 
23029                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23030             } 
23031             else {
23032                 
23033                 // eack
23034             }
23035         } else {
23036             tagName = false;
23037         }
23038         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23039             return ret;
23040         }
23041         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23042             nopadtext = true;
23043         }
23044         
23045         
23046         // Traverse the tree
23047         i = 0;
23048         var currentElementChild = currentElement.childNodes.item(i);
23049         var allText = true;
23050         var innerHTML  = '';
23051         lastnode = '';
23052         while (currentElementChild) {
23053             // Formatting code (indent the tree so it looks nice on the screen)
23054             var nopad = nopadtext;
23055             if (lastnode == 'SPAN') {
23056                 nopad  = true;
23057             }
23058             // text
23059             if  (currentElementChild.nodeName == '#text') {
23060                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23061                 toadd = nopadtext ? toadd : toadd.trim();
23062                 if (!nopad && toadd.length > 80) {
23063                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23064                 }
23065                 innerHTML  += toadd;
23066                 
23067                 i++;
23068                 currentElementChild = currentElement.childNodes.item(i);
23069                 lastNode = '';
23070                 continue;
23071             }
23072             allText = false;
23073             
23074             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23075                 
23076             // Recursively traverse the tree structure of the child node
23077             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23078             lastnode = currentElementChild.nodeName;
23079             i++;
23080             currentElementChild=currentElement.childNodes.item(i);
23081         }
23082         
23083         ret += innerHTML;
23084         
23085         if (!allText) {
23086                 // The remaining code is mostly for formatting the tree
23087             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23088         }
23089         
23090         
23091         if (tagName) {
23092             ret+= "</"+tagName+">";
23093         }
23094         return ret;
23095         
23096     },
23097         
23098     applyBlacklists : function()
23099     {
23100         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23101         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23102         
23103         this.white = [];
23104         this.black = [];
23105         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23106             if (b.indexOf(tag) > -1) {
23107                 return;
23108             }
23109             this.white.push(tag);
23110             
23111         }, this);
23112         
23113         Roo.each(w, function(tag) {
23114             if (b.indexOf(tag) > -1) {
23115                 return;
23116             }
23117             if (this.white.indexOf(tag) > -1) {
23118                 return;
23119             }
23120             this.white.push(tag);
23121             
23122         }, this);
23123         
23124         
23125         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23126             if (w.indexOf(tag) > -1) {
23127                 return;
23128             }
23129             this.black.push(tag);
23130             
23131         }, this);
23132         
23133         Roo.each(b, function(tag) {
23134             if (w.indexOf(tag) > -1) {
23135                 return;
23136             }
23137             if (this.black.indexOf(tag) > -1) {
23138                 return;
23139             }
23140             this.black.push(tag);
23141             
23142         }, this);
23143         
23144         
23145         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23146         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23147         
23148         this.cwhite = [];
23149         this.cblack = [];
23150         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23151             if (b.indexOf(tag) > -1) {
23152                 return;
23153             }
23154             this.cwhite.push(tag);
23155             
23156         }, this);
23157         
23158         Roo.each(w, function(tag) {
23159             if (b.indexOf(tag) > -1) {
23160                 return;
23161             }
23162             if (this.cwhite.indexOf(tag) > -1) {
23163                 return;
23164             }
23165             this.cwhite.push(tag);
23166             
23167         }, this);
23168         
23169         
23170         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23171             if (w.indexOf(tag) > -1) {
23172                 return;
23173             }
23174             this.cblack.push(tag);
23175             
23176         }, this);
23177         
23178         Roo.each(b, function(tag) {
23179             if (w.indexOf(tag) > -1) {
23180                 return;
23181             }
23182             if (this.cblack.indexOf(tag) > -1) {
23183                 return;
23184             }
23185             this.cblack.push(tag);
23186             
23187         }, this);
23188     },
23189     
23190     setStylesheets : function(stylesheets)
23191     {
23192         if(typeof(stylesheets) == 'string'){
23193             Roo.get(this.iframe.contentDocument.head).createChild({
23194                 tag : 'link',
23195                 rel : 'stylesheet',
23196                 type : 'text/css',
23197                 href : stylesheets
23198             });
23199             
23200             return;
23201         }
23202         var _this = this;
23203      
23204         Roo.each(stylesheets, function(s) {
23205             if(!s.length){
23206                 return;
23207             }
23208             
23209             Roo.get(_this.iframe.contentDocument.head).createChild({
23210                 tag : 'link',
23211                 rel : 'stylesheet',
23212                 type : 'text/css',
23213                 href : s
23214             });
23215         });
23216
23217         
23218     },
23219     
23220     removeStylesheets : function()
23221     {
23222         var _this = this;
23223         
23224         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23225             s.remove();
23226         });
23227     },
23228     
23229     setStyle : function(style)
23230     {
23231         Roo.get(this.iframe.contentDocument.head).createChild({
23232             tag : 'style',
23233             type : 'text/css',
23234             html : style
23235         });
23236
23237         return;
23238     }
23239     
23240     // hide stuff that is not compatible
23241     /**
23242      * @event blur
23243      * @hide
23244      */
23245     /**
23246      * @event change
23247      * @hide
23248      */
23249     /**
23250      * @event focus
23251      * @hide
23252      */
23253     /**
23254      * @event specialkey
23255      * @hide
23256      */
23257     /**
23258      * @cfg {String} fieldClass @hide
23259      */
23260     /**
23261      * @cfg {String} focusClass @hide
23262      */
23263     /**
23264      * @cfg {String} autoCreate @hide
23265      */
23266     /**
23267      * @cfg {String} inputType @hide
23268      */
23269     /**
23270      * @cfg {String} invalidClass @hide
23271      */
23272     /**
23273      * @cfg {String} invalidText @hide
23274      */
23275     /**
23276      * @cfg {String} msgFx @hide
23277      */
23278     /**
23279      * @cfg {String} validateOnBlur @hide
23280      */
23281 });
23282
23283 Roo.HtmlEditorCore.white = [
23284         'area', 'br', 'img', 'input', 'hr', 'wbr',
23285         
23286        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23287        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23288        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23289        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23290        'table',   'ul',         'xmp', 
23291        
23292        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23293       'thead',   'tr', 
23294      
23295       'dir', 'menu', 'ol', 'ul', 'dl',
23296        
23297       'embed',  'object'
23298 ];
23299
23300
23301 Roo.HtmlEditorCore.black = [
23302     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23303         'applet', // 
23304         'base',   'basefont', 'bgsound', 'blink',  'body', 
23305         'frame',  'frameset', 'head',    'html',   'ilayer', 
23306         'iframe', 'layer',  'link',     'meta',    'object',   
23307         'script', 'style' ,'title',  'xml' // clean later..
23308 ];
23309 Roo.HtmlEditorCore.clean = [
23310     'script', 'style', 'title', 'xml'
23311 ];
23312 Roo.HtmlEditorCore.remove = [
23313     'font'
23314 ];
23315 // attributes..
23316
23317 Roo.HtmlEditorCore.ablack = [
23318     'on'
23319 ];
23320     
23321 Roo.HtmlEditorCore.aclean = [ 
23322     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23323 ];
23324
23325 // protocols..
23326 Roo.HtmlEditorCore.pwhite= [
23327         'http',  'https',  'mailto'
23328 ];
23329
23330 // white listed style attributes.
23331 Roo.HtmlEditorCore.cwhite= [
23332       //  'text-align', /// default is to allow most things..
23333       
23334          
23335 //        'font-size'//??
23336 ];
23337
23338 // black listed style attributes.
23339 Roo.HtmlEditorCore.cblack= [
23340       //  'font-size' -- this can be set by the project 
23341 ];
23342
23343
23344 Roo.HtmlEditorCore.swapCodes   =[ 
23345     [    8211, "--" ], 
23346     [    8212, "--" ], 
23347     [    8216,  "'" ],  
23348     [    8217, "'" ],  
23349     [    8220, '"' ],  
23350     [    8221, '"' ],  
23351     [    8226, "*" ],  
23352     [    8230, "..." ]
23353 ]; 
23354
23355     /*
23356  * - LGPL
23357  *
23358  * HtmlEditor
23359  * 
23360  */
23361
23362 /**
23363  * @class Roo.bootstrap.HtmlEditor
23364  * @extends Roo.bootstrap.TextArea
23365  * Bootstrap HtmlEditor class
23366
23367  * @constructor
23368  * Create a new HtmlEditor
23369  * @param {Object} config The config object
23370  */
23371
23372 Roo.bootstrap.HtmlEditor = function(config){
23373     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23374     if (!this.toolbars) {
23375         this.toolbars = [];
23376     }
23377     
23378     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23379     this.addEvents({
23380             /**
23381              * @event initialize
23382              * Fires when the editor is fully initialized (including the iframe)
23383              * @param {HtmlEditor} this
23384              */
23385             initialize: true,
23386             /**
23387              * @event activate
23388              * Fires when the editor is first receives the focus. Any insertion must wait
23389              * until after this event.
23390              * @param {HtmlEditor} this
23391              */
23392             activate: true,
23393              /**
23394              * @event beforesync
23395              * Fires before the textarea is updated with content from the editor iframe. Return false
23396              * to cancel the sync.
23397              * @param {HtmlEditor} this
23398              * @param {String} html
23399              */
23400             beforesync: true,
23401              /**
23402              * @event beforepush
23403              * Fires before the iframe editor is updated with content from the textarea. Return false
23404              * to cancel the push.
23405              * @param {HtmlEditor} this
23406              * @param {String} html
23407              */
23408             beforepush: true,
23409              /**
23410              * @event sync
23411              * Fires when the textarea is updated with content from the editor iframe.
23412              * @param {HtmlEditor} this
23413              * @param {String} html
23414              */
23415             sync: true,
23416              /**
23417              * @event push
23418              * Fires when the iframe editor is updated with content from the textarea.
23419              * @param {HtmlEditor} this
23420              * @param {String} html
23421              */
23422             push: true,
23423              /**
23424              * @event editmodechange
23425              * Fires when the editor switches edit modes
23426              * @param {HtmlEditor} this
23427              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23428              */
23429             editmodechange: true,
23430             /**
23431              * @event editorevent
23432              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23433              * @param {HtmlEditor} this
23434              */
23435             editorevent: true,
23436             /**
23437              * @event firstfocus
23438              * Fires when on first focus - needed by toolbars..
23439              * @param {HtmlEditor} this
23440              */
23441             firstfocus: true,
23442             /**
23443              * @event autosave
23444              * Auto save the htmlEditor value as a file into Events
23445              * @param {HtmlEditor} this
23446              */
23447             autosave: true,
23448             /**
23449              * @event savedpreview
23450              * preview the saved version of htmlEditor
23451              * @param {HtmlEditor} this
23452              */
23453             savedpreview: true
23454         });
23455 };
23456
23457
23458 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23459     
23460     
23461       /**
23462      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23463      */
23464     toolbars : false,
23465     
23466      /**
23467     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23468     */
23469     btns : [],
23470    
23471      /**
23472      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23473      *                        Roo.resizable.
23474      */
23475     resizable : false,
23476      /**
23477      * @cfg {Number} height (in pixels)
23478      */   
23479     height: 300,
23480    /**
23481      * @cfg {Number} width (in pixels)
23482      */   
23483     width: false,
23484     
23485     /**
23486      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23487      * 
23488      */
23489     stylesheets: false,
23490     
23491     // id of frame..
23492     frameId: false,
23493     
23494     // private properties
23495     validationEvent : false,
23496     deferHeight: true,
23497     initialized : false,
23498     activated : false,
23499     
23500     onFocus : Roo.emptyFn,
23501     iframePad:3,
23502     hideMode:'offsets',
23503     
23504     tbContainer : false,
23505     
23506     bodyCls : '',
23507     
23508     toolbarContainer :function() {
23509         return this.wrap.select('.x-html-editor-tb',true).first();
23510     },
23511
23512     /**
23513      * Protected method that will not generally be called directly. It
23514      * is called when the editor creates its toolbar. Override this method if you need to
23515      * add custom toolbar buttons.
23516      * @param {HtmlEditor} editor
23517      */
23518     createToolbar : function(){
23519         Roo.log('renewing');
23520         Roo.log("create toolbars");
23521         
23522         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23523         this.toolbars[0].render(this.toolbarContainer());
23524         
23525         return;
23526         
23527 //        if (!editor.toolbars || !editor.toolbars.length) {
23528 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23529 //        }
23530 //        
23531 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23532 //            editor.toolbars[i] = Roo.factory(
23533 //                    typeof(editor.toolbars[i]) == 'string' ?
23534 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23535 //                Roo.bootstrap.HtmlEditor);
23536 //            editor.toolbars[i].init(editor);
23537 //        }
23538     },
23539
23540      
23541     // private
23542     onRender : function(ct, position)
23543     {
23544        // Roo.log("Call onRender: " + this.xtype);
23545         var _t = this;
23546         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23547       
23548         this.wrap = this.inputEl().wrap({
23549             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23550         });
23551         
23552         this.editorcore.onRender(ct, position);
23553          
23554         if (this.resizable) {
23555             this.resizeEl = new Roo.Resizable(this.wrap, {
23556                 pinned : true,
23557                 wrap: true,
23558                 dynamic : true,
23559                 minHeight : this.height,
23560                 height: this.height,
23561                 handles : this.resizable,
23562                 width: this.width,
23563                 listeners : {
23564                     resize : function(r, w, h) {
23565                         _t.onResize(w,h); // -something
23566                     }
23567                 }
23568             });
23569             
23570         }
23571         this.createToolbar(this);
23572        
23573         
23574         if(!this.width && this.resizable){
23575             this.setSize(this.wrap.getSize());
23576         }
23577         if (this.resizeEl) {
23578             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23579             // should trigger onReize..
23580         }
23581         
23582     },
23583
23584     // private
23585     onResize : function(w, h)
23586     {
23587         Roo.log('resize: ' +w + ',' + h );
23588         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23589         var ew = false;
23590         var eh = false;
23591         
23592         if(this.inputEl() ){
23593             if(typeof w == 'number'){
23594                 var aw = w - this.wrap.getFrameWidth('lr');
23595                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23596                 ew = aw;
23597             }
23598             if(typeof h == 'number'){
23599                  var tbh = -11;  // fixme it needs to tool bar size!
23600                 for (var i =0; i < this.toolbars.length;i++) {
23601                     // fixme - ask toolbars for heights?
23602                     tbh += this.toolbars[i].el.getHeight();
23603                     //if (this.toolbars[i].footer) {
23604                     //    tbh += this.toolbars[i].footer.el.getHeight();
23605                     //}
23606                 }
23607               
23608                 
23609                 
23610                 
23611                 
23612                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23613                 ah -= 5; // knock a few pixes off for look..
23614                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23615                 var eh = ah;
23616             }
23617         }
23618         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23619         this.editorcore.onResize(ew,eh);
23620         
23621     },
23622
23623     /**
23624      * Toggles the editor between standard and source edit mode.
23625      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23626      */
23627     toggleSourceEdit : function(sourceEditMode)
23628     {
23629         this.editorcore.toggleSourceEdit(sourceEditMode);
23630         
23631         if(this.editorcore.sourceEditMode){
23632             Roo.log('editor - showing textarea');
23633             
23634 //            Roo.log('in');
23635 //            Roo.log(this.syncValue());
23636             this.syncValue();
23637             this.inputEl().removeClass(['hide', 'x-hidden']);
23638             this.inputEl().dom.removeAttribute('tabIndex');
23639             this.inputEl().focus();
23640         }else{
23641             Roo.log('editor - hiding textarea');
23642 //            Roo.log('out')
23643 //            Roo.log(this.pushValue()); 
23644             this.pushValue();
23645             
23646             this.inputEl().addClass(['hide', 'x-hidden']);
23647             this.inputEl().dom.setAttribute('tabIndex', -1);
23648             //this.deferFocus();
23649         }
23650          
23651         if(this.resizable){
23652             this.setSize(this.wrap.getSize());
23653         }
23654         
23655         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23656     },
23657  
23658     // private (for BoxComponent)
23659     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23660
23661     // private (for BoxComponent)
23662     getResizeEl : function(){
23663         return this.wrap;
23664     },
23665
23666     // private (for BoxComponent)
23667     getPositionEl : function(){
23668         return this.wrap;
23669     },
23670
23671     // private
23672     initEvents : function(){
23673         this.originalValue = this.getValue();
23674     },
23675
23676 //    /**
23677 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23678 //     * @method
23679 //     */
23680 //    markInvalid : Roo.emptyFn,
23681 //    /**
23682 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23683 //     * @method
23684 //     */
23685 //    clearInvalid : Roo.emptyFn,
23686
23687     setValue : function(v){
23688         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23689         this.editorcore.pushValue();
23690     },
23691
23692      
23693     // private
23694     deferFocus : function(){
23695         this.focus.defer(10, this);
23696     },
23697
23698     // doc'ed in Field
23699     focus : function(){
23700         this.editorcore.focus();
23701         
23702     },
23703       
23704
23705     // private
23706     onDestroy : function(){
23707         
23708         
23709         
23710         if(this.rendered){
23711             
23712             for (var i =0; i < this.toolbars.length;i++) {
23713                 // fixme - ask toolbars for heights?
23714                 this.toolbars[i].onDestroy();
23715             }
23716             
23717             this.wrap.dom.innerHTML = '';
23718             this.wrap.remove();
23719         }
23720     },
23721
23722     // private
23723     onFirstFocus : function(){
23724         //Roo.log("onFirstFocus");
23725         this.editorcore.onFirstFocus();
23726          for (var i =0; i < this.toolbars.length;i++) {
23727             this.toolbars[i].onFirstFocus();
23728         }
23729         
23730     },
23731     
23732     // private
23733     syncValue : function()
23734     {   
23735         this.editorcore.syncValue();
23736     },
23737     
23738     pushValue : function()
23739     {   
23740         this.editorcore.pushValue();
23741     }
23742      
23743     
23744     // hide stuff that is not compatible
23745     /**
23746      * @event blur
23747      * @hide
23748      */
23749     /**
23750      * @event change
23751      * @hide
23752      */
23753     /**
23754      * @event focus
23755      * @hide
23756      */
23757     /**
23758      * @event specialkey
23759      * @hide
23760      */
23761     /**
23762      * @cfg {String} fieldClass @hide
23763      */
23764     /**
23765      * @cfg {String} focusClass @hide
23766      */
23767     /**
23768      * @cfg {String} autoCreate @hide
23769      */
23770     /**
23771      * @cfg {String} inputType @hide
23772      */
23773     /**
23774      * @cfg {String} invalidClass @hide
23775      */
23776     /**
23777      * @cfg {String} invalidText @hide
23778      */
23779     /**
23780      * @cfg {String} msgFx @hide
23781      */
23782     /**
23783      * @cfg {String} validateOnBlur @hide
23784      */
23785 });
23786  
23787     
23788    
23789    
23790    
23791       
23792 Roo.namespace('Roo.bootstrap.htmleditor');
23793 /**
23794  * @class Roo.bootstrap.HtmlEditorToolbar1
23795  * Basic Toolbar
23796  * 
23797  * Usage:
23798  *
23799  new Roo.bootstrap.HtmlEditor({
23800     ....
23801     toolbars : [
23802         new Roo.bootstrap.HtmlEditorToolbar1({
23803             disable : { fonts: 1 , format: 1, ..., ... , ...],
23804             btns : [ .... ]
23805         })
23806     }
23807      
23808  * 
23809  * @cfg {Object} disable List of elements to disable..
23810  * @cfg {Array} btns List of additional buttons.
23811  * 
23812  * 
23813  * NEEDS Extra CSS? 
23814  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23815  */
23816  
23817 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23818 {
23819     
23820     Roo.apply(this, config);
23821     
23822     // default disabled, based on 'good practice'..
23823     this.disable = this.disable || {};
23824     Roo.applyIf(this.disable, {
23825         fontSize : true,
23826         colors : true,
23827         specialElements : true
23828     });
23829     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23830     
23831     this.editor = config.editor;
23832     this.editorcore = config.editor.editorcore;
23833     
23834     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23835     
23836     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23837     // dont call parent... till later.
23838 }
23839 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23840      
23841     bar : true,
23842     
23843     editor : false,
23844     editorcore : false,
23845     
23846     
23847     formats : [
23848         "p" ,  
23849         "h1","h2","h3","h4","h5","h6", 
23850         "pre", "code", 
23851         "abbr", "acronym", "address", "cite", "samp", "var",
23852         'div','span'
23853     ],
23854     
23855     onRender : function(ct, position)
23856     {
23857        // Roo.log("Call onRender: " + this.xtype);
23858         
23859        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23860        Roo.log(this.el);
23861        this.el.dom.style.marginBottom = '0';
23862        var _this = this;
23863        var editorcore = this.editorcore;
23864        var editor= this.editor;
23865        
23866        var children = [];
23867        var btn = function(id,cmd , toggle, handler, html){
23868        
23869             var  event = toggle ? 'toggle' : 'click';
23870        
23871             var a = {
23872                 size : 'sm',
23873                 xtype: 'Button',
23874                 xns: Roo.bootstrap,
23875                 glyphicon : id,
23876                 cmd : id || cmd,
23877                 enableToggle:toggle !== false,
23878                 html : html || '',
23879                 pressed : toggle ? false : null,
23880                 listeners : {}
23881             };
23882             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23883                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23884             };
23885             children.push(a);
23886             return a;
23887        }
23888        
23889     //    var cb_box = function...
23890         
23891         var style = {
23892                 xtype: 'Button',
23893                 size : 'sm',
23894                 xns: Roo.bootstrap,
23895                 glyphicon : 'font',
23896                 //html : 'submit'
23897                 menu : {
23898                     xtype: 'Menu',
23899                     xns: Roo.bootstrap,
23900                     items:  []
23901                 }
23902         };
23903         Roo.each(this.formats, function(f) {
23904             style.menu.items.push({
23905                 xtype :'MenuItem',
23906                 xns: Roo.bootstrap,
23907                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23908                 tagname : f,
23909                 listeners : {
23910                     click : function()
23911                     {
23912                         editorcore.insertTag(this.tagname);
23913                         editor.focus();
23914                     }
23915                 }
23916                 
23917             });
23918         });
23919         children.push(style);   
23920         
23921         btn('bold',false,true);
23922         btn('italic',false,true);
23923         btn('align-left', 'justifyleft',true);
23924         btn('align-center', 'justifycenter',true);
23925         btn('align-right' , 'justifyright',true);
23926         btn('link', false, false, function(btn) {
23927             //Roo.log("create link?");
23928             var url = prompt(this.createLinkText, this.defaultLinkValue);
23929             if(url && url != 'http:/'+'/'){
23930                 this.editorcore.relayCmd('createlink', url);
23931             }
23932         }),
23933         btn('list','insertunorderedlist',true);
23934         btn('pencil', false,true, function(btn){
23935                 Roo.log(this);
23936                 this.toggleSourceEdit(btn.pressed);
23937         });
23938         
23939         if (this.editor.btns.length > 0) {
23940             for (var i = 0; i<this.editor.btns.length; i++) {
23941                 children.push(this.editor.btns[i]);
23942             }
23943         }
23944         
23945         /*
23946         var cog = {
23947                 xtype: 'Button',
23948                 size : 'sm',
23949                 xns: Roo.bootstrap,
23950                 glyphicon : 'cog',
23951                 //html : 'submit'
23952                 menu : {
23953                     xtype: 'Menu',
23954                     xns: Roo.bootstrap,
23955                     items:  []
23956                 }
23957         };
23958         
23959         cog.menu.items.push({
23960             xtype :'MenuItem',
23961             xns: Roo.bootstrap,
23962             html : Clean styles,
23963             tagname : f,
23964             listeners : {
23965                 click : function()
23966                 {
23967                     editorcore.insertTag(this.tagname);
23968                     editor.focus();
23969                 }
23970             }
23971             
23972         });
23973        */
23974         
23975          
23976        this.xtype = 'NavSimplebar';
23977         
23978         for(var i=0;i< children.length;i++) {
23979             
23980             this.buttons.add(this.addxtypeChild(children[i]));
23981             
23982         }
23983         
23984         editor.on('editorevent', this.updateToolbar, this);
23985     },
23986     onBtnClick : function(id)
23987     {
23988        this.editorcore.relayCmd(id);
23989        this.editorcore.focus();
23990     },
23991     
23992     /**
23993      * Protected method that will not generally be called directly. It triggers
23994      * a toolbar update by reading the markup state of the current selection in the editor.
23995      */
23996     updateToolbar: function(){
23997
23998         if(!this.editorcore.activated){
23999             this.editor.onFirstFocus(); // is this neeed?
24000             return;
24001         }
24002
24003         var btns = this.buttons; 
24004         var doc = this.editorcore.doc;
24005         btns.get('bold').setActive(doc.queryCommandState('bold'));
24006         btns.get('italic').setActive(doc.queryCommandState('italic'));
24007         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24008         
24009         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24010         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24011         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24012         
24013         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24014         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24015          /*
24016         
24017         var ans = this.editorcore.getAllAncestors();
24018         if (this.formatCombo) {
24019             
24020             
24021             var store = this.formatCombo.store;
24022             this.formatCombo.setValue("");
24023             for (var i =0; i < ans.length;i++) {
24024                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24025                     // select it..
24026                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24027                     break;
24028                 }
24029             }
24030         }
24031         
24032         
24033         
24034         // hides menus... - so this cant be on a menu...
24035         Roo.bootstrap.MenuMgr.hideAll();
24036         */
24037         Roo.bootstrap.MenuMgr.hideAll();
24038         //this.editorsyncValue();
24039     },
24040     onFirstFocus: function() {
24041         this.buttons.each(function(item){
24042            item.enable();
24043         });
24044     },
24045     toggleSourceEdit : function(sourceEditMode){
24046         
24047           
24048         if(sourceEditMode){
24049             Roo.log("disabling buttons");
24050            this.buttons.each( function(item){
24051                 if(item.cmd != 'pencil'){
24052                     item.disable();
24053                 }
24054             });
24055           
24056         }else{
24057             Roo.log("enabling buttons");
24058             if(this.editorcore.initialized){
24059                 this.buttons.each( function(item){
24060                     item.enable();
24061                 });
24062             }
24063             
24064         }
24065         Roo.log("calling toggole on editor");
24066         // tell the editor that it's been pressed..
24067         this.editor.toggleSourceEdit(sourceEditMode);
24068        
24069     }
24070 });
24071
24072
24073
24074
24075
24076 /**
24077  * @class Roo.bootstrap.Table.AbstractSelectionModel
24078  * @extends Roo.util.Observable
24079  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24080  * implemented by descendant classes.  This class should not be directly instantiated.
24081  * @constructor
24082  */
24083 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24084     this.locked = false;
24085     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24086 };
24087
24088
24089 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24090     /** @ignore Called by the grid automatically. Do not call directly. */
24091     init : function(grid){
24092         this.grid = grid;
24093         this.initEvents();
24094     },
24095
24096     /**
24097      * Locks the selections.
24098      */
24099     lock : function(){
24100         this.locked = true;
24101     },
24102
24103     /**
24104      * Unlocks the selections.
24105      */
24106     unlock : function(){
24107         this.locked = false;
24108     },
24109
24110     /**
24111      * Returns true if the selections are locked.
24112      * @return {Boolean}
24113      */
24114     isLocked : function(){
24115         return this.locked;
24116     }
24117 });
24118 /**
24119  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24120  * @class Roo.bootstrap.Table.RowSelectionModel
24121  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24122  * It supports multiple selections and keyboard selection/navigation. 
24123  * @constructor
24124  * @param {Object} config
24125  */
24126
24127 Roo.bootstrap.Table.RowSelectionModel = function(config){
24128     Roo.apply(this, config);
24129     this.selections = new Roo.util.MixedCollection(false, function(o){
24130         return o.id;
24131     });
24132
24133     this.last = false;
24134     this.lastActive = false;
24135
24136     this.addEvents({
24137         /**
24138              * @event selectionchange
24139              * Fires when the selection changes
24140              * @param {SelectionModel} this
24141              */
24142             "selectionchange" : true,
24143         /**
24144              * @event afterselectionchange
24145              * Fires after the selection changes (eg. by key press or clicking)
24146              * @param {SelectionModel} this
24147              */
24148             "afterselectionchange" : true,
24149         /**
24150              * @event beforerowselect
24151              * Fires when a row is selected being selected, return false to cancel.
24152              * @param {SelectionModel} this
24153              * @param {Number} rowIndex The selected index
24154              * @param {Boolean} keepExisting False if other selections will be cleared
24155              */
24156             "beforerowselect" : true,
24157         /**
24158              * @event rowselect
24159              * Fires when a row is selected.
24160              * @param {SelectionModel} this
24161              * @param {Number} rowIndex The selected index
24162              * @param {Roo.data.Record} r The record
24163              */
24164             "rowselect" : true,
24165         /**
24166              * @event rowdeselect
24167              * Fires when a row is deselected.
24168              * @param {SelectionModel} this
24169              * @param {Number} rowIndex The selected index
24170              */
24171         "rowdeselect" : true
24172     });
24173     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24174     this.locked = false;
24175  };
24176
24177 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24178     /**
24179      * @cfg {Boolean} singleSelect
24180      * True to allow selection of only one row at a time (defaults to false)
24181      */
24182     singleSelect : false,
24183
24184     // private
24185     initEvents : function()
24186     {
24187
24188         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24189         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24190         //}else{ // allow click to work like normal
24191          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24192         //}
24193         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24194         this.grid.on("rowclick", this.handleMouseDown, this);
24195         
24196         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24197             "up" : function(e){
24198                 if(!e.shiftKey){
24199                     this.selectPrevious(e.shiftKey);
24200                 }else if(this.last !== false && this.lastActive !== false){
24201                     var last = this.last;
24202                     this.selectRange(this.last,  this.lastActive-1);
24203                     this.grid.getView().focusRow(this.lastActive);
24204                     if(last !== false){
24205                         this.last = last;
24206                     }
24207                 }else{
24208                     this.selectFirstRow();
24209                 }
24210                 this.fireEvent("afterselectionchange", this);
24211             },
24212             "down" : function(e){
24213                 if(!e.shiftKey){
24214                     this.selectNext(e.shiftKey);
24215                 }else if(this.last !== false && this.lastActive !== false){
24216                     var last = this.last;
24217                     this.selectRange(this.last,  this.lastActive+1);
24218                     this.grid.getView().focusRow(this.lastActive);
24219                     if(last !== false){
24220                         this.last = last;
24221                     }
24222                 }else{
24223                     this.selectFirstRow();
24224                 }
24225                 this.fireEvent("afterselectionchange", this);
24226             },
24227             scope: this
24228         });
24229         this.grid.store.on('load', function(){
24230             this.selections.clear();
24231         },this);
24232         /*
24233         var view = this.grid.view;
24234         view.on("refresh", this.onRefresh, this);
24235         view.on("rowupdated", this.onRowUpdated, this);
24236         view.on("rowremoved", this.onRemove, this);
24237         */
24238     },
24239
24240     // private
24241     onRefresh : function()
24242     {
24243         var ds = this.grid.store, i, v = this.grid.view;
24244         var s = this.selections;
24245         s.each(function(r){
24246             if((i = ds.indexOfId(r.id)) != -1){
24247                 v.onRowSelect(i);
24248             }else{
24249                 s.remove(r);
24250             }
24251         });
24252     },
24253
24254     // private
24255     onRemove : function(v, index, r){
24256         this.selections.remove(r);
24257     },
24258
24259     // private
24260     onRowUpdated : function(v, index, r){
24261         if(this.isSelected(r)){
24262             v.onRowSelect(index);
24263         }
24264     },
24265
24266     /**
24267      * Select records.
24268      * @param {Array} records The records to select
24269      * @param {Boolean} keepExisting (optional) True to keep existing selections
24270      */
24271     selectRecords : function(records, keepExisting)
24272     {
24273         if(!keepExisting){
24274             this.clearSelections();
24275         }
24276             var ds = this.grid.store;
24277         for(var i = 0, len = records.length; i < len; i++){
24278             this.selectRow(ds.indexOf(records[i]), true);
24279         }
24280     },
24281
24282     /**
24283      * Gets the number of selected rows.
24284      * @return {Number}
24285      */
24286     getCount : function(){
24287         return this.selections.length;
24288     },
24289
24290     /**
24291      * Selects the first row in the grid.
24292      */
24293     selectFirstRow : function(){
24294         this.selectRow(0);
24295     },
24296
24297     /**
24298      * Select the last row.
24299      * @param {Boolean} keepExisting (optional) True to keep existing selections
24300      */
24301     selectLastRow : function(keepExisting){
24302         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24303         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24304     },
24305
24306     /**
24307      * Selects the row immediately following the last selected row.
24308      * @param {Boolean} keepExisting (optional) True to keep existing selections
24309      */
24310     selectNext : function(keepExisting)
24311     {
24312             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24313             this.selectRow(this.last+1, keepExisting);
24314             this.grid.getView().focusRow(this.last);
24315         }
24316     },
24317
24318     /**
24319      * Selects the row that precedes the last selected row.
24320      * @param {Boolean} keepExisting (optional) True to keep existing selections
24321      */
24322     selectPrevious : function(keepExisting){
24323         if(this.last){
24324             this.selectRow(this.last-1, keepExisting);
24325             this.grid.getView().focusRow(this.last);
24326         }
24327     },
24328
24329     /**
24330      * Returns the selected records
24331      * @return {Array} Array of selected records
24332      */
24333     getSelections : function(){
24334         return [].concat(this.selections.items);
24335     },
24336
24337     /**
24338      * Returns the first selected record.
24339      * @return {Record}
24340      */
24341     getSelected : function(){
24342         return this.selections.itemAt(0);
24343     },
24344
24345
24346     /**
24347      * Clears all selections.
24348      */
24349     clearSelections : function(fast)
24350     {
24351         if(this.locked) {
24352             return;
24353         }
24354         if(fast !== true){
24355                 var ds = this.grid.store;
24356             var s = this.selections;
24357             s.each(function(r){
24358                 this.deselectRow(ds.indexOfId(r.id));
24359             }, this);
24360             s.clear();
24361         }else{
24362             this.selections.clear();
24363         }
24364         this.last = false;
24365     },
24366
24367
24368     /**
24369      * Selects all rows.
24370      */
24371     selectAll : function(){
24372         if(this.locked) {
24373             return;
24374         }
24375         this.selections.clear();
24376         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24377             this.selectRow(i, true);
24378         }
24379     },
24380
24381     /**
24382      * Returns True if there is a selection.
24383      * @return {Boolean}
24384      */
24385     hasSelection : function(){
24386         return this.selections.length > 0;
24387     },
24388
24389     /**
24390      * Returns True if the specified row is selected.
24391      * @param {Number/Record} record The record or index of the record to check
24392      * @return {Boolean}
24393      */
24394     isSelected : function(index){
24395             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24396         return (r && this.selections.key(r.id) ? true : false);
24397     },
24398
24399     /**
24400      * Returns True if the specified record id is selected.
24401      * @param {String} id The id of record to check
24402      * @return {Boolean}
24403      */
24404     isIdSelected : function(id){
24405         return (this.selections.key(id) ? true : false);
24406     },
24407
24408
24409     // private
24410     handleMouseDBClick : function(e, t){
24411         
24412     },
24413     // private
24414     handleMouseDown : function(e, t)
24415     {
24416             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24417         if(this.isLocked() || rowIndex < 0 ){
24418             return;
24419         };
24420         if(e.shiftKey && this.last !== false){
24421             var last = this.last;
24422             this.selectRange(last, rowIndex, e.ctrlKey);
24423             this.last = last; // reset the last
24424             t.focus();
24425     
24426         }else{
24427             var isSelected = this.isSelected(rowIndex);
24428             //Roo.log("select row:" + rowIndex);
24429             if(isSelected){
24430                 this.deselectRow(rowIndex);
24431             } else {
24432                         this.selectRow(rowIndex, true);
24433             }
24434     
24435             /*
24436                 if(e.button !== 0 && isSelected){
24437                 alert('rowIndex 2: ' + rowIndex);
24438                     view.focusRow(rowIndex);
24439                 }else if(e.ctrlKey && isSelected){
24440                     this.deselectRow(rowIndex);
24441                 }else if(!isSelected){
24442                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24443                     view.focusRow(rowIndex);
24444                 }
24445             */
24446         }
24447         this.fireEvent("afterselectionchange", this);
24448     },
24449     // private
24450     handleDragableRowClick :  function(grid, rowIndex, e) 
24451     {
24452         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24453             this.selectRow(rowIndex, false);
24454             grid.view.focusRow(rowIndex);
24455              this.fireEvent("afterselectionchange", this);
24456         }
24457     },
24458     
24459     /**
24460      * Selects multiple rows.
24461      * @param {Array} rows Array of the indexes of the row to select
24462      * @param {Boolean} keepExisting (optional) True to keep existing selections
24463      */
24464     selectRows : function(rows, keepExisting){
24465         if(!keepExisting){
24466             this.clearSelections();
24467         }
24468         for(var i = 0, len = rows.length; i < len; i++){
24469             this.selectRow(rows[i], true);
24470         }
24471     },
24472
24473     /**
24474      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24475      * @param {Number} startRow The index of the first row in the range
24476      * @param {Number} endRow The index of the last row in the range
24477      * @param {Boolean} keepExisting (optional) True to retain existing selections
24478      */
24479     selectRange : function(startRow, endRow, keepExisting){
24480         if(this.locked) {
24481             return;
24482         }
24483         if(!keepExisting){
24484             this.clearSelections();
24485         }
24486         if(startRow <= endRow){
24487             for(var i = startRow; i <= endRow; i++){
24488                 this.selectRow(i, true);
24489             }
24490         }else{
24491             for(var i = startRow; i >= endRow; i--){
24492                 this.selectRow(i, true);
24493             }
24494         }
24495     },
24496
24497     /**
24498      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24499      * @param {Number} startRow The index of the first row in the range
24500      * @param {Number} endRow The index of the last row in the range
24501      */
24502     deselectRange : function(startRow, endRow, preventViewNotify){
24503         if(this.locked) {
24504             return;
24505         }
24506         for(var i = startRow; i <= endRow; i++){
24507             this.deselectRow(i, preventViewNotify);
24508         }
24509     },
24510
24511     /**
24512      * Selects a row.
24513      * @param {Number} row The index of the row to select
24514      * @param {Boolean} keepExisting (optional) True to keep existing selections
24515      */
24516     selectRow : function(index, keepExisting, preventViewNotify)
24517     {
24518             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24519             return;
24520         }
24521         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24522             if(!keepExisting || this.singleSelect){
24523                 this.clearSelections();
24524             }
24525             
24526             var r = this.grid.store.getAt(index);
24527             //console.log('selectRow - record id :' + r.id);
24528             
24529             this.selections.add(r);
24530             this.last = this.lastActive = index;
24531             if(!preventViewNotify){
24532                 var proxy = new Roo.Element(
24533                                 this.grid.getRowDom(index)
24534                 );
24535                 proxy.addClass('bg-info info');
24536             }
24537             this.fireEvent("rowselect", this, index, r);
24538             this.fireEvent("selectionchange", this);
24539         }
24540     },
24541
24542     /**
24543      * Deselects a row.
24544      * @param {Number} row The index of the row to deselect
24545      */
24546     deselectRow : function(index, preventViewNotify)
24547     {
24548         if(this.locked) {
24549             return;
24550         }
24551         if(this.last == index){
24552             this.last = false;
24553         }
24554         if(this.lastActive == index){
24555             this.lastActive = false;
24556         }
24557         
24558         var r = this.grid.store.getAt(index);
24559         if (!r) {
24560             return;
24561         }
24562         
24563         this.selections.remove(r);
24564         //.console.log('deselectRow - record id :' + r.id);
24565         if(!preventViewNotify){
24566         
24567             var proxy = new Roo.Element(
24568                 this.grid.getRowDom(index)
24569             );
24570             proxy.removeClass('bg-info info');
24571         }
24572         this.fireEvent("rowdeselect", this, index);
24573         this.fireEvent("selectionchange", this);
24574     },
24575
24576     // private
24577     restoreLast : function(){
24578         if(this._last){
24579             this.last = this._last;
24580         }
24581     },
24582
24583     // private
24584     acceptsNav : function(row, col, cm){
24585         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24586     },
24587
24588     // private
24589     onEditorKey : function(field, e){
24590         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24591         if(k == e.TAB){
24592             e.stopEvent();
24593             ed.completeEdit();
24594             if(e.shiftKey){
24595                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24596             }else{
24597                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24598             }
24599         }else if(k == e.ENTER && !e.ctrlKey){
24600             e.stopEvent();
24601             ed.completeEdit();
24602             if(e.shiftKey){
24603                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24604             }else{
24605                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24606             }
24607         }else if(k == e.ESC){
24608             ed.cancelEdit();
24609         }
24610         if(newCell){
24611             g.startEditing(newCell[0], newCell[1]);
24612         }
24613     }
24614 });
24615 /*
24616  * Based on:
24617  * Ext JS Library 1.1.1
24618  * Copyright(c) 2006-2007, Ext JS, LLC.
24619  *
24620  * Originally Released Under LGPL - original licence link has changed is not relivant.
24621  *
24622  * Fork - LGPL
24623  * <script type="text/javascript">
24624  */
24625  
24626 /**
24627  * @class Roo.bootstrap.PagingToolbar
24628  * @extends Roo.bootstrap.NavSimplebar
24629  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24630  * @constructor
24631  * Create a new PagingToolbar
24632  * @param {Object} config The config object
24633  * @param {Roo.data.Store} store
24634  */
24635 Roo.bootstrap.PagingToolbar = function(config)
24636 {
24637     // old args format still supported... - xtype is prefered..
24638         // created from xtype...
24639     
24640     this.ds = config.dataSource;
24641     
24642     if (config.store && !this.ds) {
24643         this.store= Roo.factory(config.store, Roo.data);
24644         this.ds = this.store;
24645         this.ds.xmodule = this.xmodule || false;
24646     }
24647     
24648     this.toolbarItems = [];
24649     if (config.items) {
24650         this.toolbarItems = config.items;
24651     }
24652     
24653     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24654     
24655     this.cursor = 0;
24656     
24657     if (this.ds) { 
24658         this.bind(this.ds);
24659     }
24660     
24661     if (Roo.bootstrap.version == 4) {
24662         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24663     } else {
24664     
24665         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24666     }
24667     
24668 };
24669
24670 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24671     /**
24672      * @cfg {Roo.data.Store} dataSource
24673      * The underlying data store providing the paged data
24674      */
24675     /**
24676      * @cfg {String/HTMLElement/Element} container
24677      * container The id or element that will contain the toolbar
24678      */
24679     /**
24680      * @cfg {Boolean} displayInfo
24681      * True to display the displayMsg (defaults to false)
24682      */
24683     /**
24684      * @cfg {Number} pageSize
24685      * The number of records to display per page (defaults to 20)
24686      */
24687     pageSize: 20,
24688     /**
24689      * @cfg {String} displayMsg
24690      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24691      */
24692     displayMsg : 'Displaying {0} - {1} of {2}',
24693     /**
24694      * @cfg {String} emptyMsg
24695      * The message to display when no records are found (defaults to "No data to display")
24696      */
24697     emptyMsg : 'No data to display',
24698     /**
24699      * Customizable piece of the default paging text (defaults to "Page")
24700      * @type String
24701      */
24702     beforePageText : "Page",
24703     /**
24704      * Customizable piece of the default paging text (defaults to "of %0")
24705      * @type String
24706      */
24707     afterPageText : "of {0}",
24708     /**
24709      * Customizable piece of the default paging text (defaults to "First Page")
24710      * @type String
24711      */
24712     firstText : "First Page",
24713     /**
24714      * Customizable piece of the default paging text (defaults to "Previous Page")
24715      * @type String
24716      */
24717     prevText : "Previous Page",
24718     /**
24719      * Customizable piece of the default paging text (defaults to "Next Page")
24720      * @type String
24721      */
24722     nextText : "Next Page",
24723     /**
24724      * Customizable piece of the default paging text (defaults to "Last Page")
24725      * @type String
24726      */
24727     lastText : "Last Page",
24728     /**
24729      * Customizable piece of the default paging text (defaults to "Refresh")
24730      * @type String
24731      */
24732     refreshText : "Refresh",
24733
24734     buttons : false,
24735     // private
24736     onRender : function(ct, position) 
24737     {
24738         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24739         this.navgroup.parentId = this.id;
24740         this.navgroup.onRender(this.el, null);
24741         // add the buttons to the navgroup
24742         
24743         if(this.displayInfo){
24744             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24745             this.displayEl = this.el.select('.x-paging-info', true).first();
24746 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24747 //            this.displayEl = navel.el.select('span',true).first();
24748         }
24749         
24750         var _this = this;
24751         
24752         if(this.buttons){
24753             Roo.each(_this.buttons, function(e){ // this might need to use render????
24754                Roo.factory(e).render(_this.el);
24755             });
24756         }
24757             
24758         Roo.each(_this.toolbarItems, function(e) {
24759             _this.navgroup.addItem(e);
24760         });
24761         
24762         
24763         this.first = this.navgroup.addItem({
24764             tooltip: this.firstText,
24765             cls: "prev",
24766             icon : 'fa fa-step-backward',
24767             disabled: true,
24768             preventDefault: true,
24769             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24770         });
24771         
24772         this.prev =  this.navgroup.addItem({
24773             tooltip: this.prevText,
24774             cls: "prev",
24775             icon : 'fa fa-backward',
24776             disabled: true,
24777             preventDefault: true,
24778             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24779         });
24780     //this.addSeparator();
24781         
24782         
24783         var field = this.navgroup.addItem( {
24784             tagtype : 'span',
24785             cls : 'x-paging-position',
24786             
24787             html : this.beforePageText  +
24788                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24789                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24790          } ); //?? escaped?
24791         
24792         this.field = field.el.select('input', true).first();
24793         this.field.on("keydown", this.onPagingKeydown, this);
24794         this.field.on("focus", function(){this.dom.select();});
24795     
24796     
24797         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24798         //this.field.setHeight(18);
24799         //this.addSeparator();
24800         this.next = this.navgroup.addItem({
24801             tooltip: this.nextText,
24802             cls: "next",
24803             html : ' <i class="fa fa-forward">',
24804             disabled: true,
24805             preventDefault: true,
24806             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24807         });
24808         this.last = this.navgroup.addItem({
24809             tooltip: this.lastText,
24810             icon : 'fa fa-step-forward',
24811             cls: "next",
24812             disabled: true,
24813             preventDefault: true,
24814             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24815         });
24816     //this.addSeparator();
24817         this.loading = this.navgroup.addItem({
24818             tooltip: this.refreshText,
24819             icon: 'fa fa-refresh',
24820             preventDefault: true,
24821             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24822         });
24823         
24824     },
24825
24826     // private
24827     updateInfo : function(){
24828         if(this.displayEl){
24829             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24830             var msg = count == 0 ?
24831                 this.emptyMsg :
24832                 String.format(
24833                     this.displayMsg,
24834                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24835                 );
24836             this.displayEl.update(msg);
24837         }
24838     },
24839
24840     // private
24841     onLoad : function(ds, r, o)
24842     {
24843         this.cursor = o.params.start ? o.params.start : 0;
24844         
24845         var d = this.getPageData(),
24846             ap = d.activePage,
24847             ps = d.pages;
24848         
24849         
24850         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24851         this.field.dom.value = ap;
24852         this.first.setDisabled(ap == 1);
24853         this.prev.setDisabled(ap == 1);
24854         this.next.setDisabled(ap == ps);
24855         this.last.setDisabled(ap == ps);
24856         this.loading.enable();
24857         this.updateInfo();
24858     },
24859
24860     // private
24861     getPageData : function(){
24862         var total = this.ds.getTotalCount();
24863         return {
24864             total : total,
24865             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24866             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24867         };
24868     },
24869
24870     // private
24871     onLoadError : function(){
24872         this.loading.enable();
24873     },
24874
24875     // private
24876     onPagingKeydown : function(e){
24877         var k = e.getKey();
24878         var d = this.getPageData();
24879         if(k == e.RETURN){
24880             var v = this.field.dom.value, pageNum;
24881             if(!v || isNaN(pageNum = parseInt(v, 10))){
24882                 this.field.dom.value = d.activePage;
24883                 return;
24884             }
24885             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24886             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24887             e.stopEvent();
24888         }
24889         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))
24890         {
24891           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24892           this.field.dom.value = pageNum;
24893           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24894           e.stopEvent();
24895         }
24896         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24897         {
24898           var v = this.field.dom.value, pageNum; 
24899           var increment = (e.shiftKey) ? 10 : 1;
24900           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24901                 increment *= -1;
24902           }
24903           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24904             this.field.dom.value = d.activePage;
24905             return;
24906           }
24907           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24908           {
24909             this.field.dom.value = parseInt(v, 10) + increment;
24910             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24911             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24912           }
24913           e.stopEvent();
24914         }
24915     },
24916
24917     // private
24918     beforeLoad : function(){
24919         if(this.loading){
24920             this.loading.disable();
24921         }
24922     },
24923
24924     // private
24925     onClick : function(which){
24926         
24927         var ds = this.ds;
24928         if (!ds) {
24929             return;
24930         }
24931         
24932         switch(which){
24933             case "first":
24934                 ds.load({params:{start: 0, limit: this.pageSize}});
24935             break;
24936             case "prev":
24937                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24938             break;
24939             case "next":
24940                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24941             break;
24942             case "last":
24943                 var total = ds.getTotalCount();
24944                 var extra = total % this.pageSize;
24945                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24946                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24947             break;
24948             case "refresh":
24949                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24950             break;
24951         }
24952     },
24953
24954     /**
24955      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24956      * @param {Roo.data.Store} store The data store to unbind
24957      */
24958     unbind : function(ds){
24959         ds.un("beforeload", this.beforeLoad, this);
24960         ds.un("load", this.onLoad, this);
24961         ds.un("loadexception", this.onLoadError, this);
24962         ds.un("remove", this.updateInfo, this);
24963         ds.un("add", this.updateInfo, this);
24964         this.ds = undefined;
24965     },
24966
24967     /**
24968      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24969      * @param {Roo.data.Store} store The data store to bind
24970      */
24971     bind : function(ds){
24972         ds.on("beforeload", this.beforeLoad, this);
24973         ds.on("load", this.onLoad, this);
24974         ds.on("loadexception", this.onLoadError, this);
24975         ds.on("remove", this.updateInfo, this);
24976         ds.on("add", this.updateInfo, this);
24977         this.ds = ds;
24978     }
24979 });/*
24980  * - LGPL
24981  *
24982  * element
24983  * 
24984  */
24985
24986 /**
24987  * @class Roo.bootstrap.MessageBar
24988  * @extends Roo.bootstrap.Component
24989  * Bootstrap MessageBar class
24990  * @cfg {String} html contents of the MessageBar
24991  * @cfg {String} weight (info | success | warning | danger) default info
24992  * @cfg {String} beforeClass insert the bar before the given class
24993  * @cfg {Boolean} closable (true | false) default false
24994  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24995  * 
24996  * @constructor
24997  * Create a new Element
24998  * @param {Object} config The config object
24999  */
25000
25001 Roo.bootstrap.MessageBar = function(config){
25002     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25003 };
25004
25005 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25006     
25007     html: '',
25008     weight: 'info',
25009     closable: false,
25010     fixed: false,
25011     beforeClass: 'bootstrap-sticky-wrap',
25012     
25013     getAutoCreate : function(){
25014         
25015         var cfg = {
25016             tag: 'div',
25017             cls: 'alert alert-dismissable alert-' + this.weight,
25018             cn: [
25019                 {
25020                     tag: 'span',
25021                     cls: 'message',
25022                     html: this.html || ''
25023                 }
25024             ]
25025         };
25026         
25027         if(this.fixed){
25028             cfg.cls += ' alert-messages-fixed';
25029         }
25030         
25031         if(this.closable){
25032             cfg.cn.push({
25033                 tag: 'button',
25034                 cls: 'close',
25035                 html: 'x'
25036             });
25037         }
25038         
25039         return cfg;
25040     },
25041     
25042     onRender : function(ct, position)
25043     {
25044         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25045         
25046         if(!this.el){
25047             var cfg = Roo.apply({},  this.getAutoCreate());
25048             cfg.id = Roo.id();
25049             
25050             if (this.cls) {
25051                 cfg.cls += ' ' + this.cls;
25052             }
25053             if (this.style) {
25054                 cfg.style = this.style;
25055             }
25056             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25057             
25058             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25059         }
25060         
25061         this.el.select('>button.close').on('click', this.hide, this);
25062         
25063     },
25064     
25065     show : function()
25066     {
25067         if (!this.rendered) {
25068             this.render();
25069         }
25070         
25071         this.el.show();
25072         
25073         this.fireEvent('show', this);
25074         
25075     },
25076     
25077     hide : function()
25078     {
25079         if (!this.rendered) {
25080             this.render();
25081         }
25082         
25083         this.el.hide();
25084         
25085         this.fireEvent('hide', this);
25086     },
25087     
25088     update : function()
25089     {
25090 //        var e = this.el.dom.firstChild;
25091 //        
25092 //        if(this.closable){
25093 //            e = e.nextSibling;
25094 //        }
25095 //        
25096 //        e.data = this.html || '';
25097
25098         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25099     }
25100    
25101 });
25102
25103  
25104
25105      /*
25106  * - LGPL
25107  *
25108  * Graph
25109  * 
25110  */
25111
25112
25113 /**
25114  * @class Roo.bootstrap.Graph
25115  * @extends Roo.bootstrap.Component
25116  * Bootstrap Graph class
25117 > Prameters
25118  -sm {number} sm 4
25119  -md {number} md 5
25120  @cfg {String} graphtype  bar | vbar | pie
25121  @cfg {number} g_x coodinator | centre x (pie)
25122  @cfg {number} g_y coodinator | centre y (pie)
25123  @cfg {number} g_r radius (pie)
25124  @cfg {number} g_height height of the chart (respected by all elements in the set)
25125  @cfg {number} g_width width of the chart (respected by all elements in the set)
25126  @cfg {Object} title The title of the chart
25127     
25128  -{Array}  values
25129  -opts (object) options for the chart 
25130      o {
25131      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25132      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25133      o vgutter (number)
25134      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.
25135      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25136      o to
25137      o stretch (boolean)
25138      o }
25139  -opts (object) options for the pie
25140      o{
25141      o cut
25142      o startAngle (number)
25143      o endAngle (number)
25144      } 
25145  *
25146  * @constructor
25147  * Create a new Input
25148  * @param {Object} config The config object
25149  */
25150
25151 Roo.bootstrap.Graph = function(config){
25152     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25153     
25154     this.addEvents({
25155         // img events
25156         /**
25157          * @event click
25158          * The img click event for the img.
25159          * @param {Roo.EventObject} e
25160          */
25161         "click" : true
25162     });
25163 };
25164
25165 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25166     
25167     sm: 4,
25168     md: 5,
25169     graphtype: 'bar',
25170     g_height: 250,
25171     g_width: 400,
25172     g_x: 50,
25173     g_y: 50,
25174     g_r: 30,
25175     opts:{
25176         //g_colors: this.colors,
25177         g_type: 'soft',
25178         g_gutter: '20%'
25179
25180     },
25181     title : false,
25182
25183     getAutoCreate : function(){
25184         
25185         var cfg = {
25186             tag: 'div',
25187             html : null
25188         };
25189         
25190         
25191         return  cfg;
25192     },
25193
25194     onRender : function(ct,position){
25195         
25196         
25197         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25198         
25199         if (typeof(Raphael) == 'undefined') {
25200             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25201             return;
25202         }
25203         
25204         this.raphael = Raphael(this.el.dom);
25205         
25206                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25207                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25208                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25209                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25210                 /*
25211                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25212                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25213                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25214                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25215                 
25216                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25217                 r.barchart(330, 10, 300, 220, data1);
25218                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25219                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25220                 */
25221                 
25222                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25223                 // r.barchart(30, 30, 560, 250,  xdata, {
25224                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25225                 //     axis : "0 0 1 1",
25226                 //     axisxlabels :  xdata
25227                 //     //yvalues : cols,
25228                    
25229                 // });
25230 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25231 //        
25232 //        this.load(null,xdata,{
25233 //                axis : "0 0 1 1",
25234 //                axisxlabels :  xdata
25235 //                });
25236
25237     },
25238
25239     load : function(graphtype,xdata,opts)
25240     {
25241         this.raphael.clear();
25242         if(!graphtype) {
25243             graphtype = this.graphtype;
25244         }
25245         if(!opts){
25246             opts = this.opts;
25247         }
25248         var r = this.raphael,
25249             fin = function () {
25250                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25251             },
25252             fout = function () {
25253                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25254             },
25255             pfin = function() {
25256                 this.sector.stop();
25257                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25258
25259                 if (this.label) {
25260                     this.label[0].stop();
25261                     this.label[0].attr({ r: 7.5 });
25262                     this.label[1].attr({ "font-weight": 800 });
25263                 }
25264             },
25265             pfout = function() {
25266                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25267
25268                 if (this.label) {
25269                     this.label[0].animate({ r: 5 }, 500, "bounce");
25270                     this.label[1].attr({ "font-weight": 400 });
25271                 }
25272             };
25273
25274         switch(graphtype){
25275             case 'bar':
25276                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25277                 break;
25278             case 'hbar':
25279                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25280                 break;
25281             case 'pie':
25282 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25283 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25284 //            
25285                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25286                 
25287                 break;
25288
25289         }
25290         
25291         if(this.title){
25292             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25293         }
25294         
25295     },
25296     
25297     setTitle: function(o)
25298     {
25299         this.title = o;
25300     },
25301     
25302     initEvents: function() {
25303         
25304         if(!this.href){
25305             this.el.on('click', this.onClick, this);
25306         }
25307     },
25308     
25309     onClick : function(e)
25310     {
25311         Roo.log('img onclick');
25312         this.fireEvent('click', this, e);
25313     }
25314    
25315 });
25316
25317  
25318 /*
25319  * - LGPL
25320  *
25321  * numberBox
25322  * 
25323  */
25324 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25325
25326 /**
25327  * @class Roo.bootstrap.dash.NumberBox
25328  * @extends Roo.bootstrap.Component
25329  * Bootstrap NumberBox class
25330  * @cfg {String} headline Box headline
25331  * @cfg {String} content Box content
25332  * @cfg {String} icon Box icon
25333  * @cfg {String} footer Footer text
25334  * @cfg {String} fhref Footer href
25335  * 
25336  * @constructor
25337  * Create a new NumberBox
25338  * @param {Object} config The config object
25339  */
25340
25341
25342 Roo.bootstrap.dash.NumberBox = function(config){
25343     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25344     
25345 };
25346
25347 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25348     
25349     headline : '',
25350     content : '',
25351     icon : '',
25352     footer : '',
25353     fhref : '',
25354     ficon : '',
25355     
25356     getAutoCreate : function(){
25357         
25358         var cfg = {
25359             tag : 'div',
25360             cls : 'small-box ',
25361             cn : [
25362                 {
25363                     tag : 'div',
25364                     cls : 'inner',
25365                     cn :[
25366                         {
25367                             tag : 'h3',
25368                             cls : 'roo-headline',
25369                             html : this.headline
25370                         },
25371                         {
25372                             tag : 'p',
25373                             cls : 'roo-content',
25374                             html : this.content
25375                         }
25376                     ]
25377                 }
25378             ]
25379         };
25380         
25381         if(this.icon){
25382             cfg.cn.push({
25383                 tag : 'div',
25384                 cls : 'icon',
25385                 cn :[
25386                     {
25387                         tag : 'i',
25388                         cls : 'ion ' + this.icon
25389                     }
25390                 ]
25391             });
25392         }
25393         
25394         if(this.footer){
25395             var footer = {
25396                 tag : 'a',
25397                 cls : 'small-box-footer',
25398                 href : this.fhref || '#',
25399                 html : this.footer
25400             };
25401             
25402             cfg.cn.push(footer);
25403             
25404         }
25405         
25406         return  cfg;
25407     },
25408
25409     onRender : function(ct,position){
25410         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25411
25412
25413        
25414                 
25415     },
25416
25417     setHeadline: function (value)
25418     {
25419         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25420     },
25421     
25422     setFooter: function (value, href)
25423     {
25424         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25425         
25426         if(href){
25427             this.el.select('a.small-box-footer',true).first().attr('href', href);
25428         }
25429         
25430     },
25431
25432     setContent: function (value)
25433     {
25434         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25435     },
25436
25437     initEvents: function() 
25438     {   
25439         
25440     }
25441     
25442 });
25443
25444  
25445 /*
25446  * - LGPL
25447  *
25448  * TabBox
25449  * 
25450  */
25451 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25452
25453 /**
25454  * @class Roo.bootstrap.dash.TabBox
25455  * @extends Roo.bootstrap.Component
25456  * Bootstrap TabBox class
25457  * @cfg {String} title Title of the TabBox
25458  * @cfg {String} icon Icon of the TabBox
25459  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25460  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25461  * 
25462  * @constructor
25463  * Create a new TabBox
25464  * @param {Object} config The config object
25465  */
25466
25467
25468 Roo.bootstrap.dash.TabBox = function(config){
25469     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25470     this.addEvents({
25471         // raw events
25472         /**
25473          * @event addpane
25474          * When a pane is added
25475          * @param {Roo.bootstrap.dash.TabPane} pane
25476          */
25477         "addpane" : true,
25478         /**
25479          * @event activatepane
25480          * When a pane is activated
25481          * @param {Roo.bootstrap.dash.TabPane} pane
25482          */
25483         "activatepane" : true
25484         
25485          
25486     });
25487     
25488     this.panes = [];
25489 };
25490
25491 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25492
25493     title : '',
25494     icon : false,
25495     showtabs : true,
25496     tabScrollable : false,
25497     
25498     getChildContainer : function()
25499     {
25500         return this.el.select('.tab-content', true).first();
25501     },
25502     
25503     getAutoCreate : function(){
25504         
25505         var header = {
25506             tag: 'li',
25507             cls: 'pull-left header',
25508             html: this.title,
25509             cn : []
25510         };
25511         
25512         if(this.icon){
25513             header.cn.push({
25514                 tag: 'i',
25515                 cls: 'fa ' + this.icon
25516             });
25517         }
25518         
25519         var h = {
25520             tag: 'ul',
25521             cls: 'nav nav-tabs pull-right',
25522             cn: [
25523                 header
25524             ]
25525         };
25526         
25527         if(this.tabScrollable){
25528             h = {
25529                 tag: 'div',
25530                 cls: 'tab-header',
25531                 cn: [
25532                     {
25533                         tag: 'ul',
25534                         cls: 'nav nav-tabs pull-right',
25535                         cn: [
25536                             header
25537                         ]
25538                     }
25539                 ]
25540             };
25541         }
25542         
25543         var cfg = {
25544             tag: 'div',
25545             cls: 'nav-tabs-custom',
25546             cn: [
25547                 h,
25548                 {
25549                     tag: 'div',
25550                     cls: 'tab-content no-padding',
25551                     cn: []
25552                 }
25553             ]
25554         };
25555
25556         return  cfg;
25557     },
25558     initEvents : function()
25559     {
25560         //Roo.log('add add pane handler');
25561         this.on('addpane', this.onAddPane, this);
25562     },
25563      /**
25564      * Updates the box title
25565      * @param {String} html to set the title to.
25566      */
25567     setTitle : function(value)
25568     {
25569         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25570     },
25571     onAddPane : function(pane)
25572     {
25573         this.panes.push(pane);
25574         //Roo.log('addpane');
25575         //Roo.log(pane);
25576         // tabs are rendere left to right..
25577         if(!this.showtabs){
25578             return;
25579         }
25580         
25581         var ctr = this.el.select('.nav-tabs', true).first();
25582          
25583          
25584         var existing = ctr.select('.nav-tab',true);
25585         var qty = existing.getCount();;
25586         
25587         
25588         var tab = ctr.createChild({
25589             tag : 'li',
25590             cls : 'nav-tab' + (qty ? '' : ' active'),
25591             cn : [
25592                 {
25593                     tag : 'a',
25594                     href:'#',
25595                     html : pane.title
25596                 }
25597             ]
25598         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25599         pane.tab = tab;
25600         
25601         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25602         if (!qty) {
25603             pane.el.addClass('active');
25604         }
25605         
25606                 
25607     },
25608     onTabClick : function(ev,un,ob,pane)
25609     {
25610         //Roo.log('tab - prev default');
25611         ev.preventDefault();
25612         
25613         
25614         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25615         pane.tab.addClass('active');
25616         //Roo.log(pane.title);
25617         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25618         // technically we should have a deactivate event.. but maybe add later.
25619         // and it should not de-activate the selected tab...
25620         this.fireEvent('activatepane', pane);
25621         pane.el.addClass('active');
25622         pane.fireEvent('activate');
25623         
25624         
25625     },
25626     
25627     getActivePane : function()
25628     {
25629         var r = false;
25630         Roo.each(this.panes, function(p) {
25631             if(p.el.hasClass('active')){
25632                 r = p;
25633                 return false;
25634             }
25635             
25636             return;
25637         });
25638         
25639         return r;
25640     }
25641     
25642     
25643 });
25644
25645  
25646 /*
25647  * - LGPL
25648  *
25649  * Tab pane
25650  * 
25651  */
25652 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25653 /**
25654  * @class Roo.bootstrap.TabPane
25655  * @extends Roo.bootstrap.Component
25656  * Bootstrap TabPane class
25657  * @cfg {Boolean} active (false | true) Default false
25658  * @cfg {String} title title of panel
25659
25660  * 
25661  * @constructor
25662  * Create a new TabPane
25663  * @param {Object} config The config object
25664  */
25665
25666 Roo.bootstrap.dash.TabPane = function(config){
25667     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25668     
25669     this.addEvents({
25670         // raw events
25671         /**
25672          * @event activate
25673          * When a pane is activated
25674          * @param {Roo.bootstrap.dash.TabPane} pane
25675          */
25676         "activate" : true
25677          
25678     });
25679 };
25680
25681 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25682     
25683     active : false,
25684     title : '',
25685     
25686     // the tabBox that this is attached to.
25687     tab : false,
25688      
25689     getAutoCreate : function() 
25690     {
25691         var cfg = {
25692             tag: 'div',
25693             cls: 'tab-pane'
25694         };
25695         
25696         if(this.active){
25697             cfg.cls += ' active';
25698         }
25699         
25700         return cfg;
25701     },
25702     initEvents  : function()
25703     {
25704         //Roo.log('trigger add pane handler');
25705         this.parent().fireEvent('addpane', this)
25706     },
25707     
25708      /**
25709      * Updates the tab title 
25710      * @param {String} html to set the title to.
25711      */
25712     setTitle: function(str)
25713     {
25714         if (!this.tab) {
25715             return;
25716         }
25717         this.title = str;
25718         this.tab.select('a', true).first().dom.innerHTML = str;
25719         
25720     }
25721     
25722     
25723     
25724 });
25725
25726  
25727
25728
25729  /*
25730  * - LGPL
25731  *
25732  * menu
25733  * 
25734  */
25735 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25736
25737 /**
25738  * @class Roo.bootstrap.menu.Menu
25739  * @extends Roo.bootstrap.Component
25740  * Bootstrap Menu class - container for Menu
25741  * @cfg {String} html Text of the menu
25742  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25743  * @cfg {String} icon Font awesome icon
25744  * @cfg {String} pos Menu align to (top | bottom) default bottom
25745  * 
25746  * 
25747  * @constructor
25748  * Create a new Menu
25749  * @param {Object} config The config object
25750  */
25751
25752
25753 Roo.bootstrap.menu.Menu = function(config){
25754     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25755     
25756     this.addEvents({
25757         /**
25758          * @event beforeshow
25759          * Fires before this menu is displayed
25760          * @param {Roo.bootstrap.menu.Menu} this
25761          */
25762         beforeshow : true,
25763         /**
25764          * @event beforehide
25765          * Fires before this menu is hidden
25766          * @param {Roo.bootstrap.menu.Menu} this
25767          */
25768         beforehide : true,
25769         /**
25770          * @event show
25771          * Fires after this menu is displayed
25772          * @param {Roo.bootstrap.menu.Menu} this
25773          */
25774         show : true,
25775         /**
25776          * @event hide
25777          * Fires after this menu is hidden
25778          * @param {Roo.bootstrap.menu.Menu} this
25779          */
25780         hide : true,
25781         /**
25782          * @event click
25783          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25784          * @param {Roo.bootstrap.menu.Menu} this
25785          * @param {Roo.EventObject} e
25786          */
25787         click : true
25788     });
25789     
25790 };
25791
25792 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25793     
25794     submenu : false,
25795     html : '',
25796     weight : 'default',
25797     icon : false,
25798     pos : 'bottom',
25799     
25800     
25801     getChildContainer : function() {
25802         if(this.isSubMenu){
25803             return this.el;
25804         }
25805         
25806         return this.el.select('ul.dropdown-menu', true).first();  
25807     },
25808     
25809     getAutoCreate : function()
25810     {
25811         var text = [
25812             {
25813                 tag : 'span',
25814                 cls : 'roo-menu-text',
25815                 html : this.html
25816             }
25817         ];
25818         
25819         if(this.icon){
25820             text.unshift({
25821                 tag : 'i',
25822                 cls : 'fa ' + this.icon
25823             })
25824         }
25825         
25826         
25827         var cfg = {
25828             tag : 'div',
25829             cls : 'btn-group',
25830             cn : [
25831                 {
25832                     tag : 'button',
25833                     cls : 'dropdown-button btn btn-' + this.weight,
25834                     cn : text
25835                 },
25836                 {
25837                     tag : 'button',
25838                     cls : 'dropdown-toggle btn btn-' + this.weight,
25839                     cn : [
25840                         {
25841                             tag : 'span',
25842                             cls : 'caret'
25843                         }
25844                     ]
25845                 },
25846                 {
25847                     tag : 'ul',
25848                     cls : 'dropdown-menu'
25849                 }
25850             ]
25851             
25852         };
25853         
25854         if(this.pos == 'top'){
25855             cfg.cls += ' dropup';
25856         }
25857         
25858         if(this.isSubMenu){
25859             cfg = {
25860                 tag : 'ul',
25861                 cls : 'dropdown-menu'
25862             }
25863         }
25864         
25865         return cfg;
25866     },
25867     
25868     onRender : function(ct, position)
25869     {
25870         this.isSubMenu = ct.hasClass('dropdown-submenu');
25871         
25872         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25873     },
25874     
25875     initEvents : function() 
25876     {
25877         if(this.isSubMenu){
25878             return;
25879         }
25880         
25881         this.hidden = true;
25882         
25883         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25884         this.triggerEl.on('click', this.onTriggerPress, this);
25885         
25886         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25887         this.buttonEl.on('click', this.onClick, this);
25888         
25889     },
25890     
25891     list : function()
25892     {
25893         if(this.isSubMenu){
25894             return this.el;
25895         }
25896         
25897         return this.el.select('ul.dropdown-menu', true).first();
25898     },
25899     
25900     onClick : function(e)
25901     {
25902         this.fireEvent("click", this, e);
25903     },
25904     
25905     onTriggerPress  : function(e)
25906     {   
25907         if (this.isVisible()) {
25908             this.hide();
25909         } else {
25910             this.show();
25911         }
25912     },
25913     
25914     isVisible : function(){
25915         return !this.hidden;
25916     },
25917     
25918     show : function()
25919     {
25920         this.fireEvent("beforeshow", this);
25921         
25922         this.hidden = false;
25923         this.el.addClass('open');
25924         
25925         Roo.get(document).on("mouseup", this.onMouseUp, this);
25926         
25927         this.fireEvent("show", this);
25928         
25929         
25930     },
25931     
25932     hide : function()
25933     {
25934         this.fireEvent("beforehide", this);
25935         
25936         this.hidden = true;
25937         this.el.removeClass('open');
25938         
25939         Roo.get(document).un("mouseup", this.onMouseUp);
25940         
25941         this.fireEvent("hide", this);
25942     },
25943     
25944     onMouseUp : function()
25945     {
25946         this.hide();
25947     }
25948     
25949 });
25950
25951  
25952  /*
25953  * - LGPL
25954  *
25955  * menu item
25956  * 
25957  */
25958 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25959
25960 /**
25961  * @class Roo.bootstrap.menu.Item
25962  * @extends Roo.bootstrap.Component
25963  * Bootstrap MenuItem class
25964  * @cfg {Boolean} submenu (true | false) default false
25965  * @cfg {String} html text of the item
25966  * @cfg {String} href the link
25967  * @cfg {Boolean} disable (true | false) default false
25968  * @cfg {Boolean} preventDefault (true | false) default true
25969  * @cfg {String} icon Font awesome icon
25970  * @cfg {String} pos Submenu align to (left | right) default right 
25971  * 
25972  * 
25973  * @constructor
25974  * Create a new Item
25975  * @param {Object} config The config object
25976  */
25977
25978
25979 Roo.bootstrap.menu.Item = function(config){
25980     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25981     this.addEvents({
25982         /**
25983          * @event mouseover
25984          * Fires when the mouse is hovering over this menu
25985          * @param {Roo.bootstrap.menu.Item} this
25986          * @param {Roo.EventObject} e
25987          */
25988         mouseover : true,
25989         /**
25990          * @event mouseout
25991          * Fires when the mouse exits this menu
25992          * @param {Roo.bootstrap.menu.Item} this
25993          * @param {Roo.EventObject} e
25994          */
25995         mouseout : true,
25996         // raw events
25997         /**
25998          * @event click
25999          * The raw click event for the entire grid.
26000          * @param {Roo.EventObject} e
26001          */
26002         click : true
26003     });
26004 };
26005
26006 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26007     
26008     submenu : false,
26009     href : '',
26010     html : '',
26011     preventDefault: true,
26012     disable : false,
26013     icon : false,
26014     pos : 'right',
26015     
26016     getAutoCreate : function()
26017     {
26018         var text = [
26019             {
26020                 tag : 'span',
26021                 cls : 'roo-menu-item-text',
26022                 html : this.html
26023             }
26024         ];
26025         
26026         if(this.icon){
26027             text.unshift({
26028                 tag : 'i',
26029                 cls : 'fa ' + this.icon
26030             })
26031         }
26032         
26033         var cfg = {
26034             tag : 'li',
26035             cn : [
26036                 {
26037                     tag : 'a',
26038                     href : this.href || '#',
26039                     cn : text
26040                 }
26041             ]
26042         };
26043         
26044         if(this.disable){
26045             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26046         }
26047         
26048         if(this.submenu){
26049             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26050             
26051             if(this.pos == 'left'){
26052                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26053             }
26054         }
26055         
26056         return cfg;
26057     },
26058     
26059     initEvents : function() 
26060     {
26061         this.el.on('mouseover', this.onMouseOver, this);
26062         this.el.on('mouseout', this.onMouseOut, this);
26063         
26064         this.el.select('a', true).first().on('click', this.onClick, this);
26065         
26066     },
26067     
26068     onClick : function(e)
26069     {
26070         if(this.preventDefault){
26071             e.preventDefault();
26072         }
26073         
26074         this.fireEvent("click", this, e);
26075     },
26076     
26077     onMouseOver : function(e)
26078     {
26079         if(this.submenu && this.pos == 'left'){
26080             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26081         }
26082         
26083         this.fireEvent("mouseover", this, e);
26084     },
26085     
26086     onMouseOut : function(e)
26087     {
26088         this.fireEvent("mouseout", this, e);
26089     }
26090 });
26091
26092  
26093
26094  /*
26095  * - LGPL
26096  *
26097  * menu separator
26098  * 
26099  */
26100 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26101
26102 /**
26103  * @class Roo.bootstrap.menu.Separator
26104  * @extends Roo.bootstrap.Component
26105  * Bootstrap Separator class
26106  * 
26107  * @constructor
26108  * Create a new Separator
26109  * @param {Object} config The config object
26110  */
26111
26112
26113 Roo.bootstrap.menu.Separator = function(config){
26114     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26115 };
26116
26117 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26118     
26119     getAutoCreate : function(){
26120         var cfg = {
26121             tag : 'li',
26122             cls: 'divider'
26123         };
26124         
26125         return cfg;
26126     }
26127    
26128 });
26129
26130  
26131
26132  /*
26133  * - LGPL
26134  *
26135  * Tooltip
26136  * 
26137  */
26138
26139 /**
26140  * @class Roo.bootstrap.Tooltip
26141  * Bootstrap Tooltip class
26142  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26143  * to determine which dom element triggers the tooltip.
26144  * 
26145  * It needs to add support for additional attributes like tooltip-position
26146  * 
26147  * @constructor
26148  * Create a new Toolti
26149  * @param {Object} config The config object
26150  */
26151
26152 Roo.bootstrap.Tooltip = function(config){
26153     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26154     
26155     this.alignment = Roo.bootstrap.Tooltip.alignment;
26156     
26157     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26158         this.alignment = config.alignment;
26159     }
26160     
26161 };
26162
26163 Roo.apply(Roo.bootstrap.Tooltip, {
26164     /**
26165      * @function init initialize tooltip monitoring.
26166      * @static
26167      */
26168     currentEl : false,
26169     currentTip : false,
26170     currentRegion : false,
26171     
26172     //  init : delay?
26173     
26174     init : function()
26175     {
26176         Roo.get(document).on('mouseover', this.enter ,this);
26177         Roo.get(document).on('mouseout', this.leave, this);
26178          
26179         
26180         this.currentTip = new Roo.bootstrap.Tooltip();
26181     },
26182     
26183     enter : function(ev)
26184     {
26185         var dom = ev.getTarget();
26186         
26187         //Roo.log(['enter',dom]);
26188         var el = Roo.fly(dom);
26189         if (this.currentEl) {
26190             //Roo.log(dom);
26191             //Roo.log(this.currentEl);
26192             //Roo.log(this.currentEl.contains(dom));
26193             if (this.currentEl == el) {
26194                 return;
26195             }
26196             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26197                 return;
26198             }
26199
26200         }
26201         
26202         if (this.currentTip.el) {
26203             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26204         }    
26205         //Roo.log(ev);
26206         
26207         if(!el || el.dom == document){
26208             return;
26209         }
26210         
26211         var bindEl = el;
26212         
26213         // you can not look for children, as if el is the body.. then everythign is the child..
26214         if (!el.attr('tooltip')) { //
26215             if (!el.select("[tooltip]").elements.length) {
26216                 return;
26217             }
26218             // is the mouse over this child...?
26219             bindEl = el.select("[tooltip]").first();
26220             var xy = ev.getXY();
26221             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26222                 //Roo.log("not in region.");
26223                 return;
26224             }
26225             //Roo.log("child element over..");
26226             
26227         }
26228         this.currentEl = bindEl;
26229         this.currentTip.bind(bindEl);
26230         this.currentRegion = Roo.lib.Region.getRegion(dom);
26231         this.currentTip.enter();
26232         
26233     },
26234     leave : function(ev)
26235     {
26236         var dom = ev.getTarget();
26237         //Roo.log(['leave',dom]);
26238         if (!this.currentEl) {
26239             return;
26240         }
26241         
26242         
26243         if (dom != this.currentEl.dom) {
26244             return;
26245         }
26246         var xy = ev.getXY();
26247         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26248             return;
26249         }
26250         // only activate leave if mouse cursor is outside... bounding box..
26251         
26252         
26253         
26254         
26255         if (this.currentTip) {
26256             this.currentTip.leave();
26257         }
26258         //Roo.log('clear currentEl');
26259         this.currentEl = false;
26260         
26261         
26262     },
26263     alignment : {
26264         'left' : ['r-l', [-2,0], 'right'],
26265         'right' : ['l-r', [2,0], 'left'],
26266         'bottom' : ['t-b', [0,2], 'top'],
26267         'top' : [ 'b-t', [0,-2], 'bottom']
26268     }
26269     
26270 });
26271
26272
26273 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26274     
26275     
26276     bindEl : false,
26277     
26278     delay : null, // can be { show : 300 , hide: 500}
26279     
26280     timeout : null,
26281     
26282     hoverState : null, //???
26283     
26284     placement : 'bottom', 
26285     
26286     alignment : false,
26287     
26288     getAutoCreate : function(){
26289     
26290         var cfg = {
26291            cls : 'tooltip',
26292            role : 'tooltip',
26293            cn : [
26294                 {
26295                     cls : 'tooltip-arrow'
26296                 },
26297                 {
26298                     cls : 'tooltip-inner'
26299                 }
26300            ]
26301         };
26302         
26303         return cfg;
26304     },
26305     bind : function(el)
26306     {
26307         this.bindEl = el;
26308     },
26309       
26310     
26311     enter : function () {
26312        
26313         if (this.timeout != null) {
26314             clearTimeout(this.timeout);
26315         }
26316         
26317         this.hoverState = 'in';
26318          //Roo.log("enter - show");
26319         if (!this.delay || !this.delay.show) {
26320             this.show();
26321             return;
26322         }
26323         var _t = this;
26324         this.timeout = setTimeout(function () {
26325             if (_t.hoverState == 'in') {
26326                 _t.show();
26327             }
26328         }, this.delay.show);
26329     },
26330     leave : function()
26331     {
26332         clearTimeout(this.timeout);
26333     
26334         this.hoverState = 'out';
26335          if (!this.delay || !this.delay.hide) {
26336             this.hide();
26337             return;
26338         }
26339        
26340         var _t = this;
26341         this.timeout = setTimeout(function () {
26342             //Roo.log("leave - timeout");
26343             
26344             if (_t.hoverState == 'out') {
26345                 _t.hide();
26346                 Roo.bootstrap.Tooltip.currentEl = false;
26347             }
26348         }, delay);
26349     },
26350     
26351     show : function (msg)
26352     {
26353         if (!this.el) {
26354             this.render(document.body);
26355         }
26356         // set content.
26357         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26358         
26359         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26360         
26361         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26362         
26363         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26364         
26365         var placement = typeof this.placement == 'function' ?
26366             this.placement.call(this, this.el, on_el) :
26367             this.placement;
26368             
26369         var autoToken = /\s?auto?\s?/i;
26370         var autoPlace = autoToken.test(placement);
26371         if (autoPlace) {
26372             placement = placement.replace(autoToken, '') || 'top';
26373         }
26374         
26375         //this.el.detach()
26376         //this.el.setXY([0,0]);
26377         this.el.show();
26378         //this.el.dom.style.display='block';
26379         
26380         //this.el.appendTo(on_el);
26381         
26382         var p = this.getPosition();
26383         var box = this.el.getBox();
26384         
26385         if (autoPlace) {
26386             // fixme..
26387         }
26388         
26389         var align = this.alignment[placement];
26390         
26391         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26392         
26393         if(placement == 'top' || placement == 'bottom'){
26394             if(xy[0] < 0){
26395                 placement = 'right';
26396             }
26397             
26398             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26399                 placement = 'left';
26400             }
26401             
26402             var scroll = Roo.select('body', true).first().getScroll();
26403             
26404             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26405                 placement = 'top';
26406             }
26407             
26408             align = this.alignment[placement];
26409         }
26410         
26411         this.el.alignTo(this.bindEl, align[0],align[1]);
26412         //var arrow = this.el.select('.arrow',true).first();
26413         //arrow.set(align[2], 
26414         
26415         this.el.addClass(placement);
26416         
26417         this.el.addClass('in fade');
26418         
26419         this.hoverState = null;
26420         
26421         if (this.el.hasClass('fade')) {
26422             // fade it?
26423         }
26424         
26425     },
26426     hide : function()
26427     {
26428          
26429         if (!this.el) {
26430             return;
26431         }
26432         //this.el.setXY([0,0]);
26433         this.el.removeClass('in');
26434         //this.el.hide();
26435         
26436     }
26437     
26438 });
26439  
26440
26441  /*
26442  * - LGPL
26443  *
26444  * Location Picker
26445  * 
26446  */
26447
26448 /**
26449  * @class Roo.bootstrap.LocationPicker
26450  * @extends Roo.bootstrap.Component
26451  * Bootstrap LocationPicker class
26452  * @cfg {Number} latitude Position when init default 0
26453  * @cfg {Number} longitude Position when init default 0
26454  * @cfg {Number} zoom default 15
26455  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26456  * @cfg {Boolean} mapTypeControl default false
26457  * @cfg {Boolean} disableDoubleClickZoom default false
26458  * @cfg {Boolean} scrollwheel default true
26459  * @cfg {Boolean} streetViewControl default false
26460  * @cfg {Number} radius default 0
26461  * @cfg {String} locationName
26462  * @cfg {Boolean} draggable default true
26463  * @cfg {Boolean} enableAutocomplete default false
26464  * @cfg {Boolean} enableReverseGeocode default true
26465  * @cfg {String} markerTitle
26466  * 
26467  * @constructor
26468  * Create a new LocationPicker
26469  * @param {Object} config The config object
26470  */
26471
26472
26473 Roo.bootstrap.LocationPicker = function(config){
26474     
26475     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26476     
26477     this.addEvents({
26478         /**
26479          * @event initial
26480          * Fires when the picker initialized.
26481          * @param {Roo.bootstrap.LocationPicker} this
26482          * @param {Google Location} location
26483          */
26484         initial : true,
26485         /**
26486          * @event positionchanged
26487          * Fires when the picker position changed.
26488          * @param {Roo.bootstrap.LocationPicker} this
26489          * @param {Google Location} location
26490          */
26491         positionchanged : true,
26492         /**
26493          * @event resize
26494          * Fires when the map resize.
26495          * @param {Roo.bootstrap.LocationPicker} this
26496          */
26497         resize : true,
26498         /**
26499          * @event show
26500          * Fires when the map show.
26501          * @param {Roo.bootstrap.LocationPicker} this
26502          */
26503         show : true,
26504         /**
26505          * @event hide
26506          * Fires when the map hide.
26507          * @param {Roo.bootstrap.LocationPicker} this
26508          */
26509         hide : true,
26510         /**
26511          * @event mapClick
26512          * Fires when click the map.
26513          * @param {Roo.bootstrap.LocationPicker} this
26514          * @param {Map event} e
26515          */
26516         mapClick : true,
26517         /**
26518          * @event mapRightClick
26519          * Fires when right click the map.
26520          * @param {Roo.bootstrap.LocationPicker} this
26521          * @param {Map event} e
26522          */
26523         mapRightClick : true,
26524         /**
26525          * @event markerClick
26526          * Fires when click the marker.
26527          * @param {Roo.bootstrap.LocationPicker} this
26528          * @param {Map event} e
26529          */
26530         markerClick : true,
26531         /**
26532          * @event markerRightClick
26533          * Fires when right click the marker.
26534          * @param {Roo.bootstrap.LocationPicker} this
26535          * @param {Map event} e
26536          */
26537         markerRightClick : true,
26538         /**
26539          * @event OverlayViewDraw
26540          * Fires when OverlayView Draw
26541          * @param {Roo.bootstrap.LocationPicker} this
26542          */
26543         OverlayViewDraw : true,
26544         /**
26545          * @event OverlayViewOnAdd
26546          * Fires when OverlayView Draw
26547          * @param {Roo.bootstrap.LocationPicker} this
26548          */
26549         OverlayViewOnAdd : true,
26550         /**
26551          * @event OverlayViewOnRemove
26552          * Fires when OverlayView Draw
26553          * @param {Roo.bootstrap.LocationPicker} this
26554          */
26555         OverlayViewOnRemove : true,
26556         /**
26557          * @event OverlayViewShow
26558          * Fires when OverlayView Draw
26559          * @param {Roo.bootstrap.LocationPicker} this
26560          * @param {Pixel} cpx
26561          */
26562         OverlayViewShow : true,
26563         /**
26564          * @event OverlayViewHide
26565          * Fires when OverlayView Draw
26566          * @param {Roo.bootstrap.LocationPicker} this
26567          */
26568         OverlayViewHide : true,
26569         /**
26570          * @event loadexception
26571          * Fires when load google lib failed.
26572          * @param {Roo.bootstrap.LocationPicker} this
26573          */
26574         loadexception : true
26575     });
26576         
26577 };
26578
26579 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26580     
26581     gMapContext: false,
26582     
26583     latitude: 0,
26584     longitude: 0,
26585     zoom: 15,
26586     mapTypeId: false,
26587     mapTypeControl: false,
26588     disableDoubleClickZoom: false,
26589     scrollwheel: true,
26590     streetViewControl: false,
26591     radius: 0,
26592     locationName: '',
26593     draggable: true,
26594     enableAutocomplete: false,
26595     enableReverseGeocode: true,
26596     markerTitle: '',
26597     
26598     getAutoCreate: function()
26599     {
26600
26601         var cfg = {
26602             tag: 'div',
26603             cls: 'roo-location-picker'
26604         };
26605         
26606         return cfg
26607     },
26608     
26609     initEvents: function(ct, position)
26610     {       
26611         if(!this.el.getWidth() || this.isApplied()){
26612             return;
26613         }
26614         
26615         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26616         
26617         this.initial();
26618     },
26619     
26620     initial: function()
26621     {
26622         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26623             this.fireEvent('loadexception', this);
26624             return;
26625         }
26626         
26627         if(!this.mapTypeId){
26628             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26629         }
26630         
26631         this.gMapContext = this.GMapContext();
26632         
26633         this.initOverlayView();
26634         
26635         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26636         
26637         var _this = this;
26638                 
26639         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26640             _this.setPosition(_this.gMapContext.marker.position);
26641         });
26642         
26643         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26644             _this.fireEvent('mapClick', this, event);
26645             
26646         });
26647
26648         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26649             _this.fireEvent('mapRightClick', this, event);
26650             
26651         });
26652         
26653         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26654             _this.fireEvent('markerClick', this, event);
26655             
26656         });
26657
26658         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26659             _this.fireEvent('markerRightClick', this, event);
26660             
26661         });
26662         
26663         this.setPosition(this.gMapContext.location);
26664         
26665         this.fireEvent('initial', this, this.gMapContext.location);
26666     },
26667     
26668     initOverlayView: function()
26669     {
26670         var _this = this;
26671         
26672         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26673             
26674             draw: function()
26675             {
26676                 _this.fireEvent('OverlayViewDraw', _this);
26677             },
26678             
26679             onAdd: function()
26680             {
26681                 _this.fireEvent('OverlayViewOnAdd', _this);
26682             },
26683             
26684             onRemove: function()
26685             {
26686                 _this.fireEvent('OverlayViewOnRemove', _this);
26687             },
26688             
26689             show: function(cpx)
26690             {
26691                 _this.fireEvent('OverlayViewShow', _this, cpx);
26692             },
26693             
26694             hide: function()
26695             {
26696                 _this.fireEvent('OverlayViewHide', _this);
26697             }
26698             
26699         });
26700     },
26701     
26702     fromLatLngToContainerPixel: function(event)
26703     {
26704         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26705     },
26706     
26707     isApplied: function() 
26708     {
26709         return this.getGmapContext() == false ? false : true;
26710     },
26711     
26712     getGmapContext: function() 
26713     {
26714         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26715     },
26716     
26717     GMapContext: function() 
26718     {
26719         var position = new google.maps.LatLng(this.latitude, this.longitude);
26720         
26721         var _map = new google.maps.Map(this.el.dom, {
26722             center: position,
26723             zoom: this.zoom,
26724             mapTypeId: this.mapTypeId,
26725             mapTypeControl: this.mapTypeControl,
26726             disableDoubleClickZoom: this.disableDoubleClickZoom,
26727             scrollwheel: this.scrollwheel,
26728             streetViewControl: this.streetViewControl,
26729             locationName: this.locationName,
26730             draggable: this.draggable,
26731             enableAutocomplete: this.enableAutocomplete,
26732             enableReverseGeocode: this.enableReverseGeocode
26733         });
26734         
26735         var _marker = new google.maps.Marker({
26736             position: position,
26737             map: _map,
26738             title: this.markerTitle,
26739             draggable: this.draggable
26740         });
26741         
26742         return {
26743             map: _map,
26744             marker: _marker,
26745             circle: null,
26746             location: position,
26747             radius: this.radius,
26748             locationName: this.locationName,
26749             addressComponents: {
26750                 formatted_address: null,
26751                 addressLine1: null,
26752                 addressLine2: null,
26753                 streetName: null,
26754                 streetNumber: null,
26755                 city: null,
26756                 district: null,
26757                 state: null,
26758                 stateOrProvince: null
26759             },
26760             settings: this,
26761             domContainer: this.el.dom,
26762             geodecoder: new google.maps.Geocoder()
26763         };
26764     },
26765     
26766     drawCircle: function(center, radius, options) 
26767     {
26768         if (this.gMapContext.circle != null) {
26769             this.gMapContext.circle.setMap(null);
26770         }
26771         if (radius > 0) {
26772             radius *= 1;
26773             options = Roo.apply({}, options, {
26774                 strokeColor: "#0000FF",
26775                 strokeOpacity: .35,
26776                 strokeWeight: 2,
26777                 fillColor: "#0000FF",
26778                 fillOpacity: .2
26779             });
26780             
26781             options.map = this.gMapContext.map;
26782             options.radius = radius;
26783             options.center = center;
26784             this.gMapContext.circle = new google.maps.Circle(options);
26785             return this.gMapContext.circle;
26786         }
26787         
26788         return null;
26789     },
26790     
26791     setPosition: function(location) 
26792     {
26793         this.gMapContext.location = location;
26794         this.gMapContext.marker.setPosition(location);
26795         this.gMapContext.map.panTo(location);
26796         this.drawCircle(location, this.gMapContext.radius, {});
26797         
26798         var _this = this;
26799         
26800         if (this.gMapContext.settings.enableReverseGeocode) {
26801             this.gMapContext.geodecoder.geocode({
26802                 latLng: this.gMapContext.location
26803             }, function(results, status) {
26804                 
26805                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26806                     _this.gMapContext.locationName = results[0].formatted_address;
26807                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26808                     
26809                     _this.fireEvent('positionchanged', this, location);
26810                 }
26811             });
26812             
26813             return;
26814         }
26815         
26816         this.fireEvent('positionchanged', this, location);
26817     },
26818     
26819     resize: function()
26820     {
26821         google.maps.event.trigger(this.gMapContext.map, "resize");
26822         
26823         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26824         
26825         this.fireEvent('resize', this);
26826     },
26827     
26828     setPositionByLatLng: function(latitude, longitude)
26829     {
26830         this.setPosition(new google.maps.LatLng(latitude, longitude));
26831     },
26832     
26833     getCurrentPosition: function() 
26834     {
26835         return {
26836             latitude: this.gMapContext.location.lat(),
26837             longitude: this.gMapContext.location.lng()
26838         };
26839     },
26840     
26841     getAddressName: function() 
26842     {
26843         return this.gMapContext.locationName;
26844     },
26845     
26846     getAddressComponents: function() 
26847     {
26848         return this.gMapContext.addressComponents;
26849     },
26850     
26851     address_component_from_google_geocode: function(address_components) 
26852     {
26853         var result = {};
26854         
26855         for (var i = 0; i < address_components.length; i++) {
26856             var component = address_components[i];
26857             if (component.types.indexOf("postal_code") >= 0) {
26858                 result.postalCode = component.short_name;
26859             } else if (component.types.indexOf("street_number") >= 0) {
26860                 result.streetNumber = component.short_name;
26861             } else if (component.types.indexOf("route") >= 0) {
26862                 result.streetName = component.short_name;
26863             } else if (component.types.indexOf("neighborhood") >= 0) {
26864                 result.city = component.short_name;
26865             } else if (component.types.indexOf("locality") >= 0) {
26866                 result.city = component.short_name;
26867             } else if (component.types.indexOf("sublocality") >= 0) {
26868                 result.district = component.short_name;
26869             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26870                 result.stateOrProvince = component.short_name;
26871             } else if (component.types.indexOf("country") >= 0) {
26872                 result.country = component.short_name;
26873             }
26874         }
26875         
26876         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26877         result.addressLine2 = "";
26878         return result;
26879     },
26880     
26881     setZoomLevel: function(zoom)
26882     {
26883         this.gMapContext.map.setZoom(zoom);
26884     },
26885     
26886     show: function()
26887     {
26888         if(!this.el){
26889             return;
26890         }
26891         
26892         this.el.show();
26893         
26894         this.resize();
26895         
26896         this.fireEvent('show', this);
26897     },
26898     
26899     hide: function()
26900     {
26901         if(!this.el){
26902             return;
26903         }
26904         
26905         this.el.hide();
26906         
26907         this.fireEvent('hide', this);
26908     }
26909     
26910 });
26911
26912 Roo.apply(Roo.bootstrap.LocationPicker, {
26913     
26914     OverlayView : function(map, options)
26915     {
26916         options = options || {};
26917         
26918         this.setMap(map);
26919     }
26920     
26921     
26922 });/*
26923  * - LGPL
26924  *
26925  * Alert
26926  * 
26927  */
26928
26929 /**
26930  * @class Roo.bootstrap.Alert
26931  * @extends Roo.bootstrap.Component
26932  * Bootstrap Alert class
26933  * @cfg {String} title The title of alert
26934  * @cfg {String} html The content of alert
26935  * @cfg {String} weight (  success | info | warning | danger )
26936  * @cfg {String} faicon font-awesomeicon
26937  * 
26938  * @constructor
26939  * Create a new alert
26940  * @param {Object} config The config object
26941  */
26942
26943
26944 Roo.bootstrap.Alert = function(config){
26945     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26946     
26947 };
26948
26949 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26950     
26951     title: '',
26952     html: '',
26953     weight: false,
26954     faicon: false,
26955     
26956     getAutoCreate : function()
26957     {
26958         
26959         var cfg = {
26960             tag : 'div',
26961             cls : 'alert',
26962             cn : [
26963                 {
26964                     tag : 'i',
26965                     cls : 'roo-alert-icon'
26966                     
26967                 },
26968                 {
26969                     tag : 'b',
26970                     cls : 'roo-alert-title',
26971                     html : this.title
26972                 },
26973                 {
26974                     tag : 'span',
26975                     cls : 'roo-alert-text',
26976                     html : this.html
26977                 }
26978             ]
26979         };
26980         
26981         if(this.faicon){
26982             cfg.cn[0].cls += ' fa ' + this.faicon;
26983         }
26984         
26985         if(this.weight){
26986             cfg.cls += ' alert-' + this.weight;
26987         }
26988         
26989         return cfg;
26990     },
26991     
26992     initEvents: function() 
26993     {
26994         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26995     },
26996     
26997     setTitle : function(str)
26998     {
26999         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27000     },
27001     
27002     setText : function(str)
27003     {
27004         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27005     },
27006     
27007     setWeight : function(weight)
27008     {
27009         if(this.weight){
27010             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27011         }
27012         
27013         this.weight = weight;
27014         
27015         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27016     },
27017     
27018     setIcon : function(icon)
27019     {
27020         if(this.faicon){
27021             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27022         }
27023         
27024         this.faicon = icon;
27025         
27026         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27027     },
27028     
27029     hide: function() 
27030     {
27031         this.el.hide();   
27032     },
27033     
27034     show: function() 
27035     {  
27036         this.el.show();   
27037     }
27038     
27039 });
27040
27041  
27042 /*
27043 * Licence: LGPL
27044 */
27045
27046 /**
27047  * @class Roo.bootstrap.UploadCropbox
27048  * @extends Roo.bootstrap.Component
27049  * Bootstrap UploadCropbox class
27050  * @cfg {String} emptyText show when image has been loaded
27051  * @cfg {String} rotateNotify show when image too small to rotate
27052  * @cfg {Number} errorTimeout default 3000
27053  * @cfg {Number} minWidth default 300
27054  * @cfg {Number} minHeight default 300
27055  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27056  * @cfg {Boolean} isDocument (true|false) default false
27057  * @cfg {String} url action url
27058  * @cfg {String} paramName default 'imageUpload'
27059  * @cfg {String} method default POST
27060  * @cfg {Boolean} loadMask (true|false) default true
27061  * @cfg {Boolean} loadingText default 'Loading...'
27062  * 
27063  * @constructor
27064  * Create a new UploadCropbox
27065  * @param {Object} config The config object
27066  */
27067
27068 Roo.bootstrap.UploadCropbox = function(config){
27069     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27070     
27071     this.addEvents({
27072         /**
27073          * @event beforeselectfile
27074          * Fire before select file
27075          * @param {Roo.bootstrap.UploadCropbox} this
27076          */
27077         "beforeselectfile" : true,
27078         /**
27079          * @event initial
27080          * Fire after initEvent
27081          * @param {Roo.bootstrap.UploadCropbox} this
27082          */
27083         "initial" : true,
27084         /**
27085          * @event crop
27086          * Fire after initEvent
27087          * @param {Roo.bootstrap.UploadCropbox} this
27088          * @param {String} data
27089          */
27090         "crop" : true,
27091         /**
27092          * @event prepare
27093          * Fire when preparing the file data
27094          * @param {Roo.bootstrap.UploadCropbox} this
27095          * @param {Object} file
27096          */
27097         "prepare" : true,
27098         /**
27099          * @event exception
27100          * Fire when get exception
27101          * @param {Roo.bootstrap.UploadCropbox} this
27102          * @param {XMLHttpRequest} xhr
27103          */
27104         "exception" : true,
27105         /**
27106          * @event beforeloadcanvas
27107          * Fire before load the canvas
27108          * @param {Roo.bootstrap.UploadCropbox} this
27109          * @param {String} src
27110          */
27111         "beforeloadcanvas" : true,
27112         /**
27113          * @event trash
27114          * Fire when trash image
27115          * @param {Roo.bootstrap.UploadCropbox} this
27116          */
27117         "trash" : true,
27118         /**
27119          * @event download
27120          * Fire when download the image
27121          * @param {Roo.bootstrap.UploadCropbox} this
27122          */
27123         "download" : true,
27124         /**
27125          * @event footerbuttonclick
27126          * Fire when footerbuttonclick
27127          * @param {Roo.bootstrap.UploadCropbox} this
27128          * @param {String} type
27129          */
27130         "footerbuttonclick" : true,
27131         /**
27132          * @event resize
27133          * Fire when resize
27134          * @param {Roo.bootstrap.UploadCropbox} this
27135          */
27136         "resize" : true,
27137         /**
27138          * @event rotate
27139          * Fire when rotate the image
27140          * @param {Roo.bootstrap.UploadCropbox} this
27141          * @param {String} pos
27142          */
27143         "rotate" : true,
27144         /**
27145          * @event inspect
27146          * Fire when inspect the file
27147          * @param {Roo.bootstrap.UploadCropbox} this
27148          * @param {Object} file
27149          */
27150         "inspect" : true,
27151         /**
27152          * @event upload
27153          * Fire when xhr upload the file
27154          * @param {Roo.bootstrap.UploadCropbox} this
27155          * @param {Object} data
27156          */
27157         "upload" : true,
27158         /**
27159          * @event arrange
27160          * Fire when arrange the file data
27161          * @param {Roo.bootstrap.UploadCropbox} this
27162          * @param {Object} formData
27163          */
27164         "arrange" : true
27165     });
27166     
27167     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27168 };
27169
27170 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27171     
27172     emptyText : 'Click to upload image',
27173     rotateNotify : 'Image is too small to rotate',
27174     errorTimeout : 3000,
27175     scale : 0,
27176     baseScale : 1,
27177     rotate : 0,
27178     dragable : false,
27179     pinching : false,
27180     mouseX : 0,
27181     mouseY : 0,
27182     cropData : false,
27183     minWidth : 300,
27184     minHeight : 300,
27185     file : false,
27186     exif : {},
27187     baseRotate : 1,
27188     cropType : 'image/jpeg',
27189     buttons : false,
27190     canvasLoaded : false,
27191     isDocument : false,
27192     method : 'POST',
27193     paramName : 'imageUpload',
27194     loadMask : true,
27195     loadingText : 'Loading...',
27196     maskEl : false,
27197     
27198     getAutoCreate : function()
27199     {
27200         var cfg = {
27201             tag : 'div',
27202             cls : 'roo-upload-cropbox',
27203             cn : [
27204                 {
27205                     tag : 'input',
27206                     cls : 'roo-upload-cropbox-selector',
27207                     type : 'file'
27208                 },
27209                 {
27210                     tag : 'div',
27211                     cls : 'roo-upload-cropbox-body',
27212                     style : 'cursor:pointer',
27213                     cn : [
27214                         {
27215                             tag : 'div',
27216                             cls : 'roo-upload-cropbox-preview'
27217                         },
27218                         {
27219                             tag : 'div',
27220                             cls : 'roo-upload-cropbox-thumb'
27221                         },
27222                         {
27223                             tag : 'div',
27224                             cls : 'roo-upload-cropbox-empty-notify',
27225                             html : this.emptyText
27226                         },
27227                         {
27228                             tag : 'div',
27229                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27230                             html : this.rotateNotify
27231                         }
27232                     ]
27233                 },
27234                 {
27235                     tag : 'div',
27236                     cls : 'roo-upload-cropbox-footer',
27237                     cn : {
27238                         tag : 'div',
27239                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27240                         cn : []
27241                     }
27242                 }
27243             ]
27244         };
27245         
27246         return cfg;
27247     },
27248     
27249     onRender : function(ct, position)
27250     {
27251         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27252         
27253         if (this.buttons.length) {
27254             
27255             Roo.each(this.buttons, function(bb) {
27256                 
27257                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27258                 
27259                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27260                 
27261             }, this);
27262         }
27263         
27264         if(this.loadMask){
27265             this.maskEl = this.el;
27266         }
27267     },
27268     
27269     initEvents : function()
27270     {
27271         this.urlAPI = (window.createObjectURL && window) || 
27272                                 (window.URL && URL.revokeObjectURL && URL) || 
27273                                 (window.webkitURL && webkitURL);
27274                         
27275         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27276         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27277         
27278         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27279         this.selectorEl.hide();
27280         
27281         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27282         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27283         
27284         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27285         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27286         this.thumbEl.hide();
27287         
27288         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27289         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27290         
27291         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27292         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27293         this.errorEl.hide();
27294         
27295         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27296         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27297         this.footerEl.hide();
27298         
27299         this.setThumbBoxSize();
27300         
27301         this.bind();
27302         
27303         this.resize();
27304         
27305         this.fireEvent('initial', this);
27306     },
27307
27308     bind : function()
27309     {
27310         var _this = this;
27311         
27312         window.addEventListener("resize", function() { _this.resize(); } );
27313         
27314         this.bodyEl.on('click', this.beforeSelectFile, this);
27315         
27316         if(Roo.isTouch){
27317             this.bodyEl.on('touchstart', this.onTouchStart, this);
27318             this.bodyEl.on('touchmove', this.onTouchMove, this);
27319             this.bodyEl.on('touchend', this.onTouchEnd, this);
27320         }
27321         
27322         if(!Roo.isTouch){
27323             this.bodyEl.on('mousedown', this.onMouseDown, this);
27324             this.bodyEl.on('mousemove', this.onMouseMove, this);
27325             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27326             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27327             Roo.get(document).on('mouseup', this.onMouseUp, this);
27328         }
27329         
27330         this.selectorEl.on('change', this.onFileSelected, this);
27331     },
27332     
27333     reset : function()
27334     {    
27335         this.scale = 0;
27336         this.baseScale = 1;
27337         this.rotate = 0;
27338         this.baseRotate = 1;
27339         this.dragable = false;
27340         this.pinching = false;
27341         this.mouseX = 0;
27342         this.mouseY = 0;
27343         this.cropData = false;
27344         this.notifyEl.dom.innerHTML = this.emptyText;
27345         
27346         this.selectorEl.dom.value = '';
27347         
27348     },
27349     
27350     resize : function()
27351     {
27352         if(this.fireEvent('resize', this) != false){
27353             this.setThumbBoxPosition();
27354             this.setCanvasPosition();
27355         }
27356     },
27357     
27358     onFooterButtonClick : function(e, el, o, type)
27359     {
27360         switch (type) {
27361             case 'rotate-left' :
27362                 this.onRotateLeft(e);
27363                 break;
27364             case 'rotate-right' :
27365                 this.onRotateRight(e);
27366                 break;
27367             case 'picture' :
27368                 this.beforeSelectFile(e);
27369                 break;
27370             case 'trash' :
27371                 this.trash(e);
27372                 break;
27373             case 'crop' :
27374                 this.crop(e);
27375                 break;
27376             case 'download' :
27377                 this.download(e);
27378                 break;
27379             default :
27380                 break;
27381         }
27382         
27383         this.fireEvent('footerbuttonclick', this, type);
27384     },
27385     
27386     beforeSelectFile : function(e)
27387     {
27388         e.preventDefault();
27389         
27390         if(this.fireEvent('beforeselectfile', this) != false){
27391             this.selectorEl.dom.click();
27392         }
27393     },
27394     
27395     onFileSelected : function(e)
27396     {
27397         e.preventDefault();
27398         
27399         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27400             return;
27401         }
27402         
27403         var file = this.selectorEl.dom.files[0];
27404         
27405         if(this.fireEvent('inspect', this, file) != false){
27406             this.prepare(file);
27407         }
27408         
27409     },
27410     
27411     trash : function(e)
27412     {
27413         this.fireEvent('trash', this);
27414     },
27415     
27416     download : function(e)
27417     {
27418         this.fireEvent('download', this);
27419     },
27420     
27421     loadCanvas : function(src)
27422     {   
27423         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27424             
27425             this.reset();
27426             
27427             this.imageEl = document.createElement('img');
27428             
27429             var _this = this;
27430             
27431             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27432             
27433             this.imageEl.src = src;
27434         }
27435     },
27436     
27437     onLoadCanvas : function()
27438     {   
27439         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27440         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27441         
27442         this.bodyEl.un('click', this.beforeSelectFile, this);
27443         
27444         this.notifyEl.hide();
27445         this.thumbEl.show();
27446         this.footerEl.show();
27447         
27448         this.baseRotateLevel();
27449         
27450         if(this.isDocument){
27451             this.setThumbBoxSize();
27452         }
27453         
27454         this.setThumbBoxPosition();
27455         
27456         this.baseScaleLevel();
27457         
27458         this.draw();
27459         
27460         this.resize();
27461         
27462         this.canvasLoaded = true;
27463         
27464         if(this.loadMask){
27465             this.maskEl.unmask();
27466         }
27467         
27468     },
27469     
27470     setCanvasPosition : function()
27471     {   
27472         if(!this.canvasEl){
27473             return;
27474         }
27475         
27476         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27477         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27478         
27479         this.previewEl.setLeft(pw);
27480         this.previewEl.setTop(ph);
27481         
27482     },
27483     
27484     onMouseDown : function(e)
27485     {   
27486         e.stopEvent();
27487         
27488         this.dragable = true;
27489         this.pinching = false;
27490         
27491         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27492             this.dragable = false;
27493             return;
27494         }
27495         
27496         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27497         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27498         
27499     },
27500     
27501     onMouseMove : function(e)
27502     {   
27503         e.stopEvent();
27504         
27505         if(!this.canvasLoaded){
27506             return;
27507         }
27508         
27509         if (!this.dragable){
27510             return;
27511         }
27512         
27513         var minX = Math.ceil(this.thumbEl.getLeft(true));
27514         var minY = Math.ceil(this.thumbEl.getTop(true));
27515         
27516         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27517         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27518         
27519         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27520         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27521         
27522         x = x - this.mouseX;
27523         y = y - this.mouseY;
27524         
27525         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27526         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27527         
27528         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27529         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27530         
27531         this.previewEl.setLeft(bgX);
27532         this.previewEl.setTop(bgY);
27533         
27534         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27535         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27536     },
27537     
27538     onMouseUp : function(e)
27539     {   
27540         e.stopEvent();
27541         
27542         this.dragable = false;
27543     },
27544     
27545     onMouseWheel : function(e)
27546     {   
27547         e.stopEvent();
27548         
27549         this.startScale = this.scale;
27550         
27551         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27552         
27553         if(!this.zoomable()){
27554             this.scale = this.startScale;
27555             return;
27556         }
27557         
27558         this.draw();
27559         
27560         return;
27561     },
27562     
27563     zoomable : function()
27564     {
27565         var minScale = this.thumbEl.getWidth() / this.minWidth;
27566         
27567         if(this.minWidth < this.minHeight){
27568             minScale = this.thumbEl.getHeight() / this.minHeight;
27569         }
27570         
27571         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27572         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27573         
27574         if(
27575                 this.isDocument &&
27576                 (this.rotate == 0 || this.rotate == 180) && 
27577                 (
27578                     width > this.imageEl.OriginWidth || 
27579                     height > this.imageEl.OriginHeight ||
27580                     (width < this.minWidth && height < this.minHeight)
27581                 )
27582         ){
27583             return false;
27584         }
27585         
27586         if(
27587                 this.isDocument &&
27588                 (this.rotate == 90 || this.rotate == 270) && 
27589                 (
27590                     width > this.imageEl.OriginWidth || 
27591                     height > this.imageEl.OriginHeight ||
27592                     (width < this.minHeight && height < this.minWidth)
27593                 )
27594         ){
27595             return false;
27596         }
27597         
27598         if(
27599                 !this.isDocument &&
27600                 (this.rotate == 0 || this.rotate == 180) && 
27601                 (
27602                     width < this.minWidth || 
27603                     width > this.imageEl.OriginWidth || 
27604                     height < this.minHeight || 
27605                     height > this.imageEl.OriginHeight
27606                 )
27607         ){
27608             return false;
27609         }
27610         
27611         if(
27612                 !this.isDocument &&
27613                 (this.rotate == 90 || this.rotate == 270) && 
27614                 (
27615                     width < this.minHeight || 
27616                     width > this.imageEl.OriginWidth || 
27617                     height < this.minWidth || 
27618                     height > this.imageEl.OriginHeight
27619                 )
27620         ){
27621             return false;
27622         }
27623         
27624         return true;
27625         
27626     },
27627     
27628     onRotateLeft : function(e)
27629     {   
27630         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27631             
27632             var minScale = this.thumbEl.getWidth() / this.minWidth;
27633             
27634             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27635             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27636             
27637             this.startScale = this.scale;
27638             
27639             while (this.getScaleLevel() < minScale){
27640             
27641                 this.scale = this.scale + 1;
27642                 
27643                 if(!this.zoomable()){
27644                     break;
27645                 }
27646                 
27647                 if(
27648                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27649                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27650                 ){
27651                     continue;
27652                 }
27653                 
27654                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27655
27656                 this.draw();
27657                 
27658                 return;
27659             }
27660             
27661             this.scale = this.startScale;
27662             
27663             this.onRotateFail();
27664             
27665             return false;
27666         }
27667         
27668         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27669
27670         if(this.isDocument){
27671             this.setThumbBoxSize();
27672             this.setThumbBoxPosition();
27673             this.setCanvasPosition();
27674         }
27675         
27676         this.draw();
27677         
27678         this.fireEvent('rotate', this, 'left');
27679         
27680     },
27681     
27682     onRotateRight : function(e)
27683     {
27684         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27685             
27686             var minScale = this.thumbEl.getWidth() / this.minWidth;
27687         
27688             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27689             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27690             
27691             this.startScale = this.scale;
27692             
27693             while (this.getScaleLevel() < minScale){
27694             
27695                 this.scale = this.scale + 1;
27696                 
27697                 if(!this.zoomable()){
27698                     break;
27699                 }
27700                 
27701                 if(
27702                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27703                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27704                 ){
27705                     continue;
27706                 }
27707                 
27708                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27709
27710                 this.draw();
27711                 
27712                 return;
27713             }
27714             
27715             this.scale = this.startScale;
27716             
27717             this.onRotateFail();
27718             
27719             return false;
27720         }
27721         
27722         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27723
27724         if(this.isDocument){
27725             this.setThumbBoxSize();
27726             this.setThumbBoxPosition();
27727             this.setCanvasPosition();
27728         }
27729         
27730         this.draw();
27731         
27732         this.fireEvent('rotate', this, 'right');
27733     },
27734     
27735     onRotateFail : function()
27736     {
27737         this.errorEl.show(true);
27738         
27739         var _this = this;
27740         
27741         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27742     },
27743     
27744     draw : function()
27745     {
27746         this.previewEl.dom.innerHTML = '';
27747         
27748         var canvasEl = document.createElement("canvas");
27749         
27750         var contextEl = canvasEl.getContext("2d");
27751         
27752         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27753         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27754         var center = this.imageEl.OriginWidth / 2;
27755         
27756         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27757             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27758             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27759             center = this.imageEl.OriginHeight / 2;
27760         }
27761         
27762         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27763         
27764         contextEl.translate(center, center);
27765         contextEl.rotate(this.rotate * Math.PI / 180);
27766
27767         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27768         
27769         this.canvasEl = document.createElement("canvas");
27770         
27771         this.contextEl = this.canvasEl.getContext("2d");
27772         
27773         switch (this.rotate) {
27774             case 0 :
27775                 
27776                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27777                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27778                 
27779                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27780                 
27781                 break;
27782             case 90 : 
27783                 
27784                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27785                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27786                 
27787                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27788                     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);
27789                     break;
27790                 }
27791                 
27792                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27793                 
27794                 break;
27795             case 180 :
27796                 
27797                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27798                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27799                 
27800                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27801                     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);
27802                     break;
27803                 }
27804                 
27805                 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);
27806                 
27807                 break;
27808             case 270 :
27809                 
27810                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27811                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27812         
27813                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27814                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27815                     break;
27816                 }
27817                 
27818                 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);
27819                 
27820                 break;
27821             default : 
27822                 break;
27823         }
27824         
27825         this.previewEl.appendChild(this.canvasEl);
27826         
27827         this.setCanvasPosition();
27828     },
27829     
27830     crop : function()
27831     {
27832         if(!this.canvasLoaded){
27833             return;
27834         }
27835         
27836         var imageCanvas = document.createElement("canvas");
27837         
27838         var imageContext = imageCanvas.getContext("2d");
27839         
27840         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27841         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27842         
27843         var center = imageCanvas.width / 2;
27844         
27845         imageContext.translate(center, center);
27846         
27847         imageContext.rotate(this.rotate * Math.PI / 180);
27848         
27849         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27850         
27851         var canvas = document.createElement("canvas");
27852         
27853         var context = canvas.getContext("2d");
27854                 
27855         canvas.width = this.minWidth;
27856         canvas.height = this.minHeight;
27857
27858         switch (this.rotate) {
27859             case 0 :
27860                 
27861                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27862                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27863                 
27864                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27865                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27866                 
27867                 var targetWidth = this.minWidth - 2 * x;
27868                 var targetHeight = this.minHeight - 2 * y;
27869                 
27870                 var scale = 1;
27871                 
27872                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27873                     scale = targetWidth / width;
27874                 }
27875                 
27876                 if(x > 0 && y == 0){
27877                     scale = targetHeight / height;
27878                 }
27879                 
27880                 if(x > 0 && y > 0){
27881                     scale = targetWidth / width;
27882                     
27883                     if(width < height){
27884                         scale = targetHeight / height;
27885                     }
27886                 }
27887                 
27888                 context.scale(scale, scale);
27889                 
27890                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27891                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27892
27893                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27894                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27895
27896                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27897                 
27898                 break;
27899             case 90 : 
27900                 
27901                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27902                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27903                 
27904                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27905                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27906                 
27907                 var targetWidth = this.minWidth - 2 * x;
27908                 var targetHeight = this.minHeight - 2 * y;
27909                 
27910                 var scale = 1;
27911                 
27912                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27913                     scale = targetWidth / width;
27914                 }
27915                 
27916                 if(x > 0 && y == 0){
27917                     scale = targetHeight / height;
27918                 }
27919                 
27920                 if(x > 0 && y > 0){
27921                     scale = targetWidth / width;
27922                     
27923                     if(width < height){
27924                         scale = targetHeight / height;
27925                     }
27926                 }
27927                 
27928                 context.scale(scale, scale);
27929                 
27930                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27931                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27932
27933                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27934                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27935                 
27936                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27937                 
27938                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27939                 
27940                 break;
27941             case 180 :
27942                 
27943                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27944                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27945                 
27946                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27947                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27948                 
27949                 var targetWidth = this.minWidth - 2 * x;
27950                 var targetHeight = this.minHeight - 2 * y;
27951                 
27952                 var scale = 1;
27953                 
27954                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27955                     scale = targetWidth / width;
27956                 }
27957                 
27958                 if(x > 0 && y == 0){
27959                     scale = targetHeight / height;
27960                 }
27961                 
27962                 if(x > 0 && y > 0){
27963                     scale = targetWidth / width;
27964                     
27965                     if(width < height){
27966                         scale = targetHeight / height;
27967                     }
27968                 }
27969                 
27970                 context.scale(scale, scale);
27971                 
27972                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27973                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27974
27975                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27976                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27977
27978                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27979                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27980                 
27981                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27982                 
27983                 break;
27984             case 270 :
27985                 
27986                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27987                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27988                 
27989                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27990                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27991                 
27992                 var targetWidth = this.minWidth - 2 * x;
27993                 var targetHeight = this.minHeight - 2 * y;
27994                 
27995                 var scale = 1;
27996                 
27997                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27998                     scale = targetWidth / width;
27999                 }
28000                 
28001                 if(x > 0 && y == 0){
28002                     scale = targetHeight / height;
28003                 }
28004                 
28005                 if(x > 0 && y > 0){
28006                     scale = targetWidth / width;
28007                     
28008                     if(width < height){
28009                         scale = targetHeight / height;
28010                     }
28011                 }
28012                 
28013                 context.scale(scale, scale);
28014                 
28015                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28016                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28017
28018                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28019                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28020                 
28021                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28022                 
28023                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28024                 
28025                 break;
28026             default : 
28027                 break;
28028         }
28029         
28030         this.cropData = canvas.toDataURL(this.cropType);
28031         
28032         if(this.fireEvent('crop', this, this.cropData) !== false){
28033             this.process(this.file, this.cropData);
28034         }
28035         
28036         return;
28037         
28038     },
28039     
28040     setThumbBoxSize : function()
28041     {
28042         var width, height;
28043         
28044         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28045             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28046             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28047             
28048             this.minWidth = width;
28049             this.minHeight = height;
28050             
28051             if(this.rotate == 90 || this.rotate == 270){
28052                 this.minWidth = height;
28053                 this.minHeight = width;
28054             }
28055         }
28056         
28057         height = 300;
28058         width = Math.ceil(this.minWidth * height / this.minHeight);
28059         
28060         if(this.minWidth > this.minHeight){
28061             width = 300;
28062             height = Math.ceil(this.minHeight * width / this.minWidth);
28063         }
28064         
28065         this.thumbEl.setStyle({
28066             width : width + 'px',
28067             height : height + 'px'
28068         });
28069
28070         return;
28071             
28072     },
28073     
28074     setThumbBoxPosition : function()
28075     {
28076         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28077         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28078         
28079         this.thumbEl.setLeft(x);
28080         this.thumbEl.setTop(y);
28081         
28082     },
28083     
28084     baseRotateLevel : function()
28085     {
28086         this.baseRotate = 1;
28087         
28088         if(
28089                 typeof(this.exif) != 'undefined' &&
28090                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28091                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28092         ){
28093             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28094         }
28095         
28096         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28097         
28098     },
28099     
28100     baseScaleLevel : function()
28101     {
28102         var width, height;
28103         
28104         if(this.isDocument){
28105             
28106             if(this.baseRotate == 6 || this.baseRotate == 8){
28107             
28108                 height = this.thumbEl.getHeight();
28109                 this.baseScale = height / this.imageEl.OriginWidth;
28110
28111                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28112                     width = this.thumbEl.getWidth();
28113                     this.baseScale = width / this.imageEl.OriginHeight;
28114                 }
28115
28116                 return;
28117             }
28118
28119             height = this.thumbEl.getHeight();
28120             this.baseScale = height / this.imageEl.OriginHeight;
28121
28122             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28123                 width = this.thumbEl.getWidth();
28124                 this.baseScale = width / this.imageEl.OriginWidth;
28125             }
28126
28127             return;
28128         }
28129         
28130         if(this.baseRotate == 6 || this.baseRotate == 8){
28131             
28132             width = this.thumbEl.getHeight();
28133             this.baseScale = width / this.imageEl.OriginHeight;
28134             
28135             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28136                 height = this.thumbEl.getWidth();
28137                 this.baseScale = height / this.imageEl.OriginHeight;
28138             }
28139             
28140             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28141                 height = this.thumbEl.getWidth();
28142                 this.baseScale = height / this.imageEl.OriginHeight;
28143                 
28144                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28145                     width = this.thumbEl.getHeight();
28146                     this.baseScale = width / this.imageEl.OriginWidth;
28147                 }
28148             }
28149             
28150             return;
28151         }
28152         
28153         width = this.thumbEl.getWidth();
28154         this.baseScale = width / this.imageEl.OriginWidth;
28155         
28156         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28157             height = this.thumbEl.getHeight();
28158             this.baseScale = height / this.imageEl.OriginHeight;
28159         }
28160         
28161         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28162             
28163             height = this.thumbEl.getHeight();
28164             this.baseScale = height / this.imageEl.OriginHeight;
28165             
28166             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28167                 width = this.thumbEl.getWidth();
28168                 this.baseScale = width / this.imageEl.OriginWidth;
28169             }
28170             
28171         }
28172         
28173         return;
28174     },
28175     
28176     getScaleLevel : function()
28177     {
28178         return this.baseScale * Math.pow(1.1, this.scale);
28179     },
28180     
28181     onTouchStart : function(e)
28182     {
28183         if(!this.canvasLoaded){
28184             this.beforeSelectFile(e);
28185             return;
28186         }
28187         
28188         var touches = e.browserEvent.touches;
28189         
28190         if(!touches){
28191             return;
28192         }
28193         
28194         if(touches.length == 1){
28195             this.onMouseDown(e);
28196             return;
28197         }
28198         
28199         if(touches.length != 2){
28200             return;
28201         }
28202         
28203         var coords = [];
28204         
28205         for(var i = 0, finger; finger = touches[i]; i++){
28206             coords.push(finger.pageX, finger.pageY);
28207         }
28208         
28209         var x = Math.pow(coords[0] - coords[2], 2);
28210         var y = Math.pow(coords[1] - coords[3], 2);
28211         
28212         this.startDistance = Math.sqrt(x + y);
28213         
28214         this.startScale = this.scale;
28215         
28216         this.pinching = true;
28217         this.dragable = false;
28218         
28219     },
28220     
28221     onTouchMove : function(e)
28222     {
28223         if(!this.pinching && !this.dragable){
28224             return;
28225         }
28226         
28227         var touches = e.browserEvent.touches;
28228         
28229         if(!touches){
28230             return;
28231         }
28232         
28233         if(this.dragable){
28234             this.onMouseMove(e);
28235             return;
28236         }
28237         
28238         var coords = [];
28239         
28240         for(var i = 0, finger; finger = touches[i]; i++){
28241             coords.push(finger.pageX, finger.pageY);
28242         }
28243         
28244         var x = Math.pow(coords[0] - coords[2], 2);
28245         var y = Math.pow(coords[1] - coords[3], 2);
28246         
28247         this.endDistance = Math.sqrt(x + y);
28248         
28249         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28250         
28251         if(!this.zoomable()){
28252             this.scale = this.startScale;
28253             return;
28254         }
28255         
28256         this.draw();
28257         
28258     },
28259     
28260     onTouchEnd : function(e)
28261     {
28262         this.pinching = false;
28263         this.dragable = false;
28264         
28265     },
28266     
28267     process : function(file, crop)
28268     {
28269         if(this.loadMask){
28270             this.maskEl.mask(this.loadingText);
28271         }
28272         
28273         this.xhr = new XMLHttpRequest();
28274         
28275         file.xhr = this.xhr;
28276
28277         this.xhr.open(this.method, this.url, true);
28278         
28279         var headers = {
28280             "Accept": "application/json",
28281             "Cache-Control": "no-cache",
28282             "X-Requested-With": "XMLHttpRequest"
28283         };
28284         
28285         for (var headerName in headers) {
28286             var headerValue = headers[headerName];
28287             if (headerValue) {
28288                 this.xhr.setRequestHeader(headerName, headerValue);
28289             }
28290         }
28291         
28292         var _this = this;
28293         
28294         this.xhr.onload = function()
28295         {
28296             _this.xhrOnLoad(_this.xhr);
28297         }
28298         
28299         this.xhr.onerror = function()
28300         {
28301             _this.xhrOnError(_this.xhr);
28302         }
28303         
28304         var formData = new FormData();
28305
28306         formData.append('returnHTML', 'NO');
28307         
28308         if(crop){
28309             formData.append('crop', crop);
28310         }
28311         
28312         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28313             formData.append(this.paramName, file, file.name);
28314         }
28315         
28316         if(typeof(file.filename) != 'undefined'){
28317             formData.append('filename', file.filename);
28318         }
28319         
28320         if(typeof(file.mimetype) != 'undefined'){
28321             formData.append('mimetype', file.mimetype);
28322         }
28323         
28324         if(this.fireEvent('arrange', this, formData) != false){
28325             this.xhr.send(formData);
28326         };
28327     },
28328     
28329     xhrOnLoad : function(xhr)
28330     {
28331         if(this.loadMask){
28332             this.maskEl.unmask();
28333         }
28334         
28335         if (xhr.readyState !== 4) {
28336             this.fireEvent('exception', this, xhr);
28337             return;
28338         }
28339
28340         var response = Roo.decode(xhr.responseText);
28341         
28342         if(!response.success){
28343             this.fireEvent('exception', this, xhr);
28344             return;
28345         }
28346         
28347         var response = Roo.decode(xhr.responseText);
28348         
28349         this.fireEvent('upload', this, response);
28350         
28351     },
28352     
28353     xhrOnError : function()
28354     {
28355         if(this.loadMask){
28356             this.maskEl.unmask();
28357         }
28358         
28359         Roo.log('xhr on error');
28360         
28361         var response = Roo.decode(xhr.responseText);
28362           
28363         Roo.log(response);
28364         
28365     },
28366     
28367     prepare : function(file)
28368     {   
28369         if(this.loadMask){
28370             this.maskEl.mask(this.loadingText);
28371         }
28372         
28373         this.file = false;
28374         this.exif = {};
28375         
28376         if(typeof(file) === 'string'){
28377             this.loadCanvas(file);
28378             return;
28379         }
28380         
28381         if(!file || !this.urlAPI){
28382             return;
28383         }
28384         
28385         this.file = file;
28386         this.cropType = file.type;
28387         
28388         var _this = this;
28389         
28390         if(this.fireEvent('prepare', this, this.file) != false){
28391             
28392             var reader = new FileReader();
28393             
28394             reader.onload = function (e) {
28395                 if (e.target.error) {
28396                     Roo.log(e.target.error);
28397                     return;
28398                 }
28399                 
28400                 var buffer = e.target.result,
28401                     dataView = new DataView(buffer),
28402                     offset = 2,
28403                     maxOffset = dataView.byteLength - 4,
28404                     markerBytes,
28405                     markerLength;
28406                 
28407                 if (dataView.getUint16(0) === 0xffd8) {
28408                     while (offset < maxOffset) {
28409                         markerBytes = dataView.getUint16(offset);
28410                         
28411                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28412                             markerLength = dataView.getUint16(offset + 2) + 2;
28413                             if (offset + markerLength > dataView.byteLength) {
28414                                 Roo.log('Invalid meta data: Invalid segment size.');
28415                                 break;
28416                             }
28417                             
28418                             if(markerBytes == 0xffe1){
28419                                 _this.parseExifData(
28420                                     dataView,
28421                                     offset,
28422                                     markerLength
28423                                 );
28424                             }
28425                             
28426                             offset += markerLength;
28427                             
28428                             continue;
28429                         }
28430                         
28431                         break;
28432                     }
28433                     
28434                 }
28435                 
28436                 var url = _this.urlAPI.createObjectURL(_this.file);
28437                 
28438                 _this.loadCanvas(url);
28439                 
28440                 return;
28441             }
28442             
28443             reader.readAsArrayBuffer(this.file);
28444             
28445         }
28446         
28447     },
28448     
28449     parseExifData : function(dataView, offset, length)
28450     {
28451         var tiffOffset = offset + 10,
28452             littleEndian,
28453             dirOffset;
28454     
28455         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28456             // No Exif data, might be XMP data instead
28457             return;
28458         }
28459         
28460         // Check for the ASCII code for "Exif" (0x45786966):
28461         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28462             // No Exif data, might be XMP data instead
28463             return;
28464         }
28465         if (tiffOffset + 8 > dataView.byteLength) {
28466             Roo.log('Invalid Exif data: Invalid segment size.');
28467             return;
28468         }
28469         // Check for the two null bytes:
28470         if (dataView.getUint16(offset + 8) !== 0x0000) {
28471             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28472             return;
28473         }
28474         // Check the byte alignment:
28475         switch (dataView.getUint16(tiffOffset)) {
28476         case 0x4949:
28477             littleEndian = true;
28478             break;
28479         case 0x4D4D:
28480             littleEndian = false;
28481             break;
28482         default:
28483             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28484             return;
28485         }
28486         // Check for the TIFF tag marker (0x002A):
28487         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28488             Roo.log('Invalid Exif data: Missing TIFF marker.');
28489             return;
28490         }
28491         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28492         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28493         
28494         this.parseExifTags(
28495             dataView,
28496             tiffOffset,
28497             tiffOffset + dirOffset,
28498             littleEndian
28499         );
28500     },
28501     
28502     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28503     {
28504         var tagsNumber,
28505             dirEndOffset,
28506             i;
28507         if (dirOffset + 6 > dataView.byteLength) {
28508             Roo.log('Invalid Exif data: Invalid directory offset.');
28509             return;
28510         }
28511         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28512         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28513         if (dirEndOffset + 4 > dataView.byteLength) {
28514             Roo.log('Invalid Exif data: Invalid directory size.');
28515             return;
28516         }
28517         for (i = 0; i < tagsNumber; i += 1) {
28518             this.parseExifTag(
28519                 dataView,
28520                 tiffOffset,
28521                 dirOffset + 2 + 12 * i, // tag offset
28522                 littleEndian
28523             );
28524         }
28525         // Return the offset to the next directory:
28526         return dataView.getUint32(dirEndOffset, littleEndian);
28527     },
28528     
28529     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28530     {
28531         var tag = dataView.getUint16(offset, littleEndian);
28532         
28533         this.exif[tag] = this.getExifValue(
28534             dataView,
28535             tiffOffset,
28536             offset,
28537             dataView.getUint16(offset + 2, littleEndian), // tag type
28538             dataView.getUint32(offset + 4, littleEndian), // tag length
28539             littleEndian
28540         );
28541     },
28542     
28543     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28544     {
28545         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28546             tagSize,
28547             dataOffset,
28548             values,
28549             i,
28550             str,
28551             c;
28552     
28553         if (!tagType) {
28554             Roo.log('Invalid Exif data: Invalid tag type.');
28555             return;
28556         }
28557         
28558         tagSize = tagType.size * length;
28559         // Determine if the value is contained in the dataOffset bytes,
28560         // or if the value at the dataOffset is a pointer to the actual data:
28561         dataOffset = tagSize > 4 ?
28562                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28563         if (dataOffset + tagSize > dataView.byteLength) {
28564             Roo.log('Invalid Exif data: Invalid data offset.');
28565             return;
28566         }
28567         if (length === 1) {
28568             return tagType.getValue(dataView, dataOffset, littleEndian);
28569         }
28570         values = [];
28571         for (i = 0; i < length; i += 1) {
28572             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28573         }
28574         
28575         if (tagType.ascii) {
28576             str = '';
28577             // Concatenate the chars:
28578             for (i = 0; i < values.length; i += 1) {
28579                 c = values[i];
28580                 // Ignore the terminating NULL byte(s):
28581                 if (c === '\u0000') {
28582                     break;
28583                 }
28584                 str += c;
28585             }
28586             return str;
28587         }
28588         return values;
28589     }
28590     
28591 });
28592
28593 Roo.apply(Roo.bootstrap.UploadCropbox, {
28594     tags : {
28595         'Orientation': 0x0112
28596     },
28597     
28598     Orientation: {
28599             1: 0, //'top-left',
28600 //            2: 'top-right',
28601             3: 180, //'bottom-right',
28602 //            4: 'bottom-left',
28603 //            5: 'left-top',
28604             6: 90, //'right-top',
28605 //            7: 'right-bottom',
28606             8: 270 //'left-bottom'
28607     },
28608     
28609     exifTagTypes : {
28610         // byte, 8-bit unsigned int:
28611         1: {
28612             getValue: function (dataView, dataOffset) {
28613                 return dataView.getUint8(dataOffset);
28614             },
28615             size: 1
28616         },
28617         // ascii, 8-bit byte:
28618         2: {
28619             getValue: function (dataView, dataOffset) {
28620                 return String.fromCharCode(dataView.getUint8(dataOffset));
28621             },
28622             size: 1,
28623             ascii: true
28624         },
28625         // short, 16 bit int:
28626         3: {
28627             getValue: function (dataView, dataOffset, littleEndian) {
28628                 return dataView.getUint16(dataOffset, littleEndian);
28629             },
28630             size: 2
28631         },
28632         // long, 32 bit int:
28633         4: {
28634             getValue: function (dataView, dataOffset, littleEndian) {
28635                 return dataView.getUint32(dataOffset, littleEndian);
28636             },
28637             size: 4
28638         },
28639         // rational = two long values, first is numerator, second is denominator:
28640         5: {
28641             getValue: function (dataView, dataOffset, littleEndian) {
28642                 return dataView.getUint32(dataOffset, littleEndian) /
28643                     dataView.getUint32(dataOffset + 4, littleEndian);
28644             },
28645             size: 8
28646         },
28647         // slong, 32 bit signed int:
28648         9: {
28649             getValue: function (dataView, dataOffset, littleEndian) {
28650                 return dataView.getInt32(dataOffset, littleEndian);
28651             },
28652             size: 4
28653         },
28654         // srational, two slongs, first is numerator, second is denominator:
28655         10: {
28656             getValue: function (dataView, dataOffset, littleEndian) {
28657                 return dataView.getInt32(dataOffset, littleEndian) /
28658                     dataView.getInt32(dataOffset + 4, littleEndian);
28659             },
28660             size: 8
28661         }
28662     },
28663     
28664     footer : {
28665         STANDARD : [
28666             {
28667                 tag : 'div',
28668                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28669                 action : 'rotate-left',
28670                 cn : [
28671                     {
28672                         tag : 'button',
28673                         cls : 'btn btn-default',
28674                         html : '<i class="fa fa-undo"></i>'
28675                     }
28676                 ]
28677             },
28678             {
28679                 tag : 'div',
28680                 cls : 'btn-group roo-upload-cropbox-picture',
28681                 action : 'picture',
28682                 cn : [
28683                     {
28684                         tag : 'button',
28685                         cls : 'btn btn-default',
28686                         html : '<i class="fa fa-picture-o"></i>'
28687                     }
28688                 ]
28689             },
28690             {
28691                 tag : 'div',
28692                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28693                 action : 'rotate-right',
28694                 cn : [
28695                     {
28696                         tag : 'button',
28697                         cls : 'btn btn-default',
28698                         html : '<i class="fa fa-repeat"></i>'
28699                     }
28700                 ]
28701             }
28702         ],
28703         DOCUMENT : [
28704             {
28705                 tag : 'div',
28706                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28707                 action : 'rotate-left',
28708                 cn : [
28709                     {
28710                         tag : 'button',
28711                         cls : 'btn btn-default',
28712                         html : '<i class="fa fa-undo"></i>'
28713                     }
28714                 ]
28715             },
28716             {
28717                 tag : 'div',
28718                 cls : 'btn-group roo-upload-cropbox-download',
28719                 action : 'download',
28720                 cn : [
28721                     {
28722                         tag : 'button',
28723                         cls : 'btn btn-default',
28724                         html : '<i class="fa fa-download"></i>'
28725                     }
28726                 ]
28727             },
28728             {
28729                 tag : 'div',
28730                 cls : 'btn-group roo-upload-cropbox-crop',
28731                 action : 'crop',
28732                 cn : [
28733                     {
28734                         tag : 'button',
28735                         cls : 'btn btn-default',
28736                         html : '<i class="fa fa-crop"></i>'
28737                     }
28738                 ]
28739             },
28740             {
28741                 tag : 'div',
28742                 cls : 'btn-group roo-upload-cropbox-trash',
28743                 action : 'trash',
28744                 cn : [
28745                     {
28746                         tag : 'button',
28747                         cls : 'btn btn-default',
28748                         html : '<i class="fa fa-trash"></i>'
28749                     }
28750                 ]
28751             },
28752             {
28753                 tag : 'div',
28754                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28755                 action : 'rotate-right',
28756                 cn : [
28757                     {
28758                         tag : 'button',
28759                         cls : 'btn btn-default',
28760                         html : '<i class="fa fa-repeat"></i>'
28761                     }
28762                 ]
28763             }
28764         ],
28765         ROTATOR : [
28766             {
28767                 tag : 'div',
28768                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28769                 action : 'rotate-left',
28770                 cn : [
28771                     {
28772                         tag : 'button',
28773                         cls : 'btn btn-default',
28774                         html : '<i class="fa fa-undo"></i>'
28775                     }
28776                 ]
28777             },
28778             {
28779                 tag : 'div',
28780                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28781                 action : 'rotate-right',
28782                 cn : [
28783                     {
28784                         tag : 'button',
28785                         cls : 'btn btn-default',
28786                         html : '<i class="fa fa-repeat"></i>'
28787                     }
28788                 ]
28789             }
28790         ]
28791     }
28792 });
28793
28794 /*
28795 * Licence: LGPL
28796 */
28797
28798 /**
28799  * @class Roo.bootstrap.DocumentManager
28800  * @extends Roo.bootstrap.Component
28801  * Bootstrap DocumentManager class
28802  * @cfg {String} paramName default 'imageUpload'
28803  * @cfg {String} toolTipName default 'filename'
28804  * @cfg {String} method default POST
28805  * @cfg {String} url action url
28806  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28807  * @cfg {Boolean} multiple multiple upload default true
28808  * @cfg {Number} thumbSize default 300
28809  * @cfg {String} fieldLabel
28810  * @cfg {Number} labelWidth default 4
28811  * @cfg {String} labelAlign (left|top) default left
28812  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28813 * @cfg {Number} labellg set the width of label (1-12)
28814  * @cfg {Number} labelmd set the width of label (1-12)
28815  * @cfg {Number} labelsm set the width of label (1-12)
28816  * @cfg {Number} labelxs set the width of label (1-12)
28817  * 
28818  * @constructor
28819  * Create a new DocumentManager
28820  * @param {Object} config The config object
28821  */
28822
28823 Roo.bootstrap.DocumentManager = function(config){
28824     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28825     
28826     this.files = [];
28827     this.delegates = [];
28828     
28829     this.addEvents({
28830         /**
28831          * @event initial
28832          * Fire when initial the DocumentManager
28833          * @param {Roo.bootstrap.DocumentManager} this
28834          */
28835         "initial" : true,
28836         /**
28837          * @event inspect
28838          * inspect selected file
28839          * @param {Roo.bootstrap.DocumentManager} this
28840          * @param {File} file
28841          */
28842         "inspect" : true,
28843         /**
28844          * @event exception
28845          * Fire when xhr load exception
28846          * @param {Roo.bootstrap.DocumentManager} this
28847          * @param {XMLHttpRequest} xhr
28848          */
28849         "exception" : true,
28850         /**
28851          * @event afterupload
28852          * Fire when xhr load exception
28853          * @param {Roo.bootstrap.DocumentManager} this
28854          * @param {XMLHttpRequest} xhr
28855          */
28856         "afterupload" : true,
28857         /**
28858          * @event prepare
28859          * prepare the form data
28860          * @param {Roo.bootstrap.DocumentManager} this
28861          * @param {Object} formData
28862          */
28863         "prepare" : true,
28864         /**
28865          * @event remove
28866          * Fire when remove the file
28867          * @param {Roo.bootstrap.DocumentManager} this
28868          * @param {Object} file
28869          */
28870         "remove" : true,
28871         /**
28872          * @event refresh
28873          * Fire after refresh the file
28874          * @param {Roo.bootstrap.DocumentManager} this
28875          */
28876         "refresh" : true,
28877         /**
28878          * @event click
28879          * Fire after click the image
28880          * @param {Roo.bootstrap.DocumentManager} this
28881          * @param {Object} file
28882          */
28883         "click" : true,
28884         /**
28885          * @event edit
28886          * Fire when upload a image and editable set to true
28887          * @param {Roo.bootstrap.DocumentManager} this
28888          * @param {Object} file
28889          */
28890         "edit" : true,
28891         /**
28892          * @event beforeselectfile
28893          * Fire before select file
28894          * @param {Roo.bootstrap.DocumentManager} this
28895          */
28896         "beforeselectfile" : true,
28897         /**
28898          * @event process
28899          * Fire before process file
28900          * @param {Roo.bootstrap.DocumentManager} this
28901          * @param {Object} file
28902          */
28903         "process" : true,
28904         /**
28905          * @event previewrendered
28906          * Fire when preview rendered
28907          * @param {Roo.bootstrap.DocumentManager} this
28908          * @param {Object} file
28909          */
28910         "previewrendered" : true,
28911         /**
28912          */
28913         "previewResize" : true
28914         
28915     });
28916 };
28917
28918 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28919     
28920     boxes : 0,
28921     inputName : '',
28922     thumbSize : 300,
28923     multiple : true,
28924     files : false,
28925     method : 'POST',
28926     url : '',
28927     paramName : 'imageUpload',
28928     toolTipName : 'filename',
28929     fieldLabel : '',
28930     labelWidth : 4,
28931     labelAlign : 'left',
28932     editable : true,
28933     delegates : false,
28934     xhr : false, 
28935     
28936     labellg : 0,
28937     labelmd : 0,
28938     labelsm : 0,
28939     labelxs : 0,
28940     
28941     getAutoCreate : function()
28942     {   
28943         var managerWidget = {
28944             tag : 'div',
28945             cls : 'roo-document-manager',
28946             cn : [
28947                 {
28948                     tag : 'input',
28949                     cls : 'roo-document-manager-selector',
28950                     type : 'file'
28951                 },
28952                 {
28953                     tag : 'div',
28954                     cls : 'roo-document-manager-uploader',
28955                     cn : [
28956                         {
28957                             tag : 'div',
28958                             cls : 'roo-document-manager-upload-btn',
28959                             html : '<i class="fa fa-plus"></i>'
28960                         }
28961                     ]
28962                     
28963                 }
28964             ]
28965         };
28966         
28967         var content = [
28968             {
28969                 tag : 'div',
28970                 cls : 'column col-md-12',
28971                 cn : managerWidget
28972             }
28973         ];
28974         
28975         if(this.fieldLabel.length){
28976             
28977             content = [
28978                 {
28979                     tag : 'div',
28980                     cls : 'column col-md-12',
28981                     html : this.fieldLabel
28982                 },
28983                 {
28984                     tag : 'div',
28985                     cls : 'column col-md-12',
28986                     cn : managerWidget
28987                 }
28988             ];
28989
28990             if(this.labelAlign == 'left'){
28991                 content = [
28992                     {
28993                         tag : 'div',
28994                         cls : 'column',
28995                         html : this.fieldLabel
28996                     },
28997                     {
28998                         tag : 'div',
28999                         cls : 'column',
29000                         cn : managerWidget
29001                     }
29002                 ];
29003                 
29004                 if(this.labelWidth > 12){
29005                     content[0].style = "width: " + this.labelWidth + 'px';
29006                 }
29007
29008                 if(this.labelWidth < 13 && this.labelmd == 0){
29009                     this.labelmd = this.labelWidth;
29010                 }
29011
29012                 if(this.labellg > 0){
29013                     content[0].cls += ' col-lg-' + this.labellg;
29014                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29015                 }
29016
29017                 if(this.labelmd > 0){
29018                     content[0].cls += ' col-md-' + this.labelmd;
29019                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29020                 }
29021
29022                 if(this.labelsm > 0){
29023                     content[0].cls += ' col-sm-' + this.labelsm;
29024                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29025                 }
29026
29027                 if(this.labelxs > 0){
29028                     content[0].cls += ' col-xs-' + this.labelxs;
29029                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29030                 }
29031                 
29032             }
29033         }
29034         
29035         var cfg = {
29036             tag : 'div',
29037             cls : 'row clearfix',
29038             cn : content
29039         };
29040         
29041         return cfg;
29042         
29043     },
29044     
29045     initEvents : function()
29046     {
29047         this.managerEl = this.el.select('.roo-document-manager', true).first();
29048         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29049         
29050         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29051         this.selectorEl.hide();
29052         
29053         if(this.multiple){
29054             this.selectorEl.attr('multiple', 'multiple');
29055         }
29056         
29057         this.selectorEl.on('change', this.onFileSelected, this);
29058         
29059         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29060         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29061         
29062         this.uploader.on('click', this.onUploaderClick, this);
29063         
29064         this.renderProgressDialog();
29065         
29066         var _this = this;
29067         
29068         window.addEventListener("resize", function() { _this.refresh(); } );
29069         
29070         this.fireEvent('initial', this);
29071     },
29072     
29073     renderProgressDialog : function()
29074     {
29075         var _this = this;
29076         
29077         this.progressDialog = new Roo.bootstrap.Modal({
29078             cls : 'roo-document-manager-progress-dialog',
29079             allow_close : false,
29080             title : '',
29081             buttons : [
29082                 {
29083                     name  :'cancel',
29084                     weight : 'danger',
29085                     html : 'Cancel'
29086                 }
29087             ], 
29088             listeners : { 
29089                 btnclick : function() {
29090                     _this.uploadCancel();
29091                     this.hide();
29092                 }
29093             }
29094         });
29095          
29096         this.progressDialog.render(Roo.get(document.body));
29097          
29098         this.progress = new Roo.bootstrap.Progress({
29099             cls : 'roo-document-manager-progress',
29100             active : true,
29101             striped : true
29102         });
29103         
29104         this.progress.render(this.progressDialog.getChildContainer());
29105         
29106         this.progressBar = new Roo.bootstrap.ProgressBar({
29107             cls : 'roo-document-manager-progress-bar',
29108             aria_valuenow : 0,
29109             aria_valuemin : 0,
29110             aria_valuemax : 12,
29111             panel : 'success'
29112         });
29113         
29114         this.progressBar.render(this.progress.getChildContainer());
29115     },
29116     
29117     onUploaderClick : function(e)
29118     {
29119         e.preventDefault();
29120      
29121         if(this.fireEvent('beforeselectfile', this) != false){
29122             this.selectorEl.dom.click();
29123         }
29124         
29125     },
29126     
29127     onFileSelected : function(e)
29128     {
29129         e.preventDefault();
29130         
29131         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29132             return;
29133         }
29134         
29135         Roo.each(this.selectorEl.dom.files, function(file){
29136             if(this.fireEvent('inspect', this, file) != false){
29137                 this.files.push(file);
29138             }
29139         }, this);
29140         
29141         this.queue();
29142         
29143     },
29144     
29145     queue : function()
29146     {
29147         this.selectorEl.dom.value = '';
29148         
29149         if(!this.files || !this.files.length){
29150             return;
29151         }
29152         
29153         if(this.boxes > 0 && this.files.length > this.boxes){
29154             this.files = this.files.slice(0, this.boxes);
29155         }
29156         
29157         this.uploader.show();
29158         
29159         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29160             this.uploader.hide();
29161         }
29162         
29163         var _this = this;
29164         
29165         var files = [];
29166         
29167         var docs = [];
29168         
29169         Roo.each(this.files, function(file){
29170             
29171             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29172                 var f = this.renderPreview(file);
29173                 files.push(f);
29174                 return;
29175             }
29176             
29177             if(file.type.indexOf('image') != -1){
29178                 this.delegates.push(
29179                     (function(){
29180                         _this.process(file);
29181                     }).createDelegate(this)
29182                 );
29183         
29184                 return;
29185             }
29186             
29187             docs.push(
29188                 (function(){
29189                     _this.process(file);
29190                 }).createDelegate(this)
29191             );
29192             
29193         }, this);
29194         
29195         this.files = files;
29196         
29197         this.delegates = this.delegates.concat(docs);
29198         
29199         if(!this.delegates.length){
29200             this.refresh();
29201             return;
29202         }
29203         
29204         this.progressBar.aria_valuemax = this.delegates.length;
29205         
29206         this.arrange();
29207         
29208         return;
29209     },
29210     
29211     arrange : function()
29212     {
29213         if(!this.delegates.length){
29214             this.progressDialog.hide();
29215             this.refresh();
29216             return;
29217         }
29218         
29219         var delegate = this.delegates.shift();
29220         
29221         this.progressDialog.show();
29222         
29223         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29224         
29225         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29226         
29227         delegate();
29228     },
29229     
29230     refresh : function()
29231     {
29232         this.uploader.show();
29233         
29234         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29235             this.uploader.hide();
29236         }
29237         
29238         Roo.isTouch ? this.closable(false) : this.closable(true);
29239         
29240         this.fireEvent('refresh', this);
29241     },
29242     
29243     onRemove : function(e, el, o)
29244     {
29245         e.preventDefault();
29246         
29247         this.fireEvent('remove', this, o);
29248         
29249     },
29250     
29251     remove : function(o)
29252     {
29253         var files = [];
29254         
29255         Roo.each(this.files, function(file){
29256             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29257                 files.push(file);
29258                 return;
29259             }
29260
29261             o.target.remove();
29262
29263         }, this);
29264         
29265         this.files = files;
29266         
29267         this.refresh();
29268     },
29269     
29270     clear : function()
29271     {
29272         Roo.each(this.files, function(file){
29273             if(!file.target){
29274                 return;
29275             }
29276             
29277             file.target.remove();
29278
29279         }, this);
29280         
29281         this.files = [];
29282         
29283         this.refresh();
29284     },
29285     
29286     onClick : function(e, el, o)
29287     {
29288         e.preventDefault();
29289         
29290         this.fireEvent('click', this, o);
29291         
29292     },
29293     
29294     closable : function(closable)
29295     {
29296         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29297             
29298             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29299             
29300             if(closable){
29301                 el.show();
29302                 return;
29303             }
29304             
29305             el.hide();
29306             
29307         }, this);
29308     },
29309     
29310     xhrOnLoad : function(xhr)
29311     {
29312         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29313             el.remove();
29314         }, this);
29315         
29316         if (xhr.readyState !== 4) {
29317             this.arrange();
29318             this.fireEvent('exception', this, xhr);
29319             return;
29320         }
29321
29322         var response = Roo.decode(xhr.responseText);
29323         
29324         if(!response.success){
29325             this.arrange();
29326             this.fireEvent('exception', this, xhr);
29327             return;
29328         }
29329         
29330         var file = this.renderPreview(response.data);
29331         
29332         this.files.push(file);
29333         
29334         this.arrange();
29335         
29336         this.fireEvent('afterupload', this, xhr);
29337         
29338     },
29339     
29340     xhrOnError : function(xhr)
29341     {
29342         Roo.log('xhr on error');
29343         
29344         var response = Roo.decode(xhr.responseText);
29345           
29346         Roo.log(response);
29347         
29348         this.arrange();
29349     },
29350     
29351     process : function(file)
29352     {
29353         if(this.fireEvent('process', this, file) !== false){
29354             if(this.editable && file.type.indexOf('image') != -1){
29355                 this.fireEvent('edit', this, file);
29356                 return;
29357             }
29358
29359             this.uploadStart(file, false);
29360
29361             return;
29362         }
29363         
29364     },
29365     
29366     uploadStart : function(file, crop)
29367     {
29368         this.xhr = new XMLHttpRequest();
29369         
29370         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29371             this.arrange();
29372             return;
29373         }
29374         
29375         file.xhr = this.xhr;
29376             
29377         this.managerEl.createChild({
29378             tag : 'div',
29379             cls : 'roo-document-manager-loading',
29380             cn : [
29381                 {
29382                     tag : 'div',
29383                     tooltip : file.name,
29384                     cls : 'roo-document-manager-thumb',
29385                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29386                 }
29387             ]
29388
29389         });
29390
29391         this.xhr.open(this.method, this.url, true);
29392         
29393         var headers = {
29394             "Accept": "application/json",
29395             "Cache-Control": "no-cache",
29396             "X-Requested-With": "XMLHttpRequest"
29397         };
29398         
29399         for (var headerName in headers) {
29400             var headerValue = headers[headerName];
29401             if (headerValue) {
29402                 this.xhr.setRequestHeader(headerName, headerValue);
29403             }
29404         }
29405         
29406         var _this = this;
29407         
29408         this.xhr.onload = function()
29409         {
29410             _this.xhrOnLoad(_this.xhr);
29411         }
29412         
29413         this.xhr.onerror = function()
29414         {
29415             _this.xhrOnError(_this.xhr);
29416         }
29417         
29418         var formData = new FormData();
29419
29420         formData.append('returnHTML', 'NO');
29421         
29422         if(crop){
29423             formData.append('crop', crop);
29424         }
29425         
29426         formData.append(this.paramName, file, file.name);
29427         
29428         var options = {
29429             file : file, 
29430             manually : false
29431         };
29432         
29433         if(this.fireEvent('prepare', this, formData, options) != false){
29434             
29435             if(options.manually){
29436                 return;
29437             }
29438             
29439             this.xhr.send(formData);
29440             return;
29441         };
29442         
29443         this.uploadCancel();
29444     },
29445     
29446     uploadCancel : function()
29447     {
29448         if (this.xhr) {
29449             this.xhr.abort();
29450         }
29451         
29452         this.delegates = [];
29453         
29454         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29455             el.remove();
29456         }, this);
29457         
29458         this.arrange();
29459     },
29460     
29461     renderPreview : function(file)
29462     {
29463         if(typeof(file.target) != 'undefined' && file.target){
29464             return file;
29465         }
29466         
29467         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29468         
29469         var previewEl = this.managerEl.createChild({
29470             tag : 'div',
29471             cls : 'roo-document-manager-preview',
29472             cn : [
29473                 {
29474                     tag : 'div',
29475                     tooltip : file[this.toolTipName],
29476                     cls : 'roo-document-manager-thumb',
29477                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29478                 },
29479                 {
29480                     tag : 'button',
29481                     cls : 'close',
29482                     html : '<i class="fa fa-times-circle"></i>'
29483                 }
29484             ]
29485         });
29486
29487         var close = previewEl.select('button.close', true).first();
29488
29489         close.on('click', this.onRemove, this, file);
29490
29491         file.target = previewEl;
29492
29493         var image = previewEl.select('img', true).first();
29494         
29495         var _this = this;
29496         
29497         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29498         
29499         image.on('click', this.onClick, this, file);
29500         
29501         this.fireEvent('previewrendered', this, file);
29502         
29503         return file;
29504         
29505     },
29506     
29507     onPreviewLoad : function(file, image)
29508     {
29509         if(typeof(file.target) == 'undefined' || !file.target){
29510             return;
29511         }
29512         
29513         var width = image.dom.naturalWidth || image.dom.width;
29514         var height = image.dom.naturalHeight || image.dom.height;
29515         
29516         if(!this.previewResize) {
29517             return;
29518         }
29519         
29520         if(width > height){
29521             file.target.addClass('wide');
29522             return;
29523         }
29524         
29525         file.target.addClass('tall');
29526         return;
29527         
29528     },
29529     
29530     uploadFromSource : function(file, crop)
29531     {
29532         this.xhr = new XMLHttpRequest();
29533         
29534         this.managerEl.createChild({
29535             tag : 'div',
29536             cls : 'roo-document-manager-loading',
29537             cn : [
29538                 {
29539                     tag : 'div',
29540                     tooltip : file.name,
29541                     cls : 'roo-document-manager-thumb',
29542                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29543                 }
29544             ]
29545
29546         });
29547
29548         this.xhr.open(this.method, this.url, true);
29549         
29550         var headers = {
29551             "Accept": "application/json",
29552             "Cache-Control": "no-cache",
29553             "X-Requested-With": "XMLHttpRequest"
29554         };
29555         
29556         for (var headerName in headers) {
29557             var headerValue = headers[headerName];
29558             if (headerValue) {
29559                 this.xhr.setRequestHeader(headerName, headerValue);
29560             }
29561         }
29562         
29563         var _this = this;
29564         
29565         this.xhr.onload = function()
29566         {
29567             _this.xhrOnLoad(_this.xhr);
29568         }
29569         
29570         this.xhr.onerror = function()
29571         {
29572             _this.xhrOnError(_this.xhr);
29573         }
29574         
29575         var formData = new FormData();
29576
29577         formData.append('returnHTML', 'NO');
29578         
29579         formData.append('crop', crop);
29580         
29581         if(typeof(file.filename) != 'undefined'){
29582             formData.append('filename', file.filename);
29583         }
29584         
29585         if(typeof(file.mimetype) != 'undefined'){
29586             formData.append('mimetype', file.mimetype);
29587         }
29588         
29589         Roo.log(formData);
29590         
29591         if(this.fireEvent('prepare', this, formData) != false){
29592             this.xhr.send(formData);
29593         };
29594     }
29595 });
29596
29597 /*
29598 * Licence: LGPL
29599 */
29600
29601 /**
29602  * @class Roo.bootstrap.DocumentViewer
29603  * @extends Roo.bootstrap.Component
29604  * Bootstrap DocumentViewer class
29605  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29606  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29607  * 
29608  * @constructor
29609  * Create a new DocumentViewer
29610  * @param {Object} config The config object
29611  */
29612
29613 Roo.bootstrap.DocumentViewer = function(config){
29614     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29615     
29616     this.addEvents({
29617         /**
29618          * @event initial
29619          * Fire after initEvent
29620          * @param {Roo.bootstrap.DocumentViewer} this
29621          */
29622         "initial" : true,
29623         /**
29624          * @event click
29625          * Fire after click
29626          * @param {Roo.bootstrap.DocumentViewer} this
29627          */
29628         "click" : true,
29629         /**
29630          * @event download
29631          * Fire after download button
29632          * @param {Roo.bootstrap.DocumentViewer} this
29633          */
29634         "download" : true,
29635         /**
29636          * @event trash
29637          * Fire after trash button
29638          * @param {Roo.bootstrap.DocumentViewer} this
29639          */
29640         "trash" : true
29641         
29642     });
29643 };
29644
29645 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29646     
29647     showDownload : true,
29648     
29649     showTrash : true,
29650     
29651     getAutoCreate : function()
29652     {
29653         var cfg = {
29654             tag : 'div',
29655             cls : 'roo-document-viewer',
29656             cn : [
29657                 {
29658                     tag : 'div',
29659                     cls : 'roo-document-viewer-body',
29660                     cn : [
29661                         {
29662                             tag : 'div',
29663                             cls : 'roo-document-viewer-thumb',
29664                             cn : [
29665                                 {
29666                                     tag : 'img',
29667                                     cls : 'roo-document-viewer-image'
29668                                 }
29669                             ]
29670                         }
29671                     ]
29672                 },
29673                 {
29674                     tag : 'div',
29675                     cls : 'roo-document-viewer-footer',
29676                     cn : {
29677                         tag : 'div',
29678                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29679                         cn : [
29680                             {
29681                                 tag : 'div',
29682                                 cls : 'btn-group roo-document-viewer-download',
29683                                 cn : [
29684                                     {
29685                                         tag : 'button',
29686                                         cls : 'btn btn-default',
29687                                         html : '<i class="fa fa-download"></i>'
29688                                     }
29689                                 ]
29690                             },
29691                             {
29692                                 tag : 'div',
29693                                 cls : 'btn-group roo-document-viewer-trash',
29694                                 cn : [
29695                                     {
29696                                         tag : 'button',
29697                                         cls : 'btn btn-default',
29698                                         html : '<i class="fa fa-trash"></i>'
29699                                     }
29700                                 ]
29701                             }
29702                         ]
29703                     }
29704                 }
29705             ]
29706         };
29707         
29708         return cfg;
29709     },
29710     
29711     initEvents : function()
29712     {
29713         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29714         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29715         
29716         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29717         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29718         
29719         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29720         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29721         
29722         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29723         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29724         
29725         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29726         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29727         
29728         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29729         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29730         
29731         this.bodyEl.on('click', this.onClick, this);
29732         this.downloadBtn.on('click', this.onDownload, this);
29733         this.trashBtn.on('click', this.onTrash, this);
29734         
29735         this.downloadBtn.hide();
29736         this.trashBtn.hide();
29737         
29738         if(this.showDownload){
29739             this.downloadBtn.show();
29740         }
29741         
29742         if(this.showTrash){
29743             this.trashBtn.show();
29744         }
29745         
29746         if(!this.showDownload && !this.showTrash) {
29747             this.footerEl.hide();
29748         }
29749         
29750     },
29751     
29752     initial : function()
29753     {
29754         this.fireEvent('initial', this);
29755         
29756     },
29757     
29758     onClick : function(e)
29759     {
29760         e.preventDefault();
29761         
29762         this.fireEvent('click', this);
29763     },
29764     
29765     onDownload : function(e)
29766     {
29767         e.preventDefault();
29768         
29769         this.fireEvent('download', this);
29770     },
29771     
29772     onTrash : function(e)
29773     {
29774         e.preventDefault();
29775         
29776         this.fireEvent('trash', this);
29777     }
29778     
29779 });
29780 /*
29781  * - LGPL
29782  *
29783  * nav progress bar
29784  * 
29785  */
29786
29787 /**
29788  * @class Roo.bootstrap.NavProgressBar
29789  * @extends Roo.bootstrap.Component
29790  * Bootstrap NavProgressBar class
29791  * 
29792  * @constructor
29793  * Create a new nav progress bar
29794  * @param {Object} config The config object
29795  */
29796
29797 Roo.bootstrap.NavProgressBar = function(config){
29798     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29799
29800     this.bullets = this.bullets || [];
29801    
29802 //    Roo.bootstrap.NavProgressBar.register(this);
29803      this.addEvents({
29804         /**
29805              * @event changed
29806              * Fires when the active item changes
29807              * @param {Roo.bootstrap.NavProgressBar} this
29808              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29809              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29810          */
29811         'changed': true
29812      });
29813     
29814 };
29815
29816 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29817     
29818     bullets : [],
29819     barItems : [],
29820     
29821     getAutoCreate : function()
29822     {
29823         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29824         
29825         cfg = {
29826             tag : 'div',
29827             cls : 'roo-navigation-bar-group',
29828             cn : [
29829                 {
29830                     tag : 'div',
29831                     cls : 'roo-navigation-top-bar'
29832                 },
29833                 {
29834                     tag : 'div',
29835                     cls : 'roo-navigation-bullets-bar',
29836                     cn : [
29837                         {
29838                             tag : 'ul',
29839                             cls : 'roo-navigation-bar'
29840                         }
29841                     ]
29842                 },
29843                 
29844                 {
29845                     tag : 'div',
29846                     cls : 'roo-navigation-bottom-bar'
29847                 }
29848             ]
29849             
29850         };
29851         
29852         return cfg;
29853         
29854     },
29855     
29856     initEvents: function() 
29857     {
29858         
29859     },
29860     
29861     onRender : function(ct, position) 
29862     {
29863         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29864         
29865         if(this.bullets.length){
29866             Roo.each(this.bullets, function(b){
29867                this.addItem(b);
29868             }, this);
29869         }
29870         
29871         this.format();
29872         
29873     },
29874     
29875     addItem : function(cfg)
29876     {
29877         var item = new Roo.bootstrap.NavProgressItem(cfg);
29878         
29879         item.parentId = this.id;
29880         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29881         
29882         if(cfg.html){
29883             var top = new Roo.bootstrap.Element({
29884                 tag : 'div',
29885                 cls : 'roo-navigation-bar-text'
29886             });
29887             
29888             var bottom = new Roo.bootstrap.Element({
29889                 tag : 'div',
29890                 cls : 'roo-navigation-bar-text'
29891             });
29892             
29893             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29894             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29895             
29896             var topText = new Roo.bootstrap.Element({
29897                 tag : 'span',
29898                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29899             });
29900             
29901             var bottomText = new Roo.bootstrap.Element({
29902                 tag : 'span',
29903                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29904             });
29905             
29906             topText.onRender(top.el, null);
29907             bottomText.onRender(bottom.el, null);
29908             
29909             item.topEl = top;
29910             item.bottomEl = bottom;
29911         }
29912         
29913         this.barItems.push(item);
29914         
29915         return item;
29916     },
29917     
29918     getActive : function()
29919     {
29920         var active = false;
29921         
29922         Roo.each(this.barItems, function(v){
29923             
29924             if (!v.isActive()) {
29925                 return;
29926             }
29927             
29928             active = v;
29929             return false;
29930             
29931         });
29932         
29933         return active;
29934     },
29935     
29936     setActiveItem : function(item)
29937     {
29938         var prev = false;
29939         
29940         Roo.each(this.barItems, function(v){
29941             if (v.rid == item.rid) {
29942                 return ;
29943             }
29944             
29945             if (v.isActive()) {
29946                 v.setActive(false);
29947                 prev = v;
29948             }
29949         });
29950
29951         item.setActive(true);
29952         
29953         this.fireEvent('changed', this, item, prev);
29954     },
29955     
29956     getBarItem: function(rid)
29957     {
29958         var ret = false;
29959         
29960         Roo.each(this.barItems, function(e) {
29961             if (e.rid != rid) {
29962                 return;
29963             }
29964             
29965             ret =  e;
29966             return false;
29967         });
29968         
29969         return ret;
29970     },
29971     
29972     indexOfItem : function(item)
29973     {
29974         var index = false;
29975         
29976         Roo.each(this.barItems, function(v, i){
29977             
29978             if (v.rid != item.rid) {
29979                 return;
29980             }
29981             
29982             index = i;
29983             return false
29984         });
29985         
29986         return index;
29987     },
29988     
29989     setActiveNext : function()
29990     {
29991         var i = this.indexOfItem(this.getActive());
29992         
29993         if (i > this.barItems.length) {
29994             return;
29995         }
29996         
29997         this.setActiveItem(this.barItems[i+1]);
29998     },
29999     
30000     setActivePrev : function()
30001     {
30002         var i = this.indexOfItem(this.getActive());
30003         
30004         if (i  < 1) {
30005             return;
30006         }
30007         
30008         this.setActiveItem(this.barItems[i-1]);
30009     },
30010     
30011     format : function()
30012     {
30013         if(!this.barItems.length){
30014             return;
30015         }
30016      
30017         var width = 100 / this.barItems.length;
30018         
30019         Roo.each(this.barItems, function(i){
30020             i.el.setStyle('width', width + '%');
30021             i.topEl.el.setStyle('width', width + '%');
30022             i.bottomEl.el.setStyle('width', width + '%');
30023         }, this);
30024         
30025     }
30026     
30027 });
30028 /*
30029  * - LGPL
30030  *
30031  * Nav Progress Item
30032  * 
30033  */
30034
30035 /**
30036  * @class Roo.bootstrap.NavProgressItem
30037  * @extends Roo.bootstrap.Component
30038  * Bootstrap NavProgressItem class
30039  * @cfg {String} rid the reference id
30040  * @cfg {Boolean} active (true|false) Is item active default false
30041  * @cfg {Boolean} disabled (true|false) Is item active default false
30042  * @cfg {String} html
30043  * @cfg {String} position (top|bottom) text position default bottom
30044  * @cfg {String} icon show icon instead of number
30045  * 
30046  * @constructor
30047  * Create a new NavProgressItem
30048  * @param {Object} config The config object
30049  */
30050 Roo.bootstrap.NavProgressItem = function(config){
30051     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30052     this.addEvents({
30053         // raw events
30054         /**
30055          * @event click
30056          * The raw click event for the entire grid.
30057          * @param {Roo.bootstrap.NavProgressItem} this
30058          * @param {Roo.EventObject} e
30059          */
30060         "click" : true
30061     });
30062    
30063 };
30064
30065 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30066     
30067     rid : '',
30068     active : false,
30069     disabled : false,
30070     html : '',
30071     position : 'bottom',
30072     icon : false,
30073     
30074     getAutoCreate : function()
30075     {
30076         var iconCls = 'roo-navigation-bar-item-icon';
30077         
30078         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30079         
30080         var cfg = {
30081             tag: 'li',
30082             cls: 'roo-navigation-bar-item',
30083             cn : [
30084                 {
30085                     tag : 'i',
30086                     cls : iconCls
30087                 }
30088             ]
30089         };
30090         
30091         if(this.active){
30092             cfg.cls += ' active';
30093         }
30094         if(this.disabled){
30095             cfg.cls += ' disabled';
30096         }
30097         
30098         return cfg;
30099     },
30100     
30101     disable : function()
30102     {
30103         this.setDisabled(true);
30104     },
30105     
30106     enable : function()
30107     {
30108         this.setDisabled(false);
30109     },
30110     
30111     initEvents: function() 
30112     {
30113         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30114         
30115         this.iconEl.on('click', this.onClick, this);
30116     },
30117     
30118     onClick : function(e)
30119     {
30120         e.preventDefault();
30121         
30122         if(this.disabled){
30123             return;
30124         }
30125         
30126         if(this.fireEvent('click', this, e) === false){
30127             return;
30128         };
30129         
30130         this.parent().setActiveItem(this);
30131     },
30132     
30133     isActive: function () 
30134     {
30135         return this.active;
30136     },
30137     
30138     setActive : function(state)
30139     {
30140         if(this.active == state){
30141             return;
30142         }
30143         
30144         this.active = state;
30145         
30146         if (state) {
30147             this.el.addClass('active');
30148             return;
30149         }
30150         
30151         this.el.removeClass('active');
30152         
30153         return;
30154     },
30155     
30156     setDisabled : function(state)
30157     {
30158         if(this.disabled == state){
30159             return;
30160         }
30161         
30162         this.disabled = state;
30163         
30164         if (state) {
30165             this.el.addClass('disabled');
30166             return;
30167         }
30168         
30169         this.el.removeClass('disabled');
30170     },
30171     
30172     tooltipEl : function()
30173     {
30174         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30175     }
30176 });
30177  
30178
30179  /*
30180  * - LGPL
30181  *
30182  * FieldLabel
30183  * 
30184  */
30185
30186 /**
30187  * @class Roo.bootstrap.FieldLabel
30188  * @extends Roo.bootstrap.Component
30189  * Bootstrap FieldLabel class
30190  * @cfg {String} html contents of the element
30191  * @cfg {String} tag tag of the element default label
30192  * @cfg {String} cls class of the element
30193  * @cfg {String} target label target 
30194  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30195  * @cfg {String} invalidClass default "text-warning"
30196  * @cfg {String} validClass default "text-success"
30197  * @cfg {String} iconTooltip default "This field is required"
30198  * @cfg {String} indicatorpos (left|right) default left
30199  * 
30200  * @constructor
30201  * Create a new FieldLabel
30202  * @param {Object} config The config object
30203  */
30204
30205 Roo.bootstrap.FieldLabel = function(config){
30206     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30207     
30208     this.addEvents({
30209             /**
30210              * @event invalid
30211              * Fires after the field has been marked as invalid.
30212              * @param {Roo.form.FieldLabel} this
30213              * @param {String} msg The validation message
30214              */
30215             invalid : true,
30216             /**
30217              * @event valid
30218              * Fires after the field has been validated with no errors.
30219              * @param {Roo.form.FieldLabel} this
30220              */
30221             valid : true
30222         });
30223 };
30224
30225 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30226     
30227     tag: 'label',
30228     cls: '',
30229     html: '',
30230     target: '',
30231     allowBlank : true,
30232     invalidClass : 'has-warning',
30233     validClass : 'has-success',
30234     iconTooltip : 'This field is required',
30235     indicatorpos : 'left',
30236     
30237     getAutoCreate : function(){
30238         
30239         var cls = "";
30240         if (!this.allowBlank) {
30241             cls  = "visible";
30242         }
30243         
30244         var cfg = {
30245             tag : this.tag,
30246             cls : 'roo-bootstrap-field-label ' + this.cls,
30247             for : this.target,
30248             cn : [
30249                 {
30250                     tag : 'i',
30251                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30252                     tooltip : this.iconTooltip
30253                 },
30254                 {
30255                     tag : 'span',
30256                     html : this.html
30257                 }
30258             ] 
30259         };
30260         
30261         if(this.indicatorpos == 'right'){
30262             var cfg = {
30263                 tag : this.tag,
30264                 cls : 'roo-bootstrap-field-label ' + this.cls,
30265                 for : this.target,
30266                 cn : [
30267                     {
30268                         tag : 'span',
30269                         html : this.html
30270                     },
30271                     {
30272                         tag : 'i',
30273                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30274                         tooltip : this.iconTooltip
30275                     }
30276                 ] 
30277             };
30278         }
30279         
30280         return cfg;
30281     },
30282     
30283     initEvents: function() 
30284     {
30285         Roo.bootstrap.Element.superclass.initEvents.call(this);
30286         
30287         this.indicator = this.indicatorEl();
30288         
30289         if(this.indicator){
30290             this.indicator.removeClass('visible');
30291             this.indicator.addClass('invisible');
30292         }
30293         
30294         Roo.bootstrap.FieldLabel.register(this);
30295     },
30296     
30297     indicatorEl : function()
30298     {
30299         var indicator = this.el.select('i.roo-required-indicator',true).first();
30300         
30301         if(!indicator){
30302             return false;
30303         }
30304         
30305         return indicator;
30306         
30307     },
30308     
30309     /**
30310      * Mark this field as valid
30311      */
30312     markValid : function()
30313     {
30314         if(this.indicator){
30315             this.indicator.removeClass('visible');
30316             this.indicator.addClass('invisible');
30317         }
30318         
30319         this.el.removeClass(this.invalidClass);
30320         
30321         this.el.addClass(this.validClass);
30322         
30323         this.fireEvent('valid', this);
30324     },
30325     
30326     /**
30327      * Mark this field as invalid
30328      * @param {String} msg The validation message
30329      */
30330     markInvalid : function(msg)
30331     {
30332         if(this.indicator){
30333             this.indicator.removeClass('invisible');
30334             this.indicator.addClass('visible');
30335         }
30336         
30337         this.el.removeClass(this.validClass);
30338         
30339         this.el.addClass(this.invalidClass);
30340         
30341         this.fireEvent('invalid', this, msg);
30342     }
30343     
30344    
30345 });
30346
30347 Roo.apply(Roo.bootstrap.FieldLabel, {
30348     
30349     groups: {},
30350     
30351      /**
30352     * register a FieldLabel Group
30353     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30354     */
30355     register : function(label)
30356     {
30357         if(this.groups.hasOwnProperty(label.target)){
30358             return;
30359         }
30360      
30361         this.groups[label.target] = label;
30362         
30363     },
30364     /**
30365     * fetch a FieldLabel Group based on the target
30366     * @param {string} target
30367     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30368     */
30369     get: function(target) {
30370         if (typeof(this.groups[target]) == 'undefined') {
30371             return false;
30372         }
30373         
30374         return this.groups[target] ;
30375     }
30376 });
30377
30378  
30379
30380  /*
30381  * - LGPL
30382  *
30383  * page DateSplitField.
30384  * 
30385  */
30386
30387
30388 /**
30389  * @class Roo.bootstrap.DateSplitField
30390  * @extends Roo.bootstrap.Component
30391  * Bootstrap DateSplitField class
30392  * @cfg {string} fieldLabel - the label associated
30393  * @cfg {Number} labelWidth set the width of label (0-12)
30394  * @cfg {String} labelAlign (top|left)
30395  * @cfg {Boolean} dayAllowBlank (true|false) default false
30396  * @cfg {Boolean} monthAllowBlank (true|false) default false
30397  * @cfg {Boolean} yearAllowBlank (true|false) default false
30398  * @cfg {string} dayPlaceholder 
30399  * @cfg {string} monthPlaceholder
30400  * @cfg {string} yearPlaceholder
30401  * @cfg {string} dayFormat default 'd'
30402  * @cfg {string} monthFormat default 'm'
30403  * @cfg {string} yearFormat default 'Y'
30404  * @cfg {Number} labellg set the width of label (1-12)
30405  * @cfg {Number} labelmd set the width of label (1-12)
30406  * @cfg {Number} labelsm set the width of label (1-12)
30407  * @cfg {Number} labelxs set the width of label (1-12)
30408
30409  *     
30410  * @constructor
30411  * Create a new DateSplitField
30412  * @param {Object} config The config object
30413  */
30414
30415 Roo.bootstrap.DateSplitField = function(config){
30416     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30417     
30418     this.addEvents({
30419         // raw events
30420          /**
30421          * @event years
30422          * getting the data of years
30423          * @param {Roo.bootstrap.DateSplitField} this
30424          * @param {Object} years
30425          */
30426         "years" : true,
30427         /**
30428          * @event days
30429          * getting the data of days
30430          * @param {Roo.bootstrap.DateSplitField} this
30431          * @param {Object} days
30432          */
30433         "days" : true,
30434         /**
30435          * @event invalid
30436          * Fires after the field has been marked as invalid.
30437          * @param {Roo.form.Field} this
30438          * @param {String} msg The validation message
30439          */
30440         invalid : true,
30441        /**
30442          * @event valid
30443          * Fires after the field has been validated with no errors.
30444          * @param {Roo.form.Field} this
30445          */
30446         valid : true
30447     });
30448 };
30449
30450 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30451     
30452     fieldLabel : '',
30453     labelAlign : 'top',
30454     labelWidth : 3,
30455     dayAllowBlank : false,
30456     monthAllowBlank : false,
30457     yearAllowBlank : false,
30458     dayPlaceholder : '',
30459     monthPlaceholder : '',
30460     yearPlaceholder : '',
30461     dayFormat : 'd',
30462     monthFormat : 'm',
30463     yearFormat : 'Y',
30464     isFormField : true,
30465     labellg : 0,
30466     labelmd : 0,
30467     labelsm : 0,
30468     labelxs : 0,
30469     
30470     getAutoCreate : function()
30471     {
30472         var cfg = {
30473             tag : 'div',
30474             cls : 'row roo-date-split-field-group',
30475             cn : [
30476                 {
30477                     tag : 'input',
30478                     type : 'hidden',
30479                     cls : 'form-hidden-field roo-date-split-field-group-value',
30480                     name : this.name
30481                 }
30482             ]
30483         };
30484         
30485         var labelCls = 'col-md-12';
30486         var contentCls = 'col-md-4';
30487         
30488         if(this.fieldLabel){
30489             
30490             var label = {
30491                 tag : 'div',
30492                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30493                 cn : [
30494                     {
30495                         tag : 'label',
30496                         html : this.fieldLabel
30497                     }
30498                 ]
30499             };
30500             
30501             if(this.labelAlign == 'left'){
30502             
30503                 if(this.labelWidth > 12){
30504                     label.style = "width: " + this.labelWidth + 'px';
30505                 }
30506
30507                 if(this.labelWidth < 13 && this.labelmd == 0){
30508                     this.labelmd = this.labelWidth;
30509                 }
30510
30511                 if(this.labellg > 0){
30512                     labelCls = ' col-lg-' + this.labellg;
30513                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30514                 }
30515
30516                 if(this.labelmd > 0){
30517                     labelCls = ' col-md-' + this.labelmd;
30518                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30519                 }
30520
30521                 if(this.labelsm > 0){
30522                     labelCls = ' col-sm-' + this.labelsm;
30523                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30524                 }
30525
30526                 if(this.labelxs > 0){
30527                     labelCls = ' col-xs-' + this.labelxs;
30528                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30529                 }
30530             }
30531             
30532             label.cls += ' ' + labelCls;
30533             
30534             cfg.cn.push(label);
30535         }
30536         
30537         Roo.each(['day', 'month', 'year'], function(t){
30538             cfg.cn.push({
30539                 tag : 'div',
30540                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30541             });
30542         }, this);
30543         
30544         return cfg;
30545     },
30546     
30547     inputEl: function ()
30548     {
30549         return this.el.select('.roo-date-split-field-group-value', true).first();
30550     },
30551     
30552     onRender : function(ct, position) 
30553     {
30554         var _this = this;
30555         
30556         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30557         
30558         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30559         
30560         this.dayField = new Roo.bootstrap.ComboBox({
30561             allowBlank : this.dayAllowBlank,
30562             alwaysQuery : true,
30563             displayField : 'value',
30564             editable : false,
30565             fieldLabel : '',
30566             forceSelection : true,
30567             mode : 'local',
30568             placeholder : this.dayPlaceholder,
30569             selectOnFocus : true,
30570             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30571             triggerAction : 'all',
30572             typeAhead : true,
30573             valueField : 'value',
30574             store : new Roo.data.SimpleStore({
30575                 data : (function() {    
30576                     var days = [];
30577                     _this.fireEvent('days', _this, days);
30578                     return days;
30579                 })(),
30580                 fields : [ 'value' ]
30581             }),
30582             listeners : {
30583                 select : function (_self, record, index)
30584                 {
30585                     _this.setValue(_this.getValue());
30586                 }
30587             }
30588         });
30589
30590         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30591         
30592         this.monthField = new Roo.bootstrap.MonthField({
30593             after : '<i class=\"fa fa-calendar\"></i>',
30594             allowBlank : this.monthAllowBlank,
30595             placeholder : this.monthPlaceholder,
30596             readOnly : true,
30597             listeners : {
30598                 render : function (_self)
30599                 {
30600                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30601                         e.preventDefault();
30602                         _self.focus();
30603                     });
30604                 },
30605                 select : function (_self, oldvalue, newvalue)
30606                 {
30607                     _this.setValue(_this.getValue());
30608                 }
30609             }
30610         });
30611         
30612         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30613         
30614         this.yearField = new Roo.bootstrap.ComboBox({
30615             allowBlank : this.yearAllowBlank,
30616             alwaysQuery : true,
30617             displayField : 'value',
30618             editable : false,
30619             fieldLabel : '',
30620             forceSelection : true,
30621             mode : 'local',
30622             placeholder : this.yearPlaceholder,
30623             selectOnFocus : true,
30624             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30625             triggerAction : 'all',
30626             typeAhead : true,
30627             valueField : 'value',
30628             store : new Roo.data.SimpleStore({
30629                 data : (function() {
30630                     var years = [];
30631                     _this.fireEvent('years', _this, years);
30632                     return years;
30633                 })(),
30634                 fields : [ 'value' ]
30635             }),
30636             listeners : {
30637                 select : function (_self, record, index)
30638                 {
30639                     _this.setValue(_this.getValue());
30640                 }
30641             }
30642         });
30643
30644         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30645     },
30646     
30647     setValue : function(v, format)
30648     {
30649         this.inputEl.dom.value = v;
30650         
30651         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30652         
30653         var d = Date.parseDate(v, f);
30654         
30655         if(!d){
30656             this.validate();
30657             return;
30658         }
30659         
30660         this.setDay(d.format(this.dayFormat));
30661         this.setMonth(d.format(this.monthFormat));
30662         this.setYear(d.format(this.yearFormat));
30663         
30664         this.validate();
30665         
30666         return;
30667     },
30668     
30669     setDay : function(v)
30670     {
30671         this.dayField.setValue(v);
30672         this.inputEl.dom.value = this.getValue();
30673         this.validate();
30674         return;
30675     },
30676     
30677     setMonth : function(v)
30678     {
30679         this.monthField.setValue(v, true);
30680         this.inputEl.dom.value = this.getValue();
30681         this.validate();
30682         return;
30683     },
30684     
30685     setYear : function(v)
30686     {
30687         this.yearField.setValue(v);
30688         this.inputEl.dom.value = this.getValue();
30689         this.validate();
30690         return;
30691     },
30692     
30693     getDay : function()
30694     {
30695         return this.dayField.getValue();
30696     },
30697     
30698     getMonth : function()
30699     {
30700         return this.monthField.getValue();
30701     },
30702     
30703     getYear : function()
30704     {
30705         return this.yearField.getValue();
30706     },
30707     
30708     getValue : function()
30709     {
30710         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30711         
30712         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30713         
30714         return date;
30715     },
30716     
30717     reset : function()
30718     {
30719         this.setDay('');
30720         this.setMonth('');
30721         this.setYear('');
30722         this.inputEl.dom.value = '';
30723         this.validate();
30724         return;
30725     },
30726     
30727     validate : function()
30728     {
30729         var d = this.dayField.validate();
30730         var m = this.monthField.validate();
30731         var y = this.yearField.validate();
30732         
30733         var valid = true;
30734         
30735         if(
30736                 (!this.dayAllowBlank && !d) ||
30737                 (!this.monthAllowBlank && !m) ||
30738                 (!this.yearAllowBlank && !y)
30739         ){
30740             valid = false;
30741         }
30742         
30743         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30744             return valid;
30745         }
30746         
30747         if(valid){
30748             this.markValid();
30749             return valid;
30750         }
30751         
30752         this.markInvalid();
30753         
30754         return valid;
30755     },
30756     
30757     markValid : function()
30758     {
30759         
30760         var label = this.el.select('label', true).first();
30761         var icon = this.el.select('i.fa-star', true).first();
30762
30763         if(label && icon){
30764             icon.remove();
30765         }
30766         
30767         this.fireEvent('valid', this);
30768     },
30769     
30770      /**
30771      * Mark this field as invalid
30772      * @param {String} msg The validation message
30773      */
30774     markInvalid : function(msg)
30775     {
30776         
30777         var label = this.el.select('label', true).first();
30778         var icon = this.el.select('i.fa-star', true).first();
30779
30780         if(label && !icon){
30781             this.el.select('.roo-date-split-field-label', true).createChild({
30782                 tag : 'i',
30783                 cls : 'text-danger fa fa-lg fa-star',
30784                 tooltip : 'This field is required',
30785                 style : 'margin-right:5px;'
30786             }, label, true);
30787         }
30788         
30789         this.fireEvent('invalid', this, msg);
30790     },
30791     
30792     clearInvalid : function()
30793     {
30794         var label = this.el.select('label', true).first();
30795         var icon = this.el.select('i.fa-star', true).first();
30796
30797         if(label && icon){
30798             icon.remove();
30799         }
30800         
30801         this.fireEvent('valid', this);
30802     },
30803     
30804     getName: function()
30805     {
30806         return this.name;
30807     }
30808     
30809 });
30810
30811  /**
30812  *
30813  * This is based on 
30814  * http://masonry.desandro.com
30815  *
30816  * The idea is to render all the bricks based on vertical width...
30817  *
30818  * The original code extends 'outlayer' - we might need to use that....
30819  * 
30820  */
30821
30822
30823 /**
30824  * @class Roo.bootstrap.LayoutMasonry
30825  * @extends Roo.bootstrap.Component
30826  * Bootstrap Layout Masonry class
30827  * 
30828  * @constructor
30829  * Create a new Element
30830  * @param {Object} config The config object
30831  */
30832
30833 Roo.bootstrap.LayoutMasonry = function(config){
30834     
30835     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30836     
30837     this.bricks = [];
30838     
30839     Roo.bootstrap.LayoutMasonry.register(this);
30840     
30841     this.addEvents({
30842         // raw events
30843         /**
30844          * @event layout
30845          * Fire after layout the items
30846          * @param {Roo.bootstrap.LayoutMasonry} this
30847          * @param {Roo.EventObject} e
30848          */
30849         "layout" : true
30850     });
30851     
30852 };
30853
30854 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30855     
30856     /**
30857      * @cfg {Boolean} isLayoutInstant = no animation?
30858      */   
30859     isLayoutInstant : false, // needed?
30860    
30861     /**
30862      * @cfg {Number} boxWidth  width of the columns
30863      */   
30864     boxWidth : 450,
30865     
30866       /**
30867      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30868      */   
30869     boxHeight : 0,
30870     
30871     /**
30872      * @cfg {Number} padWidth padding below box..
30873      */   
30874     padWidth : 10, 
30875     
30876     /**
30877      * @cfg {Number} gutter gutter width..
30878      */   
30879     gutter : 10,
30880     
30881      /**
30882      * @cfg {Number} maxCols maximum number of columns
30883      */   
30884     
30885     maxCols: 0,
30886     
30887     /**
30888      * @cfg {Boolean} isAutoInitial defalut true
30889      */   
30890     isAutoInitial : true, 
30891     
30892     containerWidth: 0,
30893     
30894     /**
30895      * @cfg {Boolean} isHorizontal defalut false
30896      */   
30897     isHorizontal : false, 
30898
30899     currentSize : null,
30900     
30901     tag: 'div',
30902     
30903     cls: '',
30904     
30905     bricks: null, //CompositeElement
30906     
30907     cols : 1,
30908     
30909     _isLayoutInited : false,
30910     
30911 //    isAlternative : false, // only use for vertical layout...
30912     
30913     /**
30914      * @cfg {Number} alternativePadWidth padding below box..
30915      */   
30916     alternativePadWidth : 50,
30917     
30918     selectedBrick : [],
30919     
30920     getAutoCreate : function(){
30921         
30922         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30923         
30924         var cfg = {
30925             tag: this.tag,
30926             cls: 'blog-masonary-wrapper ' + this.cls,
30927             cn : {
30928                 cls : 'mas-boxes masonary'
30929             }
30930         };
30931         
30932         return cfg;
30933     },
30934     
30935     getChildContainer: function( )
30936     {
30937         if (this.boxesEl) {
30938             return this.boxesEl;
30939         }
30940         
30941         this.boxesEl = this.el.select('.mas-boxes').first();
30942         
30943         return this.boxesEl;
30944     },
30945     
30946     
30947     initEvents : function()
30948     {
30949         var _this = this;
30950         
30951         if(this.isAutoInitial){
30952             Roo.log('hook children rendered');
30953             this.on('childrenrendered', function() {
30954                 Roo.log('children rendered');
30955                 _this.initial();
30956             } ,this);
30957         }
30958     },
30959     
30960     initial : function()
30961     {
30962         this.selectedBrick = [];
30963         
30964         this.currentSize = this.el.getBox(true);
30965         
30966         Roo.EventManager.onWindowResize(this.resize, this); 
30967
30968         if(!this.isAutoInitial){
30969             this.layout();
30970             return;
30971         }
30972         
30973         this.layout();
30974         
30975         return;
30976         //this.layout.defer(500,this);
30977         
30978     },
30979     
30980     resize : function()
30981     {
30982         var cs = this.el.getBox(true);
30983         
30984         if (
30985                 this.currentSize.width == cs.width && 
30986                 this.currentSize.x == cs.x && 
30987                 this.currentSize.height == cs.height && 
30988                 this.currentSize.y == cs.y 
30989         ) {
30990             Roo.log("no change in with or X or Y");
30991             return;
30992         }
30993         
30994         this.currentSize = cs;
30995         
30996         this.layout();
30997         
30998     },
30999     
31000     layout : function()
31001     {   
31002         this._resetLayout();
31003         
31004         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31005         
31006         this.layoutItems( isInstant );
31007       
31008         this._isLayoutInited = true;
31009         
31010         this.fireEvent('layout', this);
31011         
31012     },
31013     
31014     _resetLayout : function()
31015     {
31016         if(this.isHorizontal){
31017             this.horizontalMeasureColumns();
31018             return;
31019         }
31020         
31021         this.verticalMeasureColumns();
31022         
31023     },
31024     
31025     verticalMeasureColumns : function()
31026     {
31027         this.getContainerWidth();
31028         
31029 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31030 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31031 //            return;
31032 //        }
31033         
31034         var boxWidth = this.boxWidth + this.padWidth;
31035         
31036         if(this.containerWidth < this.boxWidth){
31037             boxWidth = this.containerWidth
31038         }
31039         
31040         var containerWidth = this.containerWidth;
31041         
31042         var cols = Math.floor(containerWidth / boxWidth);
31043         
31044         this.cols = Math.max( cols, 1 );
31045         
31046         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31047         
31048         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31049         
31050         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31051         
31052         this.colWidth = boxWidth + avail - this.padWidth;
31053         
31054         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31055         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31056     },
31057     
31058     horizontalMeasureColumns : function()
31059     {
31060         this.getContainerWidth();
31061         
31062         var boxWidth = this.boxWidth;
31063         
31064         if(this.containerWidth < boxWidth){
31065             boxWidth = this.containerWidth;
31066         }
31067         
31068         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31069         
31070         this.el.setHeight(boxWidth);
31071         
31072     },
31073     
31074     getContainerWidth : function()
31075     {
31076         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31077     },
31078     
31079     layoutItems : function( isInstant )
31080     {
31081         Roo.log(this.bricks);
31082         
31083         var items = Roo.apply([], this.bricks);
31084         
31085         if(this.isHorizontal){
31086             this._horizontalLayoutItems( items , isInstant );
31087             return;
31088         }
31089         
31090 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31091 //            this._verticalAlternativeLayoutItems( items , isInstant );
31092 //            return;
31093 //        }
31094         
31095         this._verticalLayoutItems( items , isInstant );
31096         
31097     },
31098     
31099     _verticalLayoutItems : function ( items , isInstant)
31100     {
31101         if ( !items || !items.length ) {
31102             return;
31103         }
31104         
31105         var standard = [
31106             ['xs', 'xs', 'xs', 'tall'],
31107             ['xs', 'xs', 'tall'],
31108             ['xs', 'xs', 'sm'],
31109             ['xs', 'xs', 'xs'],
31110             ['xs', 'tall'],
31111             ['xs', 'sm'],
31112             ['xs', 'xs'],
31113             ['xs'],
31114             
31115             ['sm', 'xs', 'xs'],
31116             ['sm', 'xs'],
31117             ['sm'],
31118             
31119             ['tall', 'xs', 'xs', 'xs'],
31120             ['tall', 'xs', 'xs'],
31121             ['tall', 'xs'],
31122             ['tall']
31123             
31124         ];
31125         
31126         var queue = [];
31127         
31128         var boxes = [];
31129         
31130         var box = [];
31131         
31132         Roo.each(items, function(item, k){
31133             
31134             switch (item.size) {
31135                 // these layouts take up a full box,
31136                 case 'md' :
31137                 case 'md-left' :
31138                 case 'md-right' :
31139                 case 'wide' :
31140                     
31141                     if(box.length){
31142                         boxes.push(box);
31143                         box = [];
31144                     }
31145                     
31146                     boxes.push([item]);
31147                     
31148                     break;
31149                     
31150                 case 'xs' :
31151                 case 'sm' :
31152                 case 'tall' :
31153                     
31154                     box.push(item);
31155                     
31156                     break;
31157                 default :
31158                     break;
31159                     
31160             }
31161             
31162         }, this);
31163         
31164         if(box.length){
31165             boxes.push(box);
31166             box = [];
31167         }
31168         
31169         var filterPattern = function(box, length)
31170         {
31171             if(!box.length){
31172                 return;
31173             }
31174             
31175             var match = false;
31176             
31177             var pattern = box.slice(0, length);
31178             
31179             var format = [];
31180             
31181             Roo.each(pattern, function(i){
31182                 format.push(i.size);
31183             }, this);
31184             
31185             Roo.each(standard, function(s){
31186                 
31187                 if(String(s) != String(format)){
31188                     return;
31189                 }
31190                 
31191                 match = true;
31192                 return false;
31193                 
31194             }, this);
31195             
31196             if(!match && length == 1){
31197                 return;
31198             }
31199             
31200             if(!match){
31201                 filterPattern(box, length - 1);
31202                 return;
31203             }
31204                 
31205             queue.push(pattern);
31206
31207             box = box.slice(length, box.length);
31208
31209             filterPattern(box, 4);
31210
31211             return;
31212             
31213         }
31214         
31215         Roo.each(boxes, function(box, k){
31216             
31217             if(!box.length){
31218                 return;
31219             }
31220             
31221             if(box.length == 1){
31222                 queue.push(box);
31223                 return;
31224             }
31225             
31226             filterPattern(box, 4);
31227             
31228         }, this);
31229         
31230         this._processVerticalLayoutQueue( queue, isInstant );
31231         
31232     },
31233     
31234 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31235 //    {
31236 //        if ( !items || !items.length ) {
31237 //            return;
31238 //        }
31239 //
31240 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31241 //        
31242 //    },
31243     
31244     _horizontalLayoutItems : function ( items , isInstant)
31245     {
31246         if ( !items || !items.length || items.length < 3) {
31247             return;
31248         }
31249         
31250         items.reverse();
31251         
31252         var eItems = items.slice(0, 3);
31253         
31254         items = items.slice(3, items.length);
31255         
31256         var standard = [
31257             ['xs', 'xs', 'xs', 'wide'],
31258             ['xs', 'xs', 'wide'],
31259             ['xs', 'xs', 'sm'],
31260             ['xs', 'xs', 'xs'],
31261             ['xs', 'wide'],
31262             ['xs', 'sm'],
31263             ['xs', 'xs'],
31264             ['xs'],
31265             
31266             ['sm', 'xs', 'xs'],
31267             ['sm', 'xs'],
31268             ['sm'],
31269             
31270             ['wide', 'xs', 'xs', 'xs'],
31271             ['wide', 'xs', 'xs'],
31272             ['wide', 'xs'],
31273             ['wide'],
31274             
31275             ['wide-thin']
31276         ];
31277         
31278         var queue = [];
31279         
31280         var boxes = [];
31281         
31282         var box = [];
31283         
31284         Roo.each(items, function(item, k){
31285             
31286             switch (item.size) {
31287                 case 'md' :
31288                 case 'md-left' :
31289                 case 'md-right' :
31290                 case 'tall' :
31291                     
31292                     if(box.length){
31293                         boxes.push(box);
31294                         box = [];
31295                     }
31296                     
31297                     boxes.push([item]);
31298                     
31299                     break;
31300                     
31301                 case 'xs' :
31302                 case 'sm' :
31303                 case 'wide' :
31304                 case 'wide-thin' :
31305                     
31306                     box.push(item);
31307                     
31308                     break;
31309                 default :
31310                     break;
31311                     
31312             }
31313             
31314         }, this);
31315         
31316         if(box.length){
31317             boxes.push(box);
31318             box = [];
31319         }
31320         
31321         var filterPattern = function(box, length)
31322         {
31323             if(!box.length){
31324                 return;
31325             }
31326             
31327             var match = false;
31328             
31329             var pattern = box.slice(0, length);
31330             
31331             var format = [];
31332             
31333             Roo.each(pattern, function(i){
31334                 format.push(i.size);
31335             }, this);
31336             
31337             Roo.each(standard, function(s){
31338                 
31339                 if(String(s) != String(format)){
31340                     return;
31341                 }
31342                 
31343                 match = true;
31344                 return false;
31345                 
31346             }, this);
31347             
31348             if(!match && length == 1){
31349                 return;
31350             }
31351             
31352             if(!match){
31353                 filterPattern(box, length - 1);
31354                 return;
31355             }
31356                 
31357             queue.push(pattern);
31358
31359             box = box.slice(length, box.length);
31360
31361             filterPattern(box, 4);
31362
31363             return;
31364             
31365         }
31366         
31367         Roo.each(boxes, function(box, k){
31368             
31369             if(!box.length){
31370                 return;
31371             }
31372             
31373             if(box.length == 1){
31374                 queue.push(box);
31375                 return;
31376             }
31377             
31378             filterPattern(box, 4);
31379             
31380         }, this);
31381         
31382         
31383         var prune = [];
31384         
31385         var pos = this.el.getBox(true);
31386         
31387         var minX = pos.x;
31388         
31389         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31390         
31391         var hit_end = false;
31392         
31393         Roo.each(queue, function(box){
31394             
31395             if(hit_end){
31396                 
31397                 Roo.each(box, function(b){
31398                 
31399                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31400                     b.el.hide();
31401
31402                 }, this);
31403
31404                 return;
31405             }
31406             
31407             var mx = 0;
31408             
31409             Roo.each(box, function(b){
31410                 
31411                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31412                 b.el.show();
31413
31414                 mx = Math.max(mx, b.x);
31415                 
31416             }, this);
31417             
31418             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31419             
31420             if(maxX < minX){
31421                 
31422                 Roo.each(box, function(b){
31423                 
31424                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31425                     b.el.hide();
31426                     
31427                 }, this);
31428                 
31429                 hit_end = true;
31430                 
31431                 return;
31432             }
31433             
31434             prune.push(box);
31435             
31436         }, this);
31437         
31438         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31439     },
31440     
31441     /** Sets position of item in DOM
31442     * @param {Element} item
31443     * @param {Number} x - horizontal position
31444     * @param {Number} y - vertical position
31445     * @param {Boolean} isInstant - disables transitions
31446     */
31447     _processVerticalLayoutQueue : function( queue, isInstant )
31448     {
31449         var pos = this.el.getBox(true);
31450         var x = pos.x;
31451         var y = pos.y;
31452         var maxY = [];
31453         
31454         for (var i = 0; i < this.cols; i++){
31455             maxY[i] = pos.y;
31456         }
31457         
31458         Roo.each(queue, function(box, k){
31459             
31460             var col = k % this.cols;
31461             
31462             Roo.each(box, function(b,kk){
31463                 
31464                 b.el.position('absolute');
31465                 
31466                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31467                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31468                 
31469                 if(b.size == 'md-left' || b.size == 'md-right'){
31470                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31471                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31472                 }
31473                 
31474                 b.el.setWidth(width);
31475                 b.el.setHeight(height);
31476                 // iframe?
31477                 b.el.select('iframe',true).setSize(width,height);
31478                 
31479             }, this);
31480             
31481             for (var i = 0; i < this.cols; i++){
31482                 
31483                 if(maxY[i] < maxY[col]){
31484                     col = i;
31485                     continue;
31486                 }
31487                 
31488                 col = Math.min(col, i);
31489                 
31490             }
31491             
31492             x = pos.x + col * (this.colWidth + this.padWidth);
31493             
31494             y = maxY[col];
31495             
31496             var positions = [];
31497             
31498             switch (box.length){
31499                 case 1 :
31500                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31501                     break;
31502                 case 2 :
31503                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31504                     break;
31505                 case 3 :
31506                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31507                     break;
31508                 case 4 :
31509                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31510                     break;
31511                 default :
31512                     break;
31513             }
31514             
31515             Roo.each(box, function(b,kk){
31516                 
31517                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31518                 
31519                 var sz = b.el.getSize();
31520                 
31521                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31522                 
31523             }, this);
31524             
31525         }, this);
31526         
31527         var mY = 0;
31528         
31529         for (var i = 0; i < this.cols; i++){
31530             mY = Math.max(mY, maxY[i]);
31531         }
31532         
31533         this.el.setHeight(mY - pos.y);
31534         
31535     },
31536     
31537 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31538 //    {
31539 //        var pos = this.el.getBox(true);
31540 //        var x = pos.x;
31541 //        var y = pos.y;
31542 //        var maxX = pos.right;
31543 //        
31544 //        var maxHeight = 0;
31545 //        
31546 //        Roo.each(items, function(item, k){
31547 //            
31548 //            var c = k % 2;
31549 //            
31550 //            item.el.position('absolute');
31551 //                
31552 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31553 //
31554 //            item.el.setWidth(width);
31555 //
31556 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31557 //
31558 //            item.el.setHeight(height);
31559 //            
31560 //            if(c == 0){
31561 //                item.el.setXY([x, y], isInstant ? false : true);
31562 //            } else {
31563 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31564 //            }
31565 //            
31566 //            y = y + height + this.alternativePadWidth;
31567 //            
31568 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31569 //            
31570 //        }, this);
31571 //        
31572 //        this.el.setHeight(maxHeight);
31573 //        
31574 //    },
31575     
31576     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31577     {
31578         var pos = this.el.getBox(true);
31579         
31580         var minX = pos.x;
31581         var minY = pos.y;
31582         
31583         var maxX = pos.right;
31584         
31585         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31586         
31587         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31588         
31589         Roo.each(queue, function(box, k){
31590             
31591             Roo.each(box, function(b, kk){
31592                 
31593                 b.el.position('absolute');
31594                 
31595                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31596                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31597                 
31598                 if(b.size == 'md-left' || b.size == 'md-right'){
31599                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31600                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31601                 }
31602                 
31603                 b.el.setWidth(width);
31604                 b.el.setHeight(height);
31605                 
31606             }, this);
31607             
31608             if(!box.length){
31609                 return;
31610             }
31611             
31612             var positions = [];
31613             
31614             switch (box.length){
31615                 case 1 :
31616                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31617                     break;
31618                 case 2 :
31619                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31620                     break;
31621                 case 3 :
31622                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31623                     break;
31624                 case 4 :
31625                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31626                     break;
31627                 default :
31628                     break;
31629             }
31630             
31631             Roo.each(box, function(b,kk){
31632                 
31633                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31634                 
31635                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31636                 
31637             }, this);
31638             
31639         }, this);
31640         
31641     },
31642     
31643     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31644     {
31645         Roo.each(eItems, function(b,k){
31646             
31647             b.size = (k == 0) ? 'sm' : 'xs';
31648             b.x = (k == 0) ? 2 : 1;
31649             b.y = (k == 0) ? 2 : 1;
31650             
31651             b.el.position('absolute');
31652             
31653             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31654                 
31655             b.el.setWidth(width);
31656             
31657             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31658             
31659             b.el.setHeight(height);
31660             
31661         }, this);
31662
31663         var positions = [];
31664         
31665         positions.push({
31666             x : maxX - this.unitWidth * 2 - this.gutter,
31667             y : minY
31668         });
31669         
31670         positions.push({
31671             x : maxX - this.unitWidth,
31672             y : minY + (this.unitWidth + this.gutter) * 2
31673         });
31674         
31675         positions.push({
31676             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31677             y : minY
31678         });
31679         
31680         Roo.each(eItems, function(b,k){
31681             
31682             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31683
31684         }, this);
31685         
31686     },
31687     
31688     getVerticalOneBoxColPositions : function(x, y, box)
31689     {
31690         var pos = [];
31691         
31692         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31693         
31694         if(box[0].size == 'md-left'){
31695             rand = 0;
31696         }
31697         
31698         if(box[0].size == 'md-right'){
31699             rand = 1;
31700         }
31701         
31702         pos.push({
31703             x : x + (this.unitWidth + this.gutter) * rand,
31704             y : y
31705         });
31706         
31707         return pos;
31708     },
31709     
31710     getVerticalTwoBoxColPositions : function(x, y, box)
31711     {
31712         var pos = [];
31713         
31714         if(box[0].size == 'xs'){
31715             
31716             pos.push({
31717                 x : x,
31718                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31719             });
31720
31721             pos.push({
31722                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31723                 y : y
31724             });
31725             
31726             return pos;
31727             
31728         }
31729         
31730         pos.push({
31731             x : x,
31732             y : y
31733         });
31734
31735         pos.push({
31736             x : x + (this.unitWidth + this.gutter) * 2,
31737             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31738         });
31739         
31740         return pos;
31741         
31742     },
31743     
31744     getVerticalThreeBoxColPositions : function(x, y, box)
31745     {
31746         var pos = [];
31747         
31748         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31749             
31750             pos.push({
31751                 x : x,
31752                 y : y
31753             });
31754
31755             pos.push({
31756                 x : x + (this.unitWidth + this.gutter) * 1,
31757                 y : y
31758             });
31759             
31760             pos.push({
31761                 x : x + (this.unitWidth + this.gutter) * 2,
31762                 y : y
31763             });
31764             
31765             return pos;
31766             
31767         }
31768         
31769         if(box[0].size == 'xs' && box[1].size == 'xs'){
31770             
31771             pos.push({
31772                 x : x,
31773                 y : y
31774             });
31775
31776             pos.push({
31777                 x : x,
31778                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31779             });
31780             
31781             pos.push({
31782                 x : x + (this.unitWidth + this.gutter) * 1,
31783                 y : y
31784             });
31785             
31786             return pos;
31787             
31788         }
31789         
31790         pos.push({
31791             x : x,
31792             y : y
31793         });
31794
31795         pos.push({
31796             x : x + (this.unitWidth + this.gutter) * 2,
31797             y : y
31798         });
31799
31800         pos.push({
31801             x : x + (this.unitWidth + this.gutter) * 2,
31802             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31803         });
31804             
31805         return pos;
31806         
31807     },
31808     
31809     getVerticalFourBoxColPositions : function(x, y, box)
31810     {
31811         var pos = [];
31812         
31813         if(box[0].size == 'xs'){
31814             
31815             pos.push({
31816                 x : x,
31817                 y : y
31818             });
31819
31820             pos.push({
31821                 x : x,
31822                 y : y + (this.unitHeight + this.gutter) * 1
31823             });
31824             
31825             pos.push({
31826                 x : x,
31827                 y : y + (this.unitHeight + this.gutter) * 2
31828             });
31829             
31830             pos.push({
31831                 x : x + (this.unitWidth + this.gutter) * 1,
31832                 y : y
31833             });
31834             
31835             return pos;
31836             
31837         }
31838         
31839         pos.push({
31840             x : x,
31841             y : y
31842         });
31843
31844         pos.push({
31845             x : x + (this.unitWidth + this.gutter) * 2,
31846             y : y
31847         });
31848
31849         pos.push({
31850             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31851             y : y + (this.unitHeight + this.gutter) * 1
31852         });
31853
31854         pos.push({
31855             x : x + (this.unitWidth + this.gutter) * 2,
31856             y : y + (this.unitWidth + this.gutter) * 2
31857         });
31858
31859         return pos;
31860         
31861     },
31862     
31863     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31864     {
31865         var pos = [];
31866         
31867         if(box[0].size == 'md-left'){
31868             pos.push({
31869                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31870                 y : minY
31871             });
31872             
31873             return pos;
31874         }
31875         
31876         if(box[0].size == 'md-right'){
31877             pos.push({
31878                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31879                 y : minY + (this.unitWidth + this.gutter) * 1
31880             });
31881             
31882             return pos;
31883         }
31884         
31885         var rand = Math.floor(Math.random() * (4 - box[0].y));
31886         
31887         pos.push({
31888             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31889             y : minY + (this.unitWidth + this.gutter) * rand
31890         });
31891         
31892         return pos;
31893         
31894     },
31895     
31896     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31897     {
31898         var pos = [];
31899         
31900         if(box[0].size == 'xs'){
31901             
31902             pos.push({
31903                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31904                 y : minY
31905             });
31906
31907             pos.push({
31908                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31909                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31910             });
31911             
31912             return pos;
31913             
31914         }
31915         
31916         pos.push({
31917             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31918             y : minY
31919         });
31920
31921         pos.push({
31922             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31923             y : minY + (this.unitWidth + this.gutter) * 2
31924         });
31925         
31926         return pos;
31927         
31928     },
31929     
31930     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31931     {
31932         var pos = [];
31933         
31934         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31935             
31936             pos.push({
31937                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31938                 y : minY
31939             });
31940
31941             pos.push({
31942                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31943                 y : minY + (this.unitWidth + this.gutter) * 1
31944             });
31945             
31946             pos.push({
31947                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31948                 y : minY + (this.unitWidth + this.gutter) * 2
31949             });
31950             
31951             return pos;
31952             
31953         }
31954         
31955         if(box[0].size == 'xs' && box[1].size == 'xs'){
31956             
31957             pos.push({
31958                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31959                 y : minY
31960             });
31961
31962             pos.push({
31963                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31964                 y : minY
31965             });
31966             
31967             pos.push({
31968                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31969                 y : minY + (this.unitWidth + this.gutter) * 1
31970             });
31971             
31972             return pos;
31973             
31974         }
31975         
31976         pos.push({
31977             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31978             y : minY
31979         });
31980
31981         pos.push({
31982             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31983             y : minY + (this.unitWidth + this.gutter) * 2
31984         });
31985
31986         pos.push({
31987             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31988             y : minY + (this.unitWidth + this.gutter) * 2
31989         });
31990             
31991         return pos;
31992         
31993     },
31994     
31995     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31996     {
31997         var pos = [];
31998         
31999         if(box[0].size == 'xs'){
32000             
32001             pos.push({
32002                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32003                 y : minY
32004             });
32005
32006             pos.push({
32007                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32008                 y : minY
32009             });
32010             
32011             pos.push({
32012                 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),
32013                 y : minY
32014             });
32015             
32016             pos.push({
32017                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32018                 y : minY + (this.unitWidth + this.gutter) * 1
32019             });
32020             
32021             return pos;
32022             
32023         }
32024         
32025         pos.push({
32026             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32027             y : minY
32028         });
32029         
32030         pos.push({
32031             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32032             y : minY + (this.unitWidth + this.gutter) * 2
32033         });
32034         
32035         pos.push({
32036             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32037             y : minY + (this.unitWidth + this.gutter) * 2
32038         });
32039         
32040         pos.push({
32041             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),
32042             y : minY + (this.unitWidth + this.gutter) * 2
32043         });
32044
32045         return pos;
32046         
32047     },
32048     
32049     /**
32050     * remove a Masonry Brick
32051     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32052     */
32053     removeBrick : function(brick_id)
32054     {
32055         if (!brick_id) {
32056             return;
32057         }
32058         
32059         for (var i = 0; i<this.bricks.length; i++) {
32060             if (this.bricks[i].id == brick_id) {
32061                 this.bricks.splice(i,1);
32062                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32063                 this.initial();
32064             }
32065         }
32066     },
32067     
32068     /**
32069     * adds a Masonry Brick
32070     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32071     */
32072     addBrick : function(cfg)
32073     {
32074         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32075         //this.register(cn);
32076         cn.parentId = this.id;
32077         cn.render(this.el);
32078         return cn;
32079     },
32080     
32081     /**
32082     * register a Masonry Brick
32083     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32084     */
32085     
32086     register : function(brick)
32087     {
32088         this.bricks.push(brick);
32089         brick.masonryId = this.id;
32090     },
32091     
32092     /**
32093     * clear all the Masonry Brick
32094     */
32095     clearAll : function()
32096     {
32097         this.bricks = [];
32098         //this.getChildContainer().dom.innerHTML = "";
32099         this.el.dom.innerHTML = '';
32100     },
32101     
32102     getSelected : function()
32103     {
32104         if (!this.selectedBrick) {
32105             return false;
32106         }
32107         
32108         return this.selectedBrick;
32109     }
32110 });
32111
32112 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32113     
32114     groups: {},
32115      /**
32116     * register a Masonry Layout
32117     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32118     */
32119     
32120     register : function(layout)
32121     {
32122         this.groups[layout.id] = layout;
32123     },
32124     /**
32125     * fetch a  Masonry Layout based on the masonry layout ID
32126     * @param {string} the masonry layout to add
32127     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32128     */
32129     
32130     get: function(layout_id) {
32131         if (typeof(this.groups[layout_id]) == 'undefined') {
32132             return false;
32133         }
32134         return this.groups[layout_id] ;
32135     }
32136     
32137     
32138     
32139 });
32140
32141  
32142
32143  /**
32144  *
32145  * This is based on 
32146  * http://masonry.desandro.com
32147  *
32148  * The idea is to render all the bricks based on vertical width...
32149  *
32150  * The original code extends 'outlayer' - we might need to use that....
32151  * 
32152  */
32153
32154
32155 /**
32156  * @class Roo.bootstrap.LayoutMasonryAuto
32157  * @extends Roo.bootstrap.Component
32158  * Bootstrap Layout Masonry class
32159  * 
32160  * @constructor
32161  * Create a new Element
32162  * @param {Object} config The config object
32163  */
32164
32165 Roo.bootstrap.LayoutMasonryAuto = function(config){
32166     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32167 };
32168
32169 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32170     
32171       /**
32172      * @cfg {Boolean} isFitWidth  - resize the width..
32173      */   
32174     isFitWidth : false,  // options..
32175     /**
32176      * @cfg {Boolean} isOriginLeft = left align?
32177      */   
32178     isOriginLeft : true,
32179     /**
32180      * @cfg {Boolean} isOriginTop = top align?
32181      */   
32182     isOriginTop : false,
32183     /**
32184      * @cfg {Boolean} isLayoutInstant = no animation?
32185      */   
32186     isLayoutInstant : false, // needed?
32187     /**
32188      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32189      */   
32190     isResizingContainer : true,
32191     /**
32192      * @cfg {Number} columnWidth  width of the columns 
32193      */   
32194     
32195     columnWidth : 0,
32196     
32197     /**
32198      * @cfg {Number} maxCols maximum number of columns
32199      */   
32200     
32201     maxCols: 0,
32202     /**
32203      * @cfg {Number} padHeight padding below box..
32204      */   
32205     
32206     padHeight : 10, 
32207     
32208     /**
32209      * @cfg {Boolean} isAutoInitial defalut true
32210      */   
32211     
32212     isAutoInitial : true, 
32213     
32214     // private?
32215     gutter : 0,
32216     
32217     containerWidth: 0,
32218     initialColumnWidth : 0,
32219     currentSize : null,
32220     
32221     colYs : null, // array.
32222     maxY : 0,
32223     padWidth: 10,
32224     
32225     
32226     tag: 'div',
32227     cls: '',
32228     bricks: null, //CompositeElement
32229     cols : 0, // array?
32230     // element : null, // wrapped now this.el
32231     _isLayoutInited : null, 
32232     
32233     
32234     getAutoCreate : function(){
32235         
32236         var cfg = {
32237             tag: this.tag,
32238             cls: 'blog-masonary-wrapper ' + this.cls,
32239             cn : {
32240                 cls : 'mas-boxes masonary'
32241             }
32242         };
32243         
32244         return cfg;
32245     },
32246     
32247     getChildContainer: function( )
32248     {
32249         if (this.boxesEl) {
32250             return this.boxesEl;
32251         }
32252         
32253         this.boxesEl = this.el.select('.mas-boxes').first();
32254         
32255         return this.boxesEl;
32256     },
32257     
32258     
32259     initEvents : function()
32260     {
32261         var _this = this;
32262         
32263         if(this.isAutoInitial){
32264             Roo.log('hook children rendered');
32265             this.on('childrenrendered', function() {
32266                 Roo.log('children rendered');
32267                 _this.initial();
32268             } ,this);
32269         }
32270         
32271     },
32272     
32273     initial : function()
32274     {
32275         this.reloadItems();
32276
32277         this.currentSize = this.el.getBox(true);
32278
32279         /// was window resize... - let's see if this works..
32280         Roo.EventManager.onWindowResize(this.resize, this); 
32281
32282         if(!this.isAutoInitial){
32283             this.layout();
32284             return;
32285         }
32286         
32287         this.layout.defer(500,this);
32288     },
32289     
32290     reloadItems: function()
32291     {
32292         this.bricks = this.el.select('.masonry-brick', true);
32293         
32294         this.bricks.each(function(b) {
32295             //Roo.log(b.getSize());
32296             if (!b.attr('originalwidth')) {
32297                 b.attr('originalwidth',  b.getSize().width);
32298             }
32299             
32300         });
32301         
32302         Roo.log(this.bricks.elements.length);
32303     },
32304     
32305     resize : function()
32306     {
32307         Roo.log('resize');
32308         var cs = this.el.getBox(true);
32309         
32310         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32311             Roo.log("no change in with or X");
32312             return;
32313         }
32314         this.currentSize = cs;
32315         this.layout();
32316     },
32317     
32318     layout : function()
32319     {
32320          Roo.log('layout');
32321         this._resetLayout();
32322         //this._manageStamps();
32323       
32324         // don't animate first layout
32325         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32326         this.layoutItems( isInstant );
32327       
32328         // flag for initalized
32329         this._isLayoutInited = true;
32330     },
32331     
32332     layoutItems : function( isInstant )
32333     {
32334         //var items = this._getItemsForLayout( this.items );
32335         // original code supports filtering layout items.. we just ignore it..
32336         
32337         this._layoutItems( this.bricks , isInstant );
32338       
32339         this._postLayout();
32340     },
32341     _layoutItems : function ( items , isInstant)
32342     {
32343        //this.fireEvent( 'layout', this, items );
32344     
32345
32346         if ( !items || !items.elements.length ) {
32347           // no items, emit event with empty array
32348             return;
32349         }
32350
32351         var queue = [];
32352         items.each(function(item) {
32353             Roo.log("layout item");
32354             Roo.log(item);
32355             // get x/y object from method
32356             var position = this._getItemLayoutPosition( item );
32357             // enqueue
32358             position.item = item;
32359             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32360             queue.push( position );
32361         }, this);
32362       
32363         this._processLayoutQueue( queue );
32364     },
32365     /** Sets position of item in DOM
32366     * @param {Element} item
32367     * @param {Number} x - horizontal position
32368     * @param {Number} y - vertical position
32369     * @param {Boolean} isInstant - disables transitions
32370     */
32371     _processLayoutQueue : function( queue )
32372     {
32373         for ( var i=0, len = queue.length; i < len; i++ ) {
32374             var obj = queue[i];
32375             obj.item.position('absolute');
32376             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32377         }
32378     },
32379       
32380     
32381     /**
32382     * Any logic you want to do after each layout,
32383     * i.e. size the container
32384     */
32385     _postLayout : function()
32386     {
32387         this.resizeContainer();
32388     },
32389     
32390     resizeContainer : function()
32391     {
32392         if ( !this.isResizingContainer ) {
32393             return;
32394         }
32395         var size = this._getContainerSize();
32396         if ( size ) {
32397             this.el.setSize(size.width,size.height);
32398             this.boxesEl.setSize(size.width,size.height);
32399         }
32400     },
32401     
32402     
32403     
32404     _resetLayout : function()
32405     {
32406         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32407         this.colWidth = this.el.getWidth();
32408         //this.gutter = this.el.getWidth(); 
32409         
32410         this.measureColumns();
32411
32412         // reset column Y
32413         var i = this.cols;
32414         this.colYs = [];
32415         while (i--) {
32416             this.colYs.push( 0 );
32417         }
32418     
32419         this.maxY = 0;
32420     },
32421
32422     measureColumns : function()
32423     {
32424         this.getContainerWidth();
32425       // if columnWidth is 0, default to outerWidth of first item
32426         if ( !this.columnWidth ) {
32427             var firstItem = this.bricks.first();
32428             Roo.log(firstItem);
32429             this.columnWidth  = this.containerWidth;
32430             if (firstItem && firstItem.attr('originalwidth') ) {
32431                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32432             }
32433             // columnWidth fall back to item of first element
32434             Roo.log("set column width?");
32435                         this.initialColumnWidth = this.columnWidth  ;
32436
32437             // if first elem has no width, default to size of container
32438             
32439         }
32440         
32441         
32442         if (this.initialColumnWidth) {
32443             this.columnWidth = this.initialColumnWidth;
32444         }
32445         
32446         
32447             
32448         // column width is fixed at the top - however if container width get's smaller we should
32449         // reduce it...
32450         
32451         // this bit calcs how man columns..
32452             
32453         var columnWidth = this.columnWidth += this.gutter;
32454       
32455         // calculate columns
32456         var containerWidth = this.containerWidth + this.gutter;
32457         
32458         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32459         // fix rounding errors, typically with gutters
32460         var excess = columnWidth - containerWidth % columnWidth;
32461         
32462         
32463         // if overshoot is less than a pixel, round up, otherwise floor it
32464         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32465         cols = Math[ mathMethod ]( cols );
32466         this.cols = Math.max( cols, 1 );
32467         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32468         
32469          // padding positioning..
32470         var totalColWidth = this.cols * this.columnWidth;
32471         var padavail = this.containerWidth - totalColWidth;
32472         // so for 2 columns - we need 3 'pads'
32473         
32474         var padNeeded = (1+this.cols) * this.padWidth;
32475         
32476         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32477         
32478         this.columnWidth += padExtra
32479         //this.padWidth = Math.floor(padavail /  ( this.cols));
32480         
32481         // adjust colum width so that padding is fixed??
32482         
32483         // we have 3 columns ... total = width * 3
32484         // we have X left over... that should be used by 
32485         
32486         //if (this.expandC) {
32487             
32488         //}
32489         
32490         
32491         
32492     },
32493     
32494     getContainerWidth : function()
32495     {
32496        /* // container is parent if fit width
32497         var container = this.isFitWidth ? this.element.parentNode : this.element;
32498         // check that this.size and size are there
32499         // IE8 triggers resize on body size change, so they might not be
32500         
32501         var size = getSize( container );  //FIXME
32502         this.containerWidth = size && size.innerWidth; //FIXME
32503         */
32504          
32505         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32506         
32507     },
32508     
32509     _getItemLayoutPosition : function( item )  // what is item?
32510     {
32511         // we resize the item to our columnWidth..
32512       
32513         item.setWidth(this.columnWidth);
32514         item.autoBoxAdjust  = false;
32515         
32516         var sz = item.getSize();
32517  
32518         // how many columns does this brick span
32519         var remainder = this.containerWidth % this.columnWidth;
32520         
32521         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32522         // round if off by 1 pixel, otherwise use ceil
32523         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32524         colSpan = Math.min( colSpan, this.cols );
32525         
32526         // normally this should be '1' as we dont' currently allow multi width columns..
32527         
32528         var colGroup = this._getColGroup( colSpan );
32529         // get the minimum Y value from the columns
32530         var minimumY = Math.min.apply( Math, colGroup );
32531         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32532         
32533         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32534          
32535         // position the brick
32536         var position = {
32537             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32538             y: this.currentSize.y + minimumY + this.padHeight
32539         };
32540         
32541         Roo.log(position);
32542         // apply setHeight to necessary columns
32543         var setHeight = minimumY + sz.height + this.padHeight;
32544         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32545         
32546         var setSpan = this.cols + 1 - colGroup.length;
32547         for ( var i = 0; i < setSpan; i++ ) {
32548           this.colYs[ shortColIndex + i ] = setHeight ;
32549         }
32550       
32551         return position;
32552     },
32553     
32554     /**
32555      * @param {Number} colSpan - number of columns the element spans
32556      * @returns {Array} colGroup
32557      */
32558     _getColGroup : function( colSpan )
32559     {
32560         if ( colSpan < 2 ) {
32561           // if brick spans only one column, use all the column Ys
32562           return this.colYs;
32563         }
32564       
32565         var colGroup = [];
32566         // how many different places could this brick fit horizontally
32567         var groupCount = this.cols + 1 - colSpan;
32568         // for each group potential horizontal position
32569         for ( var i = 0; i < groupCount; i++ ) {
32570           // make an array of colY values for that one group
32571           var groupColYs = this.colYs.slice( i, i + colSpan );
32572           // and get the max value of the array
32573           colGroup[i] = Math.max.apply( Math, groupColYs );
32574         }
32575         return colGroup;
32576     },
32577     /*
32578     _manageStamp : function( stamp )
32579     {
32580         var stampSize =  stamp.getSize();
32581         var offset = stamp.getBox();
32582         // get the columns that this stamp affects
32583         var firstX = this.isOriginLeft ? offset.x : offset.right;
32584         var lastX = firstX + stampSize.width;
32585         var firstCol = Math.floor( firstX / this.columnWidth );
32586         firstCol = Math.max( 0, firstCol );
32587         
32588         var lastCol = Math.floor( lastX / this.columnWidth );
32589         // lastCol should not go over if multiple of columnWidth #425
32590         lastCol -= lastX % this.columnWidth ? 0 : 1;
32591         lastCol = Math.min( this.cols - 1, lastCol );
32592         
32593         // set colYs to bottom of the stamp
32594         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32595             stampSize.height;
32596             
32597         for ( var i = firstCol; i <= lastCol; i++ ) {
32598           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32599         }
32600     },
32601     */
32602     
32603     _getContainerSize : function()
32604     {
32605         this.maxY = Math.max.apply( Math, this.colYs );
32606         var size = {
32607             height: this.maxY
32608         };
32609       
32610         if ( this.isFitWidth ) {
32611             size.width = this._getContainerFitWidth();
32612         }
32613       
32614         return size;
32615     },
32616     
32617     _getContainerFitWidth : function()
32618     {
32619         var unusedCols = 0;
32620         // count unused columns
32621         var i = this.cols;
32622         while ( --i ) {
32623           if ( this.colYs[i] !== 0 ) {
32624             break;
32625           }
32626           unusedCols++;
32627         }
32628         // fit container to columns that have been used
32629         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32630     },
32631     
32632     needsResizeLayout : function()
32633     {
32634         var previousWidth = this.containerWidth;
32635         this.getContainerWidth();
32636         return previousWidth !== this.containerWidth;
32637     }
32638  
32639 });
32640
32641  
32642
32643  /*
32644  * - LGPL
32645  *
32646  * element
32647  * 
32648  */
32649
32650 /**
32651  * @class Roo.bootstrap.MasonryBrick
32652  * @extends Roo.bootstrap.Component
32653  * Bootstrap MasonryBrick class
32654  * 
32655  * @constructor
32656  * Create a new MasonryBrick
32657  * @param {Object} config The config object
32658  */
32659
32660 Roo.bootstrap.MasonryBrick = function(config){
32661     
32662     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32663     
32664     Roo.bootstrap.MasonryBrick.register(this);
32665     
32666     this.addEvents({
32667         // raw events
32668         /**
32669          * @event click
32670          * When a MasonryBrick is clcik
32671          * @param {Roo.bootstrap.MasonryBrick} this
32672          * @param {Roo.EventObject} e
32673          */
32674         "click" : true
32675     });
32676 };
32677
32678 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32679     
32680     /**
32681      * @cfg {String} title
32682      */   
32683     title : '',
32684     /**
32685      * @cfg {String} html
32686      */   
32687     html : '',
32688     /**
32689      * @cfg {String} bgimage
32690      */   
32691     bgimage : '',
32692     /**
32693      * @cfg {String} videourl
32694      */   
32695     videourl : '',
32696     /**
32697      * @cfg {String} cls
32698      */   
32699     cls : '',
32700     /**
32701      * @cfg {String} href
32702      */   
32703     href : '',
32704     /**
32705      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32706      */   
32707     size : 'xs',
32708     
32709     /**
32710      * @cfg {String} placetitle (center|bottom)
32711      */   
32712     placetitle : '',
32713     
32714     /**
32715      * @cfg {Boolean} isFitContainer defalut true
32716      */   
32717     isFitContainer : true, 
32718     
32719     /**
32720      * @cfg {Boolean} preventDefault defalut false
32721      */   
32722     preventDefault : false, 
32723     
32724     /**
32725      * @cfg {Boolean} inverse defalut false
32726      */   
32727     maskInverse : false, 
32728     
32729     getAutoCreate : function()
32730     {
32731         if(!this.isFitContainer){
32732             return this.getSplitAutoCreate();
32733         }
32734         
32735         var cls = 'masonry-brick masonry-brick-full';
32736         
32737         if(this.href.length){
32738             cls += ' masonry-brick-link';
32739         }
32740         
32741         if(this.bgimage.length){
32742             cls += ' masonry-brick-image';
32743         }
32744         
32745         if(this.maskInverse){
32746             cls += ' mask-inverse';
32747         }
32748         
32749         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32750             cls += ' enable-mask';
32751         }
32752         
32753         if(this.size){
32754             cls += ' masonry-' + this.size + '-brick';
32755         }
32756         
32757         if(this.placetitle.length){
32758             
32759             switch (this.placetitle) {
32760                 case 'center' :
32761                     cls += ' masonry-center-title';
32762                     break;
32763                 case 'bottom' :
32764                     cls += ' masonry-bottom-title';
32765                     break;
32766                 default:
32767                     break;
32768             }
32769             
32770         } else {
32771             if(!this.html.length && !this.bgimage.length){
32772                 cls += ' masonry-center-title';
32773             }
32774
32775             if(!this.html.length && this.bgimage.length){
32776                 cls += ' masonry-bottom-title';
32777             }
32778         }
32779         
32780         if(this.cls){
32781             cls += ' ' + this.cls;
32782         }
32783         
32784         var cfg = {
32785             tag: (this.href.length) ? 'a' : 'div',
32786             cls: cls,
32787             cn: [
32788                 {
32789                     tag: 'div',
32790                     cls: 'masonry-brick-mask'
32791                 },
32792                 {
32793                     tag: 'div',
32794                     cls: 'masonry-brick-paragraph',
32795                     cn: []
32796                 }
32797             ]
32798         };
32799         
32800         if(this.href.length){
32801             cfg.href = this.href;
32802         }
32803         
32804         var cn = cfg.cn[1].cn;
32805         
32806         if(this.title.length){
32807             cn.push({
32808                 tag: 'h4',
32809                 cls: 'masonry-brick-title',
32810                 html: this.title
32811             });
32812         }
32813         
32814         if(this.html.length){
32815             cn.push({
32816                 tag: 'p',
32817                 cls: 'masonry-brick-text',
32818                 html: this.html
32819             });
32820         }
32821         
32822         if (!this.title.length && !this.html.length) {
32823             cfg.cn[1].cls += ' hide';
32824         }
32825         
32826         if(this.bgimage.length){
32827             cfg.cn.push({
32828                 tag: 'img',
32829                 cls: 'masonry-brick-image-view',
32830                 src: this.bgimage
32831             });
32832         }
32833         
32834         if(this.videourl.length){
32835             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32836             // youtube support only?
32837             cfg.cn.push({
32838                 tag: 'iframe',
32839                 cls: 'masonry-brick-image-view',
32840                 src: vurl,
32841                 frameborder : 0,
32842                 allowfullscreen : true
32843             });
32844         }
32845         
32846         return cfg;
32847         
32848     },
32849     
32850     getSplitAutoCreate : function()
32851     {
32852         var cls = 'masonry-brick masonry-brick-split';
32853         
32854         if(this.href.length){
32855             cls += ' masonry-brick-link';
32856         }
32857         
32858         if(this.bgimage.length){
32859             cls += ' masonry-brick-image';
32860         }
32861         
32862         if(this.size){
32863             cls += ' masonry-' + this.size + '-brick';
32864         }
32865         
32866         switch (this.placetitle) {
32867             case 'center' :
32868                 cls += ' masonry-center-title';
32869                 break;
32870             case 'bottom' :
32871                 cls += ' masonry-bottom-title';
32872                 break;
32873             default:
32874                 if(!this.bgimage.length){
32875                     cls += ' masonry-center-title';
32876                 }
32877
32878                 if(this.bgimage.length){
32879                     cls += ' masonry-bottom-title';
32880                 }
32881                 break;
32882         }
32883         
32884         if(this.cls){
32885             cls += ' ' + this.cls;
32886         }
32887         
32888         var cfg = {
32889             tag: (this.href.length) ? 'a' : 'div',
32890             cls: cls,
32891             cn: [
32892                 {
32893                     tag: 'div',
32894                     cls: 'masonry-brick-split-head',
32895                     cn: [
32896                         {
32897                             tag: 'div',
32898                             cls: 'masonry-brick-paragraph',
32899                             cn: []
32900                         }
32901                     ]
32902                 },
32903                 {
32904                     tag: 'div',
32905                     cls: 'masonry-brick-split-body',
32906                     cn: []
32907                 }
32908             ]
32909         };
32910         
32911         if(this.href.length){
32912             cfg.href = this.href;
32913         }
32914         
32915         if(this.title.length){
32916             cfg.cn[0].cn[0].cn.push({
32917                 tag: 'h4',
32918                 cls: 'masonry-brick-title',
32919                 html: this.title
32920             });
32921         }
32922         
32923         if(this.html.length){
32924             cfg.cn[1].cn.push({
32925                 tag: 'p',
32926                 cls: 'masonry-brick-text',
32927                 html: this.html
32928             });
32929         }
32930
32931         if(this.bgimage.length){
32932             cfg.cn[0].cn.push({
32933                 tag: 'img',
32934                 cls: 'masonry-brick-image-view',
32935                 src: this.bgimage
32936             });
32937         }
32938         
32939         if(this.videourl.length){
32940             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32941             // youtube support only?
32942             cfg.cn[0].cn.cn.push({
32943                 tag: 'iframe',
32944                 cls: 'masonry-brick-image-view',
32945                 src: vurl,
32946                 frameborder : 0,
32947                 allowfullscreen : true
32948             });
32949         }
32950         
32951         return cfg;
32952     },
32953     
32954     initEvents: function() 
32955     {
32956         switch (this.size) {
32957             case 'xs' :
32958                 this.x = 1;
32959                 this.y = 1;
32960                 break;
32961             case 'sm' :
32962                 this.x = 2;
32963                 this.y = 2;
32964                 break;
32965             case 'md' :
32966             case 'md-left' :
32967             case 'md-right' :
32968                 this.x = 3;
32969                 this.y = 3;
32970                 break;
32971             case 'tall' :
32972                 this.x = 2;
32973                 this.y = 3;
32974                 break;
32975             case 'wide' :
32976                 this.x = 3;
32977                 this.y = 2;
32978                 break;
32979             case 'wide-thin' :
32980                 this.x = 3;
32981                 this.y = 1;
32982                 break;
32983                         
32984             default :
32985                 break;
32986         }
32987         
32988         if(Roo.isTouch){
32989             this.el.on('touchstart', this.onTouchStart, this);
32990             this.el.on('touchmove', this.onTouchMove, this);
32991             this.el.on('touchend', this.onTouchEnd, this);
32992             this.el.on('contextmenu', this.onContextMenu, this);
32993         } else {
32994             this.el.on('mouseenter'  ,this.enter, this);
32995             this.el.on('mouseleave', this.leave, this);
32996             this.el.on('click', this.onClick, this);
32997         }
32998         
32999         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33000             this.parent().bricks.push(this);   
33001         }
33002         
33003     },
33004     
33005     onClick: function(e, el)
33006     {
33007         var time = this.endTimer - this.startTimer;
33008         // Roo.log(e.preventDefault());
33009         if(Roo.isTouch){
33010             if(time > 1000){
33011                 e.preventDefault();
33012                 return;
33013             }
33014         }
33015         
33016         if(!this.preventDefault){
33017             return;
33018         }
33019         
33020         e.preventDefault();
33021         
33022         if (this.activeClass != '') {
33023             this.selectBrick();
33024         }
33025         
33026         this.fireEvent('click', this, e);
33027     },
33028     
33029     enter: function(e, el)
33030     {
33031         e.preventDefault();
33032         
33033         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33034             return;
33035         }
33036         
33037         if(this.bgimage.length && this.html.length){
33038             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33039         }
33040     },
33041     
33042     leave: function(e, el)
33043     {
33044         e.preventDefault();
33045         
33046         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33047             return;
33048         }
33049         
33050         if(this.bgimage.length && this.html.length){
33051             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33052         }
33053     },
33054     
33055     onTouchStart: function(e, el)
33056     {
33057 //        e.preventDefault();
33058         
33059         this.touchmoved = false;
33060         
33061         if(!this.isFitContainer){
33062             return;
33063         }
33064         
33065         if(!this.bgimage.length || !this.html.length){
33066             return;
33067         }
33068         
33069         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33070         
33071         this.timer = new Date().getTime();
33072         
33073     },
33074     
33075     onTouchMove: function(e, el)
33076     {
33077         this.touchmoved = true;
33078     },
33079     
33080     onContextMenu : function(e,el)
33081     {
33082         e.preventDefault();
33083         e.stopPropagation();
33084         return false;
33085     },
33086     
33087     onTouchEnd: function(e, el)
33088     {
33089 //        e.preventDefault();
33090         
33091         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33092         
33093             this.leave(e,el);
33094             
33095             return;
33096         }
33097         
33098         if(!this.bgimage.length || !this.html.length){
33099             
33100             if(this.href.length){
33101                 window.location.href = this.href;
33102             }
33103             
33104             return;
33105         }
33106         
33107         if(!this.isFitContainer){
33108             return;
33109         }
33110         
33111         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33112         
33113         window.location.href = this.href;
33114     },
33115     
33116     //selection on single brick only
33117     selectBrick : function() {
33118         
33119         if (!this.parentId) {
33120             return;
33121         }
33122         
33123         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33124         var index = m.selectedBrick.indexOf(this.id);
33125         
33126         if ( index > -1) {
33127             m.selectedBrick.splice(index,1);
33128             this.el.removeClass(this.activeClass);
33129             return;
33130         }
33131         
33132         for(var i = 0; i < m.selectedBrick.length; i++) {
33133             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33134             b.el.removeClass(b.activeClass);
33135         }
33136         
33137         m.selectedBrick = [];
33138         
33139         m.selectedBrick.push(this.id);
33140         this.el.addClass(this.activeClass);
33141         return;
33142     },
33143     
33144     isSelected : function(){
33145         return this.el.hasClass(this.activeClass);
33146         
33147     }
33148 });
33149
33150 Roo.apply(Roo.bootstrap.MasonryBrick, {
33151     
33152     //groups: {},
33153     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33154      /**
33155     * register a Masonry Brick
33156     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33157     */
33158     
33159     register : function(brick)
33160     {
33161         //this.groups[brick.id] = brick;
33162         this.groups.add(brick.id, brick);
33163     },
33164     /**
33165     * fetch a  masonry brick based on the masonry brick ID
33166     * @param {string} the masonry brick to add
33167     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33168     */
33169     
33170     get: function(brick_id) 
33171     {
33172         // if (typeof(this.groups[brick_id]) == 'undefined') {
33173         //     return false;
33174         // }
33175         // return this.groups[brick_id] ;
33176         
33177         if(this.groups.key(brick_id)) {
33178             return this.groups.key(brick_id);
33179         }
33180         
33181         return false;
33182     }
33183     
33184     
33185     
33186 });
33187
33188  /*
33189  * - LGPL
33190  *
33191  * element
33192  * 
33193  */
33194
33195 /**
33196  * @class Roo.bootstrap.Brick
33197  * @extends Roo.bootstrap.Component
33198  * Bootstrap Brick class
33199  * 
33200  * @constructor
33201  * Create a new Brick
33202  * @param {Object} config The config object
33203  */
33204
33205 Roo.bootstrap.Brick = function(config){
33206     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33207     
33208     this.addEvents({
33209         // raw events
33210         /**
33211          * @event click
33212          * When a Brick is click
33213          * @param {Roo.bootstrap.Brick} this
33214          * @param {Roo.EventObject} e
33215          */
33216         "click" : true
33217     });
33218 };
33219
33220 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33221     
33222     /**
33223      * @cfg {String} title
33224      */   
33225     title : '',
33226     /**
33227      * @cfg {String} html
33228      */   
33229     html : '',
33230     /**
33231      * @cfg {String} bgimage
33232      */   
33233     bgimage : '',
33234     /**
33235      * @cfg {String} cls
33236      */   
33237     cls : '',
33238     /**
33239      * @cfg {String} href
33240      */   
33241     href : '',
33242     /**
33243      * @cfg {String} video
33244      */   
33245     video : '',
33246     /**
33247      * @cfg {Boolean} square
33248      */   
33249     square : true,
33250     
33251     getAutoCreate : function()
33252     {
33253         var cls = 'roo-brick';
33254         
33255         if(this.href.length){
33256             cls += ' roo-brick-link';
33257         }
33258         
33259         if(this.bgimage.length){
33260             cls += ' roo-brick-image';
33261         }
33262         
33263         if(!this.html.length && !this.bgimage.length){
33264             cls += ' roo-brick-center-title';
33265         }
33266         
33267         if(!this.html.length && this.bgimage.length){
33268             cls += ' roo-brick-bottom-title';
33269         }
33270         
33271         if(this.cls){
33272             cls += ' ' + this.cls;
33273         }
33274         
33275         var cfg = {
33276             tag: (this.href.length) ? 'a' : 'div',
33277             cls: cls,
33278             cn: [
33279                 {
33280                     tag: 'div',
33281                     cls: 'roo-brick-paragraph',
33282                     cn: []
33283                 }
33284             ]
33285         };
33286         
33287         if(this.href.length){
33288             cfg.href = this.href;
33289         }
33290         
33291         var cn = cfg.cn[0].cn;
33292         
33293         if(this.title.length){
33294             cn.push({
33295                 tag: 'h4',
33296                 cls: 'roo-brick-title',
33297                 html: this.title
33298             });
33299         }
33300         
33301         if(this.html.length){
33302             cn.push({
33303                 tag: 'p',
33304                 cls: 'roo-brick-text',
33305                 html: this.html
33306             });
33307         } else {
33308             cn.cls += ' hide';
33309         }
33310         
33311         if(this.bgimage.length){
33312             cfg.cn.push({
33313                 tag: 'img',
33314                 cls: 'roo-brick-image-view',
33315                 src: this.bgimage
33316             });
33317         }
33318         
33319         return cfg;
33320     },
33321     
33322     initEvents: function() 
33323     {
33324         if(this.title.length || this.html.length){
33325             this.el.on('mouseenter'  ,this.enter, this);
33326             this.el.on('mouseleave', this.leave, this);
33327         }
33328         
33329         Roo.EventManager.onWindowResize(this.resize, this); 
33330         
33331         if(this.bgimage.length){
33332             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33333             this.imageEl.on('load', this.onImageLoad, this);
33334             return;
33335         }
33336         
33337         this.resize();
33338     },
33339     
33340     onImageLoad : function()
33341     {
33342         this.resize();
33343     },
33344     
33345     resize : function()
33346     {
33347         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33348         
33349         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33350         
33351         if(this.bgimage.length){
33352             var image = this.el.select('.roo-brick-image-view', true).first();
33353             
33354             image.setWidth(paragraph.getWidth());
33355             
33356             if(this.square){
33357                 image.setHeight(paragraph.getWidth());
33358             }
33359             
33360             this.el.setHeight(image.getHeight());
33361             paragraph.setHeight(image.getHeight());
33362             
33363         }
33364         
33365     },
33366     
33367     enter: function(e, el)
33368     {
33369         e.preventDefault();
33370         
33371         if(this.bgimage.length){
33372             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33373             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33374         }
33375     },
33376     
33377     leave: function(e, el)
33378     {
33379         e.preventDefault();
33380         
33381         if(this.bgimage.length){
33382             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33383             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33384         }
33385     }
33386     
33387 });
33388
33389  
33390
33391  /*
33392  * - LGPL
33393  *
33394  * Number field 
33395  */
33396
33397 /**
33398  * @class Roo.bootstrap.NumberField
33399  * @extends Roo.bootstrap.Input
33400  * Bootstrap NumberField class
33401  * 
33402  * 
33403  * 
33404  * 
33405  * @constructor
33406  * Create a new NumberField
33407  * @param {Object} config The config object
33408  */
33409
33410 Roo.bootstrap.NumberField = function(config){
33411     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33412 };
33413
33414 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33415     
33416     /**
33417      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33418      */
33419     allowDecimals : true,
33420     /**
33421      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33422      */
33423     decimalSeparator : ".",
33424     /**
33425      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33426      */
33427     decimalPrecision : 2,
33428     /**
33429      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33430      */
33431     allowNegative : true,
33432     
33433     /**
33434      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33435      */
33436     allowZero: true,
33437     /**
33438      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33439      */
33440     minValue : Number.NEGATIVE_INFINITY,
33441     /**
33442      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33443      */
33444     maxValue : Number.MAX_VALUE,
33445     /**
33446      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33447      */
33448     minText : "The minimum value for this field is {0}",
33449     /**
33450      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33451      */
33452     maxText : "The maximum value for this field is {0}",
33453     /**
33454      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33455      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33456      */
33457     nanText : "{0} is not a valid number",
33458     /**
33459      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33460      */
33461     thousandsDelimiter : false,
33462     /**
33463      * @cfg {String} valueAlign alignment of value
33464      */
33465     valueAlign : "left",
33466
33467     getAutoCreate : function()
33468     {
33469         var hiddenInput = {
33470             tag: 'input',
33471             type: 'hidden',
33472             id: Roo.id(),
33473             cls: 'hidden-number-input'
33474         };
33475         
33476         if (this.name) {
33477             hiddenInput.name = this.name;
33478         }
33479         
33480         this.name = '';
33481         
33482         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33483         
33484         this.name = hiddenInput.name;
33485         
33486         if(cfg.cn.length > 0) {
33487             cfg.cn.push(hiddenInput);
33488         }
33489         
33490         return cfg;
33491     },
33492
33493     // private
33494     initEvents : function()
33495     {   
33496         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33497         
33498         var allowed = "0123456789";
33499         
33500         if(this.allowDecimals){
33501             allowed += this.decimalSeparator;
33502         }
33503         
33504         if(this.allowNegative){
33505             allowed += "-";
33506         }
33507         
33508         if(this.thousandsDelimiter) {
33509             allowed += ",";
33510         }
33511         
33512         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33513         
33514         var keyPress = function(e){
33515             
33516             var k = e.getKey();
33517             
33518             var c = e.getCharCode();
33519             
33520             if(
33521                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33522                     allowed.indexOf(String.fromCharCode(c)) === -1
33523             ){
33524                 e.stopEvent();
33525                 return;
33526             }
33527             
33528             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33529                 return;
33530             }
33531             
33532             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33533                 e.stopEvent();
33534             }
33535         };
33536         
33537         this.el.on("keypress", keyPress, this);
33538     },
33539     
33540     validateValue : function(value)
33541     {
33542         
33543         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33544             return false;
33545         }
33546         
33547         var num = this.parseValue(value);
33548         
33549         if(isNaN(num)){
33550             this.markInvalid(String.format(this.nanText, value));
33551             return false;
33552         }
33553         
33554         if(num < this.minValue){
33555             this.markInvalid(String.format(this.minText, this.minValue));
33556             return false;
33557         }
33558         
33559         if(num > this.maxValue){
33560             this.markInvalid(String.format(this.maxText, this.maxValue));
33561             return false;
33562         }
33563         
33564         return true;
33565     },
33566
33567     getValue : function()
33568     {
33569         var v = this.hiddenEl().getValue();
33570         
33571         return this.fixPrecision(this.parseValue(v));
33572     },
33573
33574     parseValue : function(value)
33575     {
33576         if(this.thousandsDelimiter) {
33577             value += "";
33578             r = new RegExp(",", "g");
33579             value = value.replace(r, "");
33580         }
33581         
33582         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33583         return isNaN(value) ? '' : value;
33584     },
33585
33586     fixPrecision : function(value)
33587     {
33588         if(this.thousandsDelimiter) {
33589             value += "";
33590             r = new RegExp(",", "g");
33591             value = value.replace(r, "");
33592         }
33593         
33594         var nan = isNaN(value);
33595         
33596         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33597             return nan ? '' : value;
33598         }
33599         return parseFloat(value).toFixed(this.decimalPrecision);
33600     },
33601
33602     setValue : function(v)
33603     {
33604         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33605         
33606         this.value = v;
33607         
33608         if(this.rendered){
33609             
33610             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33611             
33612             this.inputEl().dom.value = (v == '') ? '' :
33613                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33614             
33615             if(!this.allowZero && v === '0') {
33616                 this.hiddenEl().dom.value = '';
33617                 this.inputEl().dom.value = '';
33618             }
33619             
33620             this.validate();
33621         }
33622     },
33623
33624     decimalPrecisionFcn : function(v)
33625     {
33626         return Math.floor(v);
33627     },
33628
33629     beforeBlur : function()
33630     {
33631         var v = this.parseValue(this.getRawValue());
33632         
33633         if(v || v === 0 || v === ''){
33634             this.setValue(v);
33635         }
33636     },
33637     
33638     hiddenEl : function()
33639     {
33640         return this.el.select('input.hidden-number-input',true).first();
33641     }
33642     
33643 });
33644
33645  
33646
33647 /*
33648 * Licence: LGPL
33649 */
33650
33651 /**
33652  * @class Roo.bootstrap.DocumentSlider
33653  * @extends Roo.bootstrap.Component
33654  * Bootstrap DocumentSlider class
33655  * 
33656  * @constructor
33657  * Create a new DocumentViewer
33658  * @param {Object} config The config object
33659  */
33660
33661 Roo.bootstrap.DocumentSlider = function(config){
33662     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33663     
33664     this.files = [];
33665     
33666     this.addEvents({
33667         /**
33668          * @event initial
33669          * Fire after initEvent
33670          * @param {Roo.bootstrap.DocumentSlider} this
33671          */
33672         "initial" : true,
33673         /**
33674          * @event update
33675          * Fire after update
33676          * @param {Roo.bootstrap.DocumentSlider} this
33677          */
33678         "update" : true,
33679         /**
33680          * @event click
33681          * Fire after click
33682          * @param {Roo.bootstrap.DocumentSlider} this
33683          */
33684         "click" : true
33685     });
33686 };
33687
33688 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33689     
33690     files : false,
33691     
33692     indicator : 0,
33693     
33694     getAutoCreate : function()
33695     {
33696         var cfg = {
33697             tag : 'div',
33698             cls : 'roo-document-slider',
33699             cn : [
33700                 {
33701                     tag : 'div',
33702                     cls : 'roo-document-slider-header',
33703                     cn : [
33704                         {
33705                             tag : 'div',
33706                             cls : 'roo-document-slider-header-title'
33707                         }
33708                     ]
33709                 },
33710                 {
33711                     tag : 'div',
33712                     cls : 'roo-document-slider-body',
33713                     cn : [
33714                         {
33715                             tag : 'div',
33716                             cls : 'roo-document-slider-prev',
33717                             cn : [
33718                                 {
33719                                     tag : 'i',
33720                                     cls : 'fa fa-chevron-left'
33721                                 }
33722                             ]
33723                         },
33724                         {
33725                             tag : 'div',
33726                             cls : 'roo-document-slider-thumb',
33727                             cn : [
33728                                 {
33729                                     tag : 'img',
33730                                     cls : 'roo-document-slider-image'
33731                                 }
33732                             ]
33733                         },
33734                         {
33735                             tag : 'div',
33736                             cls : 'roo-document-slider-next',
33737                             cn : [
33738                                 {
33739                                     tag : 'i',
33740                                     cls : 'fa fa-chevron-right'
33741                                 }
33742                             ]
33743                         }
33744                     ]
33745                 }
33746             ]
33747         };
33748         
33749         return cfg;
33750     },
33751     
33752     initEvents : function()
33753     {
33754         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33755         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33756         
33757         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33758         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33759         
33760         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33761         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33762         
33763         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33764         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33765         
33766         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33767         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33768         
33769         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33770         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33771         
33772         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33773         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33774         
33775         this.thumbEl.on('click', this.onClick, this);
33776         
33777         this.prevIndicator.on('click', this.prev, this);
33778         
33779         this.nextIndicator.on('click', this.next, this);
33780         
33781     },
33782     
33783     initial : function()
33784     {
33785         if(this.files.length){
33786             this.indicator = 1;
33787             this.update()
33788         }
33789         
33790         this.fireEvent('initial', this);
33791     },
33792     
33793     update : function()
33794     {
33795         this.imageEl.attr('src', this.files[this.indicator - 1]);
33796         
33797         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33798         
33799         this.prevIndicator.show();
33800         
33801         if(this.indicator == 1){
33802             this.prevIndicator.hide();
33803         }
33804         
33805         this.nextIndicator.show();
33806         
33807         if(this.indicator == this.files.length){
33808             this.nextIndicator.hide();
33809         }
33810         
33811         this.thumbEl.scrollTo('top');
33812         
33813         this.fireEvent('update', this);
33814     },
33815     
33816     onClick : function(e)
33817     {
33818         e.preventDefault();
33819         
33820         this.fireEvent('click', this);
33821     },
33822     
33823     prev : function(e)
33824     {
33825         e.preventDefault();
33826         
33827         this.indicator = Math.max(1, this.indicator - 1);
33828         
33829         this.update();
33830     },
33831     
33832     next : function(e)
33833     {
33834         e.preventDefault();
33835         
33836         this.indicator = Math.min(this.files.length, this.indicator + 1);
33837         
33838         this.update();
33839     }
33840 });
33841 /*
33842  * - LGPL
33843  *
33844  * RadioSet
33845  *
33846  *
33847  */
33848
33849 /**
33850  * @class Roo.bootstrap.RadioSet
33851  * @extends Roo.bootstrap.Input
33852  * Bootstrap RadioSet class
33853  * @cfg {String} indicatorpos (left|right) default left
33854  * @cfg {Boolean} inline (true|false) inline the element (default true)
33855  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33856  * @constructor
33857  * Create a new RadioSet
33858  * @param {Object} config The config object
33859  */
33860
33861 Roo.bootstrap.RadioSet = function(config){
33862     
33863     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33864     
33865     this.radioes = [];
33866     
33867     Roo.bootstrap.RadioSet.register(this);
33868     
33869     this.addEvents({
33870         /**
33871         * @event check
33872         * Fires when the element is checked or unchecked.
33873         * @param {Roo.bootstrap.RadioSet} this This radio
33874         * @param {Roo.bootstrap.Radio} item The checked item
33875         */
33876        check : true,
33877        /**
33878         * @event click
33879         * Fires when the element is click.
33880         * @param {Roo.bootstrap.RadioSet} this This radio set
33881         * @param {Roo.bootstrap.Radio} item The checked item
33882         * @param {Roo.EventObject} e The event object
33883         */
33884        click : true
33885     });
33886     
33887 };
33888
33889 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33890
33891     radioes : false,
33892     
33893     inline : true,
33894     
33895     weight : '',
33896     
33897     indicatorpos : 'left',
33898     
33899     getAutoCreate : function()
33900     {
33901         var label = {
33902             tag : 'label',
33903             cls : 'roo-radio-set-label',
33904             cn : [
33905                 {
33906                     tag : 'span',
33907                     html : this.fieldLabel
33908                 }
33909             ]
33910         };
33911         
33912         if(this.indicatorpos == 'left'){
33913             label.cn.unshift({
33914                 tag : 'i',
33915                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33916                 tooltip : 'This field is required'
33917             });
33918         } else {
33919             label.cn.push({
33920                 tag : 'i',
33921                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33922                 tooltip : 'This field is required'
33923             });
33924         }
33925         
33926         var items = {
33927             tag : 'div',
33928             cls : 'roo-radio-set-items'
33929         };
33930         
33931         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33932         
33933         if (align === 'left' && this.fieldLabel.length) {
33934             
33935             items = {
33936                 cls : "roo-radio-set-right", 
33937                 cn: [
33938                     items
33939                 ]
33940             };
33941             
33942             if(this.labelWidth > 12){
33943                 label.style = "width: " + this.labelWidth + 'px';
33944             }
33945             
33946             if(this.labelWidth < 13 && this.labelmd == 0){
33947                 this.labelmd = this.labelWidth;
33948             }
33949             
33950             if(this.labellg > 0){
33951                 label.cls += ' col-lg-' + this.labellg;
33952                 items.cls += ' col-lg-' + (12 - this.labellg);
33953             }
33954             
33955             if(this.labelmd > 0){
33956                 label.cls += ' col-md-' + this.labelmd;
33957                 items.cls += ' col-md-' + (12 - this.labelmd);
33958             }
33959             
33960             if(this.labelsm > 0){
33961                 label.cls += ' col-sm-' + this.labelsm;
33962                 items.cls += ' col-sm-' + (12 - this.labelsm);
33963             }
33964             
33965             if(this.labelxs > 0){
33966                 label.cls += ' col-xs-' + this.labelxs;
33967                 items.cls += ' col-xs-' + (12 - this.labelxs);
33968             }
33969         }
33970         
33971         var cfg = {
33972             tag : 'div',
33973             cls : 'roo-radio-set',
33974             cn : [
33975                 {
33976                     tag : 'input',
33977                     cls : 'roo-radio-set-input',
33978                     type : 'hidden',
33979                     name : this.name,
33980                     value : this.value ? this.value :  ''
33981                 },
33982                 label,
33983                 items
33984             ]
33985         };
33986         
33987         if(this.weight.length){
33988             cfg.cls += ' roo-radio-' + this.weight;
33989         }
33990         
33991         if(this.inline) {
33992             cfg.cls += ' roo-radio-set-inline';
33993         }
33994         
33995         var settings=this;
33996         ['xs','sm','md','lg'].map(function(size){
33997             if (settings[size]) {
33998                 cfg.cls += ' col-' + size + '-' + settings[size];
33999             }
34000         });
34001         
34002         return cfg;
34003         
34004     },
34005
34006     initEvents : function()
34007     {
34008         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34009         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34010         
34011         if(!this.fieldLabel.length){
34012             this.labelEl.hide();
34013         }
34014         
34015         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34016         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34017         
34018         this.indicator = this.indicatorEl();
34019         
34020         if(this.indicator){
34021             this.indicator.addClass('invisible');
34022         }
34023         
34024         this.originalValue = this.getValue();
34025         
34026     },
34027     
34028     inputEl: function ()
34029     {
34030         return this.el.select('.roo-radio-set-input', true).first();
34031     },
34032     
34033     getChildContainer : function()
34034     {
34035         return this.itemsEl;
34036     },
34037     
34038     register : function(item)
34039     {
34040         this.radioes.push(item);
34041         
34042     },
34043     
34044     validate : function()
34045     {   
34046         if(this.getVisibilityEl().hasClass('hidden')){
34047             return true;
34048         }
34049         
34050         var valid = false;
34051         
34052         Roo.each(this.radioes, function(i){
34053             if(!i.checked){
34054                 return;
34055             }
34056             
34057             valid = true;
34058             return false;
34059         });
34060         
34061         if(this.allowBlank) {
34062             return true;
34063         }
34064         
34065         if(this.disabled || valid){
34066             this.markValid();
34067             return true;
34068         }
34069         
34070         this.markInvalid();
34071         return false;
34072         
34073     },
34074     
34075     markValid : function()
34076     {
34077         if(this.labelEl.isVisible(true)){
34078             this.indicatorEl().removeClass('visible');
34079             this.indicatorEl().addClass('invisible');
34080         }
34081         
34082         this.el.removeClass([this.invalidClass, this.validClass]);
34083         this.el.addClass(this.validClass);
34084         
34085         this.fireEvent('valid', this);
34086     },
34087     
34088     markInvalid : function(msg)
34089     {
34090         if(this.allowBlank || this.disabled){
34091             return;
34092         }
34093         
34094         if(this.labelEl.isVisible(true)){
34095             this.indicatorEl().removeClass('invisible');
34096             this.indicatorEl().addClass('visible');
34097         }
34098         
34099         this.el.removeClass([this.invalidClass, this.validClass]);
34100         this.el.addClass(this.invalidClass);
34101         
34102         this.fireEvent('invalid', this, msg);
34103         
34104     },
34105     
34106     setValue : function(v, suppressEvent)
34107     {   
34108         if(this.value === v){
34109             return;
34110         }
34111         
34112         this.value = v;
34113         
34114         if(this.rendered){
34115             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34116         }
34117         
34118         Roo.each(this.radioes, function(i){
34119             i.checked = false;
34120             i.el.removeClass('checked');
34121         });
34122         
34123         Roo.each(this.radioes, function(i){
34124             
34125             if(i.value === v || i.value.toString() === v.toString()){
34126                 i.checked = true;
34127                 i.el.addClass('checked');
34128                 
34129                 if(suppressEvent !== true){
34130                     this.fireEvent('check', this, i);
34131                 }
34132                 
34133                 return false;
34134             }
34135             
34136         }, this);
34137         
34138         this.validate();
34139     },
34140     
34141     clearInvalid : function(){
34142         
34143         if(!this.el || this.preventMark){
34144             return;
34145         }
34146         
34147         this.el.removeClass([this.invalidClass]);
34148         
34149         this.fireEvent('valid', this);
34150     }
34151     
34152 });
34153
34154 Roo.apply(Roo.bootstrap.RadioSet, {
34155     
34156     groups: {},
34157     
34158     register : function(set)
34159     {
34160         this.groups[set.name] = set;
34161     },
34162     
34163     get: function(name) 
34164     {
34165         if (typeof(this.groups[name]) == 'undefined') {
34166             return false;
34167         }
34168         
34169         return this.groups[name] ;
34170     }
34171     
34172 });
34173 /*
34174  * Based on:
34175  * Ext JS Library 1.1.1
34176  * Copyright(c) 2006-2007, Ext JS, LLC.
34177  *
34178  * Originally Released Under LGPL - original licence link has changed is not relivant.
34179  *
34180  * Fork - LGPL
34181  * <script type="text/javascript">
34182  */
34183
34184
34185 /**
34186  * @class Roo.bootstrap.SplitBar
34187  * @extends Roo.util.Observable
34188  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34189  * <br><br>
34190  * Usage:
34191  * <pre><code>
34192 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34193                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34194 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34195 split.minSize = 100;
34196 split.maxSize = 600;
34197 split.animate = true;
34198 split.on('moved', splitterMoved);
34199 </code></pre>
34200  * @constructor
34201  * Create a new SplitBar
34202  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34203  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34204  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34205  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34206                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34207                         position of the SplitBar).
34208  */
34209 Roo.bootstrap.SplitBar = function(cfg){
34210     
34211     /** @private */
34212     
34213     //{
34214     //  dragElement : elm
34215     //  resizingElement: el,
34216         // optional..
34217     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34218     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34219         // existingProxy ???
34220     //}
34221     
34222     this.el = Roo.get(cfg.dragElement, true);
34223     this.el.dom.unselectable = "on";
34224     /** @private */
34225     this.resizingEl = Roo.get(cfg.resizingElement, true);
34226
34227     /**
34228      * @private
34229      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34230      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34231      * @type Number
34232      */
34233     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34234     
34235     /**
34236      * The minimum size of the resizing element. (Defaults to 0)
34237      * @type Number
34238      */
34239     this.minSize = 0;
34240     
34241     /**
34242      * The maximum size of the resizing element. (Defaults to 2000)
34243      * @type Number
34244      */
34245     this.maxSize = 2000;
34246     
34247     /**
34248      * Whether to animate the transition to the new size
34249      * @type Boolean
34250      */
34251     this.animate = false;
34252     
34253     /**
34254      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34255      * @type Boolean
34256      */
34257     this.useShim = false;
34258     
34259     /** @private */
34260     this.shim = null;
34261     
34262     if(!cfg.existingProxy){
34263         /** @private */
34264         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34265     }else{
34266         this.proxy = Roo.get(cfg.existingProxy).dom;
34267     }
34268     /** @private */
34269     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34270     
34271     /** @private */
34272     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34273     
34274     /** @private */
34275     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34276     
34277     /** @private */
34278     this.dragSpecs = {};
34279     
34280     /**
34281      * @private The adapter to use to positon and resize elements
34282      */
34283     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34284     this.adapter.init(this);
34285     
34286     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34287         /** @private */
34288         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34289         this.el.addClass("roo-splitbar-h");
34290     }else{
34291         /** @private */
34292         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34293         this.el.addClass("roo-splitbar-v");
34294     }
34295     
34296     this.addEvents({
34297         /**
34298          * @event resize
34299          * Fires when the splitter is moved (alias for {@link #event-moved})
34300          * @param {Roo.bootstrap.SplitBar} this
34301          * @param {Number} newSize the new width or height
34302          */
34303         "resize" : true,
34304         /**
34305          * @event moved
34306          * Fires when the splitter is moved
34307          * @param {Roo.bootstrap.SplitBar} this
34308          * @param {Number} newSize the new width or height
34309          */
34310         "moved" : true,
34311         /**
34312          * @event beforeresize
34313          * Fires before the splitter is dragged
34314          * @param {Roo.bootstrap.SplitBar} this
34315          */
34316         "beforeresize" : true,
34317
34318         "beforeapply" : true
34319     });
34320
34321     Roo.util.Observable.call(this);
34322 };
34323
34324 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34325     onStartProxyDrag : function(x, y){
34326         this.fireEvent("beforeresize", this);
34327         if(!this.overlay){
34328             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34329             o.unselectable();
34330             o.enableDisplayMode("block");
34331             // all splitbars share the same overlay
34332             Roo.bootstrap.SplitBar.prototype.overlay = o;
34333         }
34334         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34335         this.overlay.show();
34336         Roo.get(this.proxy).setDisplayed("block");
34337         var size = this.adapter.getElementSize(this);
34338         this.activeMinSize = this.getMinimumSize();;
34339         this.activeMaxSize = this.getMaximumSize();;
34340         var c1 = size - this.activeMinSize;
34341         var c2 = Math.max(this.activeMaxSize - size, 0);
34342         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34343             this.dd.resetConstraints();
34344             this.dd.setXConstraint(
34345                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34346                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34347             );
34348             this.dd.setYConstraint(0, 0);
34349         }else{
34350             this.dd.resetConstraints();
34351             this.dd.setXConstraint(0, 0);
34352             this.dd.setYConstraint(
34353                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34354                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34355             );
34356          }
34357         this.dragSpecs.startSize = size;
34358         this.dragSpecs.startPoint = [x, y];
34359         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34360     },
34361     
34362     /** 
34363      * @private Called after the drag operation by the DDProxy
34364      */
34365     onEndProxyDrag : function(e){
34366         Roo.get(this.proxy).setDisplayed(false);
34367         var endPoint = Roo.lib.Event.getXY(e);
34368         if(this.overlay){
34369             this.overlay.hide();
34370         }
34371         var newSize;
34372         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34373             newSize = this.dragSpecs.startSize + 
34374                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34375                     endPoint[0] - this.dragSpecs.startPoint[0] :
34376                     this.dragSpecs.startPoint[0] - endPoint[0]
34377                 );
34378         }else{
34379             newSize = this.dragSpecs.startSize + 
34380                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34381                     endPoint[1] - this.dragSpecs.startPoint[1] :
34382                     this.dragSpecs.startPoint[1] - endPoint[1]
34383                 );
34384         }
34385         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34386         if(newSize != this.dragSpecs.startSize){
34387             if(this.fireEvent('beforeapply', this, newSize) !== false){
34388                 this.adapter.setElementSize(this, newSize);
34389                 this.fireEvent("moved", this, newSize);
34390                 this.fireEvent("resize", this, newSize);
34391             }
34392         }
34393     },
34394     
34395     /**
34396      * Get the adapter this SplitBar uses
34397      * @return The adapter object
34398      */
34399     getAdapter : function(){
34400         return this.adapter;
34401     },
34402     
34403     /**
34404      * Set the adapter this SplitBar uses
34405      * @param {Object} adapter A SplitBar adapter object
34406      */
34407     setAdapter : function(adapter){
34408         this.adapter = adapter;
34409         this.adapter.init(this);
34410     },
34411     
34412     /**
34413      * Gets the minimum size for the resizing element
34414      * @return {Number} The minimum size
34415      */
34416     getMinimumSize : function(){
34417         return this.minSize;
34418     },
34419     
34420     /**
34421      * Sets the minimum size for the resizing element
34422      * @param {Number} minSize The minimum size
34423      */
34424     setMinimumSize : function(minSize){
34425         this.minSize = minSize;
34426     },
34427     
34428     /**
34429      * Gets the maximum size for the resizing element
34430      * @return {Number} The maximum size
34431      */
34432     getMaximumSize : function(){
34433         return this.maxSize;
34434     },
34435     
34436     /**
34437      * Sets the maximum size for the resizing element
34438      * @param {Number} maxSize The maximum size
34439      */
34440     setMaximumSize : function(maxSize){
34441         this.maxSize = maxSize;
34442     },
34443     
34444     /**
34445      * Sets the initialize size for the resizing element
34446      * @param {Number} size The initial size
34447      */
34448     setCurrentSize : function(size){
34449         var oldAnimate = this.animate;
34450         this.animate = false;
34451         this.adapter.setElementSize(this, size);
34452         this.animate = oldAnimate;
34453     },
34454     
34455     /**
34456      * Destroy this splitbar. 
34457      * @param {Boolean} removeEl True to remove the element
34458      */
34459     destroy : function(removeEl){
34460         if(this.shim){
34461             this.shim.remove();
34462         }
34463         this.dd.unreg();
34464         this.proxy.parentNode.removeChild(this.proxy);
34465         if(removeEl){
34466             this.el.remove();
34467         }
34468     }
34469 });
34470
34471 /**
34472  * @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.
34473  */
34474 Roo.bootstrap.SplitBar.createProxy = function(dir){
34475     var proxy = new Roo.Element(document.createElement("div"));
34476     proxy.unselectable();
34477     var cls = 'roo-splitbar-proxy';
34478     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34479     document.body.appendChild(proxy.dom);
34480     return proxy.dom;
34481 };
34482
34483 /** 
34484  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34485  * Default Adapter. It assumes the splitter and resizing element are not positioned
34486  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34487  */
34488 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34489 };
34490
34491 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34492     // do nothing for now
34493     init : function(s){
34494     
34495     },
34496     /**
34497      * Called before drag operations to get the current size of the resizing element. 
34498      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34499      */
34500      getElementSize : function(s){
34501         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34502             return s.resizingEl.getWidth();
34503         }else{
34504             return s.resizingEl.getHeight();
34505         }
34506     },
34507     
34508     /**
34509      * Called after drag operations to set the size of the resizing element.
34510      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34511      * @param {Number} newSize The new size to set
34512      * @param {Function} onComplete A function to be invoked when resizing is complete
34513      */
34514     setElementSize : function(s, newSize, onComplete){
34515         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34516             if(!s.animate){
34517                 s.resizingEl.setWidth(newSize);
34518                 if(onComplete){
34519                     onComplete(s, newSize);
34520                 }
34521             }else{
34522                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34523             }
34524         }else{
34525             
34526             if(!s.animate){
34527                 s.resizingEl.setHeight(newSize);
34528                 if(onComplete){
34529                     onComplete(s, newSize);
34530                 }
34531             }else{
34532                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34533             }
34534         }
34535     }
34536 };
34537
34538 /** 
34539  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34540  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34541  * Adapter that  moves the splitter element to align with the resized sizing element. 
34542  * Used with an absolute positioned SplitBar.
34543  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34544  * document.body, make sure you assign an id to the body element.
34545  */
34546 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34547     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34548     this.container = Roo.get(container);
34549 };
34550
34551 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34552     init : function(s){
34553         this.basic.init(s);
34554     },
34555     
34556     getElementSize : function(s){
34557         return this.basic.getElementSize(s);
34558     },
34559     
34560     setElementSize : function(s, newSize, onComplete){
34561         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34562     },
34563     
34564     moveSplitter : function(s){
34565         var yes = Roo.bootstrap.SplitBar;
34566         switch(s.placement){
34567             case yes.LEFT:
34568                 s.el.setX(s.resizingEl.getRight());
34569                 break;
34570             case yes.RIGHT:
34571                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34572                 break;
34573             case yes.TOP:
34574                 s.el.setY(s.resizingEl.getBottom());
34575                 break;
34576             case yes.BOTTOM:
34577                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34578                 break;
34579         }
34580     }
34581 };
34582
34583 /**
34584  * Orientation constant - Create a vertical SplitBar
34585  * @static
34586  * @type Number
34587  */
34588 Roo.bootstrap.SplitBar.VERTICAL = 1;
34589
34590 /**
34591  * Orientation constant - Create a horizontal SplitBar
34592  * @static
34593  * @type Number
34594  */
34595 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34596
34597 /**
34598  * Placement constant - The resizing element is to the left of the splitter element
34599  * @static
34600  * @type Number
34601  */
34602 Roo.bootstrap.SplitBar.LEFT = 1;
34603
34604 /**
34605  * Placement constant - The resizing element is to the right of the splitter element
34606  * @static
34607  * @type Number
34608  */
34609 Roo.bootstrap.SplitBar.RIGHT = 2;
34610
34611 /**
34612  * Placement constant - The resizing element is positioned above the splitter element
34613  * @static
34614  * @type Number
34615  */
34616 Roo.bootstrap.SplitBar.TOP = 3;
34617
34618 /**
34619  * Placement constant - The resizing element is positioned under splitter element
34620  * @static
34621  * @type Number
34622  */
34623 Roo.bootstrap.SplitBar.BOTTOM = 4;
34624 Roo.namespace("Roo.bootstrap.layout");/*
34625  * Based on:
34626  * Ext JS Library 1.1.1
34627  * Copyright(c) 2006-2007, Ext JS, LLC.
34628  *
34629  * Originally Released Under LGPL - original licence link has changed is not relivant.
34630  *
34631  * Fork - LGPL
34632  * <script type="text/javascript">
34633  */
34634
34635 /**
34636  * @class Roo.bootstrap.layout.Manager
34637  * @extends Roo.bootstrap.Component
34638  * Base class for layout managers.
34639  */
34640 Roo.bootstrap.layout.Manager = function(config)
34641 {
34642     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34643
34644
34645
34646
34647
34648     /** false to disable window resize monitoring @type Boolean */
34649     this.monitorWindowResize = true;
34650     this.regions = {};
34651     this.addEvents({
34652         /**
34653          * @event layout
34654          * Fires when a layout is performed.
34655          * @param {Roo.LayoutManager} this
34656          */
34657         "layout" : true,
34658         /**
34659          * @event regionresized
34660          * Fires when the user resizes a region.
34661          * @param {Roo.LayoutRegion} region The resized region
34662          * @param {Number} newSize The new size (width for east/west, height for north/south)
34663          */
34664         "regionresized" : true,
34665         /**
34666          * @event regioncollapsed
34667          * Fires when a region is collapsed.
34668          * @param {Roo.LayoutRegion} region The collapsed region
34669          */
34670         "regioncollapsed" : true,
34671         /**
34672          * @event regionexpanded
34673          * Fires when a region is expanded.
34674          * @param {Roo.LayoutRegion} region The expanded region
34675          */
34676         "regionexpanded" : true
34677     });
34678     this.updating = false;
34679
34680     if (config.el) {
34681         this.el = Roo.get(config.el);
34682         this.initEvents();
34683     }
34684
34685 };
34686
34687 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34688
34689
34690     regions : null,
34691
34692     monitorWindowResize : true,
34693
34694
34695     updating : false,
34696
34697
34698     onRender : function(ct, position)
34699     {
34700         if(!this.el){
34701             this.el = Roo.get(ct);
34702             this.initEvents();
34703         }
34704         //this.fireEvent('render',this);
34705     },
34706
34707
34708     initEvents: function()
34709     {
34710
34711
34712         // ie scrollbar fix
34713         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34714             document.body.scroll = "no";
34715         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34716             this.el.position('relative');
34717         }
34718         this.id = this.el.id;
34719         this.el.addClass("roo-layout-container");
34720         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34721         if(this.el.dom != document.body ) {
34722             this.el.on('resize', this.layout,this);
34723             this.el.on('show', this.layout,this);
34724         }
34725
34726     },
34727
34728     /**
34729      * Returns true if this layout is currently being updated
34730      * @return {Boolean}
34731      */
34732     isUpdating : function(){
34733         return this.updating;
34734     },
34735
34736     /**
34737      * Suspend the LayoutManager from doing auto-layouts while
34738      * making multiple add or remove calls
34739      */
34740     beginUpdate : function(){
34741         this.updating = true;
34742     },
34743
34744     /**
34745      * Restore auto-layouts and optionally disable the manager from performing a layout
34746      * @param {Boolean} noLayout true to disable a layout update
34747      */
34748     endUpdate : function(noLayout){
34749         this.updating = false;
34750         if(!noLayout){
34751             this.layout();
34752         }
34753     },
34754
34755     layout: function(){
34756         // abstract...
34757     },
34758
34759     onRegionResized : function(region, newSize){
34760         this.fireEvent("regionresized", region, newSize);
34761         this.layout();
34762     },
34763
34764     onRegionCollapsed : function(region){
34765         this.fireEvent("regioncollapsed", region);
34766     },
34767
34768     onRegionExpanded : function(region){
34769         this.fireEvent("regionexpanded", region);
34770     },
34771
34772     /**
34773      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34774      * performs box-model adjustments.
34775      * @return {Object} The size as an object {width: (the width), height: (the height)}
34776      */
34777     getViewSize : function()
34778     {
34779         var size;
34780         if(this.el.dom != document.body){
34781             size = this.el.getSize();
34782         }else{
34783             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34784         }
34785         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34786         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34787         return size;
34788     },
34789
34790     /**
34791      * Returns the Element this layout is bound to.
34792      * @return {Roo.Element}
34793      */
34794     getEl : function(){
34795         return this.el;
34796     },
34797
34798     /**
34799      * Returns the specified region.
34800      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34801      * @return {Roo.LayoutRegion}
34802      */
34803     getRegion : function(target){
34804         return this.regions[target.toLowerCase()];
34805     },
34806
34807     onWindowResize : function(){
34808         if(this.monitorWindowResize){
34809             this.layout();
34810         }
34811     }
34812 });
34813 /*
34814  * Based on:
34815  * Ext JS Library 1.1.1
34816  * Copyright(c) 2006-2007, Ext JS, LLC.
34817  *
34818  * Originally Released Under LGPL - original licence link has changed is not relivant.
34819  *
34820  * Fork - LGPL
34821  * <script type="text/javascript">
34822  */
34823 /**
34824  * @class Roo.bootstrap.layout.Border
34825  * @extends Roo.bootstrap.layout.Manager
34826  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34827  * please see: examples/bootstrap/nested.html<br><br>
34828  
34829 <b>The container the layout is rendered into can be either the body element or any other element.
34830 If it is not the body element, the container needs to either be an absolute positioned element,
34831 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34832 the container size if it is not the body element.</b>
34833
34834 * @constructor
34835 * Create a new Border
34836 * @param {Object} config Configuration options
34837  */
34838 Roo.bootstrap.layout.Border = function(config){
34839     config = config || {};
34840     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34841     
34842     
34843     
34844     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34845         if(config[region]){
34846             config[region].region = region;
34847             this.addRegion(config[region]);
34848         }
34849     },this);
34850     
34851 };
34852
34853 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34854
34855 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34856     /**
34857      * Creates and adds a new region if it doesn't already exist.
34858      * @param {String} target The target region key (north, south, east, west or center).
34859      * @param {Object} config The regions config object
34860      * @return {BorderLayoutRegion} The new region
34861      */
34862     addRegion : function(config)
34863     {
34864         if(!this.regions[config.region]){
34865             var r = this.factory(config);
34866             this.bindRegion(r);
34867         }
34868         return this.regions[config.region];
34869     },
34870
34871     // private (kinda)
34872     bindRegion : function(r){
34873         this.regions[r.config.region] = r;
34874         
34875         r.on("visibilitychange",    this.layout, this);
34876         r.on("paneladded",          this.layout, this);
34877         r.on("panelremoved",        this.layout, this);
34878         r.on("invalidated",         this.layout, this);
34879         r.on("resized",             this.onRegionResized, this);
34880         r.on("collapsed",           this.onRegionCollapsed, this);
34881         r.on("expanded",            this.onRegionExpanded, this);
34882     },
34883
34884     /**
34885      * Performs a layout update.
34886      */
34887     layout : function()
34888     {
34889         if(this.updating) {
34890             return;
34891         }
34892         
34893         // render all the rebions if they have not been done alreayd?
34894         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34895             if(this.regions[region] && !this.regions[region].bodyEl){
34896                 this.regions[region].onRender(this.el)
34897             }
34898         },this);
34899         
34900         var size = this.getViewSize();
34901         var w = size.width;
34902         var h = size.height;
34903         var centerW = w;
34904         var centerH = h;
34905         var centerY = 0;
34906         var centerX = 0;
34907         //var x = 0, y = 0;
34908
34909         var rs = this.regions;
34910         var north = rs["north"];
34911         var south = rs["south"]; 
34912         var west = rs["west"];
34913         var east = rs["east"];
34914         var center = rs["center"];
34915         //if(this.hideOnLayout){ // not supported anymore
34916             //c.el.setStyle("display", "none");
34917         //}
34918         if(north && north.isVisible()){
34919             var b = north.getBox();
34920             var m = north.getMargins();
34921             b.width = w - (m.left+m.right);
34922             b.x = m.left;
34923             b.y = m.top;
34924             centerY = b.height + b.y + m.bottom;
34925             centerH -= centerY;
34926             north.updateBox(this.safeBox(b));
34927         }
34928         if(south && south.isVisible()){
34929             var b = south.getBox();
34930             var m = south.getMargins();
34931             b.width = w - (m.left+m.right);
34932             b.x = m.left;
34933             var totalHeight = (b.height + m.top + m.bottom);
34934             b.y = h - totalHeight + m.top;
34935             centerH -= totalHeight;
34936             south.updateBox(this.safeBox(b));
34937         }
34938         if(west && west.isVisible()){
34939             var b = west.getBox();
34940             var m = west.getMargins();
34941             b.height = centerH - (m.top+m.bottom);
34942             b.x = m.left;
34943             b.y = centerY + m.top;
34944             var totalWidth = (b.width + m.left + m.right);
34945             centerX += totalWidth;
34946             centerW -= totalWidth;
34947             west.updateBox(this.safeBox(b));
34948         }
34949         if(east && east.isVisible()){
34950             var b = east.getBox();
34951             var m = east.getMargins();
34952             b.height = centerH - (m.top+m.bottom);
34953             var totalWidth = (b.width + m.left + m.right);
34954             b.x = w - totalWidth + m.left;
34955             b.y = centerY + m.top;
34956             centerW -= totalWidth;
34957             east.updateBox(this.safeBox(b));
34958         }
34959         if(center){
34960             var m = center.getMargins();
34961             var centerBox = {
34962                 x: centerX + m.left,
34963                 y: centerY + m.top,
34964                 width: centerW - (m.left+m.right),
34965                 height: centerH - (m.top+m.bottom)
34966             };
34967             //if(this.hideOnLayout){
34968                 //center.el.setStyle("display", "block");
34969             //}
34970             center.updateBox(this.safeBox(centerBox));
34971         }
34972         this.el.repaint();
34973         this.fireEvent("layout", this);
34974     },
34975
34976     // private
34977     safeBox : function(box){
34978         box.width = Math.max(0, box.width);
34979         box.height = Math.max(0, box.height);
34980         return box;
34981     },
34982
34983     /**
34984      * Adds a ContentPanel (or subclass) to this layout.
34985      * @param {String} target The target region key (north, south, east, west or center).
34986      * @param {Roo.ContentPanel} panel The panel to add
34987      * @return {Roo.ContentPanel} The added panel
34988      */
34989     add : function(target, panel){
34990          
34991         target = target.toLowerCase();
34992         return this.regions[target].add(panel);
34993     },
34994
34995     /**
34996      * Remove a ContentPanel (or subclass) to this layout.
34997      * @param {String} target The target region key (north, south, east, west or center).
34998      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34999      * @return {Roo.ContentPanel} The removed panel
35000      */
35001     remove : function(target, panel){
35002         target = target.toLowerCase();
35003         return this.regions[target].remove(panel);
35004     },
35005
35006     /**
35007      * Searches all regions for a panel with the specified id
35008      * @param {String} panelId
35009      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35010      */
35011     findPanel : function(panelId){
35012         var rs = this.regions;
35013         for(var target in rs){
35014             if(typeof rs[target] != "function"){
35015                 var p = rs[target].getPanel(panelId);
35016                 if(p){
35017                     return p;
35018                 }
35019             }
35020         }
35021         return null;
35022     },
35023
35024     /**
35025      * Searches all regions for a panel with the specified id and activates (shows) it.
35026      * @param {String/ContentPanel} panelId The panels id or the panel itself
35027      * @return {Roo.ContentPanel} The shown panel or null
35028      */
35029     showPanel : function(panelId) {
35030       var rs = this.regions;
35031       for(var target in rs){
35032          var r = rs[target];
35033          if(typeof r != "function"){
35034             if(r.hasPanel(panelId)){
35035                return r.showPanel(panelId);
35036             }
35037          }
35038       }
35039       return null;
35040    },
35041
35042    /**
35043      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35044      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35045      */
35046    /*
35047     restoreState : function(provider){
35048         if(!provider){
35049             provider = Roo.state.Manager;
35050         }
35051         var sm = new Roo.LayoutStateManager();
35052         sm.init(this, provider);
35053     },
35054 */
35055  
35056  
35057     /**
35058      * Adds a xtype elements to the layout.
35059      * <pre><code>
35060
35061 layout.addxtype({
35062        xtype : 'ContentPanel',
35063        region: 'west',
35064        items: [ .... ]
35065    }
35066 );
35067
35068 layout.addxtype({
35069         xtype : 'NestedLayoutPanel',
35070         region: 'west',
35071         layout: {
35072            center: { },
35073            west: { }   
35074         },
35075         items : [ ... list of content panels or nested layout panels.. ]
35076    }
35077 );
35078 </code></pre>
35079      * @param {Object} cfg Xtype definition of item to add.
35080      */
35081     addxtype : function(cfg)
35082     {
35083         // basically accepts a pannel...
35084         // can accept a layout region..!?!?
35085         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35086         
35087         
35088         // theory?  children can only be panels??
35089         
35090         //if (!cfg.xtype.match(/Panel$/)) {
35091         //    return false;
35092         //}
35093         var ret = false;
35094         
35095         if (typeof(cfg.region) == 'undefined') {
35096             Roo.log("Failed to add Panel, region was not set");
35097             Roo.log(cfg);
35098             return false;
35099         }
35100         var region = cfg.region;
35101         delete cfg.region;
35102         
35103           
35104         var xitems = [];
35105         if (cfg.items) {
35106             xitems = cfg.items;
35107             delete cfg.items;
35108         }
35109         var nb = false;
35110         
35111         switch(cfg.xtype) 
35112         {
35113             case 'Content':  // ContentPanel (el, cfg)
35114             case 'Scroll':  // ContentPanel (el, cfg)
35115             case 'View': 
35116                 cfg.autoCreate = true;
35117                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35118                 //} else {
35119                 //    var el = this.el.createChild();
35120                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35121                 //}
35122                 
35123                 this.add(region, ret);
35124                 break;
35125             
35126             /*
35127             case 'TreePanel': // our new panel!
35128                 cfg.el = this.el.createChild();
35129                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35130                 this.add(region, ret);
35131                 break;
35132             */
35133             
35134             case 'Nest': 
35135                 // create a new Layout (which is  a Border Layout...
35136                 
35137                 var clayout = cfg.layout;
35138                 clayout.el  = this.el.createChild();
35139                 clayout.items   = clayout.items  || [];
35140                 
35141                 delete cfg.layout;
35142                 
35143                 // replace this exitems with the clayout ones..
35144                 xitems = clayout.items;
35145                  
35146                 // force background off if it's in center...
35147                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35148                     cfg.background = false;
35149                 }
35150                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35151                 
35152                 
35153                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35154                 //console.log('adding nested layout panel '  + cfg.toSource());
35155                 this.add(region, ret);
35156                 nb = {}; /// find first...
35157                 break;
35158             
35159             case 'Grid':
35160                 
35161                 // needs grid and region
35162                 
35163                 //var el = this.getRegion(region).el.createChild();
35164                 /*
35165                  *var el = this.el.createChild();
35166                 // create the grid first...
35167                 cfg.grid.container = el;
35168                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35169                 */
35170                 
35171                 if (region == 'center' && this.active ) {
35172                     cfg.background = false;
35173                 }
35174                 
35175                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35176                 
35177                 this.add(region, ret);
35178                 /*
35179                 if (cfg.background) {
35180                     // render grid on panel activation (if panel background)
35181                     ret.on('activate', function(gp) {
35182                         if (!gp.grid.rendered) {
35183                     //        gp.grid.render(el);
35184                         }
35185                     });
35186                 } else {
35187                   //  cfg.grid.render(el);
35188                 }
35189                 */
35190                 break;
35191            
35192            
35193             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35194                 // it was the old xcomponent building that caused this before.
35195                 // espeically if border is the top element in the tree.
35196                 ret = this;
35197                 break; 
35198                 
35199                     
35200                 
35201                 
35202                 
35203             default:
35204                 /*
35205                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35206                     
35207                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35208                     this.add(region, ret);
35209                 } else {
35210                 */
35211                     Roo.log(cfg);
35212                     throw "Can not add '" + cfg.xtype + "' to Border";
35213                     return null;
35214              
35215                                 
35216              
35217         }
35218         this.beginUpdate();
35219         // add children..
35220         var region = '';
35221         var abn = {};
35222         Roo.each(xitems, function(i)  {
35223             region = nb && i.region ? i.region : false;
35224             
35225             var add = ret.addxtype(i);
35226            
35227             if (region) {
35228                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35229                 if (!i.background) {
35230                     abn[region] = nb[region] ;
35231                 }
35232             }
35233             
35234         });
35235         this.endUpdate();
35236
35237         // make the last non-background panel active..
35238         //if (nb) { Roo.log(abn); }
35239         if (nb) {
35240             
35241             for(var r in abn) {
35242                 region = this.getRegion(r);
35243                 if (region) {
35244                     // tried using nb[r], but it does not work..
35245                      
35246                     region.showPanel(abn[r]);
35247                    
35248                 }
35249             }
35250         }
35251         return ret;
35252         
35253     },
35254     
35255     
35256 // private
35257     factory : function(cfg)
35258     {
35259         
35260         var validRegions = Roo.bootstrap.layout.Border.regions;
35261
35262         var target = cfg.region;
35263         cfg.mgr = this;
35264         
35265         var r = Roo.bootstrap.layout;
35266         Roo.log(target);
35267         switch(target){
35268             case "north":
35269                 return new r.North(cfg);
35270             case "south":
35271                 return new r.South(cfg);
35272             case "east":
35273                 return new r.East(cfg);
35274             case "west":
35275                 return new r.West(cfg);
35276             case "center":
35277                 return new r.Center(cfg);
35278         }
35279         throw 'Layout region "'+target+'" not supported.';
35280     }
35281     
35282     
35283 });
35284  /*
35285  * Based on:
35286  * Ext JS Library 1.1.1
35287  * Copyright(c) 2006-2007, Ext JS, LLC.
35288  *
35289  * Originally Released Under LGPL - original licence link has changed is not relivant.
35290  *
35291  * Fork - LGPL
35292  * <script type="text/javascript">
35293  */
35294  
35295 /**
35296  * @class Roo.bootstrap.layout.Basic
35297  * @extends Roo.util.Observable
35298  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35299  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35300  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35301  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35302  * @cfg {string}   region  the region that it inhabits..
35303  * @cfg {bool}   skipConfig skip config?
35304  * 
35305
35306  */
35307 Roo.bootstrap.layout.Basic = function(config){
35308     
35309     this.mgr = config.mgr;
35310     
35311     this.position = config.region;
35312     
35313     var skipConfig = config.skipConfig;
35314     
35315     this.events = {
35316         /**
35317          * @scope Roo.BasicLayoutRegion
35318          */
35319         
35320         /**
35321          * @event beforeremove
35322          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35323          * @param {Roo.LayoutRegion} this
35324          * @param {Roo.ContentPanel} panel The panel
35325          * @param {Object} e The cancel event object
35326          */
35327         "beforeremove" : true,
35328         /**
35329          * @event invalidated
35330          * Fires when the layout for this region is changed.
35331          * @param {Roo.LayoutRegion} this
35332          */
35333         "invalidated" : true,
35334         /**
35335          * @event visibilitychange
35336          * Fires when this region is shown or hidden 
35337          * @param {Roo.LayoutRegion} this
35338          * @param {Boolean} visibility true or false
35339          */
35340         "visibilitychange" : true,
35341         /**
35342          * @event paneladded
35343          * Fires when a panel is added. 
35344          * @param {Roo.LayoutRegion} this
35345          * @param {Roo.ContentPanel} panel The panel
35346          */
35347         "paneladded" : true,
35348         /**
35349          * @event panelremoved
35350          * Fires when a panel is removed. 
35351          * @param {Roo.LayoutRegion} this
35352          * @param {Roo.ContentPanel} panel The panel
35353          */
35354         "panelremoved" : true,
35355         /**
35356          * @event beforecollapse
35357          * Fires when this region before collapse.
35358          * @param {Roo.LayoutRegion} this
35359          */
35360         "beforecollapse" : true,
35361         /**
35362          * @event collapsed
35363          * Fires when this region is collapsed.
35364          * @param {Roo.LayoutRegion} this
35365          */
35366         "collapsed" : true,
35367         /**
35368          * @event expanded
35369          * Fires when this region is expanded.
35370          * @param {Roo.LayoutRegion} this
35371          */
35372         "expanded" : true,
35373         /**
35374          * @event slideshow
35375          * Fires when this region is slid into view.
35376          * @param {Roo.LayoutRegion} this
35377          */
35378         "slideshow" : true,
35379         /**
35380          * @event slidehide
35381          * Fires when this region slides out of view. 
35382          * @param {Roo.LayoutRegion} this
35383          */
35384         "slidehide" : true,
35385         /**
35386          * @event panelactivated
35387          * Fires when a panel is activated. 
35388          * @param {Roo.LayoutRegion} this
35389          * @param {Roo.ContentPanel} panel The activated panel
35390          */
35391         "panelactivated" : true,
35392         /**
35393          * @event resized
35394          * Fires when the user resizes this region. 
35395          * @param {Roo.LayoutRegion} this
35396          * @param {Number} newSize The new size (width for east/west, height for north/south)
35397          */
35398         "resized" : true
35399     };
35400     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35401     this.panels = new Roo.util.MixedCollection();
35402     this.panels.getKey = this.getPanelId.createDelegate(this);
35403     this.box = null;
35404     this.activePanel = null;
35405     // ensure listeners are added...
35406     
35407     if (config.listeners || config.events) {
35408         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35409             listeners : config.listeners || {},
35410             events : config.events || {}
35411         });
35412     }
35413     
35414     if(skipConfig !== true){
35415         this.applyConfig(config);
35416     }
35417 };
35418
35419 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35420 {
35421     getPanelId : function(p){
35422         return p.getId();
35423     },
35424     
35425     applyConfig : function(config){
35426         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35427         this.config = config;
35428         
35429     },
35430     
35431     /**
35432      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35433      * the width, for horizontal (north, south) the height.
35434      * @param {Number} newSize The new width or height
35435      */
35436     resizeTo : function(newSize){
35437         var el = this.el ? this.el :
35438                  (this.activePanel ? this.activePanel.getEl() : null);
35439         if(el){
35440             switch(this.position){
35441                 case "east":
35442                 case "west":
35443                     el.setWidth(newSize);
35444                     this.fireEvent("resized", this, newSize);
35445                 break;
35446                 case "north":
35447                 case "south":
35448                     el.setHeight(newSize);
35449                     this.fireEvent("resized", this, newSize);
35450                 break;                
35451             }
35452         }
35453     },
35454     
35455     getBox : function(){
35456         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35457     },
35458     
35459     getMargins : function(){
35460         return this.margins;
35461     },
35462     
35463     updateBox : function(box){
35464         this.box = box;
35465         var el = this.activePanel.getEl();
35466         el.dom.style.left = box.x + "px";
35467         el.dom.style.top = box.y + "px";
35468         this.activePanel.setSize(box.width, box.height);
35469     },
35470     
35471     /**
35472      * Returns the container element for this region.
35473      * @return {Roo.Element}
35474      */
35475     getEl : function(){
35476         return this.activePanel;
35477     },
35478     
35479     /**
35480      * Returns true if this region is currently visible.
35481      * @return {Boolean}
35482      */
35483     isVisible : function(){
35484         return this.activePanel ? true : false;
35485     },
35486     
35487     setActivePanel : function(panel){
35488         panel = this.getPanel(panel);
35489         if(this.activePanel && this.activePanel != panel){
35490             this.activePanel.setActiveState(false);
35491             this.activePanel.getEl().setLeftTop(-10000,-10000);
35492         }
35493         this.activePanel = panel;
35494         panel.setActiveState(true);
35495         if(this.box){
35496             panel.setSize(this.box.width, this.box.height);
35497         }
35498         this.fireEvent("panelactivated", this, panel);
35499         this.fireEvent("invalidated");
35500     },
35501     
35502     /**
35503      * Show the specified panel.
35504      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35505      * @return {Roo.ContentPanel} The shown panel or null
35506      */
35507     showPanel : function(panel){
35508         panel = this.getPanel(panel);
35509         if(panel){
35510             this.setActivePanel(panel);
35511         }
35512         return panel;
35513     },
35514     
35515     /**
35516      * Get the active panel for this region.
35517      * @return {Roo.ContentPanel} The active panel or null
35518      */
35519     getActivePanel : function(){
35520         return this.activePanel;
35521     },
35522     
35523     /**
35524      * Add the passed ContentPanel(s)
35525      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35526      * @return {Roo.ContentPanel} The panel added (if only one was added)
35527      */
35528     add : function(panel){
35529         if(arguments.length > 1){
35530             for(var i = 0, len = arguments.length; i < len; i++) {
35531                 this.add(arguments[i]);
35532             }
35533             return null;
35534         }
35535         if(this.hasPanel(panel)){
35536             this.showPanel(panel);
35537             return panel;
35538         }
35539         var el = panel.getEl();
35540         if(el.dom.parentNode != this.mgr.el.dom){
35541             this.mgr.el.dom.appendChild(el.dom);
35542         }
35543         if(panel.setRegion){
35544             panel.setRegion(this);
35545         }
35546         this.panels.add(panel);
35547         el.setStyle("position", "absolute");
35548         if(!panel.background){
35549             this.setActivePanel(panel);
35550             if(this.config.initialSize && this.panels.getCount()==1){
35551                 this.resizeTo(this.config.initialSize);
35552             }
35553         }
35554         this.fireEvent("paneladded", this, panel);
35555         return panel;
35556     },
35557     
35558     /**
35559      * Returns true if the panel is in this region.
35560      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35561      * @return {Boolean}
35562      */
35563     hasPanel : function(panel){
35564         if(typeof panel == "object"){ // must be panel obj
35565             panel = panel.getId();
35566         }
35567         return this.getPanel(panel) ? true : false;
35568     },
35569     
35570     /**
35571      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35572      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35573      * @param {Boolean} preservePanel Overrides the config preservePanel option
35574      * @return {Roo.ContentPanel} The panel that was removed
35575      */
35576     remove : function(panel, preservePanel){
35577         panel = this.getPanel(panel);
35578         if(!panel){
35579             return null;
35580         }
35581         var e = {};
35582         this.fireEvent("beforeremove", this, panel, e);
35583         if(e.cancel === true){
35584             return null;
35585         }
35586         var panelId = panel.getId();
35587         this.panels.removeKey(panelId);
35588         return panel;
35589     },
35590     
35591     /**
35592      * Returns the panel specified or null if it's not in this region.
35593      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35594      * @return {Roo.ContentPanel}
35595      */
35596     getPanel : function(id){
35597         if(typeof id == "object"){ // must be panel obj
35598             return id;
35599         }
35600         return this.panels.get(id);
35601     },
35602     
35603     /**
35604      * Returns this regions position (north/south/east/west/center).
35605      * @return {String} 
35606      */
35607     getPosition: function(){
35608         return this.position;    
35609     }
35610 });/*
35611  * Based on:
35612  * Ext JS Library 1.1.1
35613  * Copyright(c) 2006-2007, Ext JS, LLC.
35614  *
35615  * Originally Released Under LGPL - original licence link has changed is not relivant.
35616  *
35617  * Fork - LGPL
35618  * <script type="text/javascript">
35619  */
35620  
35621 /**
35622  * @class Roo.bootstrap.layout.Region
35623  * @extends Roo.bootstrap.layout.Basic
35624  * This class represents a region in a layout manager.
35625  
35626  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35627  * @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})
35628  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35629  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35630  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35631  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35632  * @cfg {String}    title           The title for the region (overrides panel titles)
35633  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35634  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35635  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35636  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35637  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35638  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35639  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35640  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35641  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35642  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35643
35644  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35645  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35646  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35647  * @cfg {Number}    width           For East/West panels
35648  * @cfg {Number}    height          For North/South panels
35649  * @cfg {Boolean}   split           To show the splitter
35650  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35651  * 
35652  * @cfg {string}   cls             Extra CSS classes to add to region
35653  * 
35654  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35655  * @cfg {string}   region  the region that it inhabits..
35656  *
35657
35658  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35659  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35660
35661  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35662  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35663  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35664  */
35665 Roo.bootstrap.layout.Region = function(config)
35666 {
35667     this.applyConfig(config);
35668
35669     var mgr = config.mgr;
35670     var pos = config.region;
35671     config.skipConfig = true;
35672     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35673     
35674     if (mgr.el) {
35675         this.onRender(mgr.el);   
35676     }
35677      
35678     this.visible = true;
35679     this.collapsed = false;
35680     this.unrendered_panels = [];
35681 };
35682
35683 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35684
35685     position: '', // set by wrapper (eg. north/south etc..)
35686     unrendered_panels : null,  // unrendered panels.
35687     createBody : function(){
35688         /** This region's body element 
35689         * @type Roo.Element */
35690         this.bodyEl = this.el.createChild({
35691                 tag: "div",
35692                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35693         });
35694     },
35695
35696     onRender: function(ctr, pos)
35697     {
35698         var dh = Roo.DomHelper;
35699         /** This region's container element 
35700         * @type Roo.Element */
35701         this.el = dh.append(ctr.dom, {
35702                 tag: "div",
35703                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35704             }, true);
35705         /** This region's title element 
35706         * @type Roo.Element */
35707     
35708         this.titleEl = dh.append(this.el.dom,
35709             {
35710                     tag: "div",
35711                     unselectable: "on",
35712                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35713                     children:[
35714                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35715                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35716                     ]}, true);
35717         
35718         this.titleEl.enableDisplayMode();
35719         /** This region's title text element 
35720         * @type HTMLElement */
35721         this.titleTextEl = this.titleEl.dom.firstChild;
35722         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35723         /*
35724         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35725         this.closeBtn.enableDisplayMode();
35726         this.closeBtn.on("click", this.closeClicked, this);
35727         this.closeBtn.hide();
35728     */
35729         this.createBody(this.config);
35730         if(this.config.hideWhenEmpty){
35731             this.hide();
35732             this.on("paneladded", this.validateVisibility, this);
35733             this.on("panelremoved", this.validateVisibility, this);
35734         }
35735         if(this.autoScroll){
35736             this.bodyEl.setStyle("overflow", "auto");
35737         }else{
35738             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35739         }
35740         //if(c.titlebar !== false){
35741             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35742                 this.titleEl.hide();
35743             }else{
35744                 this.titleEl.show();
35745                 if(this.config.title){
35746                     this.titleTextEl.innerHTML = this.config.title;
35747                 }
35748             }
35749         //}
35750         if(this.config.collapsed){
35751             this.collapse(true);
35752         }
35753         if(this.config.hidden){
35754             this.hide();
35755         }
35756         
35757         if (this.unrendered_panels && this.unrendered_panels.length) {
35758             for (var i =0;i< this.unrendered_panels.length; i++) {
35759                 this.add(this.unrendered_panels[i]);
35760             }
35761             this.unrendered_panels = null;
35762             
35763         }
35764         
35765     },
35766     
35767     applyConfig : function(c)
35768     {
35769         /*
35770          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35771             var dh = Roo.DomHelper;
35772             if(c.titlebar !== false){
35773                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35774                 this.collapseBtn.on("click", this.collapse, this);
35775                 this.collapseBtn.enableDisplayMode();
35776                 /*
35777                 if(c.showPin === true || this.showPin){
35778                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35779                     this.stickBtn.enableDisplayMode();
35780                     this.stickBtn.on("click", this.expand, this);
35781                     this.stickBtn.hide();
35782                 }
35783                 
35784             }
35785             */
35786             /** This region's collapsed element
35787             * @type Roo.Element */
35788             /*
35789              *
35790             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35791                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35792             ]}, true);
35793             
35794             if(c.floatable !== false){
35795                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35796                this.collapsedEl.on("click", this.collapseClick, this);
35797             }
35798
35799             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35800                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35801                    id: "message", unselectable: "on", style:{"float":"left"}});
35802                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35803              }
35804             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35805             this.expandBtn.on("click", this.expand, this);
35806             
35807         }
35808         
35809         if(this.collapseBtn){
35810             this.collapseBtn.setVisible(c.collapsible == true);
35811         }
35812         
35813         this.cmargins = c.cmargins || this.cmargins ||
35814                          (this.position == "west" || this.position == "east" ?
35815                              {top: 0, left: 2, right:2, bottom: 0} :
35816                              {top: 2, left: 0, right:0, bottom: 2});
35817         */
35818         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35819         
35820         
35821         this.bottomTabs = c.tabPosition != "top";
35822         
35823         this.autoScroll = c.autoScroll || false;
35824         
35825         
35826        
35827         
35828         this.duration = c.duration || .30;
35829         this.slideDuration = c.slideDuration || .45;
35830         this.config = c;
35831        
35832     },
35833     /**
35834      * Returns true if this region is currently visible.
35835      * @return {Boolean}
35836      */
35837     isVisible : function(){
35838         return this.visible;
35839     },
35840
35841     /**
35842      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35843      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35844      */
35845     //setCollapsedTitle : function(title){
35846     //    title = title || "&#160;";
35847      //   if(this.collapsedTitleTextEl){
35848       //      this.collapsedTitleTextEl.innerHTML = title;
35849        // }
35850     //},
35851
35852     getBox : function(){
35853         var b;
35854       //  if(!this.collapsed){
35855             b = this.el.getBox(false, true);
35856        // }else{
35857           //  b = this.collapsedEl.getBox(false, true);
35858         //}
35859         return b;
35860     },
35861
35862     getMargins : function(){
35863         return this.margins;
35864         //return this.collapsed ? this.cmargins : this.margins;
35865     },
35866 /*
35867     highlight : function(){
35868         this.el.addClass("x-layout-panel-dragover");
35869     },
35870
35871     unhighlight : function(){
35872         this.el.removeClass("x-layout-panel-dragover");
35873     },
35874 */
35875     updateBox : function(box)
35876     {
35877         if (!this.bodyEl) {
35878             return; // not rendered yet..
35879         }
35880         
35881         this.box = box;
35882         if(!this.collapsed){
35883             this.el.dom.style.left = box.x + "px";
35884             this.el.dom.style.top = box.y + "px";
35885             this.updateBody(box.width, box.height);
35886         }else{
35887             this.collapsedEl.dom.style.left = box.x + "px";
35888             this.collapsedEl.dom.style.top = box.y + "px";
35889             this.collapsedEl.setSize(box.width, box.height);
35890         }
35891         if(this.tabs){
35892             this.tabs.autoSizeTabs();
35893         }
35894     },
35895
35896     updateBody : function(w, h)
35897     {
35898         if(w !== null){
35899             this.el.setWidth(w);
35900             w -= this.el.getBorderWidth("rl");
35901             if(this.config.adjustments){
35902                 w += this.config.adjustments[0];
35903             }
35904         }
35905         if(h !== null && h > 0){
35906             this.el.setHeight(h);
35907             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35908             h -= this.el.getBorderWidth("tb");
35909             if(this.config.adjustments){
35910                 h += this.config.adjustments[1];
35911             }
35912             this.bodyEl.setHeight(h);
35913             if(this.tabs){
35914                 h = this.tabs.syncHeight(h);
35915             }
35916         }
35917         if(this.panelSize){
35918             w = w !== null ? w : this.panelSize.width;
35919             h = h !== null ? h : this.panelSize.height;
35920         }
35921         if(this.activePanel){
35922             var el = this.activePanel.getEl();
35923             w = w !== null ? w : el.getWidth();
35924             h = h !== null ? h : el.getHeight();
35925             this.panelSize = {width: w, height: h};
35926             this.activePanel.setSize(w, h);
35927         }
35928         if(Roo.isIE && this.tabs){
35929             this.tabs.el.repaint();
35930         }
35931     },
35932
35933     /**
35934      * Returns the container element for this region.
35935      * @return {Roo.Element}
35936      */
35937     getEl : function(){
35938         return this.el;
35939     },
35940
35941     /**
35942      * Hides this region.
35943      */
35944     hide : function(){
35945         //if(!this.collapsed){
35946             this.el.dom.style.left = "-2000px";
35947             this.el.hide();
35948         //}else{
35949          //   this.collapsedEl.dom.style.left = "-2000px";
35950          //   this.collapsedEl.hide();
35951        // }
35952         this.visible = false;
35953         this.fireEvent("visibilitychange", this, false);
35954     },
35955
35956     /**
35957      * Shows this region if it was previously hidden.
35958      */
35959     show : function(){
35960         //if(!this.collapsed){
35961             this.el.show();
35962         //}else{
35963         //    this.collapsedEl.show();
35964        // }
35965         this.visible = true;
35966         this.fireEvent("visibilitychange", this, true);
35967     },
35968 /*
35969     closeClicked : function(){
35970         if(this.activePanel){
35971             this.remove(this.activePanel);
35972         }
35973     },
35974
35975     collapseClick : function(e){
35976         if(this.isSlid){
35977            e.stopPropagation();
35978            this.slideIn();
35979         }else{
35980            e.stopPropagation();
35981            this.slideOut();
35982         }
35983     },
35984 */
35985     /**
35986      * Collapses this region.
35987      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35988      */
35989     /*
35990     collapse : function(skipAnim, skipCheck = false){
35991         if(this.collapsed) {
35992             return;
35993         }
35994         
35995         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35996             
35997             this.collapsed = true;
35998             if(this.split){
35999                 this.split.el.hide();
36000             }
36001             if(this.config.animate && skipAnim !== true){
36002                 this.fireEvent("invalidated", this);
36003                 this.animateCollapse();
36004             }else{
36005                 this.el.setLocation(-20000,-20000);
36006                 this.el.hide();
36007                 this.collapsedEl.show();
36008                 this.fireEvent("collapsed", this);
36009                 this.fireEvent("invalidated", this);
36010             }
36011         }
36012         
36013     },
36014 */
36015     animateCollapse : function(){
36016         // overridden
36017     },
36018
36019     /**
36020      * Expands this region if it was previously collapsed.
36021      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36022      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36023      */
36024     /*
36025     expand : function(e, skipAnim){
36026         if(e) {
36027             e.stopPropagation();
36028         }
36029         if(!this.collapsed || this.el.hasActiveFx()) {
36030             return;
36031         }
36032         if(this.isSlid){
36033             this.afterSlideIn();
36034             skipAnim = true;
36035         }
36036         this.collapsed = false;
36037         if(this.config.animate && skipAnim !== true){
36038             this.animateExpand();
36039         }else{
36040             this.el.show();
36041             if(this.split){
36042                 this.split.el.show();
36043             }
36044             this.collapsedEl.setLocation(-2000,-2000);
36045             this.collapsedEl.hide();
36046             this.fireEvent("invalidated", this);
36047             this.fireEvent("expanded", this);
36048         }
36049     },
36050 */
36051     animateExpand : function(){
36052         // overridden
36053     },
36054
36055     initTabs : function()
36056     {
36057         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36058         
36059         var ts = new Roo.bootstrap.panel.Tabs({
36060                 el: this.bodyEl.dom,
36061                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36062                 disableTooltips: this.config.disableTabTips,
36063                 toolbar : this.config.toolbar
36064             });
36065         
36066         if(this.config.hideTabs){
36067             ts.stripWrap.setDisplayed(false);
36068         }
36069         this.tabs = ts;
36070         ts.resizeTabs = this.config.resizeTabs === true;
36071         ts.minTabWidth = this.config.minTabWidth || 40;
36072         ts.maxTabWidth = this.config.maxTabWidth || 250;
36073         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36074         ts.monitorResize = false;
36075         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36076         ts.bodyEl.addClass('roo-layout-tabs-body');
36077         this.panels.each(this.initPanelAsTab, this);
36078     },
36079
36080     initPanelAsTab : function(panel){
36081         var ti = this.tabs.addTab(
36082             panel.getEl().id,
36083             panel.getTitle(),
36084             null,
36085             this.config.closeOnTab && panel.isClosable(),
36086             panel.tpl
36087         );
36088         if(panel.tabTip !== undefined){
36089             ti.setTooltip(panel.tabTip);
36090         }
36091         ti.on("activate", function(){
36092               this.setActivePanel(panel);
36093         }, this);
36094         
36095         if(this.config.closeOnTab){
36096             ti.on("beforeclose", function(t, e){
36097                 e.cancel = true;
36098                 this.remove(panel);
36099             }, this);
36100         }
36101         
36102         panel.tabItem = ti;
36103         
36104         return ti;
36105     },
36106
36107     updatePanelTitle : function(panel, title)
36108     {
36109         if(this.activePanel == panel){
36110             this.updateTitle(title);
36111         }
36112         if(this.tabs){
36113             var ti = this.tabs.getTab(panel.getEl().id);
36114             ti.setText(title);
36115             if(panel.tabTip !== undefined){
36116                 ti.setTooltip(panel.tabTip);
36117             }
36118         }
36119     },
36120
36121     updateTitle : function(title){
36122         if(this.titleTextEl && !this.config.title){
36123             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36124         }
36125     },
36126
36127     setActivePanel : function(panel)
36128     {
36129         panel = this.getPanel(panel);
36130         if(this.activePanel && this.activePanel != panel){
36131             if(this.activePanel.setActiveState(false) === false){
36132                 return;
36133             }
36134         }
36135         this.activePanel = panel;
36136         panel.setActiveState(true);
36137         if(this.panelSize){
36138             panel.setSize(this.panelSize.width, this.panelSize.height);
36139         }
36140         if(this.closeBtn){
36141             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36142         }
36143         this.updateTitle(panel.getTitle());
36144         if(this.tabs){
36145             this.fireEvent("invalidated", this);
36146         }
36147         this.fireEvent("panelactivated", this, panel);
36148     },
36149
36150     /**
36151      * Shows the specified panel.
36152      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36153      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36154      */
36155     showPanel : function(panel)
36156     {
36157         panel = this.getPanel(panel);
36158         if(panel){
36159             if(this.tabs){
36160                 var tab = this.tabs.getTab(panel.getEl().id);
36161                 if(tab.isHidden()){
36162                     this.tabs.unhideTab(tab.id);
36163                 }
36164                 tab.activate();
36165             }else{
36166                 this.setActivePanel(panel);
36167             }
36168         }
36169         return panel;
36170     },
36171
36172     /**
36173      * Get the active panel for this region.
36174      * @return {Roo.ContentPanel} The active panel or null
36175      */
36176     getActivePanel : function(){
36177         return this.activePanel;
36178     },
36179
36180     validateVisibility : function(){
36181         if(this.panels.getCount() < 1){
36182             this.updateTitle("&#160;");
36183             this.closeBtn.hide();
36184             this.hide();
36185         }else{
36186             if(!this.isVisible()){
36187                 this.show();
36188             }
36189         }
36190     },
36191
36192     /**
36193      * Adds the passed ContentPanel(s) to this region.
36194      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36195      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36196      */
36197     add : function(panel)
36198     {
36199         if(arguments.length > 1){
36200             for(var i = 0, len = arguments.length; i < len; i++) {
36201                 this.add(arguments[i]);
36202             }
36203             return null;
36204         }
36205         
36206         // if we have not been rendered yet, then we can not really do much of this..
36207         if (!this.bodyEl) {
36208             this.unrendered_panels.push(panel);
36209             return panel;
36210         }
36211         
36212         
36213         
36214         
36215         if(this.hasPanel(panel)){
36216             this.showPanel(panel);
36217             return panel;
36218         }
36219         panel.setRegion(this);
36220         this.panels.add(panel);
36221        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36222             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36223             // and hide them... ???
36224             this.bodyEl.dom.appendChild(panel.getEl().dom);
36225             if(panel.background !== true){
36226                 this.setActivePanel(panel);
36227             }
36228             this.fireEvent("paneladded", this, panel);
36229             return panel;
36230         }
36231         */
36232         if(!this.tabs){
36233             this.initTabs();
36234         }else{
36235             this.initPanelAsTab(panel);
36236         }
36237         
36238         
36239         if(panel.background !== true){
36240             this.tabs.activate(panel.getEl().id);
36241         }
36242         this.fireEvent("paneladded", this, panel);
36243         return panel;
36244     },
36245
36246     /**
36247      * Hides the tab for the specified panel.
36248      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36249      */
36250     hidePanel : function(panel){
36251         if(this.tabs && (panel = this.getPanel(panel))){
36252             this.tabs.hideTab(panel.getEl().id);
36253         }
36254     },
36255
36256     /**
36257      * Unhides the tab for a previously hidden panel.
36258      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36259      */
36260     unhidePanel : function(panel){
36261         if(this.tabs && (panel = this.getPanel(panel))){
36262             this.tabs.unhideTab(panel.getEl().id);
36263         }
36264     },
36265
36266     clearPanels : function(){
36267         while(this.panels.getCount() > 0){
36268              this.remove(this.panels.first());
36269         }
36270     },
36271
36272     /**
36273      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36274      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36275      * @param {Boolean} preservePanel Overrides the config preservePanel option
36276      * @return {Roo.ContentPanel} The panel that was removed
36277      */
36278     remove : function(panel, preservePanel)
36279     {
36280         panel = this.getPanel(panel);
36281         if(!panel){
36282             return null;
36283         }
36284         var e = {};
36285         this.fireEvent("beforeremove", this, panel, e);
36286         if(e.cancel === true){
36287             return null;
36288         }
36289         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36290         var panelId = panel.getId();
36291         this.panels.removeKey(panelId);
36292         if(preservePanel){
36293             document.body.appendChild(panel.getEl().dom);
36294         }
36295         if(this.tabs){
36296             this.tabs.removeTab(panel.getEl().id);
36297         }else if (!preservePanel){
36298             this.bodyEl.dom.removeChild(panel.getEl().dom);
36299         }
36300         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36301             var p = this.panels.first();
36302             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36303             tempEl.appendChild(p.getEl().dom);
36304             this.bodyEl.update("");
36305             this.bodyEl.dom.appendChild(p.getEl().dom);
36306             tempEl = null;
36307             this.updateTitle(p.getTitle());
36308             this.tabs = null;
36309             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36310             this.setActivePanel(p);
36311         }
36312         panel.setRegion(null);
36313         if(this.activePanel == panel){
36314             this.activePanel = null;
36315         }
36316         if(this.config.autoDestroy !== false && preservePanel !== true){
36317             try{panel.destroy();}catch(e){}
36318         }
36319         this.fireEvent("panelremoved", this, panel);
36320         return panel;
36321     },
36322
36323     /**
36324      * Returns the TabPanel component used by this region
36325      * @return {Roo.TabPanel}
36326      */
36327     getTabs : function(){
36328         return this.tabs;
36329     },
36330
36331     createTool : function(parentEl, className){
36332         var btn = Roo.DomHelper.append(parentEl, {
36333             tag: "div",
36334             cls: "x-layout-tools-button",
36335             children: [ {
36336                 tag: "div",
36337                 cls: "roo-layout-tools-button-inner " + className,
36338                 html: "&#160;"
36339             }]
36340         }, true);
36341         btn.addClassOnOver("roo-layout-tools-button-over");
36342         return btn;
36343     }
36344 });/*
36345  * Based on:
36346  * Ext JS Library 1.1.1
36347  * Copyright(c) 2006-2007, Ext JS, LLC.
36348  *
36349  * Originally Released Under LGPL - original licence link has changed is not relivant.
36350  *
36351  * Fork - LGPL
36352  * <script type="text/javascript">
36353  */
36354  
36355
36356
36357 /**
36358  * @class Roo.SplitLayoutRegion
36359  * @extends Roo.LayoutRegion
36360  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36361  */
36362 Roo.bootstrap.layout.Split = function(config){
36363     this.cursor = config.cursor;
36364     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36365 };
36366
36367 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36368 {
36369     splitTip : "Drag to resize.",
36370     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36371     useSplitTips : false,
36372
36373     applyConfig : function(config){
36374         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36375     },
36376     
36377     onRender : function(ctr,pos) {
36378         
36379         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36380         if(!this.config.split){
36381             return;
36382         }
36383         if(!this.split){
36384             
36385             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36386                             tag: "div",
36387                             id: this.el.id + "-split",
36388                             cls: "roo-layout-split roo-layout-split-"+this.position,
36389                             html: "&#160;"
36390             });
36391             /** The SplitBar for this region 
36392             * @type Roo.SplitBar */
36393             // does not exist yet...
36394             Roo.log([this.position, this.orientation]);
36395             
36396             this.split = new Roo.bootstrap.SplitBar({
36397                 dragElement : splitEl,
36398                 resizingElement: this.el,
36399                 orientation : this.orientation
36400             });
36401             
36402             this.split.on("moved", this.onSplitMove, this);
36403             this.split.useShim = this.config.useShim === true;
36404             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36405             if(this.useSplitTips){
36406                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36407             }
36408             //if(config.collapsible){
36409             //    this.split.el.on("dblclick", this.collapse,  this);
36410             //}
36411         }
36412         if(typeof this.config.minSize != "undefined"){
36413             this.split.minSize = this.config.minSize;
36414         }
36415         if(typeof this.config.maxSize != "undefined"){
36416             this.split.maxSize = this.config.maxSize;
36417         }
36418         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36419             this.hideSplitter();
36420         }
36421         
36422     },
36423
36424     getHMaxSize : function(){
36425          var cmax = this.config.maxSize || 10000;
36426          var center = this.mgr.getRegion("center");
36427          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36428     },
36429
36430     getVMaxSize : function(){
36431          var cmax = this.config.maxSize || 10000;
36432          var center = this.mgr.getRegion("center");
36433          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36434     },
36435
36436     onSplitMove : function(split, newSize){
36437         this.fireEvent("resized", this, newSize);
36438     },
36439     
36440     /** 
36441      * Returns the {@link Roo.SplitBar} for this region.
36442      * @return {Roo.SplitBar}
36443      */
36444     getSplitBar : function(){
36445         return this.split;
36446     },
36447     
36448     hide : function(){
36449         this.hideSplitter();
36450         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36451     },
36452
36453     hideSplitter : function(){
36454         if(this.split){
36455             this.split.el.setLocation(-2000,-2000);
36456             this.split.el.hide();
36457         }
36458     },
36459
36460     show : function(){
36461         if(this.split){
36462             this.split.el.show();
36463         }
36464         Roo.bootstrap.layout.Split.superclass.show.call(this);
36465     },
36466     
36467     beforeSlide: function(){
36468         if(Roo.isGecko){// firefox overflow auto bug workaround
36469             this.bodyEl.clip();
36470             if(this.tabs) {
36471                 this.tabs.bodyEl.clip();
36472             }
36473             if(this.activePanel){
36474                 this.activePanel.getEl().clip();
36475                 
36476                 if(this.activePanel.beforeSlide){
36477                     this.activePanel.beforeSlide();
36478                 }
36479             }
36480         }
36481     },
36482     
36483     afterSlide : function(){
36484         if(Roo.isGecko){// firefox overflow auto bug workaround
36485             this.bodyEl.unclip();
36486             if(this.tabs) {
36487                 this.tabs.bodyEl.unclip();
36488             }
36489             if(this.activePanel){
36490                 this.activePanel.getEl().unclip();
36491                 if(this.activePanel.afterSlide){
36492                     this.activePanel.afterSlide();
36493                 }
36494             }
36495         }
36496     },
36497
36498     initAutoHide : function(){
36499         if(this.autoHide !== false){
36500             if(!this.autoHideHd){
36501                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36502                 this.autoHideHd = {
36503                     "mouseout": function(e){
36504                         if(!e.within(this.el, true)){
36505                             st.delay(500);
36506                         }
36507                     },
36508                     "mouseover" : function(e){
36509                         st.cancel();
36510                     },
36511                     scope : this
36512                 };
36513             }
36514             this.el.on(this.autoHideHd);
36515         }
36516     },
36517
36518     clearAutoHide : function(){
36519         if(this.autoHide !== false){
36520             this.el.un("mouseout", this.autoHideHd.mouseout);
36521             this.el.un("mouseover", this.autoHideHd.mouseover);
36522         }
36523     },
36524
36525     clearMonitor : function(){
36526         Roo.get(document).un("click", this.slideInIf, this);
36527     },
36528
36529     // these names are backwards but not changed for compat
36530     slideOut : function(){
36531         if(this.isSlid || this.el.hasActiveFx()){
36532             return;
36533         }
36534         this.isSlid = true;
36535         if(this.collapseBtn){
36536             this.collapseBtn.hide();
36537         }
36538         this.closeBtnState = this.closeBtn.getStyle('display');
36539         this.closeBtn.hide();
36540         if(this.stickBtn){
36541             this.stickBtn.show();
36542         }
36543         this.el.show();
36544         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36545         this.beforeSlide();
36546         this.el.setStyle("z-index", 10001);
36547         this.el.slideIn(this.getSlideAnchor(), {
36548             callback: function(){
36549                 this.afterSlide();
36550                 this.initAutoHide();
36551                 Roo.get(document).on("click", this.slideInIf, this);
36552                 this.fireEvent("slideshow", this);
36553             },
36554             scope: this,
36555             block: true
36556         });
36557     },
36558
36559     afterSlideIn : function(){
36560         this.clearAutoHide();
36561         this.isSlid = false;
36562         this.clearMonitor();
36563         this.el.setStyle("z-index", "");
36564         if(this.collapseBtn){
36565             this.collapseBtn.show();
36566         }
36567         this.closeBtn.setStyle('display', this.closeBtnState);
36568         if(this.stickBtn){
36569             this.stickBtn.hide();
36570         }
36571         this.fireEvent("slidehide", this);
36572     },
36573
36574     slideIn : function(cb){
36575         if(!this.isSlid || this.el.hasActiveFx()){
36576             Roo.callback(cb);
36577             return;
36578         }
36579         this.isSlid = false;
36580         this.beforeSlide();
36581         this.el.slideOut(this.getSlideAnchor(), {
36582             callback: function(){
36583                 this.el.setLeftTop(-10000, -10000);
36584                 this.afterSlide();
36585                 this.afterSlideIn();
36586                 Roo.callback(cb);
36587             },
36588             scope: this,
36589             block: true
36590         });
36591     },
36592     
36593     slideInIf : function(e){
36594         if(!e.within(this.el)){
36595             this.slideIn();
36596         }
36597     },
36598
36599     animateCollapse : function(){
36600         this.beforeSlide();
36601         this.el.setStyle("z-index", 20000);
36602         var anchor = this.getSlideAnchor();
36603         this.el.slideOut(anchor, {
36604             callback : function(){
36605                 this.el.setStyle("z-index", "");
36606                 this.collapsedEl.slideIn(anchor, {duration:.3});
36607                 this.afterSlide();
36608                 this.el.setLocation(-10000,-10000);
36609                 this.el.hide();
36610                 this.fireEvent("collapsed", this);
36611             },
36612             scope: this,
36613             block: true
36614         });
36615     },
36616
36617     animateExpand : function(){
36618         this.beforeSlide();
36619         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36620         this.el.setStyle("z-index", 20000);
36621         this.collapsedEl.hide({
36622             duration:.1
36623         });
36624         this.el.slideIn(this.getSlideAnchor(), {
36625             callback : function(){
36626                 this.el.setStyle("z-index", "");
36627                 this.afterSlide();
36628                 if(this.split){
36629                     this.split.el.show();
36630                 }
36631                 this.fireEvent("invalidated", this);
36632                 this.fireEvent("expanded", this);
36633             },
36634             scope: this,
36635             block: true
36636         });
36637     },
36638
36639     anchors : {
36640         "west" : "left",
36641         "east" : "right",
36642         "north" : "top",
36643         "south" : "bottom"
36644     },
36645
36646     sanchors : {
36647         "west" : "l",
36648         "east" : "r",
36649         "north" : "t",
36650         "south" : "b"
36651     },
36652
36653     canchors : {
36654         "west" : "tl-tr",
36655         "east" : "tr-tl",
36656         "north" : "tl-bl",
36657         "south" : "bl-tl"
36658     },
36659
36660     getAnchor : function(){
36661         return this.anchors[this.position];
36662     },
36663
36664     getCollapseAnchor : function(){
36665         return this.canchors[this.position];
36666     },
36667
36668     getSlideAnchor : function(){
36669         return this.sanchors[this.position];
36670     },
36671
36672     getAlignAdj : function(){
36673         var cm = this.cmargins;
36674         switch(this.position){
36675             case "west":
36676                 return [0, 0];
36677             break;
36678             case "east":
36679                 return [0, 0];
36680             break;
36681             case "north":
36682                 return [0, 0];
36683             break;
36684             case "south":
36685                 return [0, 0];
36686             break;
36687         }
36688     },
36689
36690     getExpandAdj : function(){
36691         var c = this.collapsedEl, cm = this.cmargins;
36692         switch(this.position){
36693             case "west":
36694                 return [-(cm.right+c.getWidth()+cm.left), 0];
36695             break;
36696             case "east":
36697                 return [cm.right+c.getWidth()+cm.left, 0];
36698             break;
36699             case "north":
36700                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36701             break;
36702             case "south":
36703                 return [0, cm.top+cm.bottom+c.getHeight()];
36704             break;
36705         }
36706     }
36707 });/*
36708  * Based on:
36709  * Ext JS Library 1.1.1
36710  * Copyright(c) 2006-2007, Ext JS, LLC.
36711  *
36712  * Originally Released Under LGPL - original licence link has changed is not relivant.
36713  *
36714  * Fork - LGPL
36715  * <script type="text/javascript">
36716  */
36717 /*
36718  * These classes are private internal classes
36719  */
36720 Roo.bootstrap.layout.Center = function(config){
36721     config.region = "center";
36722     Roo.bootstrap.layout.Region.call(this, config);
36723     this.visible = true;
36724     this.minWidth = config.minWidth || 20;
36725     this.minHeight = config.minHeight || 20;
36726 };
36727
36728 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36729     hide : function(){
36730         // center panel can't be hidden
36731     },
36732     
36733     show : function(){
36734         // center panel can't be hidden
36735     },
36736     
36737     getMinWidth: function(){
36738         return this.minWidth;
36739     },
36740     
36741     getMinHeight: function(){
36742         return this.minHeight;
36743     }
36744 });
36745
36746
36747
36748
36749  
36750
36751
36752
36753
36754
36755 Roo.bootstrap.layout.North = function(config)
36756 {
36757     config.region = 'north';
36758     config.cursor = 'n-resize';
36759     
36760     Roo.bootstrap.layout.Split.call(this, config);
36761     
36762     
36763     if(this.split){
36764         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36765         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36766         this.split.el.addClass("roo-layout-split-v");
36767     }
36768     var size = config.initialSize || config.height;
36769     if(typeof size != "undefined"){
36770         this.el.setHeight(size);
36771     }
36772 };
36773 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36774 {
36775     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36776     
36777     
36778     
36779     getBox : function(){
36780         if(this.collapsed){
36781             return this.collapsedEl.getBox();
36782         }
36783         var box = this.el.getBox();
36784         if(this.split){
36785             box.height += this.split.el.getHeight();
36786         }
36787         return box;
36788     },
36789     
36790     updateBox : function(box){
36791         if(this.split && !this.collapsed){
36792             box.height -= this.split.el.getHeight();
36793             this.split.el.setLeft(box.x);
36794             this.split.el.setTop(box.y+box.height);
36795             this.split.el.setWidth(box.width);
36796         }
36797         if(this.collapsed){
36798             this.updateBody(box.width, null);
36799         }
36800         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36801     }
36802 });
36803
36804
36805
36806
36807
36808 Roo.bootstrap.layout.South = function(config){
36809     config.region = 'south';
36810     config.cursor = 's-resize';
36811     Roo.bootstrap.layout.Split.call(this, config);
36812     if(this.split){
36813         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36814         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36815         this.split.el.addClass("roo-layout-split-v");
36816     }
36817     var size = config.initialSize || config.height;
36818     if(typeof size != "undefined"){
36819         this.el.setHeight(size);
36820     }
36821 };
36822
36823 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36824     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36825     getBox : function(){
36826         if(this.collapsed){
36827             return this.collapsedEl.getBox();
36828         }
36829         var box = this.el.getBox();
36830         if(this.split){
36831             var sh = this.split.el.getHeight();
36832             box.height += sh;
36833             box.y -= sh;
36834         }
36835         return box;
36836     },
36837     
36838     updateBox : function(box){
36839         if(this.split && !this.collapsed){
36840             var sh = this.split.el.getHeight();
36841             box.height -= sh;
36842             box.y += sh;
36843             this.split.el.setLeft(box.x);
36844             this.split.el.setTop(box.y-sh);
36845             this.split.el.setWidth(box.width);
36846         }
36847         if(this.collapsed){
36848             this.updateBody(box.width, null);
36849         }
36850         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36851     }
36852 });
36853
36854 Roo.bootstrap.layout.East = function(config){
36855     config.region = "east";
36856     config.cursor = "e-resize";
36857     Roo.bootstrap.layout.Split.call(this, config);
36858     if(this.split){
36859         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36860         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36861         this.split.el.addClass("roo-layout-split-h");
36862     }
36863     var size = config.initialSize || config.width;
36864     if(typeof size != "undefined"){
36865         this.el.setWidth(size);
36866     }
36867 };
36868 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36869     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36870     getBox : function(){
36871         if(this.collapsed){
36872             return this.collapsedEl.getBox();
36873         }
36874         var box = this.el.getBox();
36875         if(this.split){
36876             var sw = this.split.el.getWidth();
36877             box.width += sw;
36878             box.x -= sw;
36879         }
36880         return box;
36881     },
36882
36883     updateBox : function(box){
36884         if(this.split && !this.collapsed){
36885             var sw = this.split.el.getWidth();
36886             box.width -= sw;
36887             this.split.el.setLeft(box.x);
36888             this.split.el.setTop(box.y);
36889             this.split.el.setHeight(box.height);
36890             box.x += sw;
36891         }
36892         if(this.collapsed){
36893             this.updateBody(null, box.height);
36894         }
36895         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36896     }
36897 });
36898
36899 Roo.bootstrap.layout.West = function(config){
36900     config.region = "west";
36901     config.cursor = "w-resize";
36902     
36903     Roo.bootstrap.layout.Split.call(this, config);
36904     if(this.split){
36905         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36906         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36907         this.split.el.addClass("roo-layout-split-h");
36908     }
36909     
36910 };
36911 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36912     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36913     
36914     onRender: function(ctr, pos)
36915     {
36916         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36917         var size = this.config.initialSize || this.config.width;
36918         if(typeof size != "undefined"){
36919             this.el.setWidth(size);
36920         }
36921     },
36922     
36923     getBox : function(){
36924         if(this.collapsed){
36925             return this.collapsedEl.getBox();
36926         }
36927         var box = this.el.getBox();
36928         if(this.split){
36929             box.width += this.split.el.getWidth();
36930         }
36931         return box;
36932     },
36933     
36934     updateBox : function(box){
36935         if(this.split && !this.collapsed){
36936             var sw = this.split.el.getWidth();
36937             box.width -= sw;
36938             this.split.el.setLeft(box.x+box.width);
36939             this.split.el.setTop(box.y);
36940             this.split.el.setHeight(box.height);
36941         }
36942         if(this.collapsed){
36943             this.updateBody(null, box.height);
36944         }
36945         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36946     }
36947 });
36948 Roo.namespace("Roo.bootstrap.panel");/*
36949  * Based on:
36950  * Ext JS Library 1.1.1
36951  * Copyright(c) 2006-2007, Ext JS, LLC.
36952  *
36953  * Originally Released Under LGPL - original licence link has changed is not relivant.
36954  *
36955  * Fork - LGPL
36956  * <script type="text/javascript">
36957  */
36958 /**
36959  * @class Roo.ContentPanel
36960  * @extends Roo.util.Observable
36961  * A basic ContentPanel element.
36962  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36963  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36964  * @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
36965  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36966  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36967  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36968  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36969  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36970  * @cfg {String} title          The title for this panel
36971  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36972  * @cfg {String} url            Calls {@link #setUrl} with this value
36973  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36974  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36975  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36976  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36977  * @cfg {Boolean} badges render the badges
36978
36979  * @constructor
36980  * Create a new ContentPanel.
36981  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36982  * @param {String/Object} config A string to set only the title or a config object
36983  * @param {String} content (optional) Set the HTML content for this panel
36984  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36985  */
36986 Roo.bootstrap.panel.Content = function( config){
36987     
36988     this.tpl = config.tpl || false;
36989     
36990     var el = config.el;
36991     var content = config.content;
36992
36993     if(config.autoCreate){ // xtype is available if this is called from factory
36994         el = Roo.id();
36995     }
36996     this.el = Roo.get(el);
36997     if(!this.el && config && config.autoCreate){
36998         if(typeof config.autoCreate == "object"){
36999             if(!config.autoCreate.id){
37000                 config.autoCreate.id = config.id||el;
37001             }
37002             this.el = Roo.DomHelper.append(document.body,
37003                         config.autoCreate, true);
37004         }else{
37005             var elcfg =  {   tag: "div",
37006                             cls: "roo-layout-inactive-content",
37007                             id: config.id||el
37008                             };
37009             if (config.html) {
37010                 elcfg.html = config.html;
37011                 
37012             }
37013                         
37014             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37015         }
37016     } 
37017     this.closable = false;
37018     this.loaded = false;
37019     this.active = false;
37020    
37021       
37022     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37023         
37024         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37025         
37026         this.wrapEl = this.el; //this.el.wrap();
37027         var ti = [];
37028         if (config.toolbar.items) {
37029             ti = config.toolbar.items ;
37030             delete config.toolbar.items ;
37031         }
37032         
37033         var nitems = [];
37034         this.toolbar.render(this.wrapEl, 'before');
37035         for(var i =0;i < ti.length;i++) {
37036           //  Roo.log(['add child', items[i]]);
37037             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37038         }
37039         this.toolbar.items = nitems;
37040         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37041         delete config.toolbar;
37042         
37043     }
37044     /*
37045     // xtype created footer. - not sure if will work as we normally have to render first..
37046     if (this.footer && !this.footer.el && this.footer.xtype) {
37047         if (!this.wrapEl) {
37048             this.wrapEl = this.el.wrap();
37049         }
37050     
37051         this.footer.container = this.wrapEl.createChild();
37052          
37053         this.footer = Roo.factory(this.footer, Roo);
37054         
37055     }
37056     */
37057     
37058      if(typeof config == "string"){
37059         this.title = config;
37060     }else{
37061         Roo.apply(this, config);
37062     }
37063     
37064     if(this.resizeEl){
37065         this.resizeEl = Roo.get(this.resizeEl, true);
37066     }else{
37067         this.resizeEl = this.el;
37068     }
37069     // handle view.xtype
37070     
37071  
37072     
37073     
37074     this.addEvents({
37075         /**
37076          * @event activate
37077          * Fires when this panel is activated. 
37078          * @param {Roo.ContentPanel} this
37079          */
37080         "activate" : true,
37081         /**
37082          * @event deactivate
37083          * Fires when this panel is activated. 
37084          * @param {Roo.ContentPanel} this
37085          */
37086         "deactivate" : true,
37087
37088         /**
37089          * @event resize
37090          * Fires when this panel is resized if fitToFrame is true.
37091          * @param {Roo.ContentPanel} this
37092          * @param {Number} width The width after any component adjustments
37093          * @param {Number} height The height after any component adjustments
37094          */
37095         "resize" : true,
37096         
37097          /**
37098          * @event render
37099          * Fires when this tab is created
37100          * @param {Roo.ContentPanel} this
37101          */
37102         "render" : true
37103         
37104         
37105         
37106     });
37107     
37108
37109     
37110     
37111     if(this.autoScroll){
37112         this.resizeEl.setStyle("overflow", "auto");
37113     } else {
37114         // fix randome scrolling
37115         //this.el.on('scroll', function() {
37116         //    Roo.log('fix random scolling');
37117         //    this.scrollTo('top',0); 
37118         //});
37119     }
37120     content = content || this.content;
37121     if(content){
37122         this.setContent(content);
37123     }
37124     if(config && config.url){
37125         this.setUrl(this.url, this.params, this.loadOnce);
37126     }
37127     
37128     
37129     
37130     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37131     
37132     if (this.view && typeof(this.view.xtype) != 'undefined') {
37133         this.view.el = this.el.appendChild(document.createElement("div"));
37134         this.view = Roo.factory(this.view); 
37135         this.view.render  &&  this.view.render(false, '');  
37136     }
37137     
37138     
37139     this.fireEvent('render', this);
37140 };
37141
37142 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37143     
37144     tabTip : '',
37145     
37146     setRegion : function(region){
37147         this.region = region;
37148         this.setActiveClass(region && !this.background);
37149     },
37150     
37151     
37152     setActiveClass: function(state)
37153     {
37154         if(state){
37155            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37156            this.el.setStyle('position','relative');
37157         }else{
37158            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37159            this.el.setStyle('position', 'absolute');
37160         } 
37161     },
37162     
37163     /**
37164      * Returns the toolbar for this Panel if one was configured. 
37165      * @return {Roo.Toolbar} 
37166      */
37167     getToolbar : function(){
37168         return this.toolbar;
37169     },
37170     
37171     setActiveState : function(active)
37172     {
37173         this.active = active;
37174         this.setActiveClass(active);
37175         if(!active){
37176             if(this.fireEvent("deactivate", this) === false){
37177                 return false;
37178             }
37179             return true;
37180         }
37181         this.fireEvent("activate", this);
37182         return true;
37183     },
37184     /**
37185      * Updates this panel's element
37186      * @param {String} content The new content
37187      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37188     */
37189     setContent : function(content, loadScripts){
37190         this.el.update(content, loadScripts);
37191     },
37192
37193     ignoreResize : function(w, h){
37194         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37195             return true;
37196         }else{
37197             this.lastSize = {width: w, height: h};
37198             return false;
37199         }
37200     },
37201     /**
37202      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37203      * @return {Roo.UpdateManager} The UpdateManager
37204      */
37205     getUpdateManager : function(){
37206         return this.el.getUpdateManager();
37207     },
37208      /**
37209      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37210      * @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:
37211 <pre><code>
37212 panel.load({
37213     url: "your-url.php",
37214     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37215     callback: yourFunction,
37216     scope: yourObject, //(optional scope)
37217     discardUrl: false,
37218     nocache: false,
37219     text: "Loading...",
37220     timeout: 30,
37221     scripts: false
37222 });
37223 </code></pre>
37224      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37225      * 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.
37226      * @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}
37227      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37228      * @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.
37229      * @return {Roo.ContentPanel} this
37230      */
37231     load : function(){
37232         var um = this.el.getUpdateManager();
37233         um.update.apply(um, arguments);
37234         return this;
37235     },
37236
37237
37238     /**
37239      * 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.
37240      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37241      * @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)
37242      * @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)
37243      * @return {Roo.UpdateManager} The UpdateManager
37244      */
37245     setUrl : function(url, params, loadOnce){
37246         if(this.refreshDelegate){
37247             this.removeListener("activate", this.refreshDelegate);
37248         }
37249         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37250         this.on("activate", this.refreshDelegate);
37251         return this.el.getUpdateManager();
37252     },
37253     
37254     _handleRefresh : function(url, params, loadOnce){
37255         if(!loadOnce || !this.loaded){
37256             var updater = this.el.getUpdateManager();
37257             updater.update(url, params, this._setLoaded.createDelegate(this));
37258         }
37259     },
37260     
37261     _setLoaded : function(){
37262         this.loaded = true;
37263     }, 
37264     
37265     /**
37266      * Returns this panel's id
37267      * @return {String} 
37268      */
37269     getId : function(){
37270         return this.el.id;
37271     },
37272     
37273     /** 
37274      * Returns this panel's element - used by regiosn to add.
37275      * @return {Roo.Element} 
37276      */
37277     getEl : function(){
37278         return this.wrapEl || this.el;
37279     },
37280     
37281    
37282     
37283     adjustForComponents : function(width, height)
37284     {
37285         //Roo.log('adjustForComponents ');
37286         if(this.resizeEl != this.el){
37287             width -= this.el.getFrameWidth('lr');
37288             height -= this.el.getFrameWidth('tb');
37289         }
37290         if(this.toolbar){
37291             var te = this.toolbar.getEl();
37292             te.setWidth(width);
37293             height -= te.getHeight();
37294         }
37295         if(this.footer){
37296             var te = this.footer.getEl();
37297             te.setWidth(width);
37298             height -= te.getHeight();
37299         }
37300         
37301         
37302         if(this.adjustments){
37303             width += this.adjustments[0];
37304             height += this.adjustments[1];
37305         }
37306         return {"width": width, "height": height};
37307     },
37308     
37309     setSize : function(width, height){
37310         if(this.fitToFrame && !this.ignoreResize(width, height)){
37311             if(this.fitContainer && this.resizeEl != this.el){
37312                 this.el.setSize(width, height);
37313             }
37314             var size = this.adjustForComponents(width, height);
37315             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37316             this.fireEvent('resize', this, size.width, size.height);
37317         }
37318     },
37319     
37320     /**
37321      * Returns this panel's title
37322      * @return {String} 
37323      */
37324     getTitle : function(){
37325         
37326         if (typeof(this.title) != 'object') {
37327             return this.title;
37328         }
37329         
37330         var t = '';
37331         for (var k in this.title) {
37332             if (!this.title.hasOwnProperty(k)) {
37333                 continue;
37334             }
37335             
37336             if (k.indexOf('-') >= 0) {
37337                 var s = k.split('-');
37338                 for (var i = 0; i<s.length; i++) {
37339                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37340                 }
37341             } else {
37342                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37343             }
37344         }
37345         return t;
37346     },
37347     
37348     /**
37349      * Set this panel's title
37350      * @param {String} title
37351      */
37352     setTitle : function(title){
37353         this.title = title;
37354         if(this.region){
37355             this.region.updatePanelTitle(this, title);
37356         }
37357     },
37358     
37359     /**
37360      * Returns true is this panel was configured to be closable
37361      * @return {Boolean} 
37362      */
37363     isClosable : function(){
37364         return this.closable;
37365     },
37366     
37367     beforeSlide : function(){
37368         this.el.clip();
37369         this.resizeEl.clip();
37370     },
37371     
37372     afterSlide : function(){
37373         this.el.unclip();
37374         this.resizeEl.unclip();
37375     },
37376     
37377     /**
37378      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37379      *   Will fail silently if the {@link #setUrl} method has not been called.
37380      *   This does not activate the panel, just updates its content.
37381      */
37382     refresh : function(){
37383         if(this.refreshDelegate){
37384            this.loaded = false;
37385            this.refreshDelegate();
37386         }
37387     },
37388     
37389     /**
37390      * Destroys this panel
37391      */
37392     destroy : function(){
37393         this.el.removeAllListeners();
37394         var tempEl = document.createElement("span");
37395         tempEl.appendChild(this.el.dom);
37396         tempEl.innerHTML = "";
37397         this.el.remove();
37398         this.el = null;
37399     },
37400     
37401     /**
37402      * form - if the content panel contains a form - this is a reference to it.
37403      * @type {Roo.form.Form}
37404      */
37405     form : false,
37406     /**
37407      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37408      *    This contains a reference to it.
37409      * @type {Roo.View}
37410      */
37411     view : false,
37412     
37413       /**
37414      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37415      * <pre><code>
37416
37417 layout.addxtype({
37418        xtype : 'Form',
37419        items: [ .... ]
37420    }
37421 );
37422
37423 </code></pre>
37424      * @param {Object} cfg Xtype definition of item to add.
37425      */
37426     
37427     
37428     getChildContainer: function () {
37429         return this.getEl();
37430     }
37431     
37432     
37433     /*
37434         var  ret = new Roo.factory(cfg);
37435         return ret;
37436         
37437         
37438         // add form..
37439         if (cfg.xtype.match(/^Form$/)) {
37440             
37441             var el;
37442             //if (this.footer) {
37443             //    el = this.footer.container.insertSibling(false, 'before');
37444             //} else {
37445                 el = this.el.createChild();
37446             //}
37447
37448             this.form = new  Roo.form.Form(cfg);
37449             
37450             
37451             if ( this.form.allItems.length) {
37452                 this.form.render(el.dom);
37453             }
37454             return this.form;
37455         }
37456         // should only have one of theses..
37457         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37458             // views.. should not be just added - used named prop 'view''
37459             
37460             cfg.el = this.el.appendChild(document.createElement("div"));
37461             // factory?
37462             
37463             var ret = new Roo.factory(cfg);
37464              
37465              ret.render && ret.render(false, ''); // render blank..
37466             this.view = ret;
37467             return ret;
37468         }
37469         return false;
37470     }
37471     \*/
37472 });
37473  
37474 /**
37475  * @class Roo.bootstrap.panel.Grid
37476  * @extends Roo.bootstrap.panel.Content
37477  * @constructor
37478  * Create a new GridPanel.
37479  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37480  * @param {Object} config A the config object
37481   
37482  */
37483
37484
37485
37486 Roo.bootstrap.panel.Grid = function(config)
37487 {
37488     
37489       
37490     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37491         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37492
37493     config.el = this.wrapper;
37494     //this.el = this.wrapper;
37495     
37496       if (config.container) {
37497         // ctor'ed from a Border/panel.grid
37498         
37499         
37500         this.wrapper.setStyle("overflow", "hidden");
37501         this.wrapper.addClass('roo-grid-container');
37502
37503     }
37504     
37505     
37506     if(config.toolbar){
37507         var tool_el = this.wrapper.createChild();    
37508         this.toolbar = Roo.factory(config.toolbar);
37509         var ti = [];
37510         if (config.toolbar.items) {
37511             ti = config.toolbar.items ;
37512             delete config.toolbar.items ;
37513         }
37514         
37515         var nitems = [];
37516         this.toolbar.render(tool_el);
37517         for(var i =0;i < ti.length;i++) {
37518           //  Roo.log(['add child', items[i]]);
37519             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37520         }
37521         this.toolbar.items = nitems;
37522         
37523         delete config.toolbar;
37524     }
37525     
37526     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37527     config.grid.scrollBody = true;;
37528     config.grid.monitorWindowResize = false; // turn off autosizing
37529     config.grid.autoHeight = false;
37530     config.grid.autoWidth = false;
37531     
37532     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37533     
37534     if (config.background) {
37535         // render grid on panel activation (if panel background)
37536         this.on('activate', function(gp) {
37537             if (!gp.grid.rendered) {
37538                 gp.grid.render(this.wrapper);
37539                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37540             }
37541         });
37542             
37543     } else {
37544         this.grid.render(this.wrapper);
37545         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37546
37547     }
37548     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37549     // ??? needed ??? config.el = this.wrapper;
37550     
37551     
37552     
37553   
37554     // xtype created footer. - not sure if will work as we normally have to render first..
37555     if (this.footer && !this.footer.el && this.footer.xtype) {
37556         
37557         var ctr = this.grid.getView().getFooterPanel(true);
37558         this.footer.dataSource = this.grid.dataSource;
37559         this.footer = Roo.factory(this.footer, Roo);
37560         this.footer.render(ctr);
37561         
37562     }
37563     
37564     
37565     
37566     
37567      
37568 };
37569
37570 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37571     getId : function(){
37572         return this.grid.id;
37573     },
37574     
37575     /**
37576      * Returns the grid for this panel
37577      * @return {Roo.bootstrap.Table} 
37578      */
37579     getGrid : function(){
37580         return this.grid;    
37581     },
37582     
37583     setSize : function(width, height){
37584         if(!this.ignoreResize(width, height)){
37585             var grid = this.grid;
37586             var size = this.adjustForComponents(width, height);
37587             var gridel = grid.getGridEl();
37588             gridel.setSize(size.width, size.height);
37589             /*
37590             var thd = grid.getGridEl().select('thead',true).first();
37591             var tbd = grid.getGridEl().select('tbody', true).first();
37592             if (tbd) {
37593                 tbd.setSize(width, height - thd.getHeight());
37594             }
37595             */
37596             grid.autoSize();
37597         }
37598     },
37599      
37600     
37601     
37602     beforeSlide : function(){
37603         this.grid.getView().scroller.clip();
37604     },
37605     
37606     afterSlide : function(){
37607         this.grid.getView().scroller.unclip();
37608     },
37609     
37610     destroy : function(){
37611         this.grid.destroy();
37612         delete this.grid;
37613         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37614     }
37615 });
37616
37617 /**
37618  * @class Roo.bootstrap.panel.Nest
37619  * @extends Roo.bootstrap.panel.Content
37620  * @constructor
37621  * Create a new Panel, that can contain a layout.Border.
37622  * 
37623  * 
37624  * @param {Roo.BorderLayout} layout The layout for this panel
37625  * @param {String/Object} config A string to set only the title or a config object
37626  */
37627 Roo.bootstrap.panel.Nest = function(config)
37628 {
37629     // construct with only one argument..
37630     /* FIXME - implement nicer consturctors
37631     if (layout.layout) {
37632         config = layout;
37633         layout = config.layout;
37634         delete config.layout;
37635     }
37636     if (layout.xtype && !layout.getEl) {
37637         // then layout needs constructing..
37638         layout = Roo.factory(layout, Roo);
37639     }
37640     */
37641     
37642     config.el =  config.layout.getEl();
37643     
37644     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37645     
37646     config.layout.monitorWindowResize = false; // turn off autosizing
37647     this.layout = config.layout;
37648     this.layout.getEl().addClass("roo-layout-nested-layout");
37649     
37650     
37651     
37652     
37653 };
37654
37655 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37656
37657     setSize : function(width, height){
37658         if(!this.ignoreResize(width, height)){
37659             var size = this.adjustForComponents(width, height);
37660             var el = this.layout.getEl();
37661             if (size.height < 1) {
37662                 el.setWidth(size.width);   
37663             } else {
37664                 el.setSize(size.width, size.height);
37665             }
37666             var touch = el.dom.offsetWidth;
37667             this.layout.layout();
37668             // ie requires a double layout on the first pass
37669             if(Roo.isIE && !this.initialized){
37670                 this.initialized = true;
37671                 this.layout.layout();
37672             }
37673         }
37674     },
37675     
37676     // activate all subpanels if not currently active..
37677     
37678     setActiveState : function(active){
37679         this.active = active;
37680         this.setActiveClass(active);
37681         
37682         if(!active){
37683             this.fireEvent("deactivate", this);
37684             return;
37685         }
37686         
37687         this.fireEvent("activate", this);
37688         // not sure if this should happen before or after..
37689         if (!this.layout) {
37690             return; // should not happen..
37691         }
37692         var reg = false;
37693         for (var r in this.layout.regions) {
37694             reg = this.layout.getRegion(r);
37695             if (reg.getActivePanel()) {
37696                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37697                 reg.setActivePanel(reg.getActivePanel());
37698                 continue;
37699             }
37700             if (!reg.panels.length) {
37701                 continue;
37702             }
37703             reg.showPanel(reg.getPanel(0));
37704         }
37705         
37706         
37707         
37708         
37709     },
37710     
37711     /**
37712      * Returns the nested BorderLayout for this panel
37713      * @return {Roo.BorderLayout} 
37714      */
37715     getLayout : function(){
37716         return this.layout;
37717     },
37718     
37719      /**
37720      * Adds a xtype elements to the layout of the nested panel
37721      * <pre><code>
37722
37723 panel.addxtype({
37724        xtype : 'ContentPanel',
37725        region: 'west',
37726        items: [ .... ]
37727    }
37728 );
37729
37730 panel.addxtype({
37731         xtype : 'NestedLayoutPanel',
37732         region: 'west',
37733         layout: {
37734            center: { },
37735            west: { }   
37736         },
37737         items : [ ... list of content panels or nested layout panels.. ]
37738    }
37739 );
37740 </code></pre>
37741      * @param {Object} cfg Xtype definition of item to add.
37742      */
37743     addxtype : function(cfg) {
37744         return this.layout.addxtype(cfg);
37745     
37746     }
37747 });        /*
37748  * Based on:
37749  * Ext JS Library 1.1.1
37750  * Copyright(c) 2006-2007, Ext JS, LLC.
37751  *
37752  * Originally Released Under LGPL - original licence link has changed is not relivant.
37753  *
37754  * Fork - LGPL
37755  * <script type="text/javascript">
37756  */
37757 /**
37758  * @class Roo.TabPanel
37759  * @extends Roo.util.Observable
37760  * A lightweight tab container.
37761  * <br><br>
37762  * Usage:
37763  * <pre><code>
37764 // basic tabs 1, built from existing content
37765 var tabs = new Roo.TabPanel("tabs1");
37766 tabs.addTab("script", "View Script");
37767 tabs.addTab("markup", "View Markup");
37768 tabs.activate("script");
37769
37770 // more advanced tabs, built from javascript
37771 var jtabs = new Roo.TabPanel("jtabs");
37772 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37773
37774 // set up the UpdateManager
37775 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37776 var updater = tab2.getUpdateManager();
37777 updater.setDefaultUrl("ajax1.htm");
37778 tab2.on('activate', updater.refresh, updater, true);
37779
37780 // Use setUrl for Ajax loading
37781 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37782 tab3.setUrl("ajax2.htm", null, true);
37783
37784 // Disabled tab
37785 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37786 tab4.disable();
37787
37788 jtabs.activate("jtabs-1");
37789  * </code></pre>
37790  * @constructor
37791  * Create a new TabPanel.
37792  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37793  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37794  */
37795 Roo.bootstrap.panel.Tabs = function(config){
37796     /**
37797     * The container element for this TabPanel.
37798     * @type Roo.Element
37799     */
37800     this.el = Roo.get(config.el);
37801     delete config.el;
37802     if(config){
37803         if(typeof config == "boolean"){
37804             this.tabPosition = config ? "bottom" : "top";
37805         }else{
37806             Roo.apply(this, config);
37807         }
37808     }
37809     
37810     if(this.tabPosition == "bottom"){
37811         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37812         this.el.addClass("roo-tabs-bottom");
37813     }
37814     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37815     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37816     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37817     if(Roo.isIE){
37818         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37819     }
37820     if(this.tabPosition != "bottom"){
37821         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37822          * @type Roo.Element
37823          */
37824         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37825         this.el.addClass("roo-tabs-top");
37826     }
37827     this.items = [];
37828
37829     this.bodyEl.setStyle("position", "relative");
37830
37831     this.active = null;
37832     this.activateDelegate = this.activate.createDelegate(this);
37833
37834     this.addEvents({
37835         /**
37836          * @event tabchange
37837          * Fires when the active tab changes
37838          * @param {Roo.TabPanel} this
37839          * @param {Roo.TabPanelItem} activePanel The new active tab
37840          */
37841         "tabchange": true,
37842         /**
37843          * @event beforetabchange
37844          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37845          * @param {Roo.TabPanel} this
37846          * @param {Object} e Set cancel to true on this object to cancel the tab change
37847          * @param {Roo.TabPanelItem} tab The tab being changed to
37848          */
37849         "beforetabchange" : true
37850     });
37851
37852     Roo.EventManager.onWindowResize(this.onResize, this);
37853     this.cpad = this.el.getPadding("lr");
37854     this.hiddenCount = 0;
37855
37856
37857     // toolbar on the tabbar support...
37858     if (this.toolbar) {
37859         alert("no toolbar support yet");
37860         this.toolbar  = false;
37861         /*
37862         var tcfg = this.toolbar;
37863         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37864         this.toolbar = new Roo.Toolbar(tcfg);
37865         if (Roo.isSafari) {
37866             var tbl = tcfg.container.child('table', true);
37867             tbl.setAttribute('width', '100%');
37868         }
37869         */
37870         
37871     }
37872    
37873
37874
37875     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37876 };
37877
37878 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37879     /*
37880      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37881      */
37882     tabPosition : "top",
37883     /*
37884      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37885      */
37886     currentTabWidth : 0,
37887     /*
37888      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37889      */
37890     minTabWidth : 40,
37891     /*
37892      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37893      */
37894     maxTabWidth : 250,
37895     /*
37896      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37897      */
37898     preferredTabWidth : 175,
37899     /*
37900      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37901      */
37902     resizeTabs : false,
37903     /*
37904      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37905      */
37906     monitorResize : true,
37907     /*
37908      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37909      */
37910     toolbar : false,
37911
37912     /**
37913      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37914      * @param {String} id The id of the div to use <b>or create</b>
37915      * @param {String} text The text for the tab
37916      * @param {String} content (optional) Content to put in the TabPanelItem body
37917      * @param {Boolean} closable (optional) True to create a close icon on the tab
37918      * @return {Roo.TabPanelItem} The created TabPanelItem
37919      */
37920     addTab : function(id, text, content, closable, tpl)
37921     {
37922         var item = new Roo.bootstrap.panel.TabItem({
37923             panel: this,
37924             id : id,
37925             text : text,
37926             closable : closable,
37927             tpl : tpl
37928         });
37929         this.addTabItem(item);
37930         if(content){
37931             item.setContent(content);
37932         }
37933         return item;
37934     },
37935
37936     /**
37937      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37938      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37939      * @return {Roo.TabPanelItem}
37940      */
37941     getTab : function(id){
37942         return this.items[id];
37943     },
37944
37945     /**
37946      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37947      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37948      */
37949     hideTab : function(id){
37950         var t = this.items[id];
37951         if(!t.isHidden()){
37952            t.setHidden(true);
37953            this.hiddenCount++;
37954            this.autoSizeTabs();
37955         }
37956     },
37957
37958     /**
37959      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37960      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37961      */
37962     unhideTab : function(id){
37963         var t = this.items[id];
37964         if(t.isHidden()){
37965            t.setHidden(false);
37966            this.hiddenCount--;
37967            this.autoSizeTabs();
37968         }
37969     },
37970
37971     /**
37972      * Adds an existing {@link Roo.TabPanelItem}.
37973      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37974      */
37975     addTabItem : function(item){
37976         this.items[item.id] = item;
37977         this.items.push(item);
37978       //  if(this.resizeTabs){
37979     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37980   //         this.autoSizeTabs();
37981 //        }else{
37982 //            item.autoSize();
37983        // }
37984     },
37985
37986     /**
37987      * Removes a {@link Roo.TabPanelItem}.
37988      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37989      */
37990     removeTab : function(id){
37991         var items = this.items;
37992         var tab = items[id];
37993         if(!tab) { return; }
37994         var index = items.indexOf(tab);
37995         if(this.active == tab && items.length > 1){
37996             var newTab = this.getNextAvailable(index);
37997             if(newTab) {
37998                 newTab.activate();
37999             }
38000         }
38001         this.stripEl.dom.removeChild(tab.pnode.dom);
38002         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38003             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38004         }
38005         items.splice(index, 1);
38006         delete this.items[tab.id];
38007         tab.fireEvent("close", tab);
38008         tab.purgeListeners();
38009         this.autoSizeTabs();
38010     },
38011
38012     getNextAvailable : function(start){
38013         var items = this.items;
38014         var index = start;
38015         // look for a next tab that will slide over to
38016         // replace the one being removed
38017         while(index < items.length){
38018             var item = items[++index];
38019             if(item && !item.isHidden()){
38020                 return item;
38021             }
38022         }
38023         // if one isn't found select the previous tab (on the left)
38024         index = start;
38025         while(index >= 0){
38026             var item = items[--index];
38027             if(item && !item.isHidden()){
38028                 return item;
38029             }
38030         }
38031         return null;
38032     },
38033
38034     /**
38035      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38036      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38037      */
38038     disableTab : function(id){
38039         var tab = this.items[id];
38040         if(tab && this.active != tab){
38041             tab.disable();
38042         }
38043     },
38044
38045     /**
38046      * Enables a {@link Roo.TabPanelItem} that is disabled.
38047      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38048      */
38049     enableTab : function(id){
38050         var tab = this.items[id];
38051         tab.enable();
38052     },
38053
38054     /**
38055      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38056      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38057      * @return {Roo.TabPanelItem} The TabPanelItem.
38058      */
38059     activate : function(id){
38060         var tab = this.items[id];
38061         if(!tab){
38062             return null;
38063         }
38064         if(tab == this.active || tab.disabled){
38065             return tab;
38066         }
38067         var e = {};
38068         this.fireEvent("beforetabchange", this, e, tab);
38069         if(e.cancel !== true && !tab.disabled){
38070             if(this.active){
38071                 this.active.hide();
38072             }
38073             this.active = this.items[id];
38074             this.active.show();
38075             this.fireEvent("tabchange", this, this.active);
38076         }
38077         return tab;
38078     },
38079
38080     /**
38081      * Gets the active {@link Roo.TabPanelItem}.
38082      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38083      */
38084     getActiveTab : function(){
38085         return this.active;
38086     },
38087
38088     /**
38089      * Updates the tab body element to fit the height of the container element
38090      * for overflow scrolling
38091      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38092      */
38093     syncHeight : function(targetHeight){
38094         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38095         var bm = this.bodyEl.getMargins();
38096         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38097         this.bodyEl.setHeight(newHeight);
38098         return newHeight;
38099     },
38100
38101     onResize : function(){
38102         if(this.monitorResize){
38103             this.autoSizeTabs();
38104         }
38105     },
38106
38107     /**
38108      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38109      */
38110     beginUpdate : function(){
38111         this.updating = true;
38112     },
38113
38114     /**
38115      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38116      */
38117     endUpdate : function(){
38118         this.updating = false;
38119         this.autoSizeTabs();
38120     },
38121
38122     /**
38123      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38124      */
38125     autoSizeTabs : function(){
38126         var count = this.items.length;
38127         var vcount = count - this.hiddenCount;
38128         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38129             return;
38130         }
38131         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38132         var availWidth = Math.floor(w / vcount);
38133         var b = this.stripBody;
38134         if(b.getWidth() > w){
38135             var tabs = this.items;
38136             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38137             if(availWidth < this.minTabWidth){
38138                 /*if(!this.sleft){    // incomplete scrolling code
38139                     this.createScrollButtons();
38140                 }
38141                 this.showScroll();
38142                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38143             }
38144         }else{
38145             if(this.currentTabWidth < this.preferredTabWidth){
38146                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38147             }
38148         }
38149     },
38150
38151     /**
38152      * Returns the number of tabs in this TabPanel.
38153      * @return {Number}
38154      */
38155      getCount : function(){
38156          return this.items.length;
38157      },
38158
38159     /**
38160      * Resizes all the tabs to the passed width
38161      * @param {Number} The new width
38162      */
38163     setTabWidth : function(width){
38164         this.currentTabWidth = width;
38165         for(var i = 0, len = this.items.length; i < len; i++) {
38166                 if(!this.items[i].isHidden()) {
38167                 this.items[i].setWidth(width);
38168             }
38169         }
38170     },
38171
38172     /**
38173      * Destroys this TabPanel
38174      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38175      */
38176     destroy : function(removeEl){
38177         Roo.EventManager.removeResizeListener(this.onResize, this);
38178         for(var i = 0, len = this.items.length; i < len; i++){
38179             this.items[i].purgeListeners();
38180         }
38181         if(removeEl === true){
38182             this.el.update("");
38183             this.el.remove();
38184         }
38185     },
38186     
38187     createStrip : function(container)
38188     {
38189         var strip = document.createElement("nav");
38190         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38191         container.appendChild(strip);
38192         return strip;
38193     },
38194     
38195     createStripList : function(strip)
38196     {
38197         // div wrapper for retard IE
38198         // returns the "tr" element.
38199         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38200         //'<div class="x-tabs-strip-wrap">'+
38201           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38202           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38203         return strip.firstChild; //.firstChild.firstChild.firstChild;
38204     },
38205     createBody : function(container)
38206     {
38207         var body = document.createElement("div");
38208         Roo.id(body, "tab-body");
38209         //Roo.fly(body).addClass("x-tabs-body");
38210         Roo.fly(body).addClass("tab-content");
38211         container.appendChild(body);
38212         return body;
38213     },
38214     createItemBody :function(bodyEl, id){
38215         var body = Roo.getDom(id);
38216         if(!body){
38217             body = document.createElement("div");
38218             body.id = id;
38219         }
38220         //Roo.fly(body).addClass("x-tabs-item-body");
38221         Roo.fly(body).addClass("tab-pane");
38222          bodyEl.insertBefore(body, bodyEl.firstChild);
38223         return body;
38224     },
38225     /** @private */
38226     createStripElements :  function(stripEl, text, closable, tpl)
38227     {
38228         var td = document.createElement("li"); // was td..
38229         
38230         
38231         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38232         
38233         
38234         stripEl.appendChild(td);
38235         /*if(closable){
38236             td.className = "x-tabs-closable";
38237             if(!this.closeTpl){
38238                 this.closeTpl = new Roo.Template(
38239                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38240                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38241                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38242                 );
38243             }
38244             var el = this.closeTpl.overwrite(td, {"text": text});
38245             var close = el.getElementsByTagName("div")[0];
38246             var inner = el.getElementsByTagName("em")[0];
38247             return {"el": el, "close": close, "inner": inner};
38248         } else {
38249         */
38250         // not sure what this is..
38251 //            if(!this.tabTpl){
38252                 //this.tabTpl = new Roo.Template(
38253                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38254                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38255                 //);
38256 //                this.tabTpl = new Roo.Template(
38257 //                   '<a href="#">' +
38258 //                   '<span unselectable="on"' +
38259 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38260 //                            ' >{text}</span></a>'
38261 //                );
38262 //                
38263 //            }
38264
38265
38266             var template = tpl || this.tabTpl || false;
38267             
38268             if(!template){
38269                 
38270                 template = new Roo.Template(
38271                    '<a href="#">' +
38272                    '<span unselectable="on"' +
38273                             (this.disableTooltips ? '' : ' title="{text}"') +
38274                             ' >{text}</span></a>'
38275                 );
38276             }
38277             
38278             switch (typeof(template)) {
38279                 case 'object' :
38280                     break;
38281                 case 'string' :
38282                     template = new Roo.Template(template);
38283                     break;
38284                 default :
38285                     break;
38286             }
38287             
38288             var el = template.overwrite(td, {"text": text});
38289             
38290             var inner = el.getElementsByTagName("span")[0];
38291             
38292             return {"el": el, "inner": inner};
38293             
38294     }
38295         
38296     
38297 });
38298
38299 /**
38300  * @class Roo.TabPanelItem
38301  * @extends Roo.util.Observable
38302  * Represents an individual item (tab plus body) in a TabPanel.
38303  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38304  * @param {String} id The id of this TabPanelItem
38305  * @param {String} text The text for the tab of this TabPanelItem
38306  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38307  */
38308 Roo.bootstrap.panel.TabItem = function(config){
38309     /**
38310      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38311      * @type Roo.TabPanel
38312      */
38313     this.tabPanel = config.panel;
38314     /**
38315      * The id for this TabPanelItem
38316      * @type String
38317      */
38318     this.id = config.id;
38319     /** @private */
38320     this.disabled = false;
38321     /** @private */
38322     this.text = config.text;
38323     /** @private */
38324     this.loaded = false;
38325     this.closable = config.closable;
38326
38327     /**
38328      * The body element for this TabPanelItem.
38329      * @type Roo.Element
38330      */
38331     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38332     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38333     this.bodyEl.setStyle("display", "block");
38334     this.bodyEl.setStyle("zoom", "1");
38335     //this.hideAction();
38336
38337     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38338     /** @private */
38339     this.el = Roo.get(els.el);
38340     this.inner = Roo.get(els.inner, true);
38341     this.textEl = Roo.get(this.el.dom.firstChild, true);
38342     this.pnode = Roo.get(els.el.parentNode, true);
38343 //    this.el.on("mousedown", this.onTabMouseDown, this);
38344     this.el.on("click", this.onTabClick, this);
38345     /** @private */
38346     if(config.closable){
38347         var c = Roo.get(els.close, true);
38348         c.dom.title = this.closeText;
38349         c.addClassOnOver("close-over");
38350         c.on("click", this.closeClick, this);
38351      }
38352
38353     this.addEvents({
38354          /**
38355          * @event activate
38356          * Fires when this tab becomes the active tab.
38357          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38358          * @param {Roo.TabPanelItem} this
38359          */
38360         "activate": true,
38361         /**
38362          * @event beforeclose
38363          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38364          * @param {Roo.TabPanelItem} this
38365          * @param {Object} e Set cancel to true on this object to cancel the close.
38366          */
38367         "beforeclose": true,
38368         /**
38369          * @event close
38370          * Fires when this tab is closed.
38371          * @param {Roo.TabPanelItem} this
38372          */
38373          "close": true,
38374         /**
38375          * @event deactivate
38376          * Fires when this tab is no longer the active tab.
38377          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38378          * @param {Roo.TabPanelItem} this
38379          */
38380          "deactivate" : true
38381     });
38382     this.hidden = false;
38383
38384     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38385 };
38386
38387 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38388            {
38389     purgeListeners : function(){
38390        Roo.util.Observable.prototype.purgeListeners.call(this);
38391        this.el.removeAllListeners();
38392     },
38393     /**
38394      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38395      */
38396     show : function(){
38397         this.pnode.addClass("active");
38398         this.showAction();
38399         if(Roo.isOpera){
38400             this.tabPanel.stripWrap.repaint();
38401         }
38402         this.fireEvent("activate", this.tabPanel, this);
38403     },
38404
38405     /**
38406      * Returns true if this tab is the active tab.
38407      * @return {Boolean}
38408      */
38409     isActive : function(){
38410         return this.tabPanel.getActiveTab() == this;
38411     },
38412
38413     /**
38414      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38415      */
38416     hide : function(){
38417         this.pnode.removeClass("active");
38418         this.hideAction();
38419         this.fireEvent("deactivate", this.tabPanel, this);
38420     },
38421
38422     hideAction : function(){
38423         this.bodyEl.hide();
38424         this.bodyEl.setStyle("position", "absolute");
38425         this.bodyEl.setLeft("-20000px");
38426         this.bodyEl.setTop("-20000px");
38427     },
38428
38429     showAction : function(){
38430         this.bodyEl.setStyle("position", "relative");
38431         this.bodyEl.setTop("");
38432         this.bodyEl.setLeft("");
38433         this.bodyEl.show();
38434     },
38435
38436     /**
38437      * Set the tooltip for the tab.
38438      * @param {String} tooltip The tab's tooltip
38439      */
38440     setTooltip : function(text){
38441         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38442             this.textEl.dom.qtip = text;
38443             this.textEl.dom.removeAttribute('title');
38444         }else{
38445             this.textEl.dom.title = text;
38446         }
38447     },
38448
38449     onTabClick : function(e){
38450         e.preventDefault();
38451         this.tabPanel.activate(this.id);
38452     },
38453
38454     onTabMouseDown : function(e){
38455         e.preventDefault();
38456         this.tabPanel.activate(this.id);
38457     },
38458 /*
38459     getWidth : function(){
38460         return this.inner.getWidth();
38461     },
38462
38463     setWidth : function(width){
38464         var iwidth = width - this.pnode.getPadding("lr");
38465         this.inner.setWidth(iwidth);
38466         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38467         this.pnode.setWidth(width);
38468     },
38469 */
38470     /**
38471      * Show or hide the tab
38472      * @param {Boolean} hidden True to hide or false to show.
38473      */
38474     setHidden : function(hidden){
38475         this.hidden = hidden;
38476         this.pnode.setStyle("display", hidden ? "none" : "");
38477     },
38478
38479     /**
38480      * Returns true if this tab is "hidden"
38481      * @return {Boolean}
38482      */
38483     isHidden : function(){
38484         return this.hidden;
38485     },
38486
38487     /**
38488      * Returns the text for this tab
38489      * @return {String}
38490      */
38491     getText : function(){
38492         return this.text;
38493     },
38494     /*
38495     autoSize : function(){
38496         //this.el.beginMeasure();
38497         this.textEl.setWidth(1);
38498         /*
38499          *  #2804 [new] Tabs in Roojs
38500          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38501          */
38502         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38503         //this.el.endMeasure();
38504     //},
38505
38506     /**
38507      * Sets the text for the tab (Note: this also sets the tooltip text)
38508      * @param {String} text The tab's text and tooltip
38509      */
38510     setText : function(text){
38511         this.text = text;
38512         this.textEl.update(text);
38513         this.setTooltip(text);
38514         //if(!this.tabPanel.resizeTabs){
38515         //    this.autoSize();
38516         //}
38517     },
38518     /**
38519      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38520      */
38521     activate : function(){
38522         this.tabPanel.activate(this.id);
38523     },
38524
38525     /**
38526      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38527      */
38528     disable : function(){
38529         if(this.tabPanel.active != this){
38530             this.disabled = true;
38531             this.pnode.addClass("disabled");
38532         }
38533     },
38534
38535     /**
38536      * Enables this TabPanelItem if it was previously disabled.
38537      */
38538     enable : function(){
38539         this.disabled = false;
38540         this.pnode.removeClass("disabled");
38541     },
38542
38543     /**
38544      * Sets the content for this TabPanelItem.
38545      * @param {String} content The content
38546      * @param {Boolean} loadScripts true to look for and load scripts
38547      */
38548     setContent : function(content, loadScripts){
38549         this.bodyEl.update(content, loadScripts);
38550     },
38551
38552     /**
38553      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38554      * @return {Roo.UpdateManager} The UpdateManager
38555      */
38556     getUpdateManager : function(){
38557         return this.bodyEl.getUpdateManager();
38558     },
38559
38560     /**
38561      * Set a URL to be used to load the content for this TabPanelItem.
38562      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38563      * @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)
38564      * @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)
38565      * @return {Roo.UpdateManager} The UpdateManager
38566      */
38567     setUrl : function(url, params, loadOnce){
38568         if(this.refreshDelegate){
38569             this.un('activate', this.refreshDelegate);
38570         }
38571         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38572         this.on("activate", this.refreshDelegate);
38573         return this.bodyEl.getUpdateManager();
38574     },
38575
38576     /** @private */
38577     _handleRefresh : function(url, params, loadOnce){
38578         if(!loadOnce || !this.loaded){
38579             var updater = this.bodyEl.getUpdateManager();
38580             updater.update(url, params, this._setLoaded.createDelegate(this));
38581         }
38582     },
38583
38584     /**
38585      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38586      *   Will fail silently if the setUrl method has not been called.
38587      *   This does not activate the panel, just updates its content.
38588      */
38589     refresh : function(){
38590         if(this.refreshDelegate){
38591            this.loaded = false;
38592            this.refreshDelegate();
38593         }
38594     },
38595
38596     /** @private */
38597     _setLoaded : function(){
38598         this.loaded = true;
38599     },
38600
38601     /** @private */
38602     closeClick : function(e){
38603         var o = {};
38604         e.stopEvent();
38605         this.fireEvent("beforeclose", this, o);
38606         if(o.cancel !== true){
38607             this.tabPanel.removeTab(this.id);
38608         }
38609     },
38610     /**
38611      * The text displayed in the tooltip for the close icon.
38612      * @type String
38613      */
38614     closeText : "Close this tab"
38615 });
38616 /**
38617 *    This script refer to:
38618 *    Title: International Telephone Input
38619 *    Author: Jack O'Connor
38620 *    Code version:  v12.1.12
38621 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38622 **/
38623
38624 Roo.bootstrap.PhoneInputData = function() {
38625     var d = [
38626       [
38627         "Afghanistan (‫افغانستان‬‎)",
38628         "af",
38629         "93"
38630       ],
38631       [
38632         "Albania (Shqipëri)",
38633         "al",
38634         "355"
38635       ],
38636       [
38637         "Algeria (‫الجزائر‬‎)",
38638         "dz",
38639         "213"
38640       ],
38641       [
38642         "American Samoa",
38643         "as",
38644         "1684"
38645       ],
38646       [
38647         "Andorra",
38648         "ad",
38649         "376"
38650       ],
38651       [
38652         "Angola",
38653         "ao",
38654         "244"
38655       ],
38656       [
38657         "Anguilla",
38658         "ai",
38659         "1264"
38660       ],
38661       [
38662         "Antigua and Barbuda",
38663         "ag",
38664         "1268"
38665       ],
38666       [
38667         "Argentina",
38668         "ar",
38669         "54"
38670       ],
38671       [
38672         "Armenia (Հայաստան)",
38673         "am",
38674         "374"
38675       ],
38676       [
38677         "Aruba",
38678         "aw",
38679         "297"
38680       ],
38681       [
38682         "Australia",
38683         "au",
38684         "61",
38685         0
38686       ],
38687       [
38688         "Austria (Österreich)",
38689         "at",
38690         "43"
38691       ],
38692       [
38693         "Azerbaijan (Azərbaycan)",
38694         "az",
38695         "994"
38696       ],
38697       [
38698         "Bahamas",
38699         "bs",
38700         "1242"
38701       ],
38702       [
38703         "Bahrain (‫البحرين‬‎)",
38704         "bh",
38705         "973"
38706       ],
38707       [
38708         "Bangladesh (বাংলাদেশ)",
38709         "bd",
38710         "880"
38711       ],
38712       [
38713         "Barbados",
38714         "bb",
38715         "1246"
38716       ],
38717       [
38718         "Belarus (Беларусь)",
38719         "by",
38720         "375"
38721       ],
38722       [
38723         "Belgium (België)",
38724         "be",
38725         "32"
38726       ],
38727       [
38728         "Belize",
38729         "bz",
38730         "501"
38731       ],
38732       [
38733         "Benin (Bénin)",
38734         "bj",
38735         "229"
38736       ],
38737       [
38738         "Bermuda",
38739         "bm",
38740         "1441"
38741       ],
38742       [
38743         "Bhutan (འབྲུག)",
38744         "bt",
38745         "975"
38746       ],
38747       [
38748         "Bolivia",
38749         "bo",
38750         "591"
38751       ],
38752       [
38753         "Bosnia and Herzegovina (Босна и Херцеговина)",
38754         "ba",
38755         "387"
38756       ],
38757       [
38758         "Botswana",
38759         "bw",
38760         "267"
38761       ],
38762       [
38763         "Brazil (Brasil)",
38764         "br",
38765         "55"
38766       ],
38767       [
38768         "British Indian Ocean Territory",
38769         "io",
38770         "246"
38771       ],
38772       [
38773         "British Virgin Islands",
38774         "vg",
38775         "1284"
38776       ],
38777       [
38778         "Brunei",
38779         "bn",
38780         "673"
38781       ],
38782       [
38783         "Bulgaria (България)",
38784         "bg",
38785         "359"
38786       ],
38787       [
38788         "Burkina Faso",
38789         "bf",
38790         "226"
38791       ],
38792       [
38793         "Burundi (Uburundi)",
38794         "bi",
38795         "257"
38796       ],
38797       [
38798         "Cambodia (កម្ពុជា)",
38799         "kh",
38800         "855"
38801       ],
38802       [
38803         "Cameroon (Cameroun)",
38804         "cm",
38805         "237"
38806       ],
38807       [
38808         "Canada",
38809         "ca",
38810         "1",
38811         1,
38812         ["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"]
38813       ],
38814       [
38815         "Cape Verde (Kabu Verdi)",
38816         "cv",
38817         "238"
38818       ],
38819       [
38820         "Caribbean Netherlands",
38821         "bq",
38822         "599",
38823         1
38824       ],
38825       [
38826         "Cayman Islands",
38827         "ky",
38828         "1345"
38829       ],
38830       [
38831         "Central African Republic (République centrafricaine)",
38832         "cf",
38833         "236"
38834       ],
38835       [
38836         "Chad (Tchad)",
38837         "td",
38838         "235"
38839       ],
38840       [
38841         "Chile",
38842         "cl",
38843         "56"
38844       ],
38845       [
38846         "China (中国)",
38847         "cn",
38848         "86"
38849       ],
38850       [
38851         "Christmas Island",
38852         "cx",
38853         "61",
38854         2
38855       ],
38856       [
38857         "Cocos (Keeling) Islands",
38858         "cc",
38859         "61",
38860         1
38861       ],
38862       [
38863         "Colombia",
38864         "co",
38865         "57"
38866       ],
38867       [
38868         "Comoros (‫جزر القمر‬‎)",
38869         "km",
38870         "269"
38871       ],
38872       [
38873         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38874         "cd",
38875         "243"
38876       ],
38877       [
38878         "Congo (Republic) (Congo-Brazzaville)",
38879         "cg",
38880         "242"
38881       ],
38882       [
38883         "Cook Islands",
38884         "ck",
38885         "682"
38886       ],
38887       [
38888         "Costa Rica",
38889         "cr",
38890         "506"
38891       ],
38892       [
38893         "Côte d’Ivoire",
38894         "ci",
38895         "225"
38896       ],
38897       [
38898         "Croatia (Hrvatska)",
38899         "hr",
38900         "385"
38901       ],
38902       [
38903         "Cuba",
38904         "cu",
38905         "53"
38906       ],
38907       [
38908         "Curaçao",
38909         "cw",
38910         "599",
38911         0
38912       ],
38913       [
38914         "Cyprus (Κύπρος)",
38915         "cy",
38916         "357"
38917       ],
38918       [
38919         "Czech Republic (Česká republika)",
38920         "cz",
38921         "420"
38922       ],
38923       [
38924         "Denmark (Danmark)",
38925         "dk",
38926         "45"
38927       ],
38928       [
38929         "Djibouti",
38930         "dj",
38931         "253"
38932       ],
38933       [
38934         "Dominica",
38935         "dm",
38936         "1767"
38937       ],
38938       [
38939         "Dominican Republic (República Dominicana)",
38940         "do",
38941         "1",
38942         2,
38943         ["809", "829", "849"]
38944       ],
38945       [
38946         "Ecuador",
38947         "ec",
38948         "593"
38949       ],
38950       [
38951         "Egypt (‫مصر‬‎)",
38952         "eg",
38953         "20"
38954       ],
38955       [
38956         "El Salvador",
38957         "sv",
38958         "503"
38959       ],
38960       [
38961         "Equatorial Guinea (Guinea Ecuatorial)",
38962         "gq",
38963         "240"
38964       ],
38965       [
38966         "Eritrea",
38967         "er",
38968         "291"
38969       ],
38970       [
38971         "Estonia (Eesti)",
38972         "ee",
38973         "372"
38974       ],
38975       [
38976         "Ethiopia",
38977         "et",
38978         "251"
38979       ],
38980       [
38981         "Falkland Islands (Islas Malvinas)",
38982         "fk",
38983         "500"
38984       ],
38985       [
38986         "Faroe Islands (Føroyar)",
38987         "fo",
38988         "298"
38989       ],
38990       [
38991         "Fiji",
38992         "fj",
38993         "679"
38994       ],
38995       [
38996         "Finland (Suomi)",
38997         "fi",
38998         "358",
38999         0
39000       ],
39001       [
39002         "France",
39003         "fr",
39004         "33"
39005       ],
39006       [
39007         "French Guiana (Guyane française)",
39008         "gf",
39009         "594"
39010       ],
39011       [
39012         "French Polynesia (Polynésie française)",
39013         "pf",
39014         "689"
39015       ],
39016       [
39017         "Gabon",
39018         "ga",
39019         "241"
39020       ],
39021       [
39022         "Gambia",
39023         "gm",
39024         "220"
39025       ],
39026       [
39027         "Georgia (საქართველო)",
39028         "ge",
39029         "995"
39030       ],
39031       [
39032         "Germany (Deutschland)",
39033         "de",
39034         "49"
39035       ],
39036       [
39037         "Ghana (Gaana)",
39038         "gh",
39039         "233"
39040       ],
39041       [
39042         "Gibraltar",
39043         "gi",
39044         "350"
39045       ],
39046       [
39047         "Greece (Ελλάδα)",
39048         "gr",
39049         "30"
39050       ],
39051       [
39052         "Greenland (Kalaallit Nunaat)",
39053         "gl",
39054         "299"
39055       ],
39056       [
39057         "Grenada",
39058         "gd",
39059         "1473"
39060       ],
39061       [
39062         "Guadeloupe",
39063         "gp",
39064         "590",
39065         0
39066       ],
39067       [
39068         "Guam",
39069         "gu",
39070         "1671"
39071       ],
39072       [
39073         "Guatemala",
39074         "gt",
39075         "502"
39076       ],
39077       [
39078         "Guernsey",
39079         "gg",
39080         "44",
39081         1
39082       ],
39083       [
39084         "Guinea (Guinée)",
39085         "gn",
39086         "224"
39087       ],
39088       [
39089         "Guinea-Bissau (Guiné Bissau)",
39090         "gw",
39091         "245"
39092       ],
39093       [
39094         "Guyana",
39095         "gy",
39096         "592"
39097       ],
39098       [
39099         "Haiti",
39100         "ht",
39101         "509"
39102       ],
39103       [
39104         "Honduras",
39105         "hn",
39106         "504"
39107       ],
39108       [
39109         "Hong Kong (香港)",
39110         "hk",
39111         "852"
39112       ],
39113       [
39114         "Hungary (Magyarország)",
39115         "hu",
39116         "36"
39117       ],
39118       [
39119         "Iceland (Ísland)",
39120         "is",
39121         "354"
39122       ],
39123       [
39124         "India (भारत)",
39125         "in",
39126         "91"
39127       ],
39128       [
39129         "Indonesia",
39130         "id",
39131         "62"
39132       ],
39133       [
39134         "Iran (‫ایران‬‎)",
39135         "ir",
39136         "98"
39137       ],
39138       [
39139         "Iraq (‫العراق‬‎)",
39140         "iq",
39141         "964"
39142       ],
39143       [
39144         "Ireland",
39145         "ie",
39146         "353"
39147       ],
39148       [
39149         "Isle of Man",
39150         "im",
39151         "44",
39152         2
39153       ],
39154       [
39155         "Israel (‫ישראל‬‎)",
39156         "il",
39157         "972"
39158       ],
39159       [
39160         "Italy (Italia)",
39161         "it",
39162         "39",
39163         0
39164       ],
39165       [
39166         "Jamaica",
39167         "jm",
39168         "1876"
39169       ],
39170       [
39171         "Japan (日本)",
39172         "jp",
39173         "81"
39174       ],
39175       [
39176         "Jersey",
39177         "je",
39178         "44",
39179         3
39180       ],
39181       [
39182         "Jordan (‫الأردن‬‎)",
39183         "jo",
39184         "962"
39185       ],
39186       [
39187         "Kazakhstan (Казахстан)",
39188         "kz",
39189         "7",
39190         1
39191       ],
39192       [
39193         "Kenya",
39194         "ke",
39195         "254"
39196       ],
39197       [
39198         "Kiribati",
39199         "ki",
39200         "686"
39201       ],
39202       [
39203         "Kosovo",
39204         "xk",
39205         "383"
39206       ],
39207       [
39208         "Kuwait (‫الكويت‬‎)",
39209         "kw",
39210         "965"
39211       ],
39212       [
39213         "Kyrgyzstan (Кыргызстан)",
39214         "kg",
39215         "996"
39216       ],
39217       [
39218         "Laos (ລາວ)",
39219         "la",
39220         "856"
39221       ],
39222       [
39223         "Latvia (Latvija)",
39224         "lv",
39225         "371"
39226       ],
39227       [
39228         "Lebanon (‫لبنان‬‎)",
39229         "lb",
39230         "961"
39231       ],
39232       [
39233         "Lesotho",
39234         "ls",
39235         "266"
39236       ],
39237       [
39238         "Liberia",
39239         "lr",
39240         "231"
39241       ],
39242       [
39243         "Libya (‫ليبيا‬‎)",
39244         "ly",
39245         "218"
39246       ],
39247       [
39248         "Liechtenstein",
39249         "li",
39250         "423"
39251       ],
39252       [
39253         "Lithuania (Lietuva)",
39254         "lt",
39255         "370"
39256       ],
39257       [
39258         "Luxembourg",
39259         "lu",
39260         "352"
39261       ],
39262       [
39263         "Macau (澳門)",
39264         "mo",
39265         "853"
39266       ],
39267       [
39268         "Macedonia (FYROM) (Македонија)",
39269         "mk",
39270         "389"
39271       ],
39272       [
39273         "Madagascar (Madagasikara)",
39274         "mg",
39275         "261"
39276       ],
39277       [
39278         "Malawi",
39279         "mw",
39280         "265"
39281       ],
39282       [
39283         "Malaysia",
39284         "my",
39285         "60"
39286       ],
39287       [
39288         "Maldives",
39289         "mv",
39290         "960"
39291       ],
39292       [
39293         "Mali",
39294         "ml",
39295         "223"
39296       ],
39297       [
39298         "Malta",
39299         "mt",
39300         "356"
39301       ],
39302       [
39303         "Marshall Islands",
39304         "mh",
39305         "692"
39306       ],
39307       [
39308         "Martinique",
39309         "mq",
39310         "596"
39311       ],
39312       [
39313         "Mauritania (‫موريتانيا‬‎)",
39314         "mr",
39315         "222"
39316       ],
39317       [
39318         "Mauritius (Moris)",
39319         "mu",
39320         "230"
39321       ],
39322       [
39323         "Mayotte",
39324         "yt",
39325         "262",
39326         1
39327       ],
39328       [
39329         "Mexico (México)",
39330         "mx",
39331         "52"
39332       ],
39333       [
39334         "Micronesia",
39335         "fm",
39336         "691"
39337       ],
39338       [
39339         "Moldova (Republica Moldova)",
39340         "md",
39341         "373"
39342       ],
39343       [
39344         "Monaco",
39345         "mc",
39346         "377"
39347       ],
39348       [
39349         "Mongolia (Монгол)",
39350         "mn",
39351         "976"
39352       ],
39353       [
39354         "Montenegro (Crna Gora)",
39355         "me",
39356         "382"
39357       ],
39358       [
39359         "Montserrat",
39360         "ms",
39361         "1664"
39362       ],
39363       [
39364         "Morocco (‫المغرب‬‎)",
39365         "ma",
39366         "212",
39367         0
39368       ],
39369       [
39370         "Mozambique (Moçambique)",
39371         "mz",
39372         "258"
39373       ],
39374       [
39375         "Myanmar (Burma) (မြန်မာ)",
39376         "mm",
39377         "95"
39378       ],
39379       [
39380         "Namibia (Namibië)",
39381         "na",
39382         "264"
39383       ],
39384       [
39385         "Nauru",
39386         "nr",
39387         "674"
39388       ],
39389       [
39390         "Nepal (नेपाल)",
39391         "np",
39392         "977"
39393       ],
39394       [
39395         "Netherlands (Nederland)",
39396         "nl",
39397         "31"
39398       ],
39399       [
39400         "New Caledonia (Nouvelle-Calédonie)",
39401         "nc",
39402         "687"
39403       ],
39404       [
39405         "New Zealand",
39406         "nz",
39407         "64"
39408       ],
39409       [
39410         "Nicaragua",
39411         "ni",
39412         "505"
39413       ],
39414       [
39415         "Niger (Nijar)",
39416         "ne",
39417         "227"
39418       ],
39419       [
39420         "Nigeria",
39421         "ng",
39422         "234"
39423       ],
39424       [
39425         "Niue",
39426         "nu",
39427         "683"
39428       ],
39429       [
39430         "Norfolk Island",
39431         "nf",
39432         "672"
39433       ],
39434       [
39435         "North Korea (조선 민주주의 인민 공화국)",
39436         "kp",
39437         "850"
39438       ],
39439       [
39440         "Northern Mariana Islands",
39441         "mp",
39442         "1670"
39443       ],
39444       [
39445         "Norway (Norge)",
39446         "no",
39447         "47",
39448         0
39449       ],
39450       [
39451         "Oman (‫عُمان‬‎)",
39452         "om",
39453         "968"
39454       ],
39455       [
39456         "Pakistan (‫پاکستان‬‎)",
39457         "pk",
39458         "92"
39459       ],
39460       [
39461         "Palau",
39462         "pw",
39463         "680"
39464       ],
39465       [
39466         "Palestine (‫فلسطين‬‎)",
39467         "ps",
39468         "970"
39469       ],
39470       [
39471         "Panama (Panamá)",
39472         "pa",
39473         "507"
39474       ],
39475       [
39476         "Papua New Guinea",
39477         "pg",
39478         "675"
39479       ],
39480       [
39481         "Paraguay",
39482         "py",
39483         "595"
39484       ],
39485       [
39486         "Peru (Perú)",
39487         "pe",
39488         "51"
39489       ],
39490       [
39491         "Philippines",
39492         "ph",
39493         "63"
39494       ],
39495       [
39496         "Poland (Polska)",
39497         "pl",
39498         "48"
39499       ],
39500       [
39501         "Portugal",
39502         "pt",
39503         "351"
39504       ],
39505       [
39506         "Puerto Rico",
39507         "pr",
39508         "1",
39509         3,
39510         ["787", "939"]
39511       ],
39512       [
39513         "Qatar (‫قطر‬‎)",
39514         "qa",
39515         "974"
39516       ],
39517       [
39518         "Réunion (La Réunion)",
39519         "re",
39520         "262",
39521         0
39522       ],
39523       [
39524         "Romania (România)",
39525         "ro",
39526         "40"
39527       ],
39528       [
39529         "Russia (Россия)",
39530         "ru",
39531         "7",
39532         0
39533       ],
39534       [
39535         "Rwanda",
39536         "rw",
39537         "250"
39538       ],
39539       [
39540         "Saint Barthélemy",
39541         "bl",
39542         "590",
39543         1
39544       ],
39545       [
39546         "Saint Helena",
39547         "sh",
39548         "290"
39549       ],
39550       [
39551         "Saint Kitts and Nevis",
39552         "kn",
39553         "1869"
39554       ],
39555       [
39556         "Saint Lucia",
39557         "lc",
39558         "1758"
39559       ],
39560       [
39561         "Saint Martin (Saint-Martin (partie française))",
39562         "mf",
39563         "590",
39564         2
39565       ],
39566       [
39567         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39568         "pm",
39569         "508"
39570       ],
39571       [
39572         "Saint Vincent and the Grenadines",
39573         "vc",
39574         "1784"
39575       ],
39576       [
39577         "Samoa",
39578         "ws",
39579         "685"
39580       ],
39581       [
39582         "San Marino",
39583         "sm",
39584         "378"
39585       ],
39586       [
39587         "São Tomé and Príncipe (São Tomé e Príncipe)",
39588         "st",
39589         "239"
39590       ],
39591       [
39592         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39593         "sa",
39594         "966"
39595       ],
39596       [
39597         "Senegal (Sénégal)",
39598         "sn",
39599         "221"
39600       ],
39601       [
39602         "Serbia (Србија)",
39603         "rs",
39604         "381"
39605       ],
39606       [
39607         "Seychelles",
39608         "sc",
39609         "248"
39610       ],
39611       [
39612         "Sierra Leone",
39613         "sl",
39614         "232"
39615       ],
39616       [
39617         "Singapore",
39618         "sg",
39619         "65"
39620       ],
39621       [
39622         "Sint Maarten",
39623         "sx",
39624         "1721"
39625       ],
39626       [
39627         "Slovakia (Slovensko)",
39628         "sk",
39629         "421"
39630       ],
39631       [
39632         "Slovenia (Slovenija)",
39633         "si",
39634         "386"
39635       ],
39636       [
39637         "Solomon Islands",
39638         "sb",
39639         "677"
39640       ],
39641       [
39642         "Somalia (Soomaaliya)",
39643         "so",
39644         "252"
39645       ],
39646       [
39647         "South Africa",
39648         "za",
39649         "27"
39650       ],
39651       [
39652         "South Korea (대한민국)",
39653         "kr",
39654         "82"
39655       ],
39656       [
39657         "South Sudan (‫جنوب السودان‬‎)",
39658         "ss",
39659         "211"
39660       ],
39661       [
39662         "Spain (España)",
39663         "es",
39664         "34"
39665       ],
39666       [
39667         "Sri Lanka (ශ්‍රී ලංකාව)",
39668         "lk",
39669         "94"
39670       ],
39671       [
39672         "Sudan (‫السودان‬‎)",
39673         "sd",
39674         "249"
39675       ],
39676       [
39677         "Suriname",
39678         "sr",
39679         "597"
39680       ],
39681       [
39682         "Svalbard and Jan Mayen",
39683         "sj",
39684         "47",
39685         1
39686       ],
39687       [
39688         "Swaziland",
39689         "sz",
39690         "268"
39691       ],
39692       [
39693         "Sweden (Sverige)",
39694         "se",
39695         "46"
39696       ],
39697       [
39698         "Switzerland (Schweiz)",
39699         "ch",
39700         "41"
39701       ],
39702       [
39703         "Syria (‫سوريا‬‎)",
39704         "sy",
39705         "963"
39706       ],
39707       [
39708         "Taiwan (台灣)",
39709         "tw",
39710         "886"
39711       ],
39712       [
39713         "Tajikistan",
39714         "tj",
39715         "992"
39716       ],
39717       [
39718         "Tanzania",
39719         "tz",
39720         "255"
39721       ],
39722       [
39723         "Thailand (ไทย)",
39724         "th",
39725         "66"
39726       ],
39727       [
39728         "Timor-Leste",
39729         "tl",
39730         "670"
39731       ],
39732       [
39733         "Togo",
39734         "tg",
39735         "228"
39736       ],
39737       [
39738         "Tokelau",
39739         "tk",
39740         "690"
39741       ],
39742       [
39743         "Tonga",
39744         "to",
39745         "676"
39746       ],
39747       [
39748         "Trinidad and Tobago",
39749         "tt",
39750         "1868"
39751       ],
39752       [
39753         "Tunisia (‫تونس‬‎)",
39754         "tn",
39755         "216"
39756       ],
39757       [
39758         "Turkey (Türkiye)",
39759         "tr",
39760         "90"
39761       ],
39762       [
39763         "Turkmenistan",
39764         "tm",
39765         "993"
39766       ],
39767       [
39768         "Turks and Caicos Islands",
39769         "tc",
39770         "1649"
39771       ],
39772       [
39773         "Tuvalu",
39774         "tv",
39775         "688"
39776       ],
39777       [
39778         "U.S. Virgin Islands",
39779         "vi",
39780         "1340"
39781       ],
39782       [
39783         "Uganda",
39784         "ug",
39785         "256"
39786       ],
39787       [
39788         "Ukraine (Україна)",
39789         "ua",
39790         "380"
39791       ],
39792       [
39793         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39794         "ae",
39795         "971"
39796       ],
39797       [
39798         "United Kingdom",
39799         "gb",
39800         "44",
39801         0
39802       ],
39803       [
39804         "United States",
39805         "us",
39806         "1",
39807         0
39808       ],
39809       [
39810         "Uruguay",
39811         "uy",
39812         "598"
39813       ],
39814       [
39815         "Uzbekistan (Oʻzbekiston)",
39816         "uz",
39817         "998"
39818       ],
39819       [
39820         "Vanuatu",
39821         "vu",
39822         "678"
39823       ],
39824       [
39825         "Vatican City (Città del Vaticano)",
39826         "va",
39827         "39",
39828         1
39829       ],
39830       [
39831         "Venezuela",
39832         "ve",
39833         "58"
39834       ],
39835       [
39836         "Vietnam (Việt Nam)",
39837         "vn",
39838         "84"
39839       ],
39840       [
39841         "Wallis and Futuna (Wallis-et-Futuna)",
39842         "wf",
39843         "681"
39844       ],
39845       [
39846         "Western Sahara (‫الصحراء الغربية‬‎)",
39847         "eh",
39848         "212",
39849         1
39850       ],
39851       [
39852         "Yemen (‫اليمن‬‎)",
39853         "ye",
39854         "967"
39855       ],
39856       [
39857         "Zambia",
39858         "zm",
39859         "260"
39860       ],
39861       [
39862         "Zimbabwe",
39863         "zw",
39864         "263"
39865       ],
39866       [
39867         "Åland Islands",
39868         "ax",
39869         "358",
39870         1
39871       ]
39872   ];
39873   
39874   return d;
39875 }/**
39876 *    This script refer to:
39877 *    Title: International Telephone Input
39878 *    Author: Jack O'Connor
39879 *    Code version:  v12.1.12
39880 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39881 **/
39882
39883 /**
39884  * @class Roo.bootstrap.PhoneInput
39885  * @extends Roo.bootstrap.TriggerField
39886  * An input with International dial-code selection
39887  
39888  * @cfg {String} defaultDialCode default '+852'
39889  * @cfg {Array} preferedCountries default []
39890   
39891  * @constructor
39892  * Create a new PhoneInput.
39893  * @param {Object} config Configuration options
39894  */
39895
39896 Roo.bootstrap.PhoneInput = function(config) {
39897     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39898 };
39899
39900 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39901         
39902         listWidth: undefined,
39903         
39904         selectedClass: 'active',
39905         
39906         invalidClass : "has-warning",
39907         
39908         validClass: 'has-success',
39909         
39910         allowed: '0123456789',
39911         
39912         max_length: 15,
39913         
39914         /**
39915          * @cfg {String} defaultDialCode The default dial code when initializing the input
39916          */
39917         defaultDialCode: '+852',
39918         
39919         /**
39920          * @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
39921          */
39922         preferedCountries: false,
39923         
39924         getAutoCreate : function()
39925         {
39926             var data = Roo.bootstrap.PhoneInputData();
39927             var align = this.labelAlign || this.parentLabelAlign();
39928             var id = Roo.id();
39929             
39930             this.allCountries = [];
39931             this.dialCodeMapping = [];
39932             
39933             for (var i = 0; i < data.length; i++) {
39934               var c = data[i];
39935               this.allCountries[i] = {
39936                 name: c[0],
39937                 iso2: c[1],
39938                 dialCode: c[2],
39939                 priority: c[3] || 0,
39940                 areaCodes: c[4] || null
39941               };
39942               this.dialCodeMapping[c[2]] = {
39943                   name: c[0],
39944                   iso2: c[1],
39945                   priority: c[3] || 0,
39946                   areaCodes: c[4] || null
39947               };
39948             }
39949             
39950             var cfg = {
39951                 cls: 'form-group',
39952                 cn: []
39953             };
39954             
39955             var input =  {
39956                 tag: 'input',
39957                 id : id,
39958                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39959                 maxlength: this.max_length,
39960                 cls : 'form-control tel-input',
39961                 autocomplete: 'new-password'
39962             };
39963             
39964             var hiddenInput = {
39965                 tag: 'input',
39966                 type: 'hidden',
39967                 cls: 'hidden-tel-input'
39968             };
39969             
39970             if (this.name) {
39971                 hiddenInput.name = this.name;
39972             }
39973             
39974             if (this.disabled) {
39975                 input.disabled = true;
39976             }
39977             
39978             var flag_container = {
39979                 tag: 'div',
39980                 cls: 'flag-box',
39981                 cn: [
39982                     {
39983                         tag: 'div',
39984                         cls: 'flag'
39985                     },
39986                     {
39987                         tag: 'div',
39988                         cls: 'caret'
39989                     }
39990                 ]
39991             };
39992             
39993             var box = {
39994                 tag: 'div',
39995                 cls: this.hasFeedback ? 'has-feedback' : '',
39996                 cn: [
39997                     hiddenInput,
39998                     input,
39999                     {
40000                         tag: 'input',
40001                         cls: 'dial-code-holder',
40002                         disabled: true
40003                     }
40004                 ]
40005             };
40006             
40007             var container = {
40008                 cls: 'roo-select2-container input-group',
40009                 cn: [
40010                     flag_container,
40011                     box
40012                 ]
40013             };
40014             
40015             if (this.fieldLabel.length) {
40016                 var indicator = {
40017                     tag: 'i',
40018                     tooltip: 'This field is required'
40019                 };
40020                 
40021                 var label = {
40022                     tag: 'label',
40023                     'for':  id,
40024                     cls: 'control-label',
40025                     cn: []
40026                 };
40027                 
40028                 var label_text = {
40029                     tag: 'span',
40030                     html: this.fieldLabel
40031                 };
40032                 
40033                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40034                 label.cn = [
40035                     indicator,
40036                     label_text
40037                 ];
40038                 
40039                 if(this.indicatorpos == 'right') {
40040                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40041                     label.cn = [
40042                         label_text,
40043                         indicator
40044                     ];
40045                 }
40046                 
40047                 if(align == 'left') {
40048                     container = {
40049                         tag: 'div',
40050                         cn: [
40051                             container
40052                         ]
40053                     };
40054                     
40055                     if(this.labelWidth > 12){
40056                         label.style = "width: " + this.labelWidth + 'px';
40057                     }
40058                     if(this.labelWidth < 13 && this.labelmd == 0){
40059                         this.labelmd = this.labelWidth;
40060                     }
40061                     if(this.labellg > 0){
40062                         label.cls += ' col-lg-' + this.labellg;
40063                         input.cls += ' col-lg-' + (12 - this.labellg);
40064                     }
40065                     if(this.labelmd > 0){
40066                         label.cls += ' col-md-' + this.labelmd;
40067                         container.cls += ' col-md-' + (12 - this.labelmd);
40068                     }
40069                     if(this.labelsm > 0){
40070                         label.cls += ' col-sm-' + this.labelsm;
40071                         container.cls += ' col-sm-' + (12 - this.labelsm);
40072                     }
40073                     if(this.labelxs > 0){
40074                         label.cls += ' col-xs-' + this.labelxs;
40075                         container.cls += ' col-xs-' + (12 - this.labelxs);
40076                     }
40077                 }
40078             }
40079             
40080             cfg.cn = [
40081                 label,
40082                 container
40083             ];
40084             
40085             var settings = this;
40086             
40087             ['xs','sm','md','lg'].map(function(size){
40088                 if (settings[size]) {
40089                     cfg.cls += ' col-' + size + '-' + settings[size];
40090                 }
40091             });
40092             
40093             this.store = new Roo.data.Store({
40094                 proxy : new Roo.data.MemoryProxy({}),
40095                 reader : new Roo.data.JsonReader({
40096                     fields : [
40097                         {
40098                             'name' : 'name',
40099                             'type' : 'string'
40100                         },
40101                         {
40102                             'name' : 'iso2',
40103                             'type' : 'string'
40104                         },
40105                         {
40106                             'name' : 'dialCode',
40107                             'type' : 'string'
40108                         },
40109                         {
40110                             'name' : 'priority',
40111                             'type' : 'string'
40112                         },
40113                         {
40114                             'name' : 'areaCodes',
40115                             'type' : 'string'
40116                         }
40117                     ]
40118                 })
40119             });
40120             
40121             if(!this.preferedCountries) {
40122                 this.preferedCountries = [
40123                     'hk',
40124                     'gb',
40125                     'us'
40126                 ];
40127             }
40128             
40129             var p = this.preferedCountries.reverse();
40130             
40131             if(p) {
40132                 for (var i = 0; i < p.length; i++) {
40133                     for (var j = 0; j < this.allCountries.length; j++) {
40134                         if(this.allCountries[j].iso2 == p[i]) {
40135                             var t = this.allCountries[j];
40136                             this.allCountries.splice(j,1);
40137                             this.allCountries.unshift(t);
40138                         }
40139                     } 
40140                 }
40141             }
40142             
40143             this.store.proxy.data = {
40144                 success: true,
40145                 data: this.allCountries
40146             };
40147             
40148             return cfg;
40149         },
40150         
40151         initEvents : function()
40152         {
40153             this.createList();
40154             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40155             
40156             this.indicator = this.indicatorEl();
40157             this.flag = this.flagEl();
40158             this.dialCodeHolder = this.dialCodeHolderEl();
40159             
40160             this.trigger = this.el.select('div.flag-box',true).first();
40161             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40162             
40163             var _this = this;
40164             
40165             (function(){
40166                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40167                 _this.list.setWidth(lw);
40168             }).defer(100);
40169             
40170             this.list.on('mouseover', this.onViewOver, this);
40171             this.list.on('mousemove', this.onViewMove, this);
40172             this.inputEl().on("keyup", this.onKeyUp, this);
40173             this.inputEl().on("keypress", this.onKeyPress, this);
40174             
40175             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40176
40177             this.view = new Roo.View(this.list, this.tpl, {
40178                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40179             });
40180             
40181             this.view.on('click', this.onViewClick, this);
40182             this.setValue(this.defaultDialCode);
40183         },
40184         
40185         onTriggerClick : function(e)
40186         {
40187             Roo.log('trigger click');
40188             if(this.disabled){
40189                 return;
40190             }
40191             
40192             if(this.isExpanded()){
40193                 this.collapse();
40194                 this.hasFocus = false;
40195             }else {
40196                 this.store.load({});
40197                 this.hasFocus = true;
40198                 this.expand();
40199             }
40200         },
40201         
40202         isExpanded : function()
40203         {
40204             return this.list.isVisible();
40205         },
40206         
40207         collapse : function()
40208         {
40209             if(!this.isExpanded()){
40210                 return;
40211             }
40212             this.list.hide();
40213             Roo.get(document).un('mousedown', this.collapseIf, this);
40214             Roo.get(document).un('mousewheel', this.collapseIf, this);
40215             this.fireEvent('collapse', this);
40216             this.validate();
40217         },
40218         
40219         expand : function()
40220         {
40221             Roo.log('expand');
40222
40223             if(this.isExpanded() || !this.hasFocus){
40224                 return;
40225             }
40226             
40227             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40228             this.list.setWidth(lw);
40229             
40230             this.list.show();
40231             this.restrictHeight();
40232             
40233             Roo.get(document).on('mousedown', this.collapseIf, this);
40234             Roo.get(document).on('mousewheel', this.collapseIf, this);
40235             
40236             this.fireEvent('expand', this);
40237         },
40238         
40239         restrictHeight : function()
40240         {
40241             this.list.alignTo(this.inputEl(), this.listAlign);
40242             this.list.alignTo(this.inputEl(), this.listAlign);
40243         },
40244         
40245         onViewOver : function(e, t)
40246         {
40247             if(this.inKeyMode){
40248                 return;
40249             }
40250             var item = this.view.findItemFromChild(t);
40251             
40252             if(item){
40253                 var index = this.view.indexOf(item);
40254                 this.select(index, false);
40255             }
40256         },
40257
40258         // private
40259         onViewClick : function(view, doFocus, el, e)
40260         {
40261             var index = this.view.getSelectedIndexes()[0];
40262             
40263             var r = this.store.getAt(index);
40264             
40265             if(r){
40266                 this.onSelect(r, index);
40267             }
40268             if(doFocus !== false && !this.blockFocus){
40269                 this.inputEl().focus();
40270             }
40271         },
40272         
40273         onViewMove : function(e, t)
40274         {
40275             this.inKeyMode = false;
40276         },
40277         
40278         select : function(index, scrollIntoView)
40279         {
40280             this.selectedIndex = index;
40281             this.view.select(index);
40282             if(scrollIntoView !== false){
40283                 var el = this.view.getNode(index);
40284                 if(el){
40285                     this.list.scrollChildIntoView(el, false);
40286                 }
40287             }
40288         },
40289         
40290         createList : function()
40291         {
40292             this.list = Roo.get(document.body).createChild({
40293                 tag: 'ul',
40294                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40295                 style: 'display:none'
40296             });
40297             
40298             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40299         },
40300         
40301         collapseIf : function(e)
40302         {
40303             var in_combo  = e.within(this.el);
40304             var in_list =  e.within(this.list);
40305             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40306             
40307             if (in_combo || in_list || is_list) {
40308                 return;
40309             }
40310             this.collapse();
40311         },
40312         
40313         onSelect : function(record, index)
40314         {
40315             if(this.fireEvent('beforeselect', this, record, index) !== false){
40316                 
40317                 this.setFlagClass(record.data.iso2);
40318                 this.setDialCode(record.data.dialCode);
40319                 this.hasFocus = false;
40320                 this.collapse();
40321                 this.fireEvent('select', this, record, index);
40322             }
40323         },
40324         
40325         flagEl : function()
40326         {
40327             var flag = this.el.select('div.flag',true).first();
40328             if(!flag){
40329                 return false;
40330             }
40331             return flag;
40332         },
40333         
40334         dialCodeHolderEl : function()
40335         {
40336             var d = this.el.select('input.dial-code-holder',true).first();
40337             if(!d){
40338                 return false;
40339             }
40340             return d;
40341         },
40342         
40343         setDialCode : function(v)
40344         {
40345             this.dialCodeHolder.dom.value = '+'+v;
40346         },
40347         
40348         setFlagClass : function(n)
40349         {
40350             this.flag.dom.className = 'flag '+n;
40351         },
40352         
40353         getValue : function()
40354         {
40355             var v = this.inputEl().getValue();
40356             if(this.dialCodeHolder) {
40357                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40358             }
40359             return v;
40360         },
40361         
40362         setValue : function(v)
40363         {
40364             var d = this.getDialCode(v);
40365             
40366             //invalid dial code
40367             if(v.length == 0 || !d || d.length == 0) {
40368                 if(this.rendered){
40369                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40370                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40371                 }
40372                 return;
40373             }
40374             
40375             //valid dial code
40376             this.setFlagClass(this.dialCodeMapping[d].iso2);
40377             this.setDialCode(d);
40378             this.inputEl().dom.value = v.replace('+'+d,'');
40379             this.hiddenEl().dom.value = this.getValue();
40380             
40381             this.validate();
40382         },
40383         
40384         getDialCode : function(v)
40385         {
40386             v = v ||  '';
40387             
40388             if (v.length == 0) {
40389                 return this.dialCodeHolder.dom.value;
40390             }
40391             
40392             var dialCode = "";
40393             if (v.charAt(0) != "+") {
40394                 return false;
40395             }
40396             var numericChars = "";
40397             for (var i = 1; i < v.length; i++) {
40398               var c = v.charAt(i);
40399               if (!isNaN(c)) {
40400                 numericChars += c;
40401                 if (this.dialCodeMapping[numericChars]) {
40402                   dialCode = v.substr(1, i);
40403                 }
40404                 if (numericChars.length == 4) {
40405                   break;
40406                 }
40407               }
40408             }
40409             return dialCode;
40410         },
40411         
40412         reset : function()
40413         {
40414             this.setValue(this.defaultDialCode);
40415             this.validate();
40416         },
40417         
40418         hiddenEl : function()
40419         {
40420             return this.el.select('input.hidden-tel-input',true).first();
40421         },
40422         
40423         // after setting val
40424         onKeyUp : function(e){
40425             this.setValue(this.getValue());
40426         },
40427         
40428         onKeyPress : function(e){
40429             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40430                 e.stopEvent();
40431             }
40432         }
40433         
40434 });
40435 /**
40436  * @class Roo.bootstrap.MoneyField
40437  * @extends Roo.bootstrap.ComboBox
40438  * Bootstrap MoneyField class
40439  * 
40440  * @constructor
40441  * Create a new MoneyField.
40442  * @param {Object} config Configuration options
40443  */
40444
40445 Roo.bootstrap.MoneyField = function(config) {
40446     
40447     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40448     
40449 };
40450
40451 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40452     
40453     /**
40454      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40455      */
40456     allowDecimals : true,
40457     /**
40458      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40459      */
40460     decimalSeparator : ".",
40461     /**
40462      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40463      */
40464     decimalPrecision : 0,
40465     /**
40466      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40467      */
40468     allowNegative : true,
40469     /**
40470      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40471      */
40472     allowZero: true,
40473     /**
40474      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40475      */
40476     minValue : Number.NEGATIVE_INFINITY,
40477     /**
40478      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40479      */
40480     maxValue : Number.MAX_VALUE,
40481     /**
40482      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40483      */
40484     minText : "The minimum value for this field is {0}",
40485     /**
40486      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40487      */
40488     maxText : "The maximum value for this field is {0}",
40489     /**
40490      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40491      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40492      */
40493     nanText : "{0} is not a valid number",
40494     /**
40495      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40496      */
40497     castInt : true,
40498     /**
40499      * @cfg {String} defaults currency of the MoneyField
40500      * value should be in lkey
40501      */
40502     defaultCurrency : false,
40503     /**
40504      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40505      */
40506     thousandsDelimiter : false,
40507     /**
40508      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40509      */
40510     max_length: false,
40511     
40512     inputlg : 9,
40513     inputmd : 9,
40514     inputsm : 9,
40515     inputxs : 6,
40516     
40517     store : false,
40518     
40519     getAutoCreate : function()
40520     {
40521         var align = this.labelAlign || this.parentLabelAlign();
40522         
40523         var id = Roo.id();
40524
40525         var cfg = {
40526             cls: 'form-group',
40527             cn: []
40528         };
40529
40530         var input =  {
40531             tag: 'input',
40532             id : id,
40533             cls : 'form-control roo-money-amount-input',
40534             autocomplete: 'new-password'
40535         };
40536         
40537         var hiddenInput = {
40538             tag: 'input',
40539             type: 'hidden',
40540             id: Roo.id(),
40541             cls: 'hidden-number-input'
40542         };
40543         
40544         if(this.max_length) {
40545             input.maxlength = this.max_length; 
40546         }
40547         
40548         if (this.name) {
40549             hiddenInput.name = this.name;
40550         }
40551
40552         if (this.disabled) {
40553             input.disabled = true;
40554         }
40555
40556         var clg = 12 - this.inputlg;
40557         var cmd = 12 - this.inputmd;
40558         var csm = 12 - this.inputsm;
40559         var cxs = 12 - this.inputxs;
40560         
40561         var container = {
40562             tag : 'div',
40563             cls : 'row roo-money-field',
40564             cn : [
40565                 {
40566                     tag : 'div',
40567                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40568                     cn : [
40569                         {
40570                             tag : 'div',
40571                             cls: 'roo-select2-container input-group',
40572                             cn: [
40573                                 {
40574                                     tag : 'input',
40575                                     cls : 'form-control roo-money-currency-input',
40576                                     autocomplete: 'new-password',
40577                                     readOnly : 1,
40578                                     name : this.currencyName
40579                                 },
40580                                 {
40581                                     tag :'span',
40582                                     cls : 'input-group-addon',
40583                                     cn : [
40584                                         {
40585                                             tag: 'span',
40586                                             cls: 'caret'
40587                                         }
40588                                     ]
40589                                 }
40590                             ]
40591                         }
40592                     ]
40593                 },
40594                 {
40595                     tag : 'div',
40596                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40597                     cn : [
40598                         {
40599                             tag: 'div',
40600                             cls: this.hasFeedback ? 'has-feedback' : '',
40601                             cn: [
40602                                 input
40603                             ]
40604                         }
40605                     ]
40606                 }
40607             ]
40608             
40609         };
40610         
40611         if (this.fieldLabel.length) {
40612             var indicator = {
40613                 tag: 'i',
40614                 tooltip: 'This field is required'
40615             };
40616
40617             var label = {
40618                 tag: 'label',
40619                 'for':  id,
40620                 cls: 'control-label',
40621                 cn: []
40622             };
40623
40624             var label_text = {
40625                 tag: 'span',
40626                 html: this.fieldLabel
40627             };
40628
40629             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40630             label.cn = [
40631                 indicator,
40632                 label_text
40633             ];
40634
40635             if(this.indicatorpos == 'right') {
40636                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40637                 label.cn = [
40638                     label_text,
40639                     indicator
40640                 ];
40641             }
40642
40643             if(align == 'left') {
40644                 container = {
40645                     tag: 'div',
40646                     cn: [
40647                         container
40648                     ]
40649                 };
40650
40651                 if(this.labelWidth > 12){
40652                     label.style = "width: " + this.labelWidth + 'px';
40653                 }
40654                 if(this.labelWidth < 13 && this.labelmd == 0){
40655                     this.labelmd = this.labelWidth;
40656                 }
40657                 if(this.labellg > 0){
40658                     label.cls += ' col-lg-' + this.labellg;
40659                     input.cls += ' col-lg-' + (12 - this.labellg);
40660                 }
40661                 if(this.labelmd > 0){
40662                     label.cls += ' col-md-' + this.labelmd;
40663                     container.cls += ' col-md-' + (12 - this.labelmd);
40664                 }
40665                 if(this.labelsm > 0){
40666                     label.cls += ' col-sm-' + this.labelsm;
40667                     container.cls += ' col-sm-' + (12 - this.labelsm);
40668                 }
40669                 if(this.labelxs > 0){
40670                     label.cls += ' col-xs-' + this.labelxs;
40671                     container.cls += ' col-xs-' + (12 - this.labelxs);
40672                 }
40673             }
40674         }
40675
40676         cfg.cn = [
40677             label,
40678             container,
40679             hiddenInput
40680         ];
40681         
40682         var settings = this;
40683
40684         ['xs','sm','md','lg'].map(function(size){
40685             if (settings[size]) {
40686                 cfg.cls += ' col-' + size + '-' + settings[size];
40687             }
40688         });
40689         
40690         return cfg;
40691     },
40692     
40693     initEvents : function()
40694     {
40695         this.indicator = this.indicatorEl();
40696         
40697         this.initCurrencyEvent();
40698         
40699         this.initNumberEvent();
40700     },
40701     
40702     initCurrencyEvent : function()
40703     {
40704         if (!this.store) {
40705             throw "can not find store for combo";
40706         }
40707         
40708         this.store = Roo.factory(this.store, Roo.data);
40709         this.store.parent = this;
40710         
40711         this.createList();
40712         
40713         this.triggerEl = this.el.select('.input-group-addon', true).first();
40714         
40715         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40716         
40717         var _this = this;
40718         
40719         (function(){
40720             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40721             _this.list.setWidth(lw);
40722         }).defer(100);
40723         
40724         this.list.on('mouseover', this.onViewOver, this);
40725         this.list.on('mousemove', this.onViewMove, this);
40726         this.list.on('scroll', this.onViewScroll, this);
40727         
40728         if(!this.tpl){
40729             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40730         }
40731         
40732         this.view = new Roo.View(this.list, this.tpl, {
40733             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40734         });
40735         
40736         this.view.on('click', this.onViewClick, this);
40737         
40738         this.store.on('beforeload', this.onBeforeLoad, this);
40739         this.store.on('load', this.onLoad, this);
40740         this.store.on('loadexception', this.onLoadException, this);
40741         
40742         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40743             "up" : function(e){
40744                 this.inKeyMode = true;
40745                 this.selectPrev();
40746             },
40747
40748             "down" : function(e){
40749                 if(!this.isExpanded()){
40750                     this.onTriggerClick();
40751                 }else{
40752                     this.inKeyMode = true;
40753                     this.selectNext();
40754                 }
40755             },
40756
40757             "enter" : function(e){
40758                 this.collapse();
40759                 
40760                 if(this.fireEvent("specialkey", this, e)){
40761                     this.onViewClick(false);
40762                 }
40763                 
40764                 return true;
40765             },
40766
40767             "esc" : function(e){
40768                 this.collapse();
40769             },
40770
40771             "tab" : function(e){
40772                 this.collapse();
40773                 
40774                 if(this.fireEvent("specialkey", this, e)){
40775                     this.onViewClick(false);
40776                 }
40777                 
40778                 return true;
40779             },
40780
40781             scope : this,
40782
40783             doRelay : function(foo, bar, hname){
40784                 if(hname == 'down' || this.scope.isExpanded()){
40785                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40786                 }
40787                 return true;
40788             },
40789
40790             forceKeyDown: true
40791         });
40792         
40793         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40794         
40795     },
40796     
40797     initNumberEvent : function(e)
40798     {
40799         this.inputEl().on("keydown" , this.fireKey,  this);
40800         this.inputEl().on("focus", this.onFocus,  this);
40801         this.inputEl().on("blur", this.onBlur,  this);
40802         
40803         this.inputEl().relayEvent('keyup', this);
40804         
40805         if(this.indicator){
40806             this.indicator.addClass('invisible');
40807         }
40808  
40809         this.originalValue = this.getValue();
40810         
40811         if(this.validationEvent == 'keyup'){
40812             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40813             this.inputEl().on('keyup', this.filterValidation, this);
40814         }
40815         else if(this.validationEvent !== false){
40816             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40817         }
40818         
40819         if(this.selectOnFocus){
40820             this.on("focus", this.preFocus, this);
40821             
40822         }
40823         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40824             this.inputEl().on("keypress", this.filterKeys, this);
40825         } else {
40826             this.inputEl().relayEvent('keypress', this);
40827         }
40828         
40829         var allowed = "0123456789";
40830         
40831         if(this.allowDecimals){
40832             allowed += this.decimalSeparator;
40833         }
40834         
40835         if(this.allowNegative){
40836             allowed += "-";
40837         }
40838         
40839         if(this.thousandsDelimiter) {
40840             allowed += ",";
40841         }
40842         
40843         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40844         
40845         var keyPress = function(e){
40846             
40847             var k = e.getKey();
40848             
40849             var c = e.getCharCode();
40850             
40851             if(
40852                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40853                     allowed.indexOf(String.fromCharCode(c)) === -1
40854             ){
40855                 e.stopEvent();
40856                 return;
40857             }
40858             
40859             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40860                 return;
40861             }
40862             
40863             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40864                 e.stopEvent();
40865             }
40866         };
40867         
40868         this.inputEl().on("keypress", keyPress, this);
40869         
40870     },
40871     
40872     onTriggerClick : function(e)
40873     {   
40874         if(this.disabled){
40875             return;
40876         }
40877         
40878         this.page = 0;
40879         this.loadNext = false;
40880         
40881         if(this.isExpanded()){
40882             this.collapse();
40883             return;
40884         }
40885         
40886         this.hasFocus = true;
40887         
40888         if(this.triggerAction == 'all') {
40889             this.doQuery(this.allQuery, true);
40890             return;
40891         }
40892         
40893         this.doQuery(this.getRawValue());
40894     },
40895     
40896     getCurrency : function()
40897     {   
40898         var v = this.currencyEl().getValue();
40899         
40900         return v;
40901     },
40902     
40903     restrictHeight : function()
40904     {
40905         this.list.alignTo(this.currencyEl(), this.listAlign);
40906         this.list.alignTo(this.currencyEl(), this.listAlign);
40907     },
40908     
40909     onViewClick : function(view, doFocus, el, e)
40910     {
40911         var index = this.view.getSelectedIndexes()[0];
40912         
40913         var r = this.store.getAt(index);
40914         
40915         if(r){
40916             this.onSelect(r, index);
40917         }
40918     },
40919     
40920     onSelect : function(record, index){
40921         
40922         if(this.fireEvent('beforeselect', this, record, index) !== false){
40923         
40924             this.setFromCurrencyData(index > -1 ? record.data : false);
40925             
40926             this.collapse();
40927             
40928             this.fireEvent('select', this, record, index);
40929         }
40930     },
40931     
40932     setFromCurrencyData : function(o)
40933     {
40934         var currency = '';
40935         
40936         this.lastCurrency = o;
40937         
40938         if (this.currencyField) {
40939             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40940         } else {
40941             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40942         }
40943         
40944         this.lastSelectionText = currency;
40945         
40946         //setting default currency
40947         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40948             this.setCurrency(this.defaultCurrency);
40949             return;
40950         }
40951         
40952         this.setCurrency(currency);
40953     },
40954     
40955     setFromData : function(o)
40956     {
40957         var c = {};
40958         
40959         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40960         
40961         this.setFromCurrencyData(c);
40962         
40963         var value = '';
40964         
40965         if (this.name) {
40966             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40967         } else {
40968             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40969         }
40970         
40971         this.setValue(value);
40972         
40973     },
40974     
40975     setCurrency : function(v)
40976     {   
40977         this.currencyValue = v;
40978         
40979         if(this.rendered){
40980             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40981             this.validate();
40982         }
40983     },
40984     
40985     setValue : function(v)
40986     {
40987         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40988         
40989         this.value = v;
40990         
40991         if(this.rendered){
40992             
40993             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40994             
40995             this.inputEl().dom.value = (v == '') ? '' :
40996                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40997             
40998             if(!this.allowZero && v === '0') {
40999                 this.hiddenEl().dom.value = '';
41000                 this.inputEl().dom.value = '';
41001             }
41002             
41003             this.validate();
41004         }
41005     },
41006     
41007     getRawValue : function()
41008     {
41009         var v = this.inputEl().getValue();
41010         
41011         return v;
41012     },
41013     
41014     getValue : function()
41015     {
41016         return this.fixPrecision(this.parseValue(this.getRawValue()));
41017     },
41018     
41019     parseValue : function(value)
41020     {
41021         if(this.thousandsDelimiter) {
41022             value += "";
41023             r = new RegExp(",", "g");
41024             value = value.replace(r, "");
41025         }
41026         
41027         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41028         return isNaN(value) ? '' : value;
41029         
41030     },
41031     
41032     fixPrecision : function(value)
41033     {
41034         if(this.thousandsDelimiter) {
41035             value += "";
41036             r = new RegExp(",", "g");
41037             value = value.replace(r, "");
41038         }
41039         
41040         var nan = isNaN(value);
41041         
41042         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41043             return nan ? '' : value;
41044         }
41045         return parseFloat(value).toFixed(this.decimalPrecision);
41046     },
41047     
41048     decimalPrecisionFcn : function(v)
41049     {
41050         return Math.floor(v);
41051     },
41052     
41053     validateValue : function(value)
41054     {
41055         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41056             return false;
41057         }
41058         
41059         var num = this.parseValue(value);
41060         
41061         if(isNaN(num)){
41062             this.markInvalid(String.format(this.nanText, value));
41063             return false;
41064         }
41065         
41066         if(num < this.minValue){
41067             this.markInvalid(String.format(this.minText, this.minValue));
41068             return false;
41069         }
41070         
41071         if(num > this.maxValue){
41072             this.markInvalid(String.format(this.maxText, this.maxValue));
41073             return false;
41074         }
41075         
41076         return true;
41077     },
41078     
41079     validate : function()
41080     {
41081         if(this.disabled || this.allowBlank){
41082             this.markValid();
41083             return true;
41084         }
41085         
41086         var currency = this.getCurrency();
41087         
41088         if(this.validateValue(this.getRawValue()) && currency.length){
41089             this.markValid();
41090             return true;
41091         }
41092         
41093         this.markInvalid();
41094         return false;
41095     },
41096     
41097     getName: function()
41098     {
41099         return this.name;
41100     },
41101     
41102     beforeBlur : function()
41103     {
41104         if(!this.castInt){
41105             return;
41106         }
41107         
41108         var v = this.parseValue(this.getRawValue());
41109         
41110         if(v || v == 0){
41111             this.setValue(v);
41112         }
41113     },
41114     
41115     onBlur : function()
41116     {
41117         this.beforeBlur();
41118         
41119         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41120             //this.el.removeClass(this.focusClass);
41121         }
41122         
41123         this.hasFocus = false;
41124         
41125         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41126             this.validate();
41127         }
41128         
41129         var v = this.getValue();
41130         
41131         if(String(v) !== String(this.startValue)){
41132             this.fireEvent('change', this, v, this.startValue);
41133         }
41134         
41135         this.fireEvent("blur", this);
41136     },
41137     
41138     inputEl : function()
41139     {
41140         return this.el.select('.roo-money-amount-input', true).first();
41141     },
41142     
41143     currencyEl : function()
41144     {
41145         return this.el.select('.roo-money-currency-input', true).first();
41146     },
41147     
41148     hiddenEl : function()
41149     {
41150         return this.el.select('input.hidden-number-input',true).first();
41151     }
41152     
41153 });