Roo/bootstrap/Menu.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     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24662     
24663 };
24664
24665 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24666     /**
24667      * @cfg {Roo.data.Store} dataSource
24668      * The underlying data store providing the paged data
24669      */
24670     /**
24671      * @cfg {String/HTMLElement/Element} container
24672      * container The id or element that will contain the toolbar
24673      */
24674     /**
24675      * @cfg {Boolean} displayInfo
24676      * True to display the displayMsg (defaults to false)
24677      */
24678     /**
24679      * @cfg {Number} pageSize
24680      * The number of records to display per page (defaults to 20)
24681      */
24682     pageSize: 20,
24683     /**
24684      * @cfg {String} displayMsg
24685      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24686      */
24687     displayMsg : 'Displaying {0} - {1} of {2}',
24688     /**
24689      * @cfg {String} emptyMsg
24690      * The message to display when no records are found (defaults to "No data to display")
24691      */
24692     emptyMsg : 'No data to display',
24693     /**
24694      * Customizable piece of the default paging text (defaults to "Page")
24695      * @type String
24696      */
24697     beforePageText : "Page",
24698     /**
24699      * Customizable piece of the default paging text (defaults to "of %0")
24700      * @type String
24701      */
24702     afterPageText : "of {0}",
24703     /**
24704      * Customizable piece of the default paging text (defaults to "First Page")
24705      * @type String
24706      */
24707     firstText : "First Page",
24708     /**
24709      * Customizable piece of the default paging text (defaults to "Previous Page")
24710      * @type String
24711      */
24712     prevText : "Previous Page",
24713     /**
24714      * Customizable piece of the default paging text (defaults to "Next Page")
24715      * @type String
24716      */
24717     nextText : "Next Page",
24718     /**
24719      * Customizable piece of the default paging text (defaults to "Last Page")
24720      * @type String
24721      */
24722     lastText : "Last Page",
24723     /**
24724      * Customizable piece of the default paging text (defaults to "Refresh")
24725      * @type String
24726      */
24727     refreshText : "Refresh",
24728
24729     buttons : false,
24730     // private
24731     onRender : function(ct, position) 
24732     {
24733         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24734         this.navgroup.parentId = this.id;
24735         this.navgroup.onRender(this.el, null);
24736         // add the buttons to the navgroup
24737         
24738         if(this.displayInfo){
24739             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24740             this.displayEl = this.el.select('.x-paging-info', true).first();
24741 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24742 //            this.displayEl = navel.el.select('span',true).first();
24743         }
24744         
24745         var _this = this;
24746         
24747         if(this.buttons){
24748             Roo.each(_this.buttons, function(e){ // this might need to use render????
24749                Roo.factory(e).render(_this.el);
24750             });
24751         }
24752             
24753         Roo.each(_this.toolbarItems, function(e) {
24754             _this.navgroup.addItem(e);
24755         });
24756         
24757         
24758         this.first = this.navgroup.addItem({
24759             tooltip: this.firstText,
24760             cls: "prev",
24761             icon : 'fa fa-step-backward',
24762             disabled: true,
24763             preventDefault: true,
24764             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24765         });
24766         
24767         this.prev =  this.navgroup.addItem({
24768             tooltip: this.prevText,
24769             cls: "prev",
24770             icon : 'fa fa-backward',
24771             disabled: true,
24772             preventDefault: true,
24773             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24774         });
24775     //this.addSeparator();
24776         
24777         
24778         var field = this.navgroup.addItem( {
24779             tagtype : 'span',
24780             cls : 'x-paging-position',
24781             
24782             html : this.beforePageText  +
24783                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24784                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24785          } ); //?? escaped?
24786         
24787         this.field = field.el.select('input', true).first();
24788         this.field.on("keydown", this.onPagingKeydown, this);
24789         this.field.on("focus", function(){this.dom.select();});
24790     
24791     
24792         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24793         //this.field.setHeight(18);
24794         //this.addSeparator();
24795         this.next = this.navgroup.addItem({
24796             tooltip: this.nextText,
24797             cls: "next",
24798             html : ' <i class="fa fa-forward">',
24799             disabled: true,
24800             preventDefault: true,
24801             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24802         });
24803         this.last = this.navgroup.addItem({
24804             tooltip: this.lastText,
24805             icon : 'fa fa-step-forward',
24806             cls: "next",
24807             disabled: true,
24808             preventDefault: true,
24809             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24810         });
24811     //this.addSeparator();
24812         this.loading = this.navgroup.addItem({
24813             tooltip: this.refreshText,
24814             icon: 'fa fa-refresh',
24815             preventDefault: true,
24816             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24817         });
24818         
24819     },
24820
24821     // private
24822     updateInfo : function(){
24823         if(this.displayEl){
24824             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24825             var msg = count == 0 ?
24826                 this.emptyMsg :
24827                 String.format(
24828                     this.displayMsg,
24829                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24830                 );
24831             this.displayEl.update(msg);
24832         }
24833     },
24834
24835     // private
24836     onLoad : function(ds, r, o)
24837     {
24838         this.cursor = o.params.start ? o.params.start : 0;
24839         
24840         var d = this.getPageData(),
24841             ap = d.activePage,
24842             ps = d.pages;
24843         
24844         
24845         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24846         this.field.dom.value = ap;
24847         this.first.setDisabled(ap == 1);
24848         this.prev.setDisabled(ap == 1);
24849         this.next.setDisabled(ap == ps);
24850         this.last.setDisabled(ap == ps);
24851         this.loading.enable();
24852         this.updateInfo();
24853     },
24854
24855     // private
24856     getPageData : function(){
24857         var total = this.ds.getTotalCount();
24858         return {
24859             total : total,
24860             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24861             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24862         };
24863     },
24864
24865     // private
24866     onLoadError : function(){
24867         this.loading.enable();
24868     },
24869
24870     // private
24871     onPagingKeydown : function(e){
24872         var k = e.getKey();
24873         var d = this.getPageData();
24874         if(k == e.RETURN){
24875             var v = this.field.dom.value, pageNum;
24876             if(!v || isNaN(pageNum = parseInt(v, 10))){
24877                 this.field.dom.value = d.activePage;
24878                 return;
24879             }
24880             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24881             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24882             e.stopEvent();
24883         }
24884         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))
24885         {
24886           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24887           this.field.dom.value = pageNum;
24888           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24889           e.stopEvent();
24890         }
24891         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24892         {
24893           var v = this.field.dom.value, pageNum; 
24894           var increment = (e.shiftKey) ? 10 : 1;
24895           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24896                 increment *= -1;
24897           }
24898           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24899             this.field.dom.value = d.activePage;
24900             return;
24901           }
24902           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24903           {
24904             this.field.dom.value = parseInt(v, 10) + increment;
24905             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24906             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24907           }
24908           e.stopEvent();
24909         }
24910     },
24911
24912     // private
24913     beforeLoad : function(){
24914         if(this.loading){
24915             this.loading.disable();
24916         }
24917     },
24918
24919     // private
24920     onClick : function(which){
24921         
24922         var ds = this.ds;
24923         if (!ds) {
24924             return;
24925         }
24926         
24927         switch(which){
24928             case "first":
24929                 ds.load({params:{start: 0, limit: this.pageSize}});
24930             break;
24931             case "prev":
24932                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24933             break;
24934             case "next":
24935                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24936             break;
24937             case "last":
24938                 var total = ds.getTotalCount();
24939                 var extra = total % this.pageSize;
24940                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24941                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24942             break;
24943             case "refresh":
24944                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24945             break;
24946         }
24947     },
24948
24949     /**
24950      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24951      * @param {Roo.data.Store} store The data store to unbind
24952      */
24953     unbind : function(ds){
24954         ds.un("beforeload", this.beforeLoad, this);
24955         ds.un("load", this.onLoad, this);
24956         ds.un("loadexception", this.onLoadError, this);
24957         ds.un("remove", this.updateInfo, this);
24958         ds.un("add", this.updateInfo, this);
24959         this.ds = undefined;
24960     },
24961
24962     /**
24963      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24964      * @param {Roo.data.Store} store The data store to bind
24965      */
24966     bind : function(ds){
24967         ds.on("beforeload", this.beforeLoad, this);
24968         ds.on("load", this.onLoad, this);
24969         ds.on("loadexception", this.onLoadError, this);
24970         ds.on("remove", this.updateInfo, this);
24971         ds.on("add", this.updateInfo, this);
24972         this.ds = ds;
24973     }
24974 });/*
24975  * - LGPL
24976  *
24977  * element
24978  * 
24979  */
24980
24981 /**
24982  * @class Roo.bootstrap.MessageBar
24983  * @extends Roo.bootstrap.Component
24984  * Bootstrap MessageBar class
24985  * @cfg {String} html contents of the MessageBar
24986  * @cfg {String} weight (info | success | warning | danger) default info
24987  * @cfg {String} beforeClass insert the bar before the given class
24988  * @cfg {Boolean} closable (true | false) default false
24989  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24990  * 
24991  * @constructor
24992  * Create a new Element
24993  * @param {Object} config The config object
24994  */
24995
24996 Roo.bootstrap.MessageBar = function(config){
24997     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24998 };
24999
25000 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25001     
25002     html: '',
25003     weight: 'info',
25004     closable: false,
25005     fixed: false,
25006     beforeClass: 'bootstrap-sticky-wrap',
25007     
25008     getAutoCreate : function(){
25009         
25010         var cfg = {
25011             tag: 'div',
25012             cls: 'alert alert-dismissable alert-' + this.weight,
25013             cn: [
25014                 {
25015                     tag: 'span',
25016                     cls: 'message',
25017                     html: this.html || ''
25018                 }
25019             ]
25020         };
25021         
25022         if(this.fixed){
25023             cfg.cls += ' alert-messages-fixed';
25024         }
25025         
25026         if(this.closable){
25027             cfg.cn.push({
25028                 tag: 'button',
25029                 cls: 'close',
25030                 html: 'x'
25031             });
25032         }
25033         
25034         return cfg;
25035     },
25036     
25037     onRender : function(ct, position)
25038     {
25039         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25040         
25041         if(!this.el){
25042             var cfg = Roo.apply({},  this.getAutoCreate());
25043             cfg.id = Roo.id();
25044             
25045             if (this.cls) {
25046                 cfg.cls += ' ' + this.cls;
25047             }
25048             if (this.style) {
25049                 cfg.style = this.style;
25050             }
25051             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25052             
25053             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25054         }
25055         
25056         this.el.select('>button.close').on('click', this.hide, this);
25057         
25058     },
25059     
25060     show : function()
25061     {
25062         if (!this.rendered) {
25063             this.render();
25064         }
25065         
25066         this.el.show();
25067         
25068         this.fireEvent('show', this);
25069         
25070     },
25071     
25072     hide : function()
25073     {
25074         if (!this.rendered) {
25075             this.render();
25076         }
25077         
25078         this.el.hide();
25079         
25080         this.fireEvent('hide', this);
25081     },
25082     
25083     update : function()
25084     {
25085 //        var e = this.el.dom.firstChild;
25086 //        
25087 //        if(this.closable){
25088 //            e = e.nextSibling;
25089 //        }
25090 //        
25091 //        e.data = this.html || '';
25092
25093         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25094     }
25095    
25096 });
25097
25098  
25099
25100      /*
25101  * - LGPL
25102  *
25103  * Graph
25104  * 
25105  */
25106
25107
25108 /**
25109  * @class Roo.bootstrap.Graph
25110  * @extends Roo.bootstrap.Component
25111  * Bootstrap Graph class
25112 > Prameters
25113  -sm {number} sm 4
25114  -md {number} md 5
25115  @cfg {String} graphtype  bar | vbar | pie
25116  @cfg {number} g_x coodinator | centre x (pie)
25117  @cfg {number} g_y coodinator | centre y (pie)
25118  @cfg {number} g_r radius (pie)
25119  @cfg {number} g_height height of the chart (respected by all elements in the set)
25120  @cfg {number} g_width width of the chart (respected by all elements in the set)
25121  @cfg {Object} title The title of the chart
25122     
25123  -{Array}  values
25124  -opts (object) options for the chart 
25125      o {
25126      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25127      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25128      o vgutter (number)
25129      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.
25130      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25131      o to
25132      o stretch (boolean)
25133      o }
25134  -opts (object) options for the pie
25135      o{
25136      o cut
25137      o startAngle (number)
25138      o endAngle (number)
25139      } 
25140  *
25141  * @constructor
25142  * Create a new Input
25143  * @param {Object} config The config object
25144  */
25145
25146 Roo.bootstrap.Graph = function(config){
25147     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25148     
25149     this.addEvents({
25150         // img events
25151         /**
25152          * @event click
25153          * The img click event for the img.
25154          * @param {Roo.EventObject} e
25155          */
25156         "click" : true
25157     });
25158 };
25159
25160 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25161     
25162     sm: 4,
25163     md: 5,
25164     graphtype: 'bar',
25165     g_height: 250,
25166     g_width: 400,
25167     g_x: 50,
25168     g_y: 50,
25169     g_r: 30,
25170     opts:{
25171         //g_colors: this.colors,
25172         g_type: 'soft',
25173         g_gutter: '20%'
25174
25175     },
25176     title : false,
25177
25178     getAutoCreate : function(){
25179         
25180         var cfg = {
25181             tag: 'div',
25182             html : null
25183         };
25184         
25185         
25186         return  cfg;
25187     },
25188
25189     onRender : function(ct,position){
25190         
25191         
25192         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25193         
25194         if (typeof(Raphael) == 'undefined') {
25195             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25196             return;
25197         }
25198         
25199         this.raphael = Raphael(this.el.dom);
25200         
25201                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25202                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25203                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25204                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25205                 /*
25206                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25207                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25208                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25209                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25210                 
25211                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25212                 r.barchart(330, 10, 300, 220, data1);
25213                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25214                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25215                 */
25216                 
25217                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25218                 // r.barchart(30, 30, 560, 250,  xdata, {
25219                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25220                 //     axis : "0 0 1 1",
25221                 //     axisxlabels :  xdata
25222                 //     //yvalues : cols,
25223                    
25224                 // });
25225 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25226 //        
25227 //        this.load(null,xdata,{
25228 //                axis : "0 0 1 1",
25229 //                axisxlabels :  xdata
25230 //                });
25231
25232     },
25233
25234     load : function(graphtype,xdata,opts)
25235     {
25236         this.raphael.clear();
25237         if(!graphtype) {
25238             graphtype = this.graphtype;
25239         }
25240         if(!opts){
25241             opts = this.opts;
25242         }
25243         var r = this.raphael,
25244             fin = function () {
25245                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25246             },
25247             fout = function () {
25248                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25249             },
25250             pfin = function() {
25251                 this.sector.stop();
25252                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25253
25254                 if (this.label) {
25255                     this.label[0].stop();
25256                     this.label[0].attr({ r: 7.5 });
25257                     this.label[1].attr({ "font-weight": 800 });
25258                 }
25259             },
25260             pfout = function() {
25261                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25262
25263                 if (this.label) {
25264                     this.label[0].animate({ r: 5 }, 500, "bounce");
25265                     this.label[1].attr({ "font-weight": 400 });
25266                 }
25267             };
25268
25269         switch(graphtype){
25270             case 'bar':
25271                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25272                 break;
25273             case 'hbar':
25274                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25275                 break;
25276             case 'pie':
25277 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25278 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25279 //            
25280                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25281                 
25282                 break;
25283
25284         }
25285         
25286         if(this.title){
25287             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25288         }
25289         
25290     },
25291     
25292     setTitle: function(o)
25293     {
25294         this.title = o;
25295     },
25296     
25297     initEvents: function() {
25298         
25299         if(!this.href){
25300             this.el.on('click', this.onClick, this);
25301         }
25302     },
25303     
25304     onClick : function(e)
25305     {
25306         Roo.log('img onclick');
25307         this.fireEvent('click', this, e);
25308     }
25309    
25310 });
25311
25312  
25313 /*
25314  * - LGPL
25315  *
25316  * numberBox
25317  * 
25318  */
25319 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25320
25321 /**
25322  * @class Roo.bootstrap.dash.NumberBox
25323  * @extends Roo.bootstrap.Component
25324  * Bootstrap NumberBox class
25325  * @cfg {String} headline Box headline
25326  * @cfg {String} content Box content
25327  * @cfg {String} icon Box icon
25328  * @cfg {String} footer Footer text
25329  * @cfg {String} fhref Footer href
25330  * 
25331  * @constructor
25332  * Create a new NumberBox
25333  * @param {Object} config The config object
25334  */
25335
25336
25337 Roo.bootstrap.dash.NumberBox = function(config){
25338     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25339     
25340 };
25341
25342 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25343     
25344     headline : '',
25345     content : '',
25346     icon : '',
25347     footer : '',
25348     fhref : '',
25349     ficon : '',
25350     
25351     getAutoCreate : function(){
25352         
25353         var cfg = {
25354             tag : 'div',
25355             cls : 'small-box ',
25356             cn : [
25357                 {
25358                     tag : 'div',
25359                     cls : 'inner',
25360                     cn :[
25361                         {
25362                             tag : 'h3',
25363                             cls : 'roo-headline',
25364                             html : this.headline
25365                         },
25366                         {
25367                             tag : 'p',
25368                             cls : 'roo-content',
25369                             html : this.content
25370                         }
25371                     ]
25372                 }
25373             ]
25374         };
25375         
25376         if(this.icon){
25377             cfg.cn.push({
25378                 tag : 'div',
25379                 cls : 'icon',
25380                 cn :[
25381                     {
25382                         tag : 'i',
25383                         cls : 'ion ' + this.icon
25384                     }
25385                 ]
25386             });
25387         }
25388         
25389         if(this.footer){
25390             var footer = {
25391                 tag : 'a',
25392                 cls : 'small-box-footer',
25393                 href : this.fhref || '#',
25394                 html : this.footer
25395             };
25396             
25397             cfg.cn.push(footer);
25398             
25399         }
25400         
25401         return  cfg;
25402     },
25403
25404     onRender : function(ct,position){
25405         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25406
25407
25408        
25409                 
25410     },
25411
25412     setHeadline: function (value)
25413     {
25414         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25415     },
25416     
25417     setFooter: function (value, href)
25418     {
25419         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25420         
25421         if(href){
25422             this.el.select('a.small-box-footer',true).first().attr('href', href);
25423         }
25424         
25425     },
25426
25427     setContent: function (value)
25428     {
25429         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25430     },
25431
25432     initEvents: function() 
25433     {   
25434         
25435     }
25436     
25437 });
25438
25439  
25440 /*
25441  * - LGPL
25442  *
25443  * TabBox
25444  * 
25445  */
25446 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25447
25448 /**
25449  * @class Roo.bootstrap.dash.TabBox
25450  * @extends Roo.bootstrap.Component
25451  * Bootstrap TabBox class
25452  * @cfg {String} title Title of the TabBox
25453  * @cfg {String} icon Icon of the TabBox
25454  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25455  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25456  * 
25457  * @constructor
25458  * Create a new TabBox
25459  * @param {Object} config The config object
25460  */
25461
25462
25463 Roo.bootstrap.dash.TabBox = function(config){
25464     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25465     this.addEvents({
25466         // raw events
25467         /**
25468          * @event addpane
25469          * When a pane is added
25470          * @param {Roo.bootstrap.dash.TabPane} pane
25471          */
25472         "addpane" : true,
25473         /**
25474          * @event activatepane
25475          * When a pane is activated
25476          * @param {Roo.bootstrap.dash.TabPane} pane
25477          */
25478         "activatepane" : true
25479         
25480          
25481     });
25482     
25483     this.panes = [];
25484 };
25485
25486 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25487
25488     title : '',
25489     icon : false,
25490     showtabs : true,
25491     tabScrollable : false,
25492     
25493     getChildContainer : function()
25494     {
25495         return this.el.select('.tab-content', true).first();
25496     },
25497     
25498     getAutoCreate : function(){
25499         
25500         var header = {
25501             tag: 'li',
25502             cls: 'pull-left header',
25503             html: this.title,
25504             cn : []
25505         };
25506         
25507         if(this.icon){
25508             header.cn.push({
25509                 tag: 'i',
25510                 cls: 'fa ' + this.icon
25511             });
25512         }
25513         
25514         var h = {
25515             tag: 'ul',
25516             cls: 'nav nav-tabs pull-right',
25517             cn: [
25518                 header
25519             ]
25520         };
25521         
25522         if(this.tabScrollable){
25523             h = {
25524                 tag: 'div',
25525                 cls: 'tab-header',
25526                 cn: [
25527                     {
25528                         tag: 'ul',
25529                         cls: 'nav nav-tabs pull-right',
25530                         cn: [
25531                             header
25532                         ]
25533                     }
25534                 ]
25535             };
25536         }
25537         
25538         var cfg = {
25539             tag: 'div',
25540             cls: 'nav-tabs-custom',
25541             cn: [
25542                 h,
25543                 {
25544                     tag: 'div',
25545                     cls: 'tab-content no-padding',
25546                     cn: []
25547                 }
25548             ]
25549         };
25550
25551         return  cfg;
25552     },
25553     initEvents : function()
25554     {
25555         //Roo.log('add add pane handler');
25556         this.on('addpane', this.onAddPane, this);
25557     },
25558      /**
25559      * Updates the box title
25560      * @param {String} html to set the title to.
25561      */
25562     setTitle : function(value)
25563     {
25564         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25565     },
25566     onAddPane : function(pane)
25567     {
25568         this.panes.push(pane);
25569         //Roo.log('addpane');
25570         //Roo.log(pane);
25571         // tabs are rendere left to right..
25572         if(!this.showtabs){
25573             return;
25574         }
25575         
25576         var ctr = this.el.select('.nav-tabs', true).first();
25577          
25578          
25579         var existing = ctr.select('.nav-tab',true);
25580         var qty = existing.getCount();;
25581         
25582         
25583         var tab = ctr.createChild({
25584             tag : 'li',
25585             cls : 'nav-tab' + (qty ? '' : ' active'),
25586             cn : [
25587                 {
25588                     tag : 'a',
25589                     href:'#',
25590                     html : pane.title
25591                 }
25592             ]
25593         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25594         pane.tab = tab;
25595         
25596         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25597         if (!qty) {
25598             pane.el.addClass('active');
25599         }
25600         
25601                 
25602     },
25603     onTabClick : function(ev,un,ob,pane)
25604     {
25605         //Roo.log('tab - prev default');
25606         ev.preventDefault();
25607         
25608         
25609         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25610         pane.tab.addClass('active');
25611         //Roo.log(pane.title);
25612         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25613         // technically we should have a deactivate event.. but maybe add later.
25614         // and it should not de-activate the selected tab...
25615         this.fireEvent('activatepane', pane);
25616         pane.el.addClass('active');
25617         pane.fireEvent('activate');
25618         
25619         
25620     },
25621     
25622     getActivePane : function()
25623     {
25624         var r = false;
25625         Roo.each(this.panes, function(p) {
25626             if(p.el.hasClass('active')){
25627                 r = p;
25628                 return false;
25629             }
25630             
25631             return;
25632         });
25633         
25634         return r;
25635     }
25636     
25637     
25638 });
25639
25640  
25641 /*
25642  * - LGPL
25643  *
25644  * Tab pane
25645  * 
25646  */
25647 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25648 /**
25649  * @class Roo.bootstrap.TabPane
25650  * @extends Roo.bootstrap.Component
25651  * Bootstrap TabPane class
25652  * @cfg {Boolean} active (false | true) Default false
25653  * @cfg {String} title title of panel
25654
25655  * 
25656  * @constructor
25657  * Create a new TabPane
25658  * @param {Object} config The config object
25659  */
25660
25661 Roo.bootstrap.dash.TabPane = function(config){
25662     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25663     
25664     this.addEvents({
25665         // raw events
25666         /**
25667          * @event activate
25668          * When a pane is activated
25669          * @param {Roo.bootstrap.dash.TabPane} pane
25670          */
25671         "activate" : true
25672          
25673     });
25674 };
25675
25676 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25677     
25678     active : false,
25679     title : '',
25680     
25681     // the tabBox that this is attached to.
25682     tab : false,
25683      
25684     getAutoCreate : function() 
25685     {
25686         var cfg = {
25687             tag: 'div',
25688             cls: 'tab-pane'
25689         };
25690         
25691         if(this.active){
25692             cfg.cls += ' active';
25693         }
25694         
25695         return cfg;
25696     },
25697     initEvents  : function()
25698     {
25699         //Roo.log('trigger add pane handler');
25700         this.parent().fireEvent('addpane', this)
25701     },
25702     
25703      /**
25704      * Updates the tab title 
25705      * @param {String} html to set the title to.
25706      */
25707     setTitle: function(str)
25708     {
25709         if (!this.tab) {
25710             return;
25711         }
25712         this.title = str;
25713         this.tab.select('a', true).first().dom.innerHTML = str;
25714         
25715     }
25716     
25717     
25718     
25719 });
25720
25721  
25722
25723
25724  /*
25725  * - LGPL
25726  *
25727  * menu
25728  * 
25729  */
25730 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25731
25732 /**
25733  * @class Roo.bootstrap.menu.Menu
25734  * @extends Roo.bootstrap.Component
25735  * Bootstrap Menu class - container for Menu
25736  * @cfg {String} html Text of the menu
25737  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25738  * @cfg {String} icon Font awesome icon
25739  * @cfg {String} pos Menu align to (top | bottom) default bottom
25740  * 
25741  * 
25742  * @constructor
25743  * Create a new Menu
25744  * @param {Object} config The config object
25745  */
25746
25747
25748 Roo.bootstrap.menu.Menu = function(config){
25749     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25750     
25751     this.addEvents({
25752         /**
25753          * @event beforeshow
25754          * Fires before this menu is displayed
25755          * @param {Roo.bootstrap.menu.Menu} this
25756          */
25757         beforeshow : true,
25758         /**
25759          * @event beforehide
25760          * Fires before this menu is hidden
25761          * @param {Roo.bootstrap.menu.Menu} this
25762          */
25763         beforehide : true,
25764         /**
25765          * @event show
25766          * Fires after this menu is displayed
25767          * @param {Roo.bootstrap.menu.Menu} this
25768          */
25769         show : true,
25770         /**
25771          * @event hide
25772          * Fires after this menu is hidden
25773          * @param {Roo.bootstrap.menu.Menu} this
25774          */
25775         hide : true,
25776         /**
25777          * @event click
25778          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25779          * @param {Roo.bootstrap.menu.Menu} this
25780          * @param {Roo.EventObject} e
25781          */
25782         click : true
25783     });
25784     
25785 };
25786
25787 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25788     
25789     submenu : false,
25790     html : '',
25791     weight : 'default',
25792     icon : false,
25793     pos : 'bottom',
25794     
25795     
25796     getChildContainer : function() {
25797         if(this.isSubMenu){
25798             return this.el;
25799         }
25800         
25801         return this.el.select('ul.dropdown-menu', true).first();  
25802     },
25803     
25804     getAutoCreate : function()
25805     {
25806         var text = [
25807             {
25808                 tag : 'span',
25809                 cls : 'roo-menu-text',
25810                 html : this.html
25811             }
25812         ];
25813         
25814         if(this.icon){
25815             text.unshift({
25816                 tag : 'i',
25817                 cls : 'fa ' + this.icon
25818             })
25819         }
25820         
25821         
25822         var cfg = {
25823             tag : 'div',
25824             cls : 'btn-group',
25825             cn : [
25826                 {
25827                     tag : 'button',
25828                     cls : 'dropdown-button btn btn-' + this.weight,
25829                     cn : text
25830                 },
25831                 {
25832                     tag : 'button',
25833                     cls : 'dropdown-toggle btn btn-' + this.weight,
25834                     cn : [
25835                         {
25836                             tag : 'span',
25837                             cls : 'caret'
25838                         }
25839                     ]
25840                 },
25841                 {
25842                     tag : 'ul',
25843                     cls : 'dropdown-menu'
25844                 }
25845             ]
25846             
25847         };
25848         
25849         if(this.pos == 'top'){
25850             cfg.cls += ' dropup';
25851         }
25852         
25853         if(this.isSubMenu){
25854             cfg = {
25855                 tag : 'ul',
25856                 cls : 'dropdown-menu'
25857             }
25858         }
25859         
25860         return cfg;
25861     },
25862     
25863     onRender : function(ct, position)
25864     {
25865         this.isSubMenu = ct.hasClass('dropdown-submenu');
25866         
25867         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25868     },
25869     
25870     initEvents : function() 
25871     {
25872         if(this.isSubMenu){
25873             return;
25874         }
25875         
25876         this.hidden = true;
25877         
25878         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25879         this.triggerEl.on('click', this.onTriggerPress, this);
25880         
25881         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25882         this.buttonEl.on('click', this.onClick, this);
25883         
25884     },
25885     
25886     list : function()
25887     {
25888         if(this.isSubMenu){
25889             return this.el;
25890         }
25891         
25892         return this.el.select('ul.dropdown-menu', true).first();
25893     },
25894     
25895     onClick : function(e)
25896     {
25897         this.fireEvent("click", this, e);
25898     },
25899     
25900     onTriggerPress  : function(e)
25901     {   
25902         if (this.isVisible()) {
25903             this.hide();
25904         } else {
25905             this.show();
25906         }
25907     },
25908     
25909     isVisible : function(){
25910         return !this.hidden;
25911     },
25912     
25913     show : function()
25914     {
25915         this.fireEvent("beforeshow", this);
25916         
25917         this.hidden = false;
25918         this.el.addClass('open');
25919         
25920         Roo.get(document).on("mouseup", this.onMouseUp, this);
25921         
25922         this.fireEvent("show", this);
25923         
25924         
25925     },
25926     
25927     hide : function()
25928     {
25929         this.fireEvent("beforehide", this);
25930         
25931         this.hidden = true;
25932         this.el.removeClass('open');
25933         
25934         Roo.get(document).un("mouseup", this.onMouseUp);
25935         
25936         this.fireEvent("hide", this);
25937     },
25938     
25939     onMouseUp : function()
25940     {
25941         this.hide();
25942     }
25943     
25944 });
25945
25946  
25947  /*
25948  * - LGPL
25949  *
25950  * menu item
25951  * 
25952  */
25953 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25954
25955 /**
25956  * @class Roo.bootstrap.menu.Item
25957  * @extends Roo.bootstrap.Component
25958  * Bootstrap MenuItem class
25959  * @cfg {Boolean} submenu (true | false) default false
25960  * @cfg {String} html text of the item
25961  * @cfg {String} href the link
25962  * @cfg {Boolean} disable (true | false) default false
25963  * @cfg {Boolean} preventDefault (true | false) default true
25964  * @cfg {String} icon Font awesome icon
25965  * @cfg {String} pos Submenu align to (left | right) default right 
25966  * 
25967  * 
25968  * @constructor
25969  * Create a new Item
25970  * @param {Object} config The config object
25971  */
25972
25973
25974 Roo.bootstrap.menu.Item = function(config){
25975     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25976     this.addEvents({
25977         /**
25978          * @event mouseover
25979          * Fires when the mouse is hovering over this menu
25980          * @param {Roo.bootstrap.menu.Item} this
25981          * @param {Roo.EventObject} e
25982          */
25983         mouseover : true,
25984         /**
25985          * @event mouseout
25986          * Fires when the mouse exits this menu
25987          * @param {Roo.bootstrap.menu.Item} this
25988          * @param {Roo.EventObject} e
25989          */
25990         mouseout : true,
25991         // raw events
25992         /**
25993          * @event click
25994          * The raw click event for the entire grid.
25995          * @param {Roo.EventObject} e
25996          */
25997         click : true
25998     });
25999 };
26000
26001 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26002     
26003     submenu : false,
26004     href : '',
26005     html : '',
26006     preventDefault: true,
26007     disable : false,
26008     icon : false,
26009     pos : 'right',
26010     
26011     getAutoCreate : function()
26012     {
26013         var text = [
26014             {
26015                 tag : 'span',
26016                 cls : 'roo-menu-item-text',
26017                 html : this.html
26018             }
26019         ];
26020         
26021         if(this.icon){
26022             text.unshift({
26023                 tag : 'i',
26024                 cls : 'fa ' + this.icon
26025             })
26026         }
26027         
26028         var cfg = {
26029             tag : 'li',
26030             cn : [
26031                 {
26032                     tag : 'a',
26033                     href : this.href || '#',
26034                     cn : text
26035                 }
26036             ]
26037         };
26038         
26039         if(this.disable){
26040             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26041         }
26042         
26043         if(this.submenu){
26044             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26045             
26046             if(this.pos == 'left'){
26047                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26048             }
26049         }
26050         
26051         return cfg;
26052     },
26053     
26054     initEvents : function() 
26055     {
26056         this.el.on('mouseover', this.onMouseOver, this);
26057         this.el.on('mouseout', this.onMouseOut, this);
26058         
26059         this.el.select('a', true).first().on('click', this.onClick, this);
26060         
26061     },
26062     
26063     onClick : function(e)
26064     {
26065         if(this.preventDefault){
26066             e.preventDefault();
26067         }
26068         
26069         this.fireEvent("click", this, e);
26070     },
26071     
26072     onMouseOver : function(e)
26073     {
26074         if(this.submenu && this.pos == 'left'){
26075             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26076         }
26077         
26078         this.fireEvent("mouseover", this, e);
26079     },
26080     
26081     onMouseOut : function(e)
26082     {
26083         this.fireEvent("mouseout", this, e);
26084     }
26085 });
26086
26087  
26088
26089  /*
26090  * - LGPL
26091  *
26092  * menu separator
26093  * 
26094  */
26095 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26096
26097 /**
26098  * @class Roo.bootstrap.menu.Separator
26099  * @extends Roo.bootstrap.Component
26100  * Bootstrap Separator class
26101  * 
26102  * @constructor
26103  * Create a new Separator
26104  * @param {Object} config The config object
26105  */
26106
26107
26108 Roo.bootstrap.menu.Separator = function(config){
26109     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26110 };
26111
26112 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26113     
26114     getAutoCreate : function(){
26115         var cfg = {
26116             tag : 'li',
26117             cls: 'divider'
26118         };
26119         
26120         return cfg;
26121     }
26122    
26123 });
26124
26125  
26126
26127  /*
26128  * - LGPL
26129  *
26130  * Tooltip
26131  * 
26132  */
26133
26134 /**
26135  * @class Roo.bootstrap.Tooltip
26136  * Bootstrap Tooltip class
26137  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26138  * to determine which dom element triggers the tooltip.
26139  * 
26140  * It needs to add support for additional attributes like tooltip-position
26141  * 
26142  * @constructor
26143  * Create a new Toolti
26144  * @param {Object} config The config object
26145  */
26146
26147 Roo.bootstrap.Tooltip = function(config){
26148     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26149     
26150     this.alignment = Roo.bootstrap.Tooltip.alignment;
26151     
26152     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26153         this.alignment = config.alignment;
26154     }
26155     
26156 };
26157
26158 Roo.apply(Roo.bootstrap.Tooltip, {
26159     /**
26160      * @function init initialize tooltip monitoring.
26161      * @static
26162      */
26163     currentEl : false,
26164     currentTip : false,
26165     currentRegion : false,
26166     
26167     //  init : delay?
26168     
26169     init : function()
26170     {
26171         Roo.get(document).on('mouseover', this.enter ,this);
26172         Roo.get(document).on('mouseout', this.leave, this);
26173          
26174         
26175         this.currentTip = new Roo.bootstrap.Tooltip();
26176     },
26177     
26178     enter : function(ev)
26179     {
26180         var dom = ev.getTarget();
26181         
26182         //Roo.log(['enter',dom]);
26183         var el = Roo.fly(dom);
26184         if (this.currentEl) {
26185             //Roo.log(dom);
26186             //Roo.log(this.currentEl);
26187             //Roo.log(this.currentEl.contains(dom));
26188             if (this.currentEl == el) {
26189                 return;
26190             }
26191             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26192                 return;
26193             }
26194
26195         }
26196         
26197         if (this.currentTip.el) {
26198             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26199         }    
26200         //Roo.log(ev);
26201         
26202         if(!el || el.dom == document){
26203             return;
26204         }
26205         
26206         var bindEl = el;
26207         
26208         // you can not look for children, as if el is the body.. then everythign is the child..
26209         if (!el.attr('tooltip')) { //
26210             if (!el.select("[tooltip]").elements.length) {
26211                 return;
26212             }
26213             // is the mouse over this child...?
26214             bindEl = el.select("[tooltip]").first();
26215             var xy = ev.getXY();
26216             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26217                 //Roo.log("not in region.");
26218                 return;
26219             }
26220             //Roo.log("child element over..");
26221             
26222         }
26223         this.currentEl = bindEl;
26224         this.currentTip.bind(bindEl);
26225         this.currentRegion = Roo.lib.Region.getRegion(dom);
26226         this.currentTip.enter();
26227         
26228     },
26229     leave : function(ev)
26230     {
26231         var dom = ev.getTarget();
26232         //Roo.log(['leave',dom]);
26233         if (!this.currentEl) {
26234             return;
26235         }
26236         
26237         
26238         if (dom != this.currentEl.dom) {
26239             return;
26240         }
26241         var xy = ev.getXY();
26242         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26243             return;
26244         }
26245         // only activate leave if mouse cursor is outside... bounding box..
26246         
26247         
26248         
26249         
26250         if (this.currentTip) {
26251             this.currentTip.leave();
26252         }
26253         //Roo.log('clear currentEl');
26254         this.currentEl = false;
26255         
26256         
26257     },
26258     alignment : {
26259         'left' : ['r-l', [-2,0], 'right'],
26260         'right' : ['l-r', [2,0], 'left'],
26261         'bottom' : ['t-b', [0,2], 'top'],
26262         'top' : [ 'b-t', [0,-2], 'bottom']
26263     }
26264     
26265 });
26266
26267
26268 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26269     
26270     
26271     bindEl : false,
26272     
26273     delay : null, // can be { show : 300 , hide: 500}
26274     
26275     timeout : null,
26276     
26277     hoverState : null, //???
26278     
26279     placement : 'bottom', 
26280     
26281     alignment : false,
26282     
26283     getAutoCreate : function(){
26284     
26285         var cfg = {
26286            cls : 'tooltip',
26287            role : 'tooltip',
26288            cn : [
26289                 {
26290                     cls : 'tooltip-arrow'
26291                 },
26292                 {
26293                     cls : 'tooltip-inner'
26294                 }
26295            ]
26296         };
26297         
26298         return cfg;
26299     },
26300     bind : function(el)
26301     {
26302         this.bindEl = el;
26303     },
26304       
26305     
26306     enter : function () {
26307        
26308         if (this.timeout != null) {
26309             clearTimeout(this.timeout);
26310         }
26311         
26312         this.hoverState = 'in';
26313          //Roo.log("enter - show");
26314         if (!this.delay || !this.delay.show) {
26315             this.show();
26316             return;
26317         }
26318         var _t = this;
26319         this.timeout = setTimeout(function () {
26320             if (_t.hoverState == 'in') {
26321                 _t.show();
26322             }
26323         }, this.delay.show);
26324     },
26325     leave : function()
26326     {
26327         clearTimeout(this.timeout);
26328     
26329         this.hoverState = 'out';
26330          if (!this.delay || !this.delay.hide) {
26331             this.hide();
26332             return;
26333         }
26334        
26335         var _t = this;
26336         this.timeout = setTimeout(function () {
26337             //Roo.log("leave - timeout");
26338             
26339             if (_t.hoverState == 'out') {
26340                 _t.hide();
26341                 Roo.bootstrap.Tooltip.currentEl = false;
26342             }
26343         }, delay);
26344     },
26345     
26346     show : function (msg)
26347     {
26348         if (!this.el) {
26349             this.render(document.body);
26350         }
26351         // set content.
26352         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26353         
26354         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26355         
26356         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26357         
26358         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26359         
26360         var placement = typeof this.placement == 'function' ?
26361             this.placement.call(this, this.el, on_el) :
26362             this.placement;
26363             
26364         var autoToken = /\s?auto?\s?/i;
26365         var autoPlace = autoToken.test(placement);
26366         if (autoPlace) {
26367             placement = placement.replace(autoToken, '') || 'top';
26368         }
26369         
26370         //this.el.detach()
26371         //this.el.setXY([0,0]);
26372         this.el.show();
26373         //this.el.dom.style.display='block';
26374         
26375         //this.el.appendTo(on_el);
26376         
26377         var p = this.getPosition();
26378         var box = this.el.getBox();
26379         
26380         if (autoPlace) {
26381             // fixme..
26382         }
26383         
26384         var align = this.alignment[placement];
26385         
26386         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26387         
26388         if(placement == 'top' || placement == 'bottom'){
26389             if(xy[0] < 0){
26390                 placement = 'right';
26391             }
26392             
26393             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26394                 placement = 'left';
26395             }
26396             
26397             var scroll = Roo.select('body', true).first().getScroll();
26398             
26399             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26400                 placement = 'top';
26401             }
26402             
26403             align = this.alignment[placement];
26404         }
26405         
26406         this.el.alignTo(this.bindEl, align[0],align[1]);
26407         //var arrow = this.el.select('.arrow',true).first();
26408         //arrow.set(align[2], 
26409         
26410         this.el.addClass(placement);
26411         
26412         this.el.addClass('in fade');
26413         
26414         this.hoverState = null;
26415         
26416         if (this.el.hasClass('fade')) {
26417             // fade it?
26418         }
26419         
26420     },
26421     hide : function()
26422     {
26423          
26424         if (!this.el) {
26425             return;
26426         }
26427         //this.el.setXY([0,0]);
26428         this.el.removeClass('in');
26429         //this.el.hide();
26430         
26431     }
26432     
26433 });
26434  
26435
26436  /*
26437  * - LGPL
26438  *
26439  * Location Picker
26440  * 
26441  */
26442
26443 /**
26444  * @class Roo.bootstrap.LocationPicker
26445  * @extends Roo.bootstrap.Component
26446  * Bootstrap LocationPicker class
26447  * @cfg {Number} latitude Position when init default 0
26448  * @cfg {Number} longitude Position when init default 0
26449  * @cfg {Number} zoom default 15
26450  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26451  * @cfg {Boolean} mapTypeControl default false
26452  * @cfg {Boolean} disableDoubleClickZoom default false
26453  * @cfg {Boolean} scrollwheel default true
26454  * @cfg {Boolean} streetViewControl default false
26455  * @cfg {Number} radius default 0
26456  * @cfg {String} locationName
26457  * @cfg {Boolean} draggable default true
26458  * @cfg {Boolean} enableAutocomplete default false
26459  * @cfg {Boolean} enableReverseGeocode default true
26460  * @cfg {String} markerTitle
26461  * 
26462  * @constructor
26463  * Create a new LocationPicker
26464  * @param {Object} config The config object
26465  */
26466
26467
26468 Roo.bootstrap.LocationPicker = function(config){
26469     
26470     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26471     
26472     this.addEvents({
26473         /**
26474          * @event initial
26475          * Fires when the picker initialized.
26476          * @param {Roo.bootstrap.LocationPicker} this
26477          * @param {Google Location} location
26478          */
26479         initial : true,
26480         /**
26481          * @event positionchanged
26482          * Fires when the picker position changed.
26483          * @param {Roo.bootstrap.LocationPicker} this
26484          * @param {Google Location} location
26485          */
26486         positionchanged : true,
26487         /**
26488          * @event resize
26489          * Fires when the map resize.
26490          * @param {Roo.bootstrap.LocationPicker} this
26491          */
26492         resize : true,
26493         /**
26494          * @event show
26495          * Fires when the map show.
26496          * @param {Roo.bootstrap.LocationPicker} this
26497          */
26498         show : true,
26499         /**
26500          * @event hide
26501          * Fires when the map hide.
26502          * @param {Roo.bootstrap.LocationPicker} this
26503          */
26504         hide : true,
26505         /**
26506          * @event mapClick
26507          * Fires when click the map.
26508          * @param {Roo.bootstrap.LocationPicker} this
26509          * @param {Map event} e
26510          */
26511         mapClick : true,
26512         /**
26513          * @event mapRightClick
26514          * Fires when right click the map.
26515          * @param {Roo.bootstrap.LocationPicker} this
26516          * @param {Map event} e
26517          */
26518         mapRightClick : true,
26519         /**
26520          * @event markerClick
26521          * Fires when click the marker.
26522          * @param {Roo.bootstrap.LocationPicker} this
26523          * @param {Map event} e
26524          */
26525         markerClick : true,
26526         /**
26527          * @event markerRightClick
26528          * Fires when right click the marker.
26529          * @param {Roo.bootstrap.LocationPicker} this
26530          * @param {Map event} e
26531          */
26532         markerRightClick : true,
26533         /**
26534          * @event OverlayViewDraw
26535          * Fires when OverlayView Draw
26536          * @param {Roo.bootstrap.LocationPicker} this
26537          */
26538         OverlayViewDraw : true,
26539         /**
26540          * @event OverlayViewOnAdd
26541          * Fires when OverlayView Draw
26542          * @param {Roo.bootstrap.LocationPicker} this
26543          */
26544         OverlayViewOnAdd : true,
26545         /**
26546          * @event OverlayViewOnRemove
26547          * Fires when OverlayView Draw
26548          * @param {Roo.bootstrap.LocationPicker} this
26549          */
26550         OverlayViewOnRemove : true,
26551         /**
26552          * @event OverlayViewShow
26553          * Fires when OverlayView Draw
26554          * @param {Roo.bootstrap.LocationPicker} this
26555          * @param {Pixel} cpx
26556          */
26557         OverlayViewShow : true,
26558         /**
26559          * @event OverlayViewHide
26560          * Fires when OverlayView Draw
26561          * @param {Roo.bootstrap.LocationPicker} this
26562          */
26563         OverlayViewHide : true,
26564         /**
26565          * @event loadexception
26566          * Fires when load google lib failed.
26567          * @param {Roo.bootstrap.LocationPicker} this
26568          */
26569         loadexception : true
26570     });
26571         
26572 };
26573
26574 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26575     
26576     gMapContext: false,
26577     
26578     latitude: 0,
26579     longitude: 0,
26580     zoom: 15,
26581     mapTypeId: false,
26582     mapTypeControl: false,
26583     disableDoubleClickZoom: false,
26584     scrollwheel: true,
26585     streetViewControl: false,
26586     radius: 0,
26587     locationName: '',
26588     draggable: true,
26589     enableAutocomplete: false,
26590     enableReverseGeocode: true,
26591     markerTitle: '',
26592     
26593     getAutoCreate: function()
26594     {
26595
26596         var cfg = {
26597             tag: 'div',
26598             cls: 'roo-location-picker'
26599         };
26600         
26601         return cfg
26602     },
26603     
26604     initEvents: function(ct, position)
26605     {       
26606         if(!this.el.getWidth() || this.isApplied()){
26607             return;
26608         }
26609         
26610         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26611         
26612         this.initial();
26613     },
26614     
26615     initial: function()
26616     {
26617         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26618             this.fireEvent('loadexception', this);
26619             return;
26620         }
26621         
26622         if(!this.mapTypeId){
26623             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26624         }
26625         
26626         this.gMapContext = this.GMapContext();
26627         
26628         this.initOverlayView();
26629         
26630         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26631         
26632         var _this = this;
26633                 
26634         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26635             _this.setPosition(_this.gMapContext.marker.position);
26636         });
26637         
26638         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26639             _this.fireEvent('mapClick', this, event);
26640             
26641         });
26642
26643         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26644             _this.fireEvent('mapRightClick', this, event);
26645             
26646         });
26647         
26648         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26649             _this.fireEvent('markerClick', this, event);
26650             
26651         });
26652
26653         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26654             _this.fireEvent('markerRightClick', this, event);
26655             
26656         });
26657         
26658         this.setPosition(this.gMapContext.location);
26659         
26660         this.fireEvent('initial', this, this.gMapContext.location);
26661     },
26662     
26663     initOverlayView: function()
26664     {
26665         var _this = this;
26666         
26667         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26668             
26669             draw: function()
26670             {
26671                 _this.fireEvent('OverlayViewDraw', _this);
26672             },
26673             
26674             onAdd: function()
26675             {
26676                 _this.fireEvent('OverlayViewOnAdd', _this);
26677             },
26678             
26679             onRemove: function()
26680             {
26681                 _this.fireEvent('OverlayViewOnRemove', _this);
26682             },
26683             
26684             show: function(cpx)
26685             {
26686                 _this.fireEvent('OverlayViewShow', _this, cpx);
26687             },
26688             
26689             hide: function()
26690             {
26691                 _this.fireEvent('OverlayViewHide', _this);
26692             }
26693             
26694         });
26695     },
26696     
26697     fromLatLngToContainerPixel: function(event)
26698     {
26699         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26700     },
26701     
26702     isApplied: function() 
26703     {
26704         return this.getGmapContext() == false ? false : true;
26705     },
26706     
26707     getGmapContext: function() 
26708     {
26709         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26710     },
26711     
26712     GMapContext: function() 
26713     {
26714         var position = new google.maps.LatLng(this.latitude, this.longitude);
26715         
26716         var _map = new google.maps.Map(this.el.dom, {
26717             center: position,
26718             zoom: this.zoom,
26719             mapTypeId: this.mapTypeId,
26720             mapTypeControl: this.mapTypeControl,
26721             disableDoubleClickZoom: this.disableDoubleClickZoom,
26722             scrollwheel: this.scrollwheel,
26723             streetViewControl: this.streetViewControl,
26724             locationName: this.locationName,
26725             draggable: this.draggable,
26726             enableAutocomplete: this.enableAutocomplete,
26727             enableReverseGeocode: this.enableReverseGeocode
26728         });
26729         
26730         var _marker = new google.maps.Marker({
26731             position: position,
26732             map: _map,
26733             title: this.markerTitle,
26734             draggable: this.draggable
26735         });
26736         
26737         return {
26738             map: _map,
26739             marker: _marker,
26740             circle: null,
26741             location: position,
26742             radius: this.radius,
26743             locationName: this.locationName,
26744             addressComponents: {
26745                 formatted_address: null,
26746                 addressLine1: null,
26747                 addressLine2: null,
26748                 streetName: null,
26749                 streetNumber: null,
26750                 city: null,
26751                 district: null,
26752                 state: null,
26753                 stateOrProvince: null
26754             },
26755             settings: this,
26756             domContainer: this.el.dom,
26757             geodecoder: new google.maps.Geocoder()
26758         };
26759     },
26760     
26761     drawCircle: function(center, radius, options) 
26762     {
26763         if (this.gMapContext.circle != null) {
26764             this.gMapContext.circle.setMap(null);
26765         }
26766         if (radius > 0) {
26767             radius *= 1;
26768             options = Roo.apply({}, options, {
26769                 strokeColor: "#0000FF",
26770                 strokeOpacity: .35,
26771                 strokeWeight: 2,
26772                 fillColor: "#0000FF",
26773                 fillOpacity: .2
26774             });
26775             
26776             options.map = this.gMapContext.map;
26777             options.radius = radius;
26778             options.center = center;
26779             this.gMapContext.circle = new google.maps.Circle(options);
26780             return this.gMapContext.circle;
26781         }
26782         
26783         return null;
26784     },
26785     
26786     setPosition: function(location) 
26787     {
26788         this.gMapContext.location = location;
26789         this.gMapContext.marker.setPosition(location);
26790         this.gMapContext.map.panTo(location);
26791         this.drawCircle(location, this.gMapContext.radius, {});
26792         
26793         var _this = this;
26794         
26795         if (this.gMapContext.settings.enableReverseGeocode) {
26796             this.gMapContext.geodecoder.geocode({
26797                 latLng: this.gMapContext.location
26798             }, function(results, status) {
26799                 
26800                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26801                     _this.gMapContext.locationName = results[0].formatted_address;
26802                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26803                     
26804                     _this.fireEvent('positionchanged', this, location);
26805                 }
26806             });
26807             
26808             return;
26809         }
26810         
26811         this.fireEvent('positionchanged', this, location);
26812     },
26813     
26814     resize: function()
26815     {
26816         google.maps.event.trigger(this.gMapContext.map, "resize");
26817         
26818         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26819         
26820         this.fireEvent('resize', this);
26821     },
26822     
26823     setPositionByLatLng: function(latitude, longitude)
26824     {
26825         this.setPosition(new google.maps.LatLng(latitude, longitude));
26826     },
26827     
26828     getCurrentPosition: function() 
26829     {
26830         return {
26831             latitude: this.gMapContext.location.lat(),
26832             longitude: this.gMapContext.location.lng()
26833         };
26834     },
26835     
26836     getAddressName: function() 
26837     {
26838         return this.gMapContext.locationName;
26839     },
26840     
26841     getAddressComponents: function() 
26842     {
26843         return this.gMapContext.addressComponents;
26844     },
26845     
26846     address_component_from_google_geocode: function(address_components) 
26847     {
26848         var result = {};
26849         
26850         for (var i = 0; i < address_components.length; i++) {
26851             var component = address_components[i];
26852             if (component.types.indexOf("postal_code") >= 0) {
26853                 result.postalCode = component.short_name;
26854             } else if (component.types.indexOf("street_number") >= 0) {
26855                 result.streetNumber = component.short_name;
26856             } else if (component.types.indexOf("route") >= 0) {
26857                 result.streetName = component.short_name;
26858             } else if (component.types.indexOf("neighborhood") >= 0) {
26859                 result.city = component.short_name;
26860             } else if (component.types.indexOf("locality") >= 0) {
26861                 result.city = component.short_name;
26862             } else if (component.types.indexOf("sublocality") >= 0) {
26863                 result.district = component.short_name;
26864             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26865                 result.stateOrProvince = component.short_name;
26866             } else if (component.types.indexOf("country") >= 0) {
26867                 result.country = component.short_name;
26868             }
26869         }
26870         
26871         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26872         result.addressLine2 = "";
26873         return result;
26874     },
26875     
26876     setZoomLevel: function(zoom)
26877     {
26878         this.gMapContext.map.setZoom(zoom);
26879     },
26880     
26881     show: function()
26882     {
26883         if(!this.el){
26884             return;
26885         }
26886         
26887         this.el.show();
26888         
26889         this.resize();
26890         
26891         this.fireEvent('show', this);
26892     },
26893     
26894     hide: function()
26895     {
26896         if(!this.el){
26897             return;
26898         }
26899         
26900         this.el.hide();
26901         
26902         this.fireEvent('hide', this);
26903     }
26904     
26905 });
26906
26907 Roo.apply(Roo.bootstrap.LocationPicker, {
26908     
26909     OverlayView : function(map, options)
26910     {
26911         options = options || {};
26912         
26913         this.setMap(map);
26914     }
26915     
26916     
26917 });/*
26918  * - LGPL
26919  *
26920  * Alert
26921  * 
26922  */
26923
26924 /**
26925  * @class Roo.bootstrap.Alert
26926  * @extends Roo.bootstrap.Component
26927  * Bootstrap Alert class
26928  * @cfg {String} title The title of alert
26929  * @cfg {String} html The content of alert
26930  * @cfg {String} weight (  success | info | warning | danger )
26931  * @cfg {String} faicon font-awesomeicon
26932  * 
26933  * @constructor
26934  * Create a new alert
26935  * @param {Object} config The config object
26936  */
26937
26938
26939 Roo.bootstrap.Alert = function(config){
26940     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26941     
26942 };
26943
26944 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26945     
26946     title: '',
26947     html: '',
26948     weight: false,
26949     faicon: false,
26950     
26951     getAutoCreate : function()
26952     {
26953         
26954         var cfg = {
26955             tag : 'div',
26956             cls : 'alert',
26957             cn : [
26958                 {
26959                     tag : 'i',
26960                     cls : 'roo-alert-icon'
26961                     
26962                 },
26963                 {
26964                     tag : 'b',
26965                     cls : 'roo-alert-title',
26966                     html : this.title
26967                 },
26968                 {
26969                     tag : 'span',
26970                     cls : 'roo-alert-text',
26971                     html : this.html
26972                 }
26973             ]
26974         };
26975         
26976         if(this.faicon){
26977             cfg.cn[0].cls += ' fa ' + this.faicon;
26978         }
26979         
26980         if(this.weight){
26981             cfg.cls += ' alert-' + this.weight;
26982         }
26983         
26984         return cfg;
26985     },
26986     
26987     initEvents: function() 
26988     {
26989         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26990     },
26991     
26992     setTitle : function(str)
26993     {
26994         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26995     },
26996     
26997     setText : function(str)
26998     {
26999         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27000     },
27001     
27002     setWeight : function(weight)
27003     {
27004         if(this.weight){
27005             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27006         }
27007         
27008         this.weight = weight;
27009         
27010         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27011     },
27012     
27013     setIcon : function(icon)
27014     {
27015         if(this.faicon){
27016             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27017         }
27018         
27019         this.faicon = icon;
27020         
27021         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27022     },
27023     
27024     hide: function() 
27025     {
27026         this.el.hide();   
27027     },
27028     
27029     show: function() 
27030     {  
27031         this.el.show();   
27032     }
27033     
27034 });
27035
27036  
27037 /*
27038 * Licence: LGPL
27039 */
27040
27041 /**
27042  * @class Roo.bootstrap.UploadCropbox
27043  * @extends Roo.bootstrap.Component
27044  * Bootstrap UploadCropbox class
27045  * @cfg {String} emptyText show when image has been loaded
27046  * @cfg {String} rotateNotify show when image too small to rotate
27047  * @cfg {Number} errorTimeout default 3000
27048  * @cfg {Number} minWidth default 300
27049  * @cfg {Number} minHeight default 300
27050  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27051  * @cfg {Boolean} isDocument (true|false) default false
27052  * @cfg {String} url action url
27053  * @cfg {String} paramName default 'imageUpload'
27054  * @cfg {String} method default POST
27055  * @cfg {Boolean} loadMask (true|false) default true
27056  * @cfg {Boolean} loadingText default 'Loading...'
27057  * 
27058  * @constructor
27059  * Create a new UploadCropbox
27060  * @param {Object} config The config object
27061  */
27062
27063 Roo.bootstrap.UploadCropbox = function(config){
27064     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27065     
27066     this.addEvents({
27067         /**
27068          * @event beforeselectfile
27069          * Fire before select file
27070          * @param {Roo.bootstrap.UploadCropbox} this
27071          */
27072         "beforeselectfile" : true,
27073         /**
27074          * @event initial
27075          * Fire after initEvent
27076          * @param {Roo.bootstrap.UploadCropbox} this
27077          */
27078         "initial" : true,
27079         /**
27080          * @event crop
27081          * Fire after initEvent
27082          * @param {Roo.bootstrap.UploadCropbox} this
27083          * @param {String} data
27084          */
27085         "crop" : true,
27086         /**
27087          * @event prepare
27088          * Fire when preparing the file data
27089          * @param {Roo.bootstrap.UploadCropbox} this
27090          * @param {Object} file
27091          */
27092         "prepare" : true,
27093         /**
27094          * @event exception
27095          * Fire when get exception
27096          * @param {Roo.bootstrap.UploadCropbox} this
27097          * @param {XMLHttpRequest} xhr
27098          */
27099         "exception" : true,
27100         /**
27101          * @event beforeloadcanvas
27102          * Fire before load the canvas
27103          * @param {Roo.bootstrap.UploadCropbox} this
27104          * @param {String} src
27105          */
27106         "beforeloadcanvas" : true,
27107         /**
27108          * @event trash
27109          * Fire when trash image
27110          * @param {Roo.bootstrap.UploadCropbox} this
27111          */
27112         "trash" : true,
27113         /**
27114          * @event download
27115          * Fire when download the image
27116          * @param {Roo.bootstrap.UploadCropbox} this
27117          */
27118         "download" : true,
27119         /**
27120          * @event footerbuttonclick
27121          * Fire when footerbuttonclick
27122          * @param {Roo.bootstrap.UploadCropbox} this
27123          * @param {String} type
27124          */
27125         "footerbuttonclick" : true,
27126         /**
27127          * @event resize
27128          * Fire when resize
27129          * @param {Roo.bootstrap.UploadCropbox} this
27130          */
27131         "resize" : true,
27132         /**
27133          * @event rotate
27134          * Fire when rotate the image
27135          * @param {Roo.bootstrap.UploadCropbox} this
27136          * @param {String} pos
27137          */
27138         "rotate" : true,
27139         /**
27140          * @event inspect
27141          * Fire when inspect the file
27142          * @param {Roo.bootstrap.UploadCropbox} this
27143          * @param {Object} file
27144          */
27145         "inspect" : true,
27146         /**
27147          * @event upload
27148          * Fire when xhr upload the file
27149          * @param {Roo.bootstrap.UploadCropbox} this
27150          * @param {Object} data
27151          */
27152         "upload" : true,
27153         /**
27154          * @event arrange
27155          * Fire when arrange the file data
27156          * @param {Roo.bootstrap.UploadCropbox} this
27157          * @param {Object} formData
27158          */
27159         "arrange" : true
27160     });
27161     
27162     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27163 };
27164
27165 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27166     
27167     emptyText : 'Click to upload image',
27168     rotateNotify : 'Image is too small to rotate',
27169     errorTimeout : 3000,
27170     scale : 0,
27171     baseScale : 1,
27172     rotate : 0,
27173     dragable : false,
27174     pinching : false,
27175     mouseX : 0,
27176     mouseY : 0,
27177     cropData : false,
27178     minWidth : 300,
27179     minHeight : 300,
27180     file : false,
27181     exif : {},
27182     baseRotate : 1,
27183     cropType : 'image/jpeg',
27184     buttons : false,
27185     canvasLoaded : false,
27186     isDocument : false,
27187     method : 'POST',
27188     paramName : 'imageUpload',
27189     loadMask : true,
27190     loadingText : 'Loading...',
27191     maskEl : false,
27192     
27193     getAutoCreate : function()
27194     {
27195         var cfg = {
27196             tag : 'div',
27197             cls : 'roo-upload-cropbox',
27198             cn : [
27199                 {
27200                     tag : 'input',
27201                     cls : 'roo-upload-cropbox-selector',
27202                     type : 'file'
27203                 },
27204                 {
27205                     tag : 'div',
27206                     cls : 'roo-upload-cropbox-body',
27207                     style : 'cursor:pointer',
27208                     cn : [
27209                         {
27210                             tag : 'div',
27211                             cls : 'roo-upload-cropbox-preview'
27212                         },
27213                         {
27214                             tag : 'div',
27215                             cls : 'roo-upload-cropbox-thumb'
27216                         },
27217                         {
27218                             tag : 'div',
27219                             cls : 'roo-upload-cropbox-empty-notify',
27220                             html : this.emptyText
27221                         },
27222                         {
27223                             tag : 'div',
27224                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27225                             html : this.rotateNotify
27226                         }
27227                     ]
27228                 },
27229                 {
27230                     tag : 'div',
27231                     cls : 'roo-upload-cropbox-footer',
27232                     cn : {
27233                         tag : 'div',
27234                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27235                         cn : []
27236                     }
27237                 }
27238             ]
27239         };
27240         
27241         return cfg;
27242     },
27243     
27244     onRender : function(ct, position)
27245     {
27246         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27247         
27248         if (this.buttons.length) {
27249             
27250             Roo.each(this.buttons, function(bb) {
27251                 
27252                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27253                 
27254                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27255                 
27256             }, this);
27257         }
27258         
27259         if(this.loadMask){
27260             this.maskEl = this.el;
27261         }
27262     },
27263     
27264     initEvents : function()
27265     {
27266         this.urlAPI = (window.createObjectURL && window) || 
27267                                 (window.URL && URL.revokeObjectURL && URL) || 
27268                                 (window.webkitURL && webkitURL);
27269                         
27270         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27271         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27272         
27273         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27274         this.selectorEl.hide();
27275         
27276         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27277         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27278         
27279         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27280         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27281         this.thumbEl.hide();
27282         
27283         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27284         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27285         
27286         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27287         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27288         this.errorEl.hide();
27289         
27290         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27291         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27292         this.footerEl.hide();
27293         
27294         this.setThumbBoxSize();
27295         
27296         this.bind();
27297         
27298         this.resize();
27299         
27300         this.fireEvent('initial', this);
27301     },
27302
27303     bind : function()
27304     {
27305         var _this = this;
27306         
27307         window.addEventListener("resize", function() { _this.resize(); } );
27308         
27309         this.bodyEl.on('click', this.beforeSelectFile, this);
27310         
27311         if(Roo.isTouch){
27312             this.bodyEl.on('touchstart', this.onTouchStart, this);
27313             this.bodyEl.on('touchmove', this.onTouchMove, this);
27314             this.bodyEl.on('touchend', this.onTouchEnd, this);
27315         }
27316         
27317         if(!Roo.isTouch){
27318             this.bodyEl.on('mousedown', this.onMouseDown, this);
27319             this.bodyEl.on('mousemove', this.onMouseMove, this);
27320             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27321             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27322             Roo.get(document).on('mouseup', this.onMouseUp, this);
27323         }
27324         
27325         this.selectorEl.on('change', this.onFileSelected, this);
27326     },
27327     
27328     reset : function()
27329     {    
27330         this.scale = 0;
27331         this.baseScale = 1;
27332         this.rotate = 0;
27333         this.baseRotate = 1;
27334         this.dragable = false;
27335         this.pinching = false;
27336         this.mouseX = 0;
27337         this.mouseY = 0;
27338         this.cropData = false;
27339         this.notifyEl.dom.innerHTML = this.emptyText;
27340         
27341         this.selectorEl.dom.value = '';
27342         
27343     },
27344     
27345     resize : function()
27346     {
27347         if(this.fireEvent('resize', this) != false){
27348             this.setThumbBoxPosition();
27349             this.setCanvasPosition();
27350         }
27351     },
27352     
27353     onFooterButtonClick : function(e, el, o, type)
27354     {
27355         switch (type) {
27356             case 'rotate-left' :
27357                 this.onRotateLeft(e);
27358                 break;
27359             case 'rotate-right' :
27360                 this.onRotateRight(e);
27361                 break;
27362             case 'picture' :
27363                 this.beforeSelectFile(e);
27364                 break;
27365             case 'trash' :
27366                 this.trash(e);
27367                 break;
27368             case 'crop' :
27369                 this.crop(e);
27370                 break;
27371             case 'download' :
27372                 this.download(e);
27373                 break;
27374             default :
27375                 break;
27376         }
27377         
27378         this.fireEvent('footerbuttonclick', this, type);
27379     },
27380     
27381     beforeSelectFile : function(e)
27382     {
27383         e.preventDefault();
27384         
27385         if(this.fireEvent('beforeselectfile', this) != false){
27386             this.selectorEl.dom.click();
27387         }
27388     },
27389     
27390     onFileSelected : function(e)
27391     {
27392         e.preventDefault();
27393         
27394         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27395             return;
27396         }
27397         
27398         var file = this.selectorEl.dom.files[0];
27399         
27400         if(this.fireEvent('inspect', this, file) != false){
27401             this.prepare(file);
27402         }
27403         
27404     },
27405     
27406     trash : function(e)
27407     {
27408         this.fireEvent('trash', this);
27409     },
27410     
27411     download : function(e)
27412     {
27413         this.fireEvent('download', this);
27414     },
27415     
27416     loadCanvas : function(src)
27417     {   
27418         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27419             
27420             this.reset();
27421             
27422             this.imageEl = document.createElement('img');
27423             
27424             var _this = this;
27425             
27426             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27427             
27428             this.imageEl.src = src;
27429         }
27430     },
27431     
27432     onLoadCanvas : function()
27433     {   
27434         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27435         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27436         
27437         this.bodyEl.un('click', this.beforeSelectFile, this);
27438         
27439         this.notifyEl.hide();
27440         this.thumbEl.show();
27441         this.footerEl.show();
27442         
27443         this.baseRotateLevel();
27444         
27445         if(this.isDocument){
27446             this.setThumbBoxSize();
27447         }
27448         
27449         this.setThumbBoxPosition();
27450         
27451         this.baseScaleLevel();
27452         
27453         this.draw();
27454         
27455         this.resize();
27456         
27457         this.canvasLoaded = true;
27458         
27459         if(this.loadMask){
27460             this.maskEl.unmask();
27461         }
27462         
27463     },
27464     
27465     setCanvasPosition : function()
27466     {   
27467         if(!this.canvasEl){
27468             return;
27469         }
27470         
27471         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27472         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27473         
27474         this.previewEl.setLeft(pw);
27475         this.previewEl.setTop(ph);
27476         
27477     },
27478     
27479     onMouseDown : function(e)
27480     {   
27481         e.stopEvent();
27482         
27483         this.dragable = true;
27484         this.pinching = false;
27485         
27486         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27487             this.dragable = false;
27488             return;
27489         }
27490         
27491         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27492         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27493         
27494     },
27495     
27496     onMouseMove : function(e)
27497     {   
27498         e.stopEvent();
27499         
27500         if(!this.canvasLoaded){
27501             return;
27502         }
27503         
27504         if (!this.dragable){
27505             return;
27506         }
27507         
27508         var minX = Math.ceil(this.thumbEl.getLeft(true));
27509         var minY = Math.ceil(this.thumbEl.getTop(true));
27510         
27511         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27512         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27513         
27514         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27515         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27516         
27517         x = x - this.mouseX;
27518         y = y - this.mouseY;
27519         
27520         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27521         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27522         
27523         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27524         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27525         
27526         this.previewEl.setLeft(bgX);
27527         this.previewEl.setTop(bgY);
27528         
27529         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27530         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27531     },
27532     
27533     onMouseUp : function(e)
27534     {   
27535         e.stopEvent();
27536         
27537         this.dragable = false;
27538     },
27539     
27540     onMouseWheel : function(e)
27541     {   
27542         e.stopEvent();
27543         
27544         this.startScale = this.scale;
27545         
27546         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27547         
27548         if(!this.zoomable()){
27549             this.scale = this.startScale;
27550             return;
27551         }
27552         
27553         this.draw();
27554         
27555         return;
27556     },
27557     
27558     zoomable : function()
27559     {
27560         var minScale = this.thumbEl.getWidth() / this.minWidth;
27561         
27562         if(this.minWidth < this.minHeight){
27563             minScale = this.thumbEl.getHeight() / this.minHeight;
27564         }
27565         
27566         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27567         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27568         
27569         if(
27570                 this.isDocument &&
27571                 (this.rotate == 0 || this.rotate == 180) && 
27572                 (
27573                     width > this.imageEl.OriginWidth || 
27574                     height > this.imageEl.OriginHeight ||
27575                     (width < this.minWidth && height < this.minHeight)
27576                 )
27577         ){
27578             return false;
27579         }
27580         
27581         if(
27582                 this.isDocument &&
27583                 (this.rotate == 90 || this.rotate == 270) && 
27584                 (
27585                     width > this.imageEl.OriginWidth || 
27586                     height > this.imageEl.OriginHeight ||
27587                     (width < this.minHeight && height < this.minWidth)
27588                 )
27589         ){
27590             return false;
27591         }
27592         
27593         if(
27594                 !this.isDocument &&
27595                 (this.rotate == 0 || this.rotate == 180) && 
27596                 (
27597                     width < this.minWidth || 
27598                     width > this.imageEl.OriginWidth || 
27599                     height < this.minHeight || 
27600                     height > this.imageEl.OriginHeight
27601                 )
27602         ){
27603             return false;
27604         }
27605         
27606         if(
27607                 !this.isDocument &&
27608                 (this.rotate == 90 || this.rotate == 270) && 
27609                 (
27610                     width < this.minHeight || 
27611                     width > this.imageEl.OriginWidth || 
27612                     height < this.minWidth || 
27613                     height > this.imageEl.OriginHeight
27614                 )
27615         ){
27616             return false;
27617         }
27618         
27619         return true;
27620         
27621     },
27622     
27623     onRotateLeft : function(e)
27624     {   
27625         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27626             
27627             var minScale = this.thumbEl.getWidth() / this.minWidth;
27628             
27629             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27630             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27631             
27632             this.startScale = this.scale;
27633             
27634             while (this.getScaleLevel() < minScale){
27635             
27636                 this.scale = this.scale + 1;
27637                 
27638                 if(!this.zoomable()){
27639                     break;
27640                 }
27641                 
27642                 if(
27643                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27644                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27645                 ){
27646                     continue;
27647                 }
27648                 
27649                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27650
27651                 this.draw();
27652                 
27653                 return;
27654             }
27655             
27656             this.scale = this.startScale;
27657             
27658             this.onRotateFail();
27659             
27660             return false;
27661         }
27662         
27663         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27664
27665         if(this.isDocument){
27666             this.setThumbBoxSize();
27667             this.setThumbBoxPosition();
27668             this.setCanvasPosition();
27669         }
27670         
27671         this.draw();
27672         
27673         this.fireEvent('rotate', this, 'left');
27674         
27675     },
27676     
27677     onRotateRight : function(e)
27678     {
27679         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27680             
27681             var minScale = this.thumbEl.getWidth() / this.minWidth;
27682         
27683             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27684             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27685             
27686             this.startScale = this.scale;
27687             
27688             while (this.getScaleLevel() < minScale){
27689             
27690                 this.scale = this.scale + 1;
27691                 
27692                 if(!this.zoomable()){
27693                     break;
27694                 }
27695                 
27696                 if(
27697                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27698                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27699                 ){
27700                     continue;
27701                 }
27702                 
27703                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27704
27705                 this.draw();
27706                 
27707                 return;
27708             }
27709             
27710             this.scale = this.startScale;
27711             
27712             this.onRotateFail();
27713             
27714             return false;
27715         }
27716         
27717         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27718
27719         if(this.isDocument){
27720             this.setThumbBoxSize();
27721             this.setThumbBoxPosition();
27722             this.setCanvasPosition();
27723         }
27724         
27725         this.draw();
27726         
27727         this.fireEvent('rotate', this, 'right');
27728     },
27729     
27730     onRotateFail : function()
27731     {
27732         this.errorEl.show(true);
27733         
27734         var _this = this;
27735         
27736         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27737     },
27738     
27739     draw : function()
27740     {
27741         this.previewEl.dom.innerHTML = '';
27742         
27743         var canvasEl = document.createElement("canvas");
27744         
27745         var contextEl = canvasEl.getContext("2d");
27746         
27747         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27748         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27749         var center = this.imageEl.OriginWidth / 2;
27750         
27751         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27752             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27753             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27754             center = this.imageEl.OriginHeight / 2;
27755         }
27756         
27757         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27758         
27759         contextEl.translate(center, center);
27760         contextEl.rotate(this.rotate * Math.PI / 180);
27761
27762         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27763         
27764         this.canvasEl = document.createElement("canvas");
27765         
27766         this.contextEl = this.canvasEl.getContext("2d");
27767         
27768         switch (this.rotate) {
27769             case 0 :
27770                 
27771                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27772                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27773                 
27774                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27775                 
27776                 break;
27777             case 90 : 
27778                 
27779                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27780                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27781                 
27782                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27783                     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);
27784                     break;
27785                 }
27786                 
27787                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27788                 
27789                 break;
27790             case 180 :
27791                 
27792                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27793                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27794                 
27795                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27796                     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);
27797                     break;
27798                 }
27799                 
27800                 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);
27801                 
27802                 break;
27803             case 270 :
27804                 
27805                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27806                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27807         
27808                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27809                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27810                     break;
27811                 }
27812                 
27813                 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);
27814                 
27815                 break;
27816             default : 
27817                 break;
27818         }
27819         
27820         this.previewEl.appendChild(this.canvasEl);
27821         
27822         this.setCanvasPosition();
27823     },
27824     
27825     crop : function()
27826     {
27827         if(!this.canvasLoaded){
27828             return;
27829         }
27830         
27831         var imageCanvas = document.createElement("canvas");
27832         
27833         var imageContext = imageCanvas.getContext("2d");
27834         
27835         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27836         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27837         
27838         var center = imageCanvas.width / 2;
27839         
27840         imageContext.translate(center, center);
27841         
27842         imageContext.rotate(this.rotate * Math.PI / 180);
27843         
27844         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27845         
27846         var canvas = document.createElement("canvas");
27847         
27848         var context = canvas.getContext("2d");
27849                 
27850         canvas.width = this.minWidth;
27851         canvas.height = this.minHeight;
27852
27853         switch (this.rotate) {
27854             case 0 :
27855                 
27856                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27857                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27858                 
27859                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27860                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27861                 
27862                 var targetWidth = this.minWidth - 2 * x;
27863                 var targetHeight = this.minHeight - 2 * y;
27864                 
27865                 var scale = 1;
27866                 
27867                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27868                     scale = targetWidth / width;
27869                 }
27870                 
27871                 if(x > 0 && y == 0){
27872                     scale = targetHeight / height;
27873                 }
27874                 
27875                 if(x > 0 && y > 0){
27876                     scale = targetWidth / width;
27877                     
27878                     if(width < height){
27879                         scale = targetHeight / height;
27880                     }
27881                 }
27882                 
27883                 context.scale(scale, scale);
27884                 
27885                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27886                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27887
27888                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27889                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27890
27891                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27892                 
27893                 break;
27894             case 90 : 
27895                 
27896                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27897                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27898                 
27899                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27900                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27901                 
27902                 var targetWidth = this.minWidth - 2 * x;
27903                 var targetHeight = this.minHeight - 2 * y;
27904                 
27905                 var scale = 1;
27906                 
27907                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27908                     scale = targetWidth / width;
27909                 }
27910                 
27911                 if(x > 0 && y == 0){
27912                     scale = targetHeight / height;
27913                 }
27914                 
27915                 if(x > 0 && y > 0){
27916                     scale = targetWidth / width;
27917                     
27918                     if(width < height){
27919                         scale = targetHeight / height;
27920                     }
27921                 }
27922                 
27923                 context.scale(scale, scale);
27924                 
27925                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27926                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27927
27928                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27929                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27930                 
27931                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27932                 
27933                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27934                 
27935                 break;
27936             case 180 :
27937                 
27938                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27939                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27940                 
27941                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27942                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27943                 
27944                 var targetWidth = this.minWidth - 2 * x;
27945                 var targetHeight = this.minHeight - 2 * y;
27946                 
27947                 var scale = 1;
27948                 
27949                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27950                     scale = targetWidth / width;
27951                 }
27952                 
27953                 if(x > 0 && y == 0){
27954                     scale = targetHeight / height;
27955                 }
27956                 
27957                 if(x > 0 && y > 0){
27958                     scale = targetWidth / width;
27959                     
27960                     if(width < height){
27961                         scale = targetHeight / height;
27962                     }
27963                 }
27964                 
27965                 context.scale(scale, scale);
27966                 
27967                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27968                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27969
27970                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27971                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27972
27973                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27974                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27975                 
27976                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27977                 
27978                 break;
27979             case 270 :
27980                 
27981                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27982                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27983                 
27984                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27985                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27986                 
27987                 var targetWidth = this.minWidth - 2 * x;
27988                 var targetHeight = this.minHeight - 2 * y;
27989                 
27990                 var scale = 1;
27991                 
27992                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27993                     scale = targetWidth / width;
27994                 }
27995                 
27996                 if(x > 0 && y == 0){
27997                     scale = targetHeight / height;
27998                 }
27999                 
28000                 if(x > 0 && y > 0){
28001                     scale = targetWidth / width;
28002                     
28003                     if(width < height){
28004                         scale = targetHeight / height;
28005                     }
28006                 }
28007                 
28008                 context.scale(scale, scale);
28009                 
28010                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28011                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28012
28013                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28014                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28015                 
28016                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28017                 
28018                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28019                 
28020                 break;
28021             default : 
28022                 break;
28023         }
28024         
28025         this.cropData = canvas.toDataURL(this.cropType);
28026         
28027         if(this.fireEvent('crop', this, this.cropData) !== false){
28028             this.process(this.file, this.cropData);
28029         }
28030         
28031         return;
28032         
28033     },
28034     
28035     setThumbBoxSize : function()
28036     {
28037         var width, height;
28038         
28039         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28040             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28041             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28042             
28043             this.minWidth = width;
28044             this.minHeight = height;
28045             
28046             if(this.rotate == 90 || this.rotate == 270){
28047                 this.minWidth = height;
28048                 this.minHeight = width;
28049             }
28050         }
28051         
28052         height = 300;
28053         width = Math.ceil(this.minWidth * height / this.minHeight);
28054         
28055         if(this.minWidth > this.minHeight){
28056             width = 300;
28057             height = Math.ceil(this.minHeight * width / this.minWidth);
28058         }
28059         
28060         this.thumbEl.setStyle({
28061             width : width + 'px',
28062             height : height + 'px'
28063         });
28064
28065         return;
28066             
28067     },
28068     
28069     setThumbBoxPosition : function()
28070     {
28071         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28072         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28073         
28074         this.thumbEl.setLeft(x);
28075         this.thumbEl.setTop(y);
28076         
28077     },
28078     
28079     baseRotateLevel : function()
28080     {
28081         this.baseRotate = 1;
28082         
28083         if(
28084                 typeof(this.exif) != 'undefined' &&
28085                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28086                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28087         ){
28088             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28089         }
28090         
28091         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28092         
28093     },
28094     
28095     baseScaleLevel : function()
28096     {
28097         var width, height;
28098         
28099         if(this.isDocument){
28100             
28101             if(this.baseRotate == 6 || this.baseRotate == 8){
28102             
28103                 height = this.thumbEl.getHeight();
28104                 this.baseScale = height / this.imageEl.OriginWidth;
28105
28106                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28107                     width = this.thumbEl.getWidth();
28108                     this.baseScale = width / this.imageEl.OriginHeight;
28109                 }
28110
28111                 return;
28112             }
28113
28114             height = this.thumbEl.getHeight();
28115             this.baseScale = height / this.imageEl.OriginHeight;
28116
28117             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28118                 width = this.thumbEl.getWidth();
28119                 this.baseScale = width / this.imageEl.OriginWidth;
28120             }
28121
28122             return;
28123         }
28124         
28125         if(this.baseRotate == 6 || this.baseRotate == 8){
28126             
28127             width = this.thumbEl.getHeight();
28128             this.baseScale = width / this.imageEl.OriginHeight;
28129             
28130             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28131                 height = this.thumbEl.getWidth();
28132                 this.baseScale = height / this.imageEl.OriginHeight;
28133             }
28134             
28135             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28136                 height = this.thumbEl.getWidth();
28137                 this.baseScale = height / this.imageEl.OriginHeight;
28138                 
28139                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28140                     width = this.thumbEl.getHeight();
28141                     this.baseScale = width / this.imageEl.OriginWidth;
28142                 }
28143             }
28144             
28145             return;
28146         }
28147         
28148         width = this.thumbEl.getWidth();
28149         this.baseScale = width / this.imageEl.OriginWidth;
28150         
28151         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28152             height = this.thumbEl.getHeight();
28153             this.baseScale = height / this.imageEl.OriginHeight;
28154         }
28155         
28156         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28157             
28158             height = this.thumbEl.getHeight();
28159             this.baseScale = height / this.imageEl.OriginHeight;
28160             
28161             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28162                 width = this.thumbEl.getWidth();
28163                 this.baseScale = width / this.imageEl.OriginWidth;
28164             }
28165             
28166         }
28167         
28168         return;
28169     },
28170     
28171     getScaleLevel : function()
28172     {
28173         return this.baseScale * Math.pow(1.1, this.scale);
28174     },
28175     
28176     onTouchStart : function(e)
28177     {
28178         if(!this.canvasLoaded){
28179             this.beforeSelectFile(e);
28180             return;
28181         }
28182         
28183         var touches = e.browserEvent.touches;
28184         
28185         if(!touches){
28186             return;
28187         }
28188         
28189         if(touches.length == 1){
28190             this.onMouseDown(e);
28191             return;
28192         }
28193         
28194         if(touches.length != 2){
28195             return;
28196         }
28197         
28198         var coords = [];
28199         
28200         for(var i = 0, finger; finger = touches[i]; i++){
28201             coords.push(finger.pageX, finger.pageY);
28202         }
28203         
28204         var x = Math.pow(coords[0] - coords[2], 2);
28205         var y = Math.pow(coords[1] - coords[3], 2);
28206         
28207         this.startDistance = Math.sqrt(x + y);
28208         
28209         this.startScale = this.scale;
28210         
28211         this.pinching = true;
28212         this.dragable = false;
28213         
28214     },
28215     
28216     onTouchMove : function(e)
28217     {
28218         if(!this.pinching && !this.dragable){
28219             return;
28220         }
28221         
28222         var touches = e.browserEvent.touches;
28223         
28224         if(!touches){
28225             return;
28226         }
28227         
28228         if(this.dragable){
28229             this.onMouseMove(e);
28230             return;
28231         }
28232         
28233         var coords = [];
28234         
28235         for(var i = 0, finger; finger = touches[i]; i++){
28236             coords.push(finger.pageX, finger.pageY);
28237         }
28238         
28239         var x = Math.pow(coords[0] - coords[2], 2);
28240         var y = Math.pow(coords[1] - coords[3], 2);
28241         
28242         this.endDistance = Math.sqrt(x + y);
28243         
28244         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28245         
28246         if(!this.zoomable()){
28247             this.scale = this.startScale;
28248             return;
28249         }
28250         
28251         this.draw();
28252         
28253     },
28254     
28255     onTouchEnd : function(e)
28256     {
28257         this.pinching = false;
28258         this.dragable = false;
28259         
28260     },
28261     
28262     process : function(file, crop)
28263     {
28264         if(this.loadMask){
28265             this.maskEl.mask(this.loadingText);
28266         }
28267         
28268         this.xhr = new XMLHttpRequest();
28269         
28270         file.xhr = this.xhr;
28271
28272         this.xhr.open(this.method, this.url, true);
28273         
28274         var headers = {
28275             "Accept": "application/json",
28276             "Cache-Control": "no-cache",
28277             "X-Requested-With": "XMLHttpRequest"
28278         };
28279         
28280         for (var headerName in headers) {
28281             var headerValue = headers[headerName];
28282             if (headerValue) {
28283                 this.xhr.setRequestHeader(headerName, headerValue);
28284             }
28285         }
28286         
28287         var _this = this;
28288         
28289         this.xhr.onload = function()
28290         {
28291             _this.xhrOnLoad(_this.xhr);
28292         }
28293         
28294         this.xhr.onerror = function()
28295         {
28296             _this.xhrOnError(_this.xhr);
28297         }
28298         
28299         var formData = new FormData();
28300
28301         formData.append('returnHTML', 'NO');
28302         
28303         if(crop){
28304             formData.append('crop', crop);
28305         }
28306         
28307         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28308             formData.append(this.paramName, file, file.name);
28309         }
28310         
28311         if(typeof(file.filename) != 'undefined'){
28312             formData.append('filename', file.filename);
28313         }
28314         
28315         if(typeof(file.mimetype) != 'undefined'){
28316             formData.append('mimetype', file.mimetype);
28317         }
28318         
28319         if(this.fireEvent('arrange', this, formData) != false){
28320             this.xhr.send(formData);
28321         };
28322     },
28323     
28324     xhrOnLoad : function(xhr)
28325     {
28326         if(this.loadMask){
28327             this.maskEl.unmask();
28328         }
28329         
28330         if (xhr.readyState !== 4) {
28331             this.fireEvent('exception', this, xhr);
28332             return;
28333         }
28334
28335         var response = Roo.decode(xhr.responseText);
28336         
28337         if(!response.success){
28338             this.fireEvent('exception', this, xhr);
28339             return;
28340         }
28341         
28342         var response = Roo.decode(xhr.responseText);
28343         
28344         this.fireEvent('upload', this, response);
28345         
28346     },
28347     
28348     xhrOnError : function()
28349     {
28350         if(this.loadMask){
28351             this.maskEl.unmask();
28352         }
28353         
28354         Roo.log('xhr on error');
28355         
28356         var response = Roo.decode(xhr.responseText);
28357           
28358         Roo.log(response);
28359         
28360     },
28361     
28362     prepare : function(file)
28363     {   
28364         if(this.loadMask){
28365             this.maskEl.mask(this.loadingText);
28366         }
28367         
28368         this.file = false;
28369         this.exif = {};
28370         
28371         if(typeof(file) === 'string'){
28372             this.loadCanvas(file);
28373             return;
28374         }
28375         
28376         if(!file || !this.urlAPI){
28377             return;
28378         }
28379         
28380         this.file = file;
28381         this.cropType = file.type;
28382         
28383         var _this = this;
28384         
28385         if(this.fireEvent('prepare', this, this.file) != false){
28386             
28387             var reader = new FileReader();
28388             
28389             reader.onload = function (e) {
28390                 if (e.target.error) {
28391                     Roo.log(e.target.error);
28392                     return;
28393                 }
28394                 
28395                 var buffer = e.target.result,
28396                     dataView = new DataView(buffer),
28397                     offset = 2,
28398                     maxOffset = dataView.byteLength - 4,
28399                     markerBytes,
28400                     markerLength;
28401                 
28402                 if (dataView.getUint16(0) === 0xffd8) {
28403                     while (offset < maxOffset) {
28404                         markerBytes = dataView.getUint16(offset);
28405                         
28406                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28407                             markerLength = dataView.getUint16(offset + 2) + 2;
28408                             if (offset + markerLength > dataView.byteLength) {
28409                                 Roo.log('Invalid meta data: Invalid segment size.');
28410                                 break;
28411                             }
28412                             
28413                             if(markerBytes == 0xffe1){
28414                                 _this.parseExifData(
28415                                     dataView,
28416                                     offset,
28417                                     markerLength
28418                                 );
28419                             }
28420                             
28421                             offset += markerLength;
28422                             
28423                             continue;
28424                         }
28425                         
28426                         break;
28427                     }
28428                     
28429                 }
28430                 
28431                 var url = _this.urlAPI.createObjectURL(_this.file);
28432                 
28433                 _this.loadCanvas(url);
28434                 
28435                 return;
28436             }
28437             
28438             reader.readAsArrayBuffer(this.file);
28439             
28440         }
28441         
28442     },
28443     
28444     parseExifData : function(dataView, offset, length)
28445     {
28446         var tiffOffset = offset + 10,
28447             littleEndian,
28448             dirOffset;
28449     
28450         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28451             // No Exif data, might be XMP data instead
28452             return;
28453         }
28454         
28455         // Check for the ASCII code for "Exif" (0x45786966):
28456         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28457             // No Exif data, might be XMP data instead
28458             return;
28459         }
28460         if (tiffOffset + 8 > dataView.byteLength) {
28461             Roo.log('Invalid Exif data: Invalid segment size.');
28462             return;
28463         }
28464         // Check for the two null bytes:
28465         if (dataView.getUint16(offset + 8) !== 0x0000) {
28466             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28467             return;
28468         }
28469         // Check the byte alignment:
28470         switch (dataView.getUint16(tiffOffset)) {
28471         case 0x4949:
28472             littleEndian = true;
28473             break;
28474         case 0x4D4D:
28475             littleEndian = false;
28476             break;
28477         default:
28478             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28479             return;
28480         }
28481         // Check for the TIFF tag marker (0x002A):
28482         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28483             Roo.log('Invalid Exif data: Missing TIFF marker.');
28484             return;
28485         }
28486         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28487         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28488         
28489         this.parseExifTags(
28490             dataView,
28491             tiffOffset,
28492             tiffOffset + dirOffset,
28493             littleEndian
28494         );
28495     },
28496     
28497     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28498     {
28499         var tagsNumber,
28500             dirEndOffset,
28501             i;
28502         if (dirOffset + 6 > dataView.byteLength) {
28503             Roo.log('Invalid Exif data: Invalid directory offset.');
28504             return;
28505         }
28506         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28507         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28508         if (dirEndOffset + 4 > dataView.byteLength) {
28509             Roo.log('Invalid Exif data: Invalid directory size.');
28510             return;
28511         }
28512         for (i = 0; i < tagsNumber; i += 1) {
28513             this.parseExifTag(
28514                 dataView,
28515                 tiffOffset,
28516                 dirOffset + 2 + 12 * i, // tag offset
28517                 littleEndian
28518             );
28519         }
28520         // Return the offset to the next directory:
28521         return dataView.getUint32(dirEndOffset, littleEndian);
28522     },
28523     
28524     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28525     {
28526         var tag = dataView.getUint16(offset, littleEndian);
28527         
28528         this.exif[tag] = this.getExifValue(
28529             dataView,
28530             tiffOffset,
28531             offset,
28532             dataView.getUint16(offset + 2, littleEndian), // tag type
28533             dataView.getUint32(offset + 4, littleEndian), // tag length
28534             littleEndian
28535         );
28536     },
28537     
28538     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28539     {
28540         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28541             tagSize,
28542             dataOffset,
28543             values,
28544             i,
28545             str,
28546             c;
28547     
28548         if (!tagType) {
28549             Roo.log('Invalid Exif data: Invalid tag type.');
28550             return;
28551         }
28552         
28553         tagSize = tagType.size * length;
28554         // Determine if the value is contained in the dataOffset bytes,
28555         // or if the value at the dataOffset is a pointer to the actual data:
28556         dataOffset = tagSize > 4 ?
28557                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28558         if (dataOffset + tagSize > dataView.byteLength) {
28559             Roo.log('Invalid Exif data: Invalid data offset.');
28560             return;
28561         }
28562         if (length === 1) {
28563             return tagType.getValue(dataView, dataOffset, littleEndian);
28564         }
28565         values = [];
28566         for (i = 0; i < length; i += 1) {
28567             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28568         }
28569         
28570         if (tagType.ascii) {
28571             str = '';
28572             // Concatenate the chars:
28573             for (i = 0; i < values.length; i += 1) {
28574                 c = values[i];
28575                 // Ignore the terminating NULL byte(s):
28576                 if (c === '\u0000') {
28577                     break;
28578                 }
28579                 str += c;
28580             }
28581             return str;
28582         }
28583         return values;
28584     }
28585     
28586 });
28587
28588 Roo.apply(Roo.bootstrap.UploadCropbox, {
28589     tags : {
28590         'Orientation': 0x0112
28591     },
28592     
28593     Orientation: {
28594             1: 0, //'top-left',
28595 //            2: 'top-right',
28596             3: 180, //'bottom-right',
28597 //            4: 'bottom-left',
28598 //            5: 'left-top',
28599             6: 90, //'right-top',
28600 //            7: 'right-bottom',
28601             8: 270 //'left-bottom'
28602     },
28603     
28604     exifTagTypes : {
28605         // byte, 8-bit unsigned int:
28606         1: {
28607             getValue: function (dataView, dataOffset) {
28608                 return dataView.getUint8(dataOffset);
28609             },
28610             size: 1
28611         },
28612         // ascii, 8-bit byte:
28613         2: {
28614             getValue: function (dataView, dataOffset) {
28615                 return String.fromCharCode(dataView.getUint8(dataOffset));
28616             },
28617             size: 1,
28618             ascii: true
28619         },
28620         // short, 16 bit int:
28621         3: {
28622             getValue: function (dataView, dataOffset, littleEndian) {
28623                 return dataView.getUint16(dataOffset, littleEndian);
28624             },
28625             size: 2
28626         },
28627         // long, 32 bit int:
28628         4: {
28629             getValue: function (dataView, dataOffset, littleEndian) {
28630                 return dataView.getUint32(dataOffset, littleEndian);
28631             },
28632             size: 4
28633         },
28634         // rational = two long values, first is numerator, second is denominator:
28635         5: {
28636             getValue: function (dataView, dataOffset, littleEndian) {
28637                 return dataView.getUint32(dataOffset, littleEndian) /
28638                     dataView.getUint32(dataOffset + 4, littleEndian);
28639             },
28640             size: 8
28641         },
28642         // slong, 32 bit signed int:
28643         9: {
28644             getValue: function (dataView, dataOffset, littleEndian) {
28645                 return dataView.getInt32(dataOffset, littleEndian);
28646             },
28647             size: 4
28648         },
28649         // srational, two slongs, first is numerator, second is denominator:
28650         10: {
28651             getValue: function (dataView, dataOffset, littleEndian) {
28652                 return dataView.getInt32(dataOffset, littleEndian) /
28653                     dataView.getInt32(dataOffset + 4, littleEndian);
28654             },
28655             size: 8
28656         }
28657     },
28658     
28659     footer : {
28660         STANDARD : [
28661             {
28662                 tag : 'div',
28663                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28664                 action : 'rotate-left',
28665                 cn : [
28666                     {
28667                         tag : 'button',
28668                         cls : 'btn btn-default',
28669                         html : '<i class="fa fa-undo"></i>'
28670                     }
28671                 ]
28672             },
28673             {
28674                 tag : 'div',
28675                 cls : 'btn-group roo-upload-cropbox-picture',
28676                 action : 'picture',
28677                 cn : [
28678                     {
28679                         tag : 'button',
28680                         cls : 'btn btn-default',
28681                         html : '<i class="fa fa-picture-o"></i>'
28682                     }
28683                 ]
28684             },
28685             {
28686                 tag : 'div',
28687                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28688                 action : 'rotate-right',
28689                 cn : [
28690                     {
28691                         tag : 'button',
28692                         cls : 'btn btn-default',
28693                         html : '<i class="fa fa-repeat"></i>'
28694                     }
28695                 ]
28696             }
28697         ],
28698         DOCUMENT : [
28699             {
28700                 tag : 'div',
28701                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28702                 action : 'rotate-left',
28703                 cn : [
28704                     {
28705                         tag : 'button',
28706                         cls : 'btn btn-default',
28707                         html : '<i class="fa fa-undo"></i>'
28708                     }
28709                 ]
28710             },
28711             {
28712                 tag : 'div',
28713                 cls : 'btn-group roo-upload-cropbox-download',
28714                 action : 'download',
28715                 cn : [
28716                     {
28717                         tag : 'button',
28718                         cls : 'btn btn-default',
28719                         html : '<i class="fa fa-download"></i>'
28720                     }
28721                 ]
28722             },
28723             {
28724                 tag : 'div',
28725                 cls : 'btn-group roo-upload-cropbox-crop',
28726                 action : 'crop',
28727                 cn : [
28728                     {
28729                         tag : 'button',
28730                         cls : 'btn btn-default',
28731                         html : '<i class="fa fa-crop"></i>'
28732                     }
28733                 ]
28734             },
28735             {
28736                 tag : 'div',
28737                 cls : 'btn-group roo-upload-cropbox-trash',
28738                 action : 'trash',
28739                 cn : [
28740                     {
28741                         tag : 'button',
28742                         cls : 'btn btn-default',
28743                         html : '<i class="fa fa-trash"></i>'
28744                     }
28745                 ]
28746             },
28747             {
28748                 tag : 'div',
28749                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28750                 action : 'rotate-right',
28751                 cn : [
28752                     {
28753                         tag : 'button',
28754                         cls : 'btn btn-default',
28755                         html : '<i class="fa fa-repeat"></i>'
28756                     }
28757                 ]
28758             }
28759         ],
28760         ROTATOR : [
28761             {
28762                 tag : 'div',
28763                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28764                 action : 'rotate-left',
28765                 cn : [
28766                     {
28767                         tag : 'button',
28768                         cls : 'btn btn-default',
28769                         html : '<i class="fa fa-undo"></i>'
28770                     }
28771                 ]
28772             },
28773             {
28774                 tag : 'div',
28775                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28776                 action : 'rotate-right',
28777                 cn : [
28778                     {
28779                         tag : 'button',
28780                         cls : 'btn btn-default',
28781                         html : '<i class="fa fa-repeat"></i>'
28782                     }
28783                 ]
28784             }
28785         ]
28786     }
28787 });
28788
28789 /*
28790 * Licence: LGPL
28791 */
28792
28793 /**
28794  * @class Roo.bootstrap.DocumentManager
28795  * @extends Roo.bootstrap.Component
28796  * Bootstrap DocumentManager class
28797  * @cfg {String} paramName default 'imageUpload'
28798  * @cfg {String} toolTipName default 'filename'
28799  * @cfg {String} method default POST
28800  * @cfg {String} url action url
28801  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28802  * @cfg {Boolean} multiple multiple upload default true
28803  * @cfg {Number} thumbSize default 300
28804  * @cfg {String} fieldLabel
28805  * @cfg {Number} labelWidth default 4
28806  * @cfg {String} labelAlign (left|top) default left
28807  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28808 * @cfg {Number} labellg set the width of label (1-12)
28809  * @cfg {Number} labelmd set the width of label (1-12)
28810  * @cfg {Number} labelsm set the width of label (1-12)
28811  * @cfg {Number} labelxs set the width of label (1-12)
28812  * 
28813  * @constructor
28814  * Create a new DocumentManager
28815  * @param {Object} config The config object
28816  */
28817
28818 Roo.bootstrap.DocumentManager = function(config){
28819     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28820     
28821     this.files = [];
28822     this.delegates = [];
28823     
28824     this.addEvents({
28825         /**
28826          * @event initial
28827          * Fire when initial the DocumentManager
28828          * @param {Roo.bootstrap.DocumentManager} this
28829          */
28830         "initial" : true,
28831         /**
28832          * @event inspect
28833          * inspect selected file
28834          * @param {Roo.bootstrap.DocumentManager} this
28835          * @param {File} file
28836          */
28837         "inspect" : true,
28838         /**
28839          * @event exception
28840          * Fire when xhr load exception
28841          * @param {Roo.bootstrap.DocumentManager} this
28842          * @param {XMLHttpRequest} xhr
28843          */
28844         "exception" : true,
28845         /**
28846          * @event afterupload
28847          * Fire when xhr load exception
28848          * @param {Roo.bootstrap.DocumentManager} this
28849          * @param {XMLHttpRequest} xhr
28850          */
28851         "afterupload" : true,
28852         /**
28853          * @event prepare
28854          * prepare the form data
28855          * @param {Roo.bootstrap.DocumentManager} this
28856          * @param {Object} formData
28857          */
28858         "prepare" : true,
28859         /**
28860          * @event remove
28861          * Fire when remove the file
28862          * @param {Roo.bootstrap.DocumentManager} this
28863          * @param {Object} file
28864          */
28865         "remove" : true,
28866         /**
28867          * @event refresh
28868          * Fire after refresh the file
28869          * @param {Roo.bootstrap.DocumentManager} this
28870          */
28871         "refresh" : true,
28872         /**
28873          * @event click
28874          * Fire after click the image
28875          * @param {Roo.bootstrap.DocumentManager} this
28876          * @param {Object} file
28877          */
28878         "click" : true,
28879         /**
28880          * @event edit
28881          * Fire when upload a image and editable set to true
28882          * @param {Roo.bootstrap.DocumentManager} this
28883          * @param {Object} file
28884          */
28885         "edit" : true,
28886         /**
28887          * @event beforeselectfile
28888          * Fire before select file
28889          * @param {Roo.bootstrap.DocumentManager} this
28890          */
28891         "beforeselectfile" : true,
28892         /**
28893          * @event process
28894          * Fire before process file
28895          * @param {Roo.bootstrap.DocumentManager} this
28896          * @param {Object} file
28897          */
28898         "process" : true,
28899         /**
28900          * @event previewrendered
28901          * Fire when preview rendered
28902          * @param {Roo.bootstrap.DocumentManager} this
28903          * @param {Object} file
28904          */
28905         "previewrendered" : true,
28906         /**
28907          */
28908         "previewResize" : true
28909         
28910     });
28911 };
28912
28913 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28914     
28915     boxes : 0,
28916     inputName : '',
28917     thumbSize : 300,
28918     multiple : true,
28919     files : false,
28920     method : 'POST',
28921     url : '',
28922     paramName : 'imageUpload',
28923     toolTipName : 'filename',
28924     fieldLabel : '',
28925     labelWidth : 4,
28926     labelAlign : 'left',
28927     editable : true,
28928     delegates : false,
28929     xhr : false, 
28930     
28931     labellg : 0,
28932     labelmd : 0,
28933     labelsm : 0,
28934     labelxs : 0,
28935     
28936     getAutoCreate : function()
28937     {   
28938         var managerWidget = {
28939             tag : 'div',
28940             cls : 'roo-document-manager',
28941             cn : [
28942                 {
28943                     tag : 'input',
28944                     cls : 'roo-document-manager-selector',
28945                     type : 'file'
28946                 },
28947                 {
28948                     tag : 'div',
28949                     cls : 'roo-document-manager-uploader',
28950                     cn : [
28951                         {
28952                             tag : 'div',
28953                             cls : 'roo-document-manager-upload-btn',
28954                             html : '<i class="fa fa-plus"></i>'
28955                         }
28956                     ]
28957                     
28958                 }
28959             ]
28960         };
28961         
28962         var content = [
28963             {
28964                 tag : 'div',
28965                 cls : 'column col-md-12',
28966                 cn : managerWidget
28967             }
28968         ];
28969         
28970         if(this.fieldLabel.length){
28971             
28972             content = [
28973                 {
28974                     tag : 'div',
28975                     cls : 'column col-md-12',
28976                     html : this.fieldLabel
28977                 },
28978                 {
28979                     tag : 'div',
28980                     cls : 'column col-md-12',
28981                     cn : managerWidget
28982                 }
28983             ];
28984
28985             if(this.labelAlign == 'left'){
28986                 content = [
28987                     {
28988                         tag : 'div',
28989                         cls : 'column',
28990                         html : this.fieldLabel
28991                     },
28992                     {
28993                         tag : 'div',
28994                         cls : 'column',
28995                         cn : managerWidget
28996                     }
28997                 ];
28998                 
28999                 if(this.labelWidth > 12){
29000                     content[0].style = "width: " + this.labelWidth + 'px';
29001                 }
29002
29003                 if(this.labelWidth < 13 && this.labelmd == 0){
29004                     this.labelmd = this.labelWidth;
29005                 }
29006
29007                 if(this.labellg > 0){
29008                     content[0].cls += ' col-lg-' + this.labellg;
29009                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29010                 }
29011
29012                 if(this.labelmd > 0){
29013                     content[0].cls += ' col-md-' + this.labelmd;
29014                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29015                 }
29016
29017                 if(this.labelsm > 0){
29018                     content[0].cls += ' col-sm-' + this.labelsm;
29019                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29020                 }
29021
29022                 if(this.labelxs > 0){
29023                     content[0].cls += ' col-xs-' + this.labelxs;
29024                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29025                 }
29026                 
29027             }
29028         }
29029         
29030         var cfg = {
29031             tag : 'div',
29032             cls : 'row clearfix',
29033             cn : content
29034         };
29035         
29036         return cfg;
29037         
29038     },
29039     
29040     initEvents : function()
29041     {
29042         this.managerEl = this.el.select('.roo-document-manager', true).first();
29043         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29044         
29045         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29046         this.selectorEl.hide();
29047         
29048         if(this.multiple){
29049             this.selectorEl.attr('multiple', 'multiple');
29050         }
29051         
29052         this.selectorEl.on('change', this.onFileSelected, this);
29053         
29054         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29055         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29056         
29057         this.uploader.on('click', this.onUploaderClick, this);
29058         
29059         this.renderProgressDialog();
29060         
29061         var _this = this;
29062         
29063         window.addEventListener("resize", function() { _this.refresh(); } );
29064         
29065         this.fireEvent('initial', this);
29066     },
29067     
29068     renderProgressDialog : function()
29069     {
29070         var _this = this;
29071         
29072         this.progressDialog = new Roo.bootstrap.Modal({
29073             cls : 'roo-document-manager-progress-dialog',
29074             allow_close : false,
29075             title : '',
29076             buttons : [
29077                 {
29078                     name  :'cancel',
29079                     weight : 'danger',
29080                     html : 'Cancel'
29081                 }
29082             ], 
29083             listeners : { 
29084                 btnclick : function() {
29085                     _this.uploadCancel();
29086                     this.hide();
29087                 }
29088             }
29089         });
29090          
29091         this.progressDialog.render(Roo.get(document.body));
29092          
29093         this.progress = new Roo.bootstrap.Progress({
29094             cls : 'roo-document-manager-progress',
29095             active : true,
29096             striped : true
29097         });
29098         
29099         this.progress.render(this.progressDialog.getChildContainer());
29100         
29101         this.progressBar = new Roo.bootstrap.ProgressBar({
29102             cls : 'roo-document-manager-progress-bar',
29103             aria_valuenow : 0,
29104             aria_valuemin : 0,
29105             aria_valuemax : 12,
29106             panel : 'success'
29107         });
29108         
29109         this.progressBar.render(this.progress.getChildContainer());
29110     },
29111     
29112     onUploaderClick : function(e)
29113     {
29114         e.preventDefault();
29115      
29116         if(this.fireEvent('beforeselectfile', this) != false){
29117             this.selectorEl.dom.click();
29118         }
29119         
29120     },
29121     
29122     onFileSelected : function(e)
29123     {
29124         e.preventDefault();
29125         
29126         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29127             return;
29128         }
29129         
29130         Roo.each(this.selectorEl.dom.files, function(file){
29131             if(this.fireEvent('inspect', this, file) != false){
29132                 this.files.push(file);
29133             }
29134         }, this);
29135         
29136         this.queue();
29137         
29138     },
29139     
29140     queue : function()
29141     {
29142         this.selectorEl.dom.value = '';
29143         
29144         if(!this.files || !this.files.length){
29145             return;
29146         }
29147         
29148         if(this.boxes > 0 && this.files.length > this.boxes){
29149             this.files = this.files.slice(0, this.boxes);
29150         }
29151         
29152         this.uploader.show();
29153         
29154         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29155             this.uploader.hide();
29156         }
29157         
29158         var _this = this;
29159         
29160         var files = [];
29161         
29162         var docs = [];
29163         
29164         Roo.each(this.files, function(file){
29165             
29166             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29167                 var f = this.renderPreview(file);
29168                 files.push(f);
29169                 return;
29170             }
29171             
29172             if(file.type.indexOf('image') != -1){
29173                 this.delegates.push(
29174                     (function(){
29175                         _this.process(file);
29176                     }).createDelegate(this)
29177                 );
29178         
29179                 return;
29180             }
29181             
29182             docs.push(
29183                 (function(){
29184                     _this.process(file);
29185                 }).createDelegate(this)
29186             );
29187             
29188         }, this);
29189         
29190         this.files = files;
29191         
29192         this.delegates = this.delegates.concat(docs);
29193         
29194         if(!this.delegates.length){
29195             this.refresh();
29196             return;
29197         }
29198         
29199         this.progressBar.aria_valuemax = this.delegates.length;
29200         
29201         this.arrange();
29202         
29203         return;
29204     },
29205     
29206     arrange : function()
29207     {
29208         if(!this.delegates.length){
29209             this.progressDialog.hide();
29210             this.refresh();
29211             return;
29212         }
29213         
29214         var delegate = this.delegates.shift();
29215         
29216         this.progressDialog.show();
29217         
29218         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29219         
29220         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29221         
29222         delegate();
29223     },
29224     
29225     refresh : function()
29226     {
29227         this.uploader.show();
29228         
29229         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29230             this.uploader.hide();
29231         }
29232         
29233         Roo.isTouch ? this.closable(false) : this.closable(true);
29234         
29235         this.fireEvent('refresh', this);
29236     },
29237     
29238     onRemove : function(e, el, o)
29239     {
29240         e.preventDefault();
29241         
29242         this.fireEvent('remove', this, o);
29243         
29244     },
29245     
29246     remove : function(o)
29247     {
29248         var files = [];
29249         
29250         Roo.each(this.files, function(file){
29251             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29252                 files.push(file);
29253                 return;
29254             }
29255
29256             o.target.remove();
29257
29258         }, this);
29259         
29260         this.files = files;
29261         
29262         this.refresh();
29263     },
29264     
29265     clear : function()
29266     {
29267         Roo.each(this.files, function(file){
29268             if(!file.target){
29269                 return;
29270             }
29271             
29272             file.target.remove();
29273
29274         }, this);
29275         
29276         this.files = [];
29277         
29278         this.refresh();
29279     },
29280     
29281     onClick : function(e, el, o)
29282     {
29283         e.preventDefault();
29284         
29285         this.fireEvent('click', this, o);
29286         
29287     },
29288     
29289     closable : function(closable)
29290     {
29291         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29292             
29293             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29294             
29295             if(closable){
29296                 el.show();
29297                 return;
29298             }
29299             
29300             el.hide();
29301             
29302         }, this);
29303     },
29304     
29305     xhrOnLoad : function(xhr)
29306     {
29307         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29308             el.remove();
29309         }, this);
29310         
29311         if (xhr.readyState !== 4) {
29312             this.arrange();
29313             this.fireEvent('exception', this, xhr);
29314             return;
29315         }
29316
29317         var response = Roo.decode(xhr.responseText);
29318         
29319         if(!response.success){
29320             this.arrange();
29321             this.fireEvent('exception', this, xhr);
29322             return;
29323         }
29324         
29325         var file = this.renderPreview(response.data);
29326         
29327         this.files.push(file);
29328         
29329         this.arrange();
29330         
29331         this.fireEvent('afterupload', this, xhr);
29332         
29333     },
29334     
29335     xhrOnError : function(xhr)
29336     {
29337         Roo.log('xhr on error');
29338         
29339         var response = Roo.decode(xhr.responseText);
29340           
29341         Roo.log(response);
29342         
29343         this.arrange();
29344     },
29345     
29346     process : function(file)
29347     {
29348         if(this.fireEvent('process', this, file) !== false){
29349             if(this.editable && file.type.indexOf('image') != -1){
29350                 this.fireEvent('edit', this, file);
29351                 return;
29352             }
29353
29354             this.uploadStart(file, false);
29355
29356             return;
29357         }
29358         
29359     },
29360     
29361     uploadStart : function(file, crop)
29362     {
29363         this.xhr = new XMLHttpRequest();
29364         
29365         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29366             this.arrange();
29367             return;
29368         }
29369         
29370         file.xhr = this.xhr;
29371             
29372         this.managerEl.createChild({
29373             tag : 'div',
29374             cls : 'roo-document-manager-loading',
29375             cn : [
29376                 {
29377                     tag : 'div',
29378                     tooltip : file.name,
29379                     cls : 'roo-document-manager-thumb',
29380                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29381                 }
29382             ]
29383
29384         });
29385
29386         this.xhr.open(this.method, this.url, true);
29387         
29388         var headers = {
29389             "Accept": "application/json",
29390             "Cache-Control": "no-cache",
29391             "X-Requested-With": "XMLHttpRequest"
29392         };
29393         
29394         for (var headerName in headers) {
29395             var headerValue = headers[headerName];
29396             if (headerValue) {
29397                 this.xhr.setRequestHeader(headerName, headerValue);
29398             }
29399         }
29400         
29401         var _this = this;
29402         
29403         this.xhr.onload = function()
29404         {
29405             _this.xhrOnLoad(_this.xhr);
29406         }
29407         
29408         this.xhr.onerror = function()
29409         {
29410             _this.xhrOnError(_this.xhr);
29411         }
29412         
29413         var formData = new FormData();
29414
29415         formData.append('returnHTML', 'NO');
29416         
29417         if(crop){
29418             formData.append('crop', crop);
29419         }
29420         
29421         formData.append(this.paramName, file, file.name);
29422         
29423         var options = {
29424             file : file, 
29425             manually : false
29426         };
29427         
29428         if(this.fireEvent('prepare', this, formData, options) != false){
29429             
29430             if(options.manually){
29431                 return;
29432             }
29433             
29434             this.xhr.send(formData);
29435             return;
29436         };
29437         
29438         this.uploadCancel();
29439     },
29440     
29441     uploadCancel : function()
29442     {
29443         if (this.xhr) {
29444             this.xhr.abort();
29445         }
29446         
29447         this.delegates = [];
29448         
29449         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29450             el.remove();
29451         }, this);
29452         
29453         this.arrange();
29454     },
29455     
29456     renderPreview : function(file)
29457     {
29458         if(typeof(file.target) != 'undefined' && file.target){
29459             return file;
29460         }
29461         
29462         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29463         
29464         var previewEl = this.managerEl.createChild({
29465             tag : 'div',
29466             cls : 'roo-document-manager-preview',
29467             cn : [
29468                 {
29469                     tag : 'div',
29470                     tooltip : file[this.toolTipName],
29471                     cls : 'roo-document-manager-thumb',
29472                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29473                 },
29474                 {
29475                     tag : 'button',
29476                     cls : 'close',
29477                     html : '<i class="fa fa-times-circle"></i>'
29478                 }
29479             ]
29480         });
29481
29482         var close = previewEl.select('button.close', true).first();
29483
29484         close.on('click', this.onRemove, this, file);
29485
29486         file.target = previewEl;
29487
29488         var image = previewEl.select('img', true).first();
29489         
29490         var _this = this;
29491         
29492         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29493         
29494         image.on('click', this.onClick, this, file);
29495         
29496         this.fireEvent('previewrendered', this, file);
29497         
29498         return file;
29499         
29500     },
29501     
29502     onPreviewLoad : function(file, image)
29503     {
29504         if(typeof(file.target) == 'undefined' || !file.target){
29505             return;
29506         }
29507         
29508         var width = image.dom.naturalWidth || image.dom.width;
29509         var height = image.dom.naturalHeight || image.dom.height;
29510         
29511         if(!this.previewResize) {
29512             return;
29513         }
29514         
29515         if(width > height){
29516             file.target.addClass('wide');
29517             return;
29518         }
29519         
29520         file.target.addClass('tall');
29521         return;
29522         
29523     },
29524     
29525     uploadFromSource : function(file, crop)
29526     {
29527         this.xhr = new XMLHttpRequest();
29528         
29529         this.managerEl.createChild({
29530             tag : 'div',
29531             cls : 'roo-document-manager-loading',
29532             cn : [
29533                 {
29534                     tag : 'div',
29535                     tooltip : file.name,
29536                     cls : 'roo-document-manager-thumb',
29537                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29538                 }
29539             ]
29540
29541         });
29542
29543         this.xhr.open(this.method, this.url, true);
29544         
29545         var headers = {
29546             "Accept": "application/json",
29547             "Cache-Control": "no-cache",
29548             "X-Requested-With": "XMLHttpRequest"
29549         };
29550         
29551         for (var headerName in headers) {
29552             var headerValue = headers[headerName];
29553             if (headerValue) {
29554                 this.xhr.setRequestHeader(headerName, headerValue);
29555             }
29556         }
29557         
29558         var _this = this;
29559         
29560         this.xhr.onload = function()
29561         {
29562             _this.xhrOnLoad(_this.xhr);
29563         }
29564         
29565         this.xhr.onerror = function()
29566         {
29567             _this.xhrOnError(_this.xhr);
29568         }
29569         
29570         var formData = new FormData();
29571
29572         formData.append('returnHTML', 'NO');
29573         
29574         formData.append('crop', crop);
29575         
29576         if(typeof(file.filename) != 'undefined'){
29577             formData.append('filename', file.filename);
29578         }
29579         
29580         if(typeof(file.mimetype) != 'undefined'){
29581             formData.append('mimetype', file.mimetype);
29582         }
29583         
29584         Roo.log(formData);
29585         
29586         if(this.fireEvent('prepare', this, formData) != false){
29587             this.xhr.send(formData);
29588         };
29589     }
29590 });
29591
29592 /*
29593 * Licence: LGPL
29594 */
29595
29596 /**
29597  * @class Roo.bootstrap.DocumentViewer
29598  * @extends Roo.bootstrap.Component
29599  * Bootstrap DocumentViewer class
29600  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29601  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29602  * 
29603  * @constructor
29604  * Create a new DocumentViewer
29605  * @param {Object} config The config object
29606  */
29607
29608 Roo.bootstrap.DocumentViewer = function(config){
29609     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29610     
29611     this.addEvents({
29612         /**
29613          * @event initial
29614          * Fire after initEvent
29615          * @param {Roo.bootstrap.DocumentViewer} this
29616          */
29617         "initial" : true,
29618         /**
29619          * @event click
29620          * Fire after click
29621          * @param {Roo.bootstrap.DocumentViewer} this
29622          */
29623         "click" : true,
29624         /**
29625          * @event download
29626          * Fire after download button
29627          * @param {Roo.bootstrap.DocumentViewer} this
29628          */
29629         "download" : true,
29630         /**
29631          * @event trash
29632          * Fire after trash button
29633          * @param {Roo.bootstrap.DocumentViewer} this
29634          */
29635         "trash" : true
29636         
29637     });
29638 };
29639
29640 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29641     
29642     showDownload : true,
29643     
29644     showTrash : true,
29645     
29646     getAutoCreate : function()
29647     {
29648         var cfg = {
29649             tag : 'div',
29650             cls : 'roo-document-viewer',
29651             cn : [
29652                 {
29653                     tag : 'div',
29654                     cls : 'roo-document-viewer-body',
29655                     cn : [
29656                         {
29657                             tag : 'div',
29658                             cls : 'roo-document-viewer-thumb',
29659                             cn : [
29660                                 {
29661                                     tag : 'img',
29662                                     cls : 'roo-document-viewer-image'
29663                                 }
29664                             ]
29665                         }
29666                     ]
29667                 },
29668                 {
29669                     tag : 'div',
29670                     cls : 'roo-document-viewer-footer',
29671                     cn : {
29672                         tag : 'div',
29673                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29674                         cn : [
29675                             {
29676                                 tag : 'div',
29677                                 cls : 'btn-group roo-document-viewer-download',
29678                                 cn : [
29679                                     {
29680                                         tag : 'button',
29681                                         cls : 'btn btn-default',
29682                                         html : '<i class="fa fa-download"></i>'
29683                                     }
29684                                 ]
29685                             },
29686                             {
29687                                 tag : 'div',
29688                                 cls : 'btn-group roo-document-viewer-trash',
29689                                 cn : [
29690                                     {
29691                                         tag : 'button',
29692                                         cls : 'btn btn-default',
29693                                         html : '<i class="fa fa-trash"></i>'
29694                                     }
29695                                 ]
29696                             }
29697                         ]
29698                     }
29699                 }
29700             ]
29701         };
29702         
29703         return cfg;
29704     },
29705     
29706     initEvents : function()
29707     {
29708         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29709         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29710         
29711         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29712         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29713         
29714         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29715         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29716         
29717         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29718         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29719         
29720         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29721         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29722         
29723         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29724         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29725         
29726         this.bodyEl.on('click', this.onClick, this);
29727         this.downloadBtn.on('click', this.onDownload, this);
29728         this.trashBtn.on('click', this.onTrash, this);
29729         
29730         this.downloadBtn.hide();
29731         this.trashBtn.hide();
29732         
29733         if(this.showDownload){
29734             this.downloadBtn.show();
29735         }
29736         
29737         if(this.showTrash){
29738             this.trashBtn.show();
29739         }
29740         
29741         if(!this.showDownload && !this.showTrash) {
29742             this.footerEl.hide();
29743         }
29744         
29745     },
29746     
29747     initial : function()
29748     {
29749         this.fireEvent('initial', this);
29750         
29751     },
29752     
29753     onClick : function(e)
29754     {
29755         e.preventDefault();
29756         
29757         this.fireEvent('click', this);
29758     },
29759     
29760     onDownload : function(e)
29761     {
29762         e.preventDefault();
29763         
29764         this.fireEvent('download', this);
29765     },
29766     
29767     onTrash : function(e)
29768     {
29769         e.preventDefault();
29770         
29771         this.fireEvent('trash', this);
29772     }
29773     
29774 });
29775 /*
29776  * - LGPL
29777  *
29778  * nav progress bar
29779  * 
29780  */
29781
29782 /**
29783  * @class Roo.bootstrap.NavProgressBar
29784  * @extends Roo.bootstrap.Component
29785  * Bootstrap NavProgressBar class
29786  * 
29787  * @constructor
29788  * Create a new nav progress bar
29789  * @param {Object} config The config object
29790  */
29791
29792 Roo.bootstrap.NavProgressBar = function(config){
29793     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29794
29795     this.bullets = this.bullets || [];
29796    
29797 //    Roo.bootstrap.NavProgressBar.register(this);
29798      this.addEvents({
29799         /**
29800              * @event changed
29801              * Fires when the active item changes
29802              * @param {Roo.bootstrap.NavProgressBar} this
29803              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29804              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29805          */
29806         'changed': true
29807      });
29808     
29809 };
29810
29811 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29812     
29813     bullets : [],
29814     barItems : [],
29815     
29816     getAutoCreate : function()
29817     {
29818         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29819         
29820         cfg = {
29821             tag : 'div',
29822             cls : 'roo-navigation-bar-group',
29823             cn : [
29824                 {
29825                     tag : 'div',
29826                     cls : 'roo-navigation-top-bar'
29827                 },
29828                 {
29829                     tag : 'div',
29830                     cls : 'roo-navigation-bullets-bar',
29831                     cn : [
29832                         {
29833                             tag : 'ul',
29834                             cls : 'roo-navigation-bar'
29835                         }
29836                     ]
29837                 },
29838                 
29839                 {
29840                     tag : 'div',
29841                     cls : 'roo-navigation-bottom-bar'
29842                 }
29843             ]
29844             
29845         };
29846         
29847         return cfg;
29848         
29849     },
29850     
29851     initEvents: function() 
29852     {
29853         
29854     },
29855     
29856     onRender : function(ct, position) 
29857     {
29858         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29859         
29860         if(this.bullets.length){
29861             Roo.each(this.bullets, function(b){
29862                this.addItem(b);
29863             }, this);
29864         }
29865         
29866         this.format();
29867         
29868     },
29869     
29870     addItem : function(cfg)
29871     {
29872         var item = new Roo.bootstrap.NavProgressItem(cfg);
29873         
29874         item.parentId = this.id;
29875         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29876         
29877         if(cfg.html){
29878             var top = new Roo.bootstrap.Element({
29879                 tag : 'div',
29880                 cls : 'roo-navigation-bar-text'
29881             });
29882             
29883             var bottom = new Roo.bootstrap.Element({
29884                 tag : 'div',
29885                 cls : 'roo-navigation-bar-text'
29886             });
29887             
29888             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29889             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29890             
29891             var topText = new Roo.bootstrap.Element({
29892                 tag : 'span',
29893                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29894             });
29895             
29896             var bottomText = new Roo.bootstrap.Element({
29897                 tag : 'span',
29898                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29899             });
29900             
29901             topText.onRender(top.el, null);
29902             bottomText.onRender(bottom.el, null);
29903             
29904             item.topEl = top;
29905             item.bottomEl = bottom;
29906         }
29907         
29908         this.barItems.push(item);
29909         
29910         return item;
29911     },
29912     
29913     getActive : function()
29914     {
29915         var active = false;
29916         
29917         Roo.each(this.barItems, function(v){
29918             
29919             if (!v.isActive()) {
29920                 return;
29921             }
29922             
29923             active = v;
29924             return false;
29925             
29926         });
29927         
29928         return active;
29929     },
29930     
29931     setActiveItem : function(item)
29932     {
29933         var prev = false;
29934         
29935         Roo.each(this.barItems, function(v){
29936             if (v.rid == item.rid) {
29937                 return ;
29938             }
29939             
29940             if (v.isActive()) {
29941                 v.setActive(false);
29942                 prev = v;
29943             }
29944         });
29945
29946         item.setActive(true);
29947         
29948         this.fireEvent('changed', this, item, prev);
29949     },
29950     
29951     getBarItem: function(rid)
29952     {
29953         var ret = false;
29954         
29955         Roo.each(this.barItems, function(e) {
29956             if (e.rid != rid) {
29957                 return;
29958             }
29959             
29960             ret =  e;
29961             return false;
29962         });
29963         
29964         return ret;
29965     },
29966     
29967     indexOfItem : function(item)
29968     {
29969         var index = false;
29970         
29971         Roo.each(this.barItems, function(v, i){
29972             
29973             if (v.rid != item.rid) {
29974                 return;
29975             }
29976             
29977             index = i;
29978             return false
29979         });
29980         
29981         return index;
29982     },
29983     
29984     setActiveNext : function()
29985     {
29986         var i = this.indexOfItem(this.getActive());
29987         
29988         if (i > this.barItems.length) {
29989             return;
29990         }
29991         
29992         this.setActiveItem(this.barItems[i+1]);
29993     },
29994     
29995     setActivePrev : function()
29996     {
29997         var i = this.indexOfItem(this.getActive());
29998         
29999         if (i  < 1) {
30000             return;
30001         }
30002         
30003         this.setActiveItem(this.barItems[i-1]);
30004     },
30005     
30006     format : function()
30007     {
30008         if(!this.barItems.length){
30009             return;
30010         }
30011      
30012         var width = 100 / this.barItems.length;
30013         
30014         Roo.each(this.barItems, function(i){
30015             i.el.setStyle('width', width + '%');
30016             i.topEl.el.setStyle('width', width + '%');
30017             i.bottomEl.el.setStyle('width', width + '%');
30018         }, this);
30019         
30020     }
30021     
30022 });
30023 /*
30024  * - LGPL
30025  *
30026  * Nav Progress Item
30027  * 
30028  */
30029
30030 /**
30031  * @class Roo.bootstrap.NavProgressItem
30032  * @extends Roo.bootstrap.Component
30033  * Bootstrap NavProgressItem class
30034  * @cfg {String} rid the reference id
30035  * @cfg {Boolean} active (true|false) Is item active default false
30036  * @cfg {Boolean} disabled (true|false) Is item active default false
30037  * @cfg {String} html
30038  * @cfg {String} position (top|bottom) text position default bottom
30039  * @cfg {String} icon show icon instead of number
30040  * 
30041  * @constructor
30042  * Create a new NavProgressItem
30043  * @param {Object} config The config object
30044  */
30045 Roo.bootstrap.NavProgressItem = function(config){
30046     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30047     this.addEvents({
30048         // raw events
30049         /**
30050          * @event click
30051          * The raw click event for the entire grid.
30052          * @param {Roo.bootstrap.NavProgressItem} this
30053          * @param {Roo.EventObject} e
30054          */
30055         "click" : true
30056     });
30057    
30058 };
30059
30060 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30061     
30062     rid : '',
30063     active : false,
30064     disabled : false,
30065     html : '',
30066     position : 'bottom',
30067     icon : false,
30068     
30069     getAutoCreate : function()
30070     {
30071         var iconCls = 'roo-navigation-bar-item-icon';
30072         
30073         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30074         
30075         var cfg = {
30076             tag: 'li',
30077             cls: 'roo-navigation-bar-item',
30078             cn : [
30079                 {
30080                     tag : 'i',
30081                     cls : iconCls
30082                 }
30083             ]
30084         };
30085         
30086         if(this.active){
30087             cfg.cls += ' active';
30088         }
30089         if(this.disabled){
30090             cfg.cls += ' disabled';
30091         }
30092         
30093         return cfg;
30094     },
30095     
30096     disable : function()
30097     {
30098         this.setDisabled(true);
30099     },
30100     
30101     enable : function()
30102     {
30103         this.setDisabled(false);
30104     },
30105     
30106     initEvents: function() 
30107     {
30108         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30109         
30110         this.iconEl.on('click', this.onClick, this);
30111     },
30112     
30113     onClick : function(e)
30114     {
30115         e.preventDefault();
30116         
30117         if(this.disabled){
30118             return;
30119         }
30120         
30121         if(this.fireEvent('click', this, e) === false){
30122             return;
30123         };
30124         
30125         this.parent().setActiveItem(this);
30126     },
30127     
30128     isActive: function () 
30129     {
30130         return this.active;
30131     },
30132     
30133     setActive : function(state)
30134     {
30135         if(this.active == state){
30136             return;
30137         }
30138         
30139         this.active = state;
30140         
30141         if (state) {
30142             this.el.addClass('active');
30143             return;
30144         }
30145         
30146         this.el.removeClass('active');
30147         
30148         return;
30149     },
30150     
30151     setDisabled : function(state)
30152     {
30153         if(this.disabled == state){
30154             return;
30155         }
30156         
30157         this.disabled = state;
30158         
30159         if (state) {
30160             this.el.addClass('disabled');
30161             return;
30162         }
30163         
30164         this.el.removeClass('disabled');
30165     },
30166     
30167     tooltipEl : function()
30168     {
30169         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30170     }
30171 });
30172  
30173
30174  /*
30175  * - LGPL
30176  *
30177  * FieldLabel
30178  * 
30179  */
30180
30181 /**
30182  * @class Roo.bootstrap.FieldLabel
30183  * @extends Roo.bootstrap.Component
30184  * Bootstrap FieldLabel class
30185  * @cfg {String} html contents of the element
30186  * @cfg {String} tag tag of the element default label
30187  * @cfg {String} cls class of the element
30188  * @cfg {String} target label target 
30189  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30190  * @cfg {String} invalidClass default "text-warning"
30191  * @cfg {String} validClass default "text-success"
30192  * @cfg {String} iconTooltip default "This field is required"
30193  * @cfg {String} indicatorpos (left|right) default left
30194  * 
30195  * @constructor
30196  * Create a new FieldLabel
30197  * @param {Object} config The config object
30198  */
30199
30200 Roo.bootstrap.FieldLabel = function(config){
30201     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30202     
30203     this.addEvents({
30204             /**
30205              * @event invalid
30206              * Fires after the field has been marked as invalid.
30207              * @param {Roo.form.FieldLabel} this
30208              * @param {String} msg The validation message
30209              */
30210             invalid : true,
30211             /**
30212              * @event valid
30213              * Fires after the field has been validated with no errors.
30214              * @param {Roo.form.FieldLabel} this
30215              */
30216             valid : true
30217         });
30218 };
30219
30220 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30221     
30222     tag: 'label',
30223     cls: '',
30224     html: '',
30225     target: '',
30226     allowBlank : true,
30227     invalidClass : 'has-warning',
30228     validClass : 'has-success',
30229     iconTooltip : 'This field is required',
30230     indicatorpos : 'left',
30231     
30232     getAutoCreate : function(){
30233         
30234         var cls = "";
30235         if (!this.allowBlank) {
30236             cls  = "visible";
30237         }
30238         
30239         var cfg = {
30240             tag : this.tag,
30241             cls : 'roo-bootstrap-field-label ' + this.cls,
30242             for : this.target,
30243             cn : [
30244                 {
30245                     tag : 'i',
30246                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30247                     tooltip : this.iconTooltip
30248                 },
30249                 {
30250                     tag : 'span',
30251                     html : this.html
30252                 }
30253             ] 
30254         };
30255         
30256         if(this.indicatorpos == 'right'){
30257             var cfg = {
30258                 tag : this.tag,
30259                 cls : 'roo-bootstrap-field-label ' + this.cls,
30260                 for : this.target,
30261                 cn : [
30262                     {
30263                         tag : 'span',
30264                         html : this.html
30265                     },
30266                     {
30267                         tag : 'i',
30268                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30269                         tooltip : this.iconTooltip
30270                     }
30271                 ] 
30272             };
30273         }
30274         
30275         return cfg;
30276     },
30277     
30278     initEvents: function() 
30279     {
30280         Roo.bootstrap.Element.superclass.initEvents.call(this);
30281         
30282         this.indicator = this.indicatorEl();
30283         
30284         if(this.indicator){
30285             this.indicator.removeClass('visible');
30286             this.indicator.addClass('invisible');
30287         }
30288         
30289         Roo.bootstrap.FieldLabel.register(this);
30290     },
30291     
30292     indicatorEl : function()
30293     {
30294         var indicator = this.el.select('i.roo-required-indicator',true).first();
30295         
30296         if(!indicator){
30297             return false;
30298         }
30299         
30300         return indicator;
30301         
30302     },
30303     
30304     /**
30305      * Mark this field as valid
30306      */
30307     markValid : function()
30308     {
30309         if(this.indicator){
30310             this.indicator.removeClass('visible');
30311             this.indicator.addClass('invisible');
30312         }
30313         
30314         this.el.removeClass(this.invalidClass);
30315         
30316         this.el.addClass(this.validClass);
30317         
30318         this.fireEvent('valid', this);
30319     },
30320     
30321     /**
30322      * Mark this field as invalid
30323      * @param {String} msg The validation message
30324      */
30325     markInvalid : function(msg)
30326     {
30327         if(this.indicator){
30328             this.indicator.removeClass('invisible');
30329             this.indicator.addClass('visible');
30330         }
30331         
30332         this.el.removeClass(this.validClass);
30333         
30334         this.el.addClass(this.invalidClass);
30335         
30336         this.fireEvent('invalid', this, msg);
30337     }
30338     
30339    
30340 });
30341
30342 Roo.apply(Roo.bootstrap.FieldLabel, {
30343     
30344     groups: {},
30345     
30346      /**
30347     * register a FieldLabel Group
30348     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30349     */
30350     register : function(label)
30351     {
30352         if(this.groups.hasOwnProperty(label.target)){
30353             return;
30354         }
30355      
30356         this.groups[label.target] = label;
30357         
30358     },
30359     /**
30360     * fetch a FieldLabel Group based on the target
30361     * @param {string} target
30362     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30363     */
30364     get: function(target) {
30365         if (typeof(this.groups[target]) == 'undefined') {
30366             return false;
30367         }
30368         
30369         return this.groups[target] ;
30370     }
30371 });
30372
30373  
30374
30375  /*
30376  * - LGPL
30377  *
30378  * page DateSplitField.
30379  * 
30380  */
30381
30382
30383 /**
30384  * @class Roo.bootstrap.DateSplitField
30385  * @extends Roo.bootstrap.Component
30386  * Bootstrap DateSplitField class
30387  * @cfg {string} fieldLabel - the label associated
30388  * @cfg {Number} labelWidth set the width of label (0-12)
30389  * @cfg {String} labelAlign (top|left)
30390  * @cfg {Boolean} dayAllowBlank (true|false) default false
30391  * @cfg {Boolean} monthAllowBlank (true|false) default false
30392  * @cfg {Boolean} yearAllowBlank (true|false) default false
30393  * @cfg {string} dayPlaceholder 
30394  * @cfg {string} monthPlaceholder
30395  * @cfg {string} yearPlaceholder
30396  * @cfg {string} dayFormat default 'd'
30397  * @cfg {string} monthFormat default 'm'
30398  * @cfg {string} yearFormat default 'Y'
30399  * @cfg {Number} labellg set the width of label (1-12)
30400  * @cfg {Number} labelmd set the width of label (1-12)
30401  * @cfg {Number} labelsm set the width of label (1-12)
30402  * @cfg {Number} labelxs set the width of label (1-12)
30403
30404  *     
30405  * @constructor
30406  * Create a new DateSplitField
30407  * @param {Object} config The config object
30408  */
30409
30410 Roo.bootstrap.DateSplitField = function(config){
30411     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30412     
30413     this.addEvents({
30414         // raw events
30415          /**
30416          * @event years
30417          * getting the data of years
30418          * @param {Roo.bootstrap.DateSplitField} this
30419          * @param {Object} years
30420          */
30421         "years" : true,
30422         /**
30423          * @event days
30424          * getting the data of days
30425          * @param {Roo.bootstrap.DateSplitField} this
30426          * @param {Object} days
30427          */
30428         "days" : true,
30429         /**
30430          * @event invalid
30431          * Fires after the field has been marked as invalid.
30432          * @param {Roo.form.Field} this
30433          * @param {String} msg The validation message
30434          */
30435         invalid : true,
30436        /**
30437          * @event valid
30438          * Fires after the field has been validated with no errors.
30439          * @param {Roo.form.Field} this
30440          */
30441         valid : true
30442     });
30443 };
30444
30445 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30446     
30447     fieldLabel : '',
30448     labelAlign : 'top',
30449     labelWidth : 3,
30450     dayAllowBlank : false,
30451     monthAllowBlank : false,
30452     yearAllowBlank : false,
30453     dayPlaceholder : '',
30454     monthPlaceholder : '',
30455     yearPlaceholder : '',
30456     dayFormat : 'd',
30457     monthFormat : 'm',
30458     yearFormat : 'Y',
30459     isFormField : true,
30460     labellg : 0,
30461     labelmd : 0,
30462     labelsm : 0,
30463     labelxs : 0,
30464     
30465     getAutoCreate : function()
30466     {
30467         var cfg = {
30468             tag : 'div',
30469             cls : 'row roo-date-split-field-group',
30470             cn : [
30471                 {
30472                     tag : 'input',
30473                     type : 'hidden',
30474                     cls : 'form-hidden-field roo-date-split-field-group-value',
30475                     name : this.name
30476                 }
30477             ]
30478         };
30479         
30480         var labelCls = 'col-md-12';
30481         var contentCls = 'col-md-4';
30482         
30483         if(this.fieldLabel){
30484             
30485             var label = {
30486                 tag : 'div',
30487                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30488                 cn : [
30489                     {
30490                         tag : 'label',
30491                         html : this.fieldLabel
30492                     }
30493                 ]
30494             };
30495             
30496             if(this.labelAlign == 'left'){
30497             
30498                 if(this.labelWidth > 12){
30499                     label.style = "width: " + this.labelWidth + 'px';
30500                 }
30501
30502                 if(this.labelWidth < 13 && this.labelmd == 0){
30503                     this.labelmd = this.labelWidth;
30504                 }
30505
30506                 if(this.labellg > 0){
30507                     labelCls = ' col-lg-' + this.labellg;
30508                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30509                 }
30510
30511                 if(this.labelmd > 0){
30512                     labelCls = ' col-md-' + this.labelmd;
30513                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30514                 }
30515
30516                 if(this.labelsm > 0){
30517                     labelCls = ' col-sm-' + this.labelsm;
30518                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30519                 }
30520
30521                 if(this.labelxs > 0){
30522                     labelCls = ' col-xs-' + this.labelxs;
30523                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30524                 }
30525             }
30526             
30527             label.cls += ' ' + labelCls;
30528             
30529             cfg.cn.push(label);
30530         }
30531         
30532         Roo.each(['day', 'month', 'year'], function(t){
30533             cfg.cn.push({
30534                 tag : 'div',
30535                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30536             });
30537         }, this);
30538         
30539         return cfg;
30540     },
30541     
30542     inputEl: function ()
30543     {
30544         return this.el.select('.roo-date-split-field-group-value', true).first();
30545     },
30546     
30547     onRender : function(ct, position) 
30548     {
30549         var _this = this;
30550         
30551         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30552         
30553         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30554         
30555         this.dayField = new Roo.bootstrap.ComboBox({
30556             allowBlank : this.dayAllowBlank,
30557             alwaysQuery : true,
30558             displayField : 'value',
30559             editable : false,
30560             fieldLabel : '',
30561             forceSelection : true,
30562             mode : 'local',
30563             placeholder : this.dayPlaceholder,
30564             selectOnFocus : true,
30565             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30566             triggerAction : 'all',
30567             typeAhead : true,
30568             valueField : 'value',
30569             store : new Roo.data.SimpleStore({
30570                 data : (function() {    
30571                     var days = [];
30572                     _this.fireEvent('days', _this, days);
30573                     return days;
30574                 })(),
30575                 fields : [ 'value' ]
30576             }),
30577             listeners : {
30578                 select : function (_self, record, index)
30579                 {
30580                     _this.setValue(_this.getValue());
30581                 }
30582             }
30583         });
30584
30585         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30586         
30587         this.monthField = new Roo.bootstrap.MonthField({
30588             after : '<i class=\"fa fa-calendar\"></i>',
30589             allowBlank : this.monthAllowBlank,
30590             placeholder : this.monthPlaceholder,
30591             readOnly : true,
30592             listeners : {
30593                 render : function (_self)
30594                 {
30595                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30596                         e.preventDefault();
30597                         _self.focus();
30598                     });
30599                 },
30600                 select : function (_self, oldvalue, newvalue)
30601                 {
30602                     _this.setValue(_this.getValue());
30603                 }
30604             }
30605         });
30606         
30607         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30608         
30609         this.yearField = new Roo.bootstrap.ComboBox({
30610             allowBlank : this.yearAllowBlank,
30611             alwaysQuery : true,
30612             displayField : 'value',
30613             editable : false,
30614             fieldLabel : '',
30615             forceSelection : true,
30616             mode : 'local',
30617             placeholder : this.yearPlaceholder,
30618             selectOnFocus : true,
30619             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30620             triggerAction : 'all',
30621             typeAhead : true,
30622             valueField : 'value',
30623             store : new Roo.data.SimpleStore({
30624                 data : (function() {
30625                     var years = [];
30626                     _this.fireEvent('years', _this, years);
30627                     return years;
30628                 })(),
30629                 fields : [ 'value' ]
30630             }),
30631             listeners : {
30632                 select : function (_self, record, index)
30633                 {
30634                     _this.setValue(_this.getValue());
30635                 }
30636             }
30637         });
30638
30639         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30640     },
30641     
30642     setValue : function(v, format)
30643     {
30644         this.inputEl.dom.value = v;
30645         
30646         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30647         
30648         var d = Date.parseDate(v, f);
30649         
30650         if(!d){
30651             this.validate();
30652             return;
30653         }
30654         
30655         this.setDay(d.format(this.dayFormat));
30656         this.setMonth(d.format(this.monthFormat));
30657         this.setYear(d.format(this.yearFormat));
30658         
30659         this.validate();
30660         
30661         return;
30662     },
30663     
30664     setDay : function(v)
30665     {
30666         this.dayField.setValue(v);
30667         this.inputEl.dom.value = this.getValue();
30668         this.validate();
30669         return;
30670     },
30671     
30672     setMonth : function(v)
30673     {
30674         this.monthField.setValue(v, true);
30675         this.inputEl.dom.value = this.getValue();
30676         this.validate();
30677         return;
30678     },
30679     
30680     setYear : function(v)
30681     {
30682         this.yearField.setValue(v);
30683         this.inputEl.dom.value = this.getValue();
30684         this.validate();
30685         return;
30686     },
30687     
30688     getDay : function()
30689     {
30690         return this.dayField.getValue();
30691     },
30692     
30693     getMonth : function()
30694     {
30695         return this.monthField.getValue();
30696     },
30697     
30698     getYear : function()
30699     {
30700         return this.yearField.getValue();
30701     },
30702     
30703     getValue : function()
30704     {
30705         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30706         
30707         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30708         
30709         return date;
30710     },
30711     
30712     reset : function()
30713     {
30714         this.setDay('');
30715         this.setMonth('');
30716         this.setYear('');
30717         this.inputEl.dom.value = '';
30718         this.validate();
30719         return;
30720     },
30721     
30722     validate : function()
30723     {
30724         var d = this.dayField.validate();
30725         var m = this.monthField.validate();
30726         var y = this.yearField.validate();
30727         
30728         var valid = true;
30729         
30730         if(
30731                 (!this.dayAllowBlank && !d) ||
30732                 (!this.monthAllowBlank && !m) ||
30733                 (!this.yearAllowBlank && !y)
30734         ){
30735             valid = false;
30736         }
30737         
30738         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30739             return valid;
30740         }
30741         
30742         if(valid){
30743             this.markValid();
30744             return valid;
30745         }
30746         
30747         this.markInvalid();
30748         
30749         return valid;
30750     },
30751     
30752     markValid : function()
30753     {
30754         
30755         var label = this.el.select('label', true).first();
30756         var icon = this.el.select('i.fa-star', true).first();
30757
30758         if(label && icon){
30759             icon.remove();
30760         }
30761         
30762         this.fireEvent('valid', this);
30763     },
30764     
30765      /**
30766      * Mark this field as invalid
30767      * @param {String} msg The validation message
30768      */
30769     markInvalid : function(msg)
30770     {
30771         
30772         var label = this.el.select('label', true).first();
30773         var icon = this.el.select('i.fa-star', true).first();
30774
30775         if(label && !icon){
30776             this.el.select('.roo-date-split-field-label', true).createChild({
30777                 tag : 'i',
30778                 cls : 'text-danger fa fa-lg fa-star',
30779                 tooltip : 'This field is required',
30780                 style : 'margin-right:5px;'
30781             }, label, true);
30782         }
30783         
30784         this.fireEvent('invalid', this, msg);
30785     },
30786     
30787     clearInvalid : function()
30788     {
30789         var label = this.el.select('label', true).first();
30790         var icon = this.el.select('i.fa-star', true).first();
30791
30792         if(label && icon){
30793             icon.remove();
30794         }
30795         
30796         this.fireEvent('valid', this);
30797     },
30798     
30799     getName: function()
30800     {
30801         return this.name;
30802     }
30803     
30804 });
30805
30806  /**
30807  *
30808  * This is based on 
30809  * http://masonry.desandro.com
30810  *
30811  * The idea is to render all the bricks based on vertical width...
30812  *
30813  * The original code extends 'outlayer' - we might need to use that....
30814  * 
30815  */
30816
30817
30818 /**
30819  * @class Roo.bootstrap.LayoutMasonry
30820  * @extends Roo.bootstrap.Component
30821  * Bootstrap Layout Masonry class
30822  * 
30823  * @constructor
30824  * Create a new Element
30825  * @param {Object} config The config object
30826  */
30827
30828 Roo.bootstrap.LayoutMasonry = function(config){
30829     
30830     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30831     
30832     this.bricks = [];
30833     
30834     Roo.bootstrap.LayoutMasonry.register(this);
30835     
30836     this.addEvents({
30837         // raw events
30838         /**
30839          * @event layout
30840          * Fire after layout the items
30841          * @param {Roo.bootstrap.LayoutMasonry} this
30842          * @param {Roo.EventObject} e
30843          */
30844         "layout" : true
30845     });
30846     
30847 };
30848
30849 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30850     
30851     /**
30852      * @cfg {Boolean} isLayoutInstant = no animation?
30853      */   
30854     isLayoutInstant : false, // needed?
30855    
30856     /**
30857      * @cfg {Number} boxWidth  width of the columns
30858      */   
30859     boxWidth : 450,
30860     
30861       /**
30862      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30863      */   
30864     boxHeight : 0,
30865     
30866     /**
30867      * @cfg {Number} padWidth padding below box..
30868      */   
30869     padWidth : 10, 
30870     
30871     /**
30872      * @cfg {Number} gutter gutter width..
30873      */   
30874     gutter : 10,
30875     
30876      /**
30877      * @cfg {Number} maxCols maximum number of columns
30878      */   
30879     
30880     maxCols: 0,
30881     
30882     /**
30883      * @cfg {Boolean} isAutoInitial defalut true
30884      */   
30885     isAutoInitial : true, 
30886     
30887     containerWidth: 0,
30888     
30889     /**
30890      * @cfg {Boolean} isHorizontal defalut false
30891      */   
30892     isHorizontal : false, 
30893
30894     currentSize : null,
30895     
30896     tag: 'div',
30897     
30898     cls: '',
30899     
30900     bricks: null, //CompositeElement
30901     
30902     cols : 1,
30903     
30904     _isLayoutInited : false,
30905     
30906 //    isAlternative : false, // only use for vertical layout...
30907     
30908     /**
30909      * @cfg {Number} alternativePadWidth padding below box..
30910      */   
30911     alternativePadWidth : 50,
30912     
30913     selectedBrick : [],
30914     
30915     getAutoCreate : function(){
30916         
30917         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30918         
30919         var cfg = {
30920             tag: this.tag,
30921             cls: 'blog-masonary-wrapper ' + this.cls,
30922             cn : {
30923                 cls : 'mas-boxes masonary'
30924             }
30925         };
30926         
30927         return cfg;
30928     },
30929     
30930     getChildContainer: function( )
30931     {
30932         if (this.boxesEl) {
30933             return this.boxesEl;
30934         }
30935         
30936         this.boxesEl = this.el.select('.mas-boxes').first();
30937         
30938         return this.boxesEl;
30939     },
30940     
30941     
30942     initEvents : function()
30943     {
30944         var _this = this;
30945         
30946         if(this.isAutoInitial){
30947             Roo.log('hook children rendered');
30948             this.on('childrenrendered', function() {
30949                 Roo.log('children rendered');
30950                 _this.initial();
30951             } ,this);
30952         }
30953     },
30954     
30955     initial : function()
30956     {
30957         this.selectedBrick = [];
30958         
30959         this.currentSize = this.el.getBox(true);
30960         
30961         Roo.EventManager.onWindowResize(this.resize, this); 
30962
30963         if(!this.isAutoInitial){
30964             this.layout();
30965             return;
30966         }
30967         
30968         this.layout();
30969         
30970         return;
30971         //this.layout.defer(500,this);
30972         
30973     },
30974     
30975     resize : function()
30976     {
30977         var cs = this.el.getBox(true);
30978         
30979         if (
30980                 this.currentSize.width == cs.width && 
30981                 this.currentSize.x == cs.x && 
30982                 this.currentSize.height == cs.height && 
30983                 this.currentSize.y == cs.y 
30984         ) {
30985             Roo.log("no change in with or X or Y");
30986             return;
30987         }
30988         
30989         this.currentSize = cs;
30990         
30991         this.layout();
30992         
30993     },
30994     
30995     layout : function()
30996     {   
30997         this._resetLayout();
30998         
30999         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31000         
31001         this.layoutItems( isInstant );
31002       
31003         this._isLayoutInited = true;
31004         
31005         this.fireEvent('layout', this);
31006         
31007     },
31008     
31009     _resetLayout : function()
31010     {
31011         if(this.isHorizontal){
31012             this.horizontalMeasureColumns();
31013             return;
31014         }
31015         
31016         this.verticalMeasureColumns();
31017         
31018     },
31019     
31020     verticalMeasureColumns : function()
31021     {
31022         this.getContainerWidth();
31023         
31024 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31025 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31026 //            return;
31027 //        }
31028         
31029         var boxWidth = this.boxWidth + this.padWidth;
31030         
31031         if(this.containerWidth < this.boxWidth){
31032             boxWidth = this.containerWidth
31033         }
31034         
31035         var containerWidth = this.containerWidth;
31036         
31037         var cols = Math.floor(containerWidth / boxWidth);
31038         
31039         this.cols = Math.max( cols, 1 );
31040         
31041         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31042         
31043         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31044         
31045         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31046         
31047         this.colWidth = boxWidth + avail - this.padWidth;
31048         
31049         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31050         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31051     },
31052     
31053     horizontalMeasureColumns : function()
31054     {
31055         this.getContainerWidth();
31056         
31057         var boxWidth = this.boxWidth;
31058         
31059         if(this.containerWidth < boxWidth){
31060             boxWidth = this.containerWidth;
31061         }
31062         
31063         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31064         
31065         this.el.setHeight(boxWidth);
31066         
31067     },
31068     
31069     getContainerWidth : function()
31070     {
31071         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31072     },
31073     
31074     layoutItems : function( isInstant )
31075     {
31076         Roo.log(this.bricks);
31077         
31078         var items = Roo.apply([], this.bricks);
31079         
31080         if(this.isHorizontal){
31081             this._horizontalLayoutItems( items , isInstant );
31082             return;
31083         }
31084         
31085 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31086 //            this._verticalAlternativeLayoutItems( items , isInstant );
31087 //            return;
31088 //        }
31089         
31090         this._verticalLayoutItems( items , isInstant );
31091         
31092     },
31093     
31094     _verticalLayoutItems : function ( items , isInstant)
31095     {
31096         if ( !items || !items.length ) {
31097             return;
31098         }
31099         
31100         var standard = [
31101             ['xs', 'xs', 'xs', 'tall'],
31102             ['xs', 'xs', 'tall'],
31103             ['xs', 'xs', 'sm'],
31104             ['xs', 'xs', 'xs'],
31105             ['xs', 'tall'],
31106             ['xs', 'sm'],
31107             ['xs', 'xs'],
31108             ['xs'],
31109             
31110             ['sm', 'xs', 'xs'],
31111             ['sm', 'xs'],
31112             ['sm'],
31113             
31114             ['tall', 'xs', 'xs', 'xs'],
31115             ['tall', 'xs', 'xs'],
31116             ['tall', 'xs'],
31117             ['tall']
31118             
31119         ];
31120         
31121         var queue = [];
31122         
31123         var boxes = [];
31124         
31125         var box = [];
31126         
31127         Roo.each(items, function(item, k){
31128             
31129             switch (item.size) {
31130                 // these layouts take up a full box,
31131                 case 'md' :
31132                 case 'md-left' :
31133                 case 'md-right' :
31134                 case 'wide' :
31135                     
31136                     if(box.length){
31137                         boxes.push(box);
31138                         box = [];
31139                     }
31140                     
31141                     boxes.push([item]);
31142                     
31143                     break;
31144                     
31145                 case 'xs' :
31146                 case 'sm' :
31147                 case 'tall' :
31148                     
31149                     box.push(item);
31150                     
31151                     break;
31152                 default :
31153                     break;
31154                     
31155             }
31156             
31157         }, this);
31158         
31159         if(box.length){
31160             boxes.push(box);
31161             box = [];
31162         }
31163         
31164         var filterPattern = function(box, length)
31165         {
31166             if(!box.length){
31167                 return;
31168             }
31169             
31170             var match = false;
31171             
31172             var pattern = box.slice(0, length);
31173             
31174             var format = [];
31175             
31176             Roo.each(pattern, function(i){
31177                 format.push(i.size);
31178             }, this);
31179             
31180             Roo.each(standard, function(s){
31181                 
31182                 if(String(s) != String(format)){
31183                     return;
31184                 }
31185                 
31186                 match = true;
31187                 return false;
31188                 
31189             }, this);
31190             
31191             if(!match && length == 1){
31192                 return;
31193             }
31194             
31195             if(!match){
31196                 filterPattern(box, length - 1);
31197                 return;
31198             }
31199                 
31200             queue.push(pattern);
31201
31202             box = box.slice(length, box.length);
31203
31204             filterPattern(box, 4);
31205
31206             return;
31207             
31208         }
31209         
31210         Roo.each(boxes, function(box, k){
31211             
31212             if(!box.length){
31213                 return;
31214             }
31215             
31216             if(box.length == 1){
31217                 queue.push(box);
31218                 return;
31219             }
31220             
31221             filterPattern(box, 4);
31222             
31223         }, this);
31224         
31225         this._processVerticalLayoutQueue( queue, isInstant );
31226         
31227     },
31228     
31229 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31230 //    {
31231 //        if ( !items || !items.length ) {
31232 //            return;
31233 //        }
31234 //
31235 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31236 //        
31237 //    },
31238     
31239     _horizontalLayoutItems : function ( items , isInstant)
31240     {
31241         if ( !items || !items.length || items.length < 3) {
31242             return;
31243         }
31244         
31245         items.reverse();
31246         
31247         var eItems = items.slice(0, 3);
31248         
31249         items = items.slice(3, items.length);
31250         
31251         var standard = [
31252             ['xs', 'xs', 'xs', 'wide'],
31253             ['xs', 'xs', 'wide'],
31254             ['xs', 'xs', 'sm'],
31255             ['xs', 'xs', 'xs'],
31256             ['xs', 'wide'],
31257             ['xs', 'sm'],
31258             ['xs', 'xs'],
31259             ['xs'],
31260             
31261             ['sm', 'xs', 'xs'],
31262             ['sm', 'xs'],
31263             ['sm'],
31264             
31265             ['wide', 'xs', 'xs', 'xs'],
31266             ['wide', 'xs', 'xs'],
31267             ['wide', 'xs'],
31268             ['wide'],
31269             
31270             ['wide-thin']
31271         ];
31272         
31273         var queue = [];
31274         
31275         var boxes = [];
31276         
31277         var box = [];
31278         
31279         Roo.each(items, function(item, k){
31280             
31281             switch (item.size) {
31282                 case 'md' :
31283                 case 'md-left' :
31284                 case 'md-right' :
31285                 case 'tall' :
31286                     
31287                     if(box.length){
31288                         boxes.push(box);
31289                         box = [];
31290                     }
31291                     
31292                     boxes.push([item]);
31293                     
31294                     break;
31295                     
31296                 case 'xs' :
31297                 case 'sm' :
31298                 case 'wide' :
31299                 case 'wide-thin' :
31300                     
31301                     box.push(item);
31302                     
31303                     break;
31304                 default :
31305                     break;
31306                     
31307             }
31308             
31309         }, this);
31310         
31311         if(box.length){
31312             boxes.push(box);
31313             box = [];
31314         }
31315         
31316         var filterPattern = function(box, length)
31317         {
31318             if(!box.length){
31319                 return;
31320             }
31321             
31322             var match = false;
31323             
31324             var pattern = box.slice(0, length);
31325             
31326             var format = [];
31327             
31328             Roo.each(pattern, function(i){
31329                 format.push(i.size);
31330             }, this);
31331             
31332             Roo.each(standard, function(s){
31333                 
31334                 if(String(s) != String(format)){
31335                     return;
31336                 }
31337                 
31338                 match = true;
31339                 return false;
31340                 
31341             }, this);
31342             
31343             if(!match && length == 1){
31344                 return;
31345             }
31346             
31347             if(!match){
31348                 filterPattern(box, length - 1);
31349                 return;
31350             }
31351                 
31352             queue.push(pattern);
31353
31354             box = box.slice(length, box.length);
31355
31356             filterPattern(box, 4);
31357
31358             return;
31359             
31360         }
31361         
31362         Roo.each(boxes, function(box, k){
31363             
31364             if(!box.length){
31365                 return;
31366             }
31367             
31368             if(box.length == 1){
31369                 queue.push(box);
31370                 return;
31371             }
31372             
31373             filterPattern(box, 4);
31374             
31375         }, this);
31376         
31377         
31378         var prune = [];
31379         
31380         var pos = this.el.getBox(true);
31381         
31382         var minX = pos.x;
31383         
31384         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31385         
31386         var hit_end = false;
31387         
31388         Roo.each(queue, function(box){
31389             
31390             if(hit_end){
31391                 
31392                 Roo.each(box, function(b){
31393                 
31394                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31395                     b.el.hide();
31396
31397                 }, this);
31398
31399                 return;
31400             }
31401             
31402             var mx = 0;
31403             
31404             Roo.each(box, function(b){
31405                 
31406                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31407                 b.el.show();
31408
31409                 mx = Math.max(mx, b.x);
31410                 
31411             }, this);
31412             
31413             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31414             
31415             if(maxX < minX){
31416                 
31417                 Roo.each(box, function(b){
31418                 
31419                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31420                     b.el.hide();
31421                     
31422                 }, this);
31423                 
31424                 hit_end = true;
31425                 
31426                 return;
31427             }
31428             
31429             prune.push(box);
31430             
31431         }, this);
31432         
31433         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31434     },
31435     
31436     /** Sets position of item in DOM
31437     * @param {Element} item
31438     * @param {Number} x - horizontal position
31439     * @param {Number} y - vertical position
31440     * @param {Boolean} isInstant - disables transitions
31441     */
31442     _processVerticalLayoutQueue : function( queue, isInstant )
31443     {
31444         var pos = this.el.getBox(true);
31445         var x = pos.x;
31446         var y = pos.y;
31447         var maxY = [];
31448         
31449         for (var i = 0; i < this.cols; i++){
31450             maxY[i] = pos.y;
31451         }
31452         
31453         Roo.each(queue, function(box, k){
31454             
31455             var col = k % this.cols;
31456             
31457             Roo.each(box, function(b,kk){
31458                 
31459                 b.el.position('absolute');
31460                 
31461                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31462                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31463                 
31464                 if(b.size == 'md-left' || b.size == 'md-right'){
31465                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31466                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31467                 }
31468                 
31469                 b.el.setWidth(width);
31470                 b.el.setHeight(height);
31471                 // iframe?
31472                 b.el.select('iframe',true).setSize(width,height);
31473                 
31474             }, this);
31475             
31476             for (var i = 0; i < this.cols; i++){
31477                 
31478                 if(maxY[i] < maxY[col]){
31479                     col = i;
31480                     continue;
31481                 }
31482                 
31483                 col = Math.min(col, i);
31484                 
31485             }
31486             
31487             x = pos.x + col * (this.colWidth + this.padWidth);
31488             
31489             y = maxY[col];
31490             
31491             var positions = [];
31492             
31493             switch (box.length){
31494                 case 1 :
31495                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31496                     break;
31497                 case 2 :
31498                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31499                     break;
31500                 case 3 :
31501                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31502                     break;
31503                 case 4 :
31504                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31505                     break;
31506                 default :
31507                     break;
31508             }
31509             
31510             Roo.each(box, function(b,kk){
31511                 
31512                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31513                 
31514                 var sz = b.el.getSize();
31515                 
31516                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31517                 
31518             }, this);
31519             
31520         }, this);
31521         
31522         var mY = 0;
31523         
31524         for (var i = 0; i < this.cols; i++){
31525             mY = Math.max(mY, maxY[i]);
31526         }
31527         
31528         this.el.setHeight(mY - pos.y);
31529         
31530     },
31531     
31532 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31533 //    {
31534 //        var pos = this.el.getBox(true);
31535 //        var x = pos.x;
31536 //        var y = pos.y;
31537 //        var maxX = pos.right;
31538 //        
31539 //        var maxHeight = 0;
31540 //        
31541 //        Roo.each(items, function(item, k){
31542 //            
31543 //            var c = k % 2;
31544 //            
31545 //            item.el.position('absolute');
31546 //                
31547 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31548 //
31549 //            item.el.setWidth(width);
31550 //
31551 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31552 //
31553 //            item.el.setHeight(height);
31554 //            
31555 //            if(c == 0){
31556 //                item.el.setXY([x, y], isInstant ? false : true);
31557 //            } else {
31558 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31559 //            }
31560 //            
31561 //            y = y + height + this.alternativePadWidth;
31562 //            
31563 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31564 //            
31565 //        }, this);
31566 //        
31567 //        this.el.setHeight(maxHeight);
31568 //        
31569 //    },
31570     
31571     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31572     {
31573         var pos = this.el.getBox(true);
31574         
31575         var minX = pos.x;
31576         var minY = pos.y;
31577         
31578         var maxX = pos.right;
31579         
31580         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31581         
31582         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31583         
31584         Roo.each(queue, function(box, k){
31585             
31586             Roo.each(box, function(b, kk){
31587                 
31588                 b.el.position('absolute');
31589                 
31590                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31591                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31592                 
31593                 if(b.size == 'md-left' || b.size == 'md-right'){
31594                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31595                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31596                 }
31597                 
31598                 b.el.setWidth(width);
31599                 b.el.setHeight(height);
31600                 
31601             }, this);
31602             
31603             if(!box.length){
31604                 return;
31605             }
31606             
31607             var positions = [];
31608             
31609             switch (box.length){
31610                 case 1 :
31611                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31612                     break;
31613                 case 2 :
31614                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31615                     break;
31616                 case 3 :
31617                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31618                     break;
31619                 case 4 :
31620                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31621                     break;
31622                 default :
31623                     break;
31624             }
31625             
31626             Roo.each(box, function(b,kk){
31627                 
31628                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31629                 
31630                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31631                 
31632             }, this);
31633             
31634         }, this);
31635         
31636     },
31637     
31638     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31639     {
31640         Roo.each(eItems, function(b,k){
31641             
31642             b.size = (k == 0) ? 'sm' : 'xs';
31643             b.x = (k == 0) ? 2 : 1;
31644             b.y = (k == 0) ? 2 : 1;
31645             
31646             b.el.position('absolute');
31647             
31648             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31649                 
31650             b.el.setWidth(width);
31651             
31652             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31653             
31654             b.el.setHeight(height);
31655             
31656         }, this);
31657
31658         var positions = [];
31659         
31660         positions.push({
31661             x : maxX - this.unitWidth * 2 - this.gutter,
31662             y : minY
31663         });
31664         
31665         positions.push({
31666             x : maxX - this.unitWidth,
31667             y : minY + (this.unitWidth + this.gutter) * 2
31668         });
31669         
31670         positions.push({
31671             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31672             y : minY
31673         });
31674         
31675         Roo.each(eItems, function(b,k){
31676             
31677             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31678
31679         }, this);
31680         
31681     },
31682     
31683     getVerticalOneBoxColPositions : function(x, y, box)
31684     {
31685         var pos = [];
31686         
31687         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31688         
31689         if(box[0].size == 'md-left'){
31690             rand = 0;
31691         }
31692         
31693         if(box[0].size == 'md-right'){
31694             rand = 1;
31695         }
31696         
31697         pos.push({
31698             x : x + (this.unitWidth + this.gutter) * rand,
31699             y : y
31700         });
31701         
31702         return pos;
31703     },
31704     
31705     getVerticalTwoBoxColPositions : function(x, y, box)
31706     {
31707         var pos = [];
31708         
31709         if(box[0].size == 'xs'){
31710             
31711             pos.push({
31712                 x : x,
31713                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31714             });
31715
31716             pos.push({
31717                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31718                 y : y
31719             });
31720             
31721             return pos;
31722             
31723         }
31724         
31725         pos.push({
31726             x : x,
31727             y : y
31728         });
31729
31730         pos.push({
31731             x : x + (this.unitWidth + this.gutter) * 2,
31732             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31733         });
31734         
31735         return pos;
31736         
31737     },
31738     
31739     getVerticalThreeBoxColPositions : function(x, y, box)
31740     {
31741         var pos = [];
31742         
31743         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31744             
31745             pos.push({
31746                 x : x,
31747                 y : y
31748             });
31749
31750             pos.push({
31751                 x : x + (this.unitWidth + this.gutter) * 1,
31752                 y : y
31753             });
31754             
31755             pos.push({
31756                 x : x + (this.unitWidth + this.gutter) * 2,
31757                 y : y
31758             });
31759             
31760             return pos;
31761             
31762         }
31763         
31764         if(box[0].size == 'xs' && box[1].size == 'xs'){
31765             
31766             pos.push({
31767                 x : x,
31768                 y : y
31769             });
31770
31771             pos.push({
31772                 x : x,
31773                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31774             });
31775             
31776             pos.push({
31777                 x : x + (this.unitWidth + this.gutter) * 1,
31778                 y : y
31779             });
31780             
31781             return pos;
31782             
31783         }
31784         
31785         pos.push({
31786             x : x,
31787             y : y
31788         });
31789
31790         pos.push({
31791             x : x + (this.unitWidth + this.gutter) * 2,
31792             y : y
31793         });
31794
31795         pos.push({
31796             x : x + (this.unitWidth + this.gutter) * 2,
31797             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31798         });
31799             
31800         return pos;
31801         
31802     },
31803     
31804     getVerticalFourBoxColPositions : function(x, y, box)
31805     {
31806         var pos = [];
31807         
31808         if(box[0].size == 'xs'){
31809             
31810             pos.push({
31811                 x : x,
31812                 y : y
31813             });
31814
31815             pos.push({
31816                 x : x,
31817                 y : y + (this.unitHeight + this.gutter) * 1
31818             });
31819             
31820             pos.push({
31821                 x : x,
31822                 y : y + (this.unitHeight + this.gutter) * 2
31823             });
31824             
31825             pos.push({
31826                 x : x + (this.unitWidth + this.gutter) * 1,
31827                 y : y
31828             });
31829             
31830             return pos;
31831             
31832         }
31833         
31834         pos.push({
31835             x : x,
31836             y : y
31837         });
31838
31839         pos.push({
31840             x : x + (this.unitWidth + this.gutter) * 2,
31841             y : y
31842         });
31843
31844         pos.push({
31845             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31846             y : y + (this.unitHeight + this.gutter) * 1
31847         });
31848
31849         pos.push({
31850             x : x + (this.unitWidth + this.gutter) * 2,
31851             y : y + (this.unitWidth + this.gutter) * 2
31852         });
31853
31854         return pos;
31855         
31856     },
31857     
31858     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31859     {
31860         var pos = [];
31861         
31862         if(box[0].size == 'md-left'){
31863             pos.push({
31864                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31865                 y : minY
31866             });
31867             
31868             return pos;
31869         }
31870         
31871         if(box[0].size == 'md-right'){
31872             pos.push({
31873                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31874                 y : minY + (this.unitWidth + this.gutter) * 1
31875             });
31876             
31877             return pos;
31878         }
31879         
31880         var rand = Math.floor(Math.random() * (4 - box[0].y));
31881         
31882         pos.push({
31883             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31884             y : minY + (this.unitWidth + this.gutter) * rand
31885         });
31886         
31887         return pos;
31888         
31889     },
31890     
31891     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31892     {
31893         var pos = [];
31894         
31895         if(box[0].size == 'xs'){
31896             
31897             pos.push({
31898                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31899                 y : minY
31900             });
31901
31902             pos.push({
31903                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31904                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31905             });
31906             
31907             return pos;
31908             
31909         }
31910         
31911         pos.push({
31912             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31913             y : minY
31914         });
31915
31916         pos.push({
31917             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31918             y : minY + (this.unitWidth + this.gutter) * 2
31919         });
31920         
31921         return pos;
31922         
31923     },
31924     
31925     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31926     {
31927         var pos = [];
31928         
31929         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31930             
31931             pos.push({
31932                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31933                 y : minY
31934             });
31935
31936             pos.push({
31937                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31938                 y : minY + (this.unitWidth + this.gutter) * 1
31939             });
31940             
31941             pos.push({
31942                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31943                 y : minY + (this.unitWidth + this.gutter) * 2
31944             });
31945             
31946             return pos;
31947             
31948         }
31949         
31950         if(box[0].size == 'xs' && box[1].size == 'xs'){
31951             
31952             pos.push({
31953                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31954                 y : minY
31955             });
31956
31957             pos.push({
31958                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31959                 y : minY
31960             });
31961             
31962             pos.push({
31963                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31964                 y : minY + (this.unitWidth + this.gutter) * 1
31965             });
31966             
31967             return pos;
31968             
31969         }
31970         
31971         pos.push({
31972             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31973             y : minY
31974         });
31975
31976         pos.push({
31977             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31978             y : minY + (this.unitWidth + this.gutter) * 2
31979         });
31980
31981         pos.push({
31982             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31983             y : minY + (this.unitWidth + this.gutter) * 2
31984         });
31985             
31986         return pos;
31987         
31988     },
31989     
31990     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31991     {
31992         var pos = [];
31993         
31994         if(box[0].size == 'xs'){
31995             
31996             pos.push({
31997                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31998                 y : minY
31999             });
32000
32001             pos.push({
32002                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].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) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32008                 y : minY
32009             });
32010             
32011             pos.push({
32012                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32013                 y : minY + (this.unitWidth + this.gutter) * 1
32014             });
32015             
32016             return pos;
32017             
32018         }
32019         
32020         pos.push({
32021             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32022             y : minY
32023         });
32024         
32025         pos.push({
32026             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32027             y : minY + (this.unitWidth + this.gutter) * 2
32028         });
32029         
32030         pos.push({
32031             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].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) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32037             y : minY + (this.unitWidth + this.gutter) * 2
32038         });
32039
32040         return pos;
32041         
32042     },
32043     
32044     /**
32045     * remove a Masonry Brick
32046     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32047     */
32048     removeBrick : function(brick_id)
32049     {
32050         if (!brick_id) {
32051             return;
32052         }
32053         
32054         for (var i = 0; i<this.bricks.length; i++) {
32055             if (this.bricks[i].id == brick_id) {
32056                 this.bricks.splice(i,1);
32057                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32058                 this.initial();
32059             }
32060         }
32061     },
32062     
32063     /**
32064     * adds a Masonry Brick
32065     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32066     */
32067     addBrick : function(cfg)
32068     {
32069         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32070         //this.register(cn);
32071         cn.parentId = this.id;
32072         cn.render(this.el);
32073         return cn;
32074     },
32075     
32076     /**
32077     * register a Masonry Brick
32078     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32079     */
32080     
32081     register : function(brick)
32082     {
32083         this.bricks.push(brick);
32084         brick.masonryId = this.id;
32085     },
32086     
32087     /**
32088     * clear all the Masonry Brick
32089     */
32090     clearAll : function()
32091     {
32092         this.bricks = [];
32093         //this.getChildContainer().dom.innerHTML = "";
32094         this.el.dom.innerHTML = '';
32095     },
32096     
32097     getSelected : function()
32098     {
32099         if (!this.selectedBrick) {
32100             return false;
32101         }
32102         
32103         return this.selectedBrick;
32104     }
32105 });
32106
32107 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32108     
32109     groups: {},
32110      /**
32111     * register a Masonry Layout
32112     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32113     */
32114     
32115     register : function(layout)
32116     {
32117         this.groups[layout.id] = layout;
32118     },
32119     /**
32120     * fetch a  Masonry Layout based on the masonry layout ID
32121     * @param {string} the masonry layout to add
32122     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32123     */
32124     
32125     get: function(layout_id) {
32126         if (typeof(this.groups[layout_id]) == 'undefined') {
32127             return false;
32128         }
32129         return this.groups[layout_id] ;
32130     }
32131     
32132     
32133     
32134 });
32135
32136  
32137
32138  /**
32139  *
32140  * This is based on 
32141  * http://masonry.desandro.com
32142  *
32143  * The idea is to render all the bricks based on vertical width...
32144  *
32145  * The original code extends 'outlayer' - we might need to use that....
32146  * 
32147  */
32148
32149
32150 /**
32151  * @class Roo.bootstrap.LayoutMasonryAuto
32152  * @extends Roo.bootstrap.Component
32153  * Bootstrap Layout Masonry class
32154  * 
32155  * @constructor
32156  * Create a new Element
32157  * @param {Object} config The config object
32158  */
32159
32160 Roo.bootstrap.LayoutMasonryAuto = function(config){
32161     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32162 };
32163
32164 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32165     
32166       /**
32167      * @cfg {Boolean} isFitWidth  - resize the width..
32168      */   
32169     isFitWidth : false,  // options..
32170     /**
32171      * @cfg {Boolean} isOriginLeft = left align?
32172      */   
32173     isOriginLeft : true,
32174     /**
32175      * @cfg {Boolean} isOriginTop = top align?
32176      */   
32177     isOriginTop : false,
32178     /**
32179      * @cfg {Boolean} isLayoutInstant = no animation?
32180      */   
32181     isLayoutInstant : false, // needed?
32182     /**
32183      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32184      */   
32185     isResizingContainer : true,
32186     /**
32187      * @cfg {Number} columnWidth  width of the columns 
32188      */   
32189     
32190     columnWidth : 0,
32191     
32192     /**
32193      * @cfg {Number} maxCols maximum number of columns
32194      */   
32195     
32196     maxCols: 0,
32197     /**
32198      * @cfg {Number} padHeight padding below box..
32199      */   
32200     
32201     padHeight : 10, 
32202     
32203     /**
32204      * @cfg {Boolean} isAutoInitial defalut true
32205      */   
32206     
32207     isAutoInitial : true, 
32208     
32209     // private?
32210     gutter : 0,
32211     
32212     containerWidth: 0,
32213     initialColumnWidth : 0,
32214     currentSize : null,
32215     
32216     colYs : null, // array.
32217     maxY : 0,
32218     padWidth: 10,
32219     
32220     
32221     tag: 'div',
32222     cls: '',
32223     bricks: null, //CompositeElement
32224     cols : 0, // array?
32225     // element : null, // wrapped now this.el
32226     _isLayoutInited : null, 
32227     
32228     
32229     getAutoCreate : function(){
32230         
32231         var cfg = {
32232             tag: this.tag,
32233             cls: 'blog-masonary-wrapper ' + this.cls,
32234             cn : {
32235                 cls : 'mas-boxes masonary'
32236             }
32237         };
32238         
32239         return cfg;
32240     },
32241     
32242     getChildContainer: function( )
32243     {
32244         if (this.boxesEl) {
32245             return this.boxesEl;
32246         }
32247         
32248         this.boxesEl = this.el.select('.mas-boxes').first();
32249         
32250         return this.boxesEl;
32251     },
32252     
32253     
32254     initEvents : function()
32255     {
32256         var _this = this;
32257         
32258         if(this.isAutoInitial){
32259             Roo.log('hook children rendered');
32260             this.on('childrenrendered', function() {
32261                 Roo.log('children rendered');
32262                 _this.initial();
32263             } ,this);
32264         }
32265         
32266     },
32267     
32268     initial : function()
32269     {
32270         this.reloadItems();
32271
32272         this.currentSize = this.el.getBox(true);
32273
32274         /// was window resize... - let's see if this works..
32275         Roo.EventManager.onWindowResize(this.resize, this); 
32276
32277         if(!this.isAutoInitial){
32278             this.layout();
32279             return;
32280         }
32281         
32282         this.layout.defer(500,this);
32283     },
32284     
32285     reloadItems: function()
32286     {
32287         this.bricks = this.el.select('.masonry-brick', true);
32288         
32289         this.bricks.each(function(b) {
32290             //Roo.log(b.getSize());
32291             if (!b.attr('originalwidth')) {
32292                 b.attr('originalwidth',  b.getSize().width);
32293             }
32294             
32295         });
32296         
32297         Roo.log(this.bricks.elements.length);
32298     },
32299     
32300     resize : function()
32301     {
32302         Roo.log('resize');
32303         var cs = this.el.getBox(true);
32304         
32305         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32306             Roo.log("no change in with or X");
32307             return;
32308         }
32309         this.currentSize = cs;
32310         this.layout();
32311     },
32312     
32313     layout : function()
32314     {
32315          Roo.log('layout');
32316         this._resetLayout();
32317         //this._manageStamps();
32318       
32319         // don't animate first layout
32320         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32321         this.layoutItems( isInstant );
32322       
32323         // flag for initalized
32324         this._isLayoutInited = true;
32325     },
32326     
32327     layoutItems : function( isInstant )
32328     {
32329         //var items = this._getItemsForLayout( this.items );
32330         // original code supports filtering layout items.. we just ignore it..
32331         
32332         this._layoutItems( this.bricks , isInstant );
32333       
32334         this._postLayout();
32335     },
32336     _layoutItems : function ( items , isInstant)
32337     {
32338        //this.fireEvent( 'layout', this, items );
32339     
32340
32341         if ( !items || !items.elements.length ) {
32342           // no items, emit event with empty array
32343             return;
32344         }
32345
32346         var queue = [];
32347         items.each(function(item) {
32348             Roo.log("layout item");
32349             Roo.log(item);
32350             // get x/y object from method
32351             var position = this._getItemLayoutPosition( item );
32352             // enqueue
32353             position.item = item;
32354             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32355             queue.push( position );
32356         }, this);
32357       
32358         this._processLayoutQueue( queue );
32359     },
32360     /** Sets position of item in DOM
32361     * @param {Element} item
32362     * @param {Number} x - horizontal position
32363     * @param {Number} y - vertical position
32364     * @param {Boolean} isInstant - disables transitions
32365     */
32366     _processLayoutQueue : function( queue )
32367     {
32368         for ( var i=0, len = queue.length; i < len; i++ ) {
32369             var obj = queue[i];
32370             obj.item.position('absolute');
32371             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32372         }
32373     },
32374       
32375     
32376     /**
32377     * Any logic you want to do after each layout,
32378     * i.e. size the container
32379     */
32380     _postLayout : function()
32381     {
32382         this.resizeContainer();
32383     },
32384     
32385     resizeContainer : function()
32386     {
32387         if ( !this.isResizingContainer ) {
32388             return;
32389         }
32390         var size = this._getContainerSize();
32391         if ( size ) {
32392             this.el.setSize(size.width,size.height);
32393             this.boxesEl.setSize(size.width,size.height);
32394         }
32395     },
32396     
32397     
32398     
32399     _resetLayout : function()
32400     {
32401         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32402         this.colWidth = this.el.getWidth();
32403         //this.gutter = this.el.getWidth(); 
32404         
32405         this.measureColumns();
32406
32407         // reset column Y
32408         var i = this.cols;
32409         this.colYs = [];
32410         while (i--) {
32411             this.colYs.push( 0 );
32412         }
32413     
32414         this.maxY = 0;
32415     },
32416
32417     measureColumns : function()
32418     {
32419         this.getContainerWidth();
32420       // if columnWidth is 0, default to outerWidth of first item
32421         if ( !this.columnWidth ) {
32422             var firstItem = this.bricks.first();
32423             Roo.log(firstItem);
32424             this.columnWidth  = this.containerWidth;
32425             if (firstItem && firstItem.attr('originalwidth') ) {
32426                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32427             }
32428             // columnWidth fall back to item of first element
32429             Roo.log("set column width?");
32430                         this.initialColumnWidth = this.columnWidth  ;
32431
32432             // if first elem has no width, default to size of container
32433             
32434         }
32435         
32436         
32437         if (this.initialColumnWidth) {
32438             this.columnWidth = this.initialColumnWidth;
32439         }
32440         
32441         
32442             
32443         // column width is fixed at the top - however if container width get's smaller we should
32444         // reduce it...
32445         
32446         // this bit calcs how man columns..
32447             
32448         var columnWidth = this.columnWidth += this.gutter;
32449       
32450         // calculate columns
32451         var containerWidth = this.containerWidth + this.gutter;
32452         
32453         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32454         // fix rounding errors, typically with gutters
32455         var excess = columnWidth - containerWidth % columnWidth;
32456         
32457         
32458         // if overshoot is less than a pixel, round up, otherwise floor it
32459         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32460         cols = Math[ mathMethod ]( cols );
32461         this.cols = Math.max( cols, 1 );
32462         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32463         
32464          // padding positioning..
32465         var totalColWidth = this.cols * this.columnWidth;
32466         var padavail = this.containerWidth - totalColWidth;
32467         // so for 2 columns - we need 3 'pads'
32468         
32469         var padNeeded = (1+this.cols) * this.padWidth;
32470         
32471         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32472         
32473         this.columnWidth += padExtra
32474         //this.padWidth = Math.floor(padavail /  ( this.cols));
32475         
32476         // adjust colum width so that padding is fixed??
32477         
32478         // we have 3 columns ... total = width * 3
32479         // we have X left over... that should be used by 
32480         
32481         //if (this.expandC) {
32482             
32483         //}
32484         
32485         
32486         
32487     },
32488     
32489     getContainerWidth : function()
32490     {
32491        /* // container is parent if fit width
32492         var container = this.isFitWidth ? this.element.parentNode : this.element;
32493         // check that this.size and size are there
32494         // IE8 triggers resize on body size change, so they might not be
32495         
32496         var size = getSize( container );  //FIXME
32497         this.containerWidth = size && size.innerWidth; //FIXME
32498         */
32499          
32500         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32501         
32502     },
32503     
32504     _getItemLayoutPosition : function( item )  // what is item?
32505     {
32506         // we resize the item to our columnWidth..
32507       
32508         item.setWidth(this.columnWidth);
32509         item.autoBoxAdjust  = false;
32510         
32511         var sz = item.getSize();
32512  
32513         // how many columns does this brick span
32514         var remainder = this.containerWidth % this.columnWidth;
32515         
32516         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32517         // round if off by 1 pixel, otherwise use ceil
32518         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32519         colSpan = Math.min( colSpan, this.cols );
32520         
32521         // normally this should be '1' as we dont' currently allow multi width columns..
32522         
32523         var colGroup = this._getColGroup( colSpan );
32524         // get the minimum Y value from the columns
32525         var minimumY = Math.min.apply( Math, colGroup );
32526         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32527         
32528         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32529          
32530         // position the brick
32531         var position = {
32532             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32533             y: this.currentSize.y + minimumY + this.padHeight
32534         };
32535         
32536         Roo.log(position);
32537         // apply setHeight to necessary columns
32538         var setHeight = minimumY + sz.height + this.padHeight;
32539         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32540         
32541         var setSpan = this.cols + 1 - colGroup.length;
32542         for ( var i = 0; i < setSpan; i++ ) {
32543           this.colYs[ shortColIndex + i ] = setHeight ;
32544         }
32545       
32546         return position;
32547     },
32548     
32549     /**
32550      * @param {Number} colSpan - number of columns the element spans
32551      * @returns {Array} colGroup
32552      */
32553     _getColGroup : function( colSpan )
32554     {
32555         if ( colSpan < 2 ) {
32556           // if brick spans only one column, use all the column Ys
32557           return this.colYs;
32558         }
32559       
32560         var colGroup = [];
32561         // how many different places could this brick fit horizontally
32562         var groupCount = this.cols + 1 - colSpan;
32563         // for each group potential horizontal position
32564         for ( var i = 0; i < groupCount; i++ ) {
32565           // make an array of colY values for that one group
32566           var groupColYs = this.colYs.slice( i, i + colSpan );
32567           // and get the max value of the array
32568           colGroup[i] = Math.max.apply( Math, groupColYs );
32569         }
32570         return colGroup;
32571     },
32572     /*
32573     _manageStamp : function( stamp )
32574     {
32575         var stampSize =  stamp.getSize();
32576         var offset = stamp.getBox();
32577         // get the columns that this stamp affects
32578         var firstX = this.isOriginLeft ? offset.x : offset.right;
32579         var lastX = firstX + stampSize.width;
32580         var firstCol = Math.floor( firstX / this.columnWidth );
32581         firstCol = Math.max( 0, firstCol );
32582         
32583         var lastCol = Math.floor( lastX / this.columnWidth );
32584         // lastCol should not go over if multiple of columnWidth #425
32585         lastCol -= lastX % this.columnWidth ? 0 : 1;
32586         lastCol = Math.min( this.cols - 1, lastCol );
32587         
32588         // set colYs to bottom of the stamp
32589         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32590             stampSize.height;
32591             
32592         for ( var i = firstCol; i <= lastCol; i++ ) {
32593           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32594         }
32595     },
32596     */
32597     
32598     _getContainerSize : function()
32599     {
32600         this.maxY = Math.max.apply( Math, this.colYs );
32601         var size = {
32602             height: this.maxY
32603         };
32604       
32605         if ( this.isFitWidth ) {
32606             size.width = this._getContainerFitWidth();
32607         }
32608       
32609         return size;
32610     },
32611     
32612     _getContainerFitWidth : function()
32613     {
32614         var unusedCols = 0;
32615         // count unused columns
32616         var i = this.cols;
32617         while ( --i ) {
32618           if ( this.colYs[i] !== 0 ) {
32619             break;
32620           }
32621           unusedCols++;
32622         }
32623         // fit container to columns that have been used
32624         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32625     },
32626     
32627     needsResizeLayout : function()
32628     {
32629         var previousWidth = this.containerWidth;
32630         this.getContainerWidth();
32631         return previousWidth !== this.containerWidth;
32632     }
32633  
32634 });
32635
32636  
32637
32638  /*
32639  * - LGPL
32640  *
32641  * element
32642  * 
32643  */
32644
32645 /**
32646  * @class Roo.bootstrap.MasonryBrick
32647  * @extends Roo.bootstrap.Component
32648  * Bootstrap MasonryBrick class
32649  * 
32650  * @constructor
32651  * Create a new MasonryBrick
32652  * @param {Object} config The config object
32653  */
32654
32655 Roo.bootstrap.MasonryBrick = function(config){
32656     
32657     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32658     
32659     Roo.bootstrap.MasonryBrick.register(this);
32660     
32661     this.addEvents({
32662         // raw events
32663         /**
32664          * @event click
32665          * When a MasonryBrick is clcik
32666          * @param {Roo.bootstrap.MasonryBrick} this
32667          * @param {Roo.EventObject} e
32668          */
32669         "click" : true
32670     });
32671 };
32672
32673 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32674     
32675     /**
32676      * @cfg {String} title
32677      */   
32678     title : '',
32679     /**
32680      * @cfg {String} html
32681      */   
32682     html : '',
32683     /**
32684      * @cfg {String} bgimage
32685      */   
32686     bgimage : '',
32687     /**
32688      * @cfg {String} videourl
32689      */   
32690     videourl : '',
32691     /**
32692      * @cfg {String} cls
32693      */   
32694     cls : '',
32695     /**
32696      * @cfg {String} href
32697      */   
32698     href : '',
32699     /**
32700      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32701      */   
32702     size : 'xs',
32703     
32704     /**
32705      * @cfg {String} placetitle (center|bottom)
32706      */   
32707     placetitle : '',
32708     
32709     /**
32710      * @cfg {Boolean} isFitContainer defalut true
32711      */   
32712     isFitContainer : true, 
32713     
32714     /**
32715      * @cfg {Boolean} preventDefault defalut false
32716      */   
32717     preventDefault : false, 
32718     
32719     /**
32720      * @cfg {Boolean} inverse defalut false
32721      */   
32722     maskInverse : false, 
32723     
32724     getAutoCreate : function()
32725     {
32726         if(!this.isFitContainer){
32727             return this.getSplitAutoCreate();
32728         }
32729         
32730         var cls = 'masonry-brick masonry-brick-full';
32731         
32732         if(this.href.length){
32733             cls += ' masonry-brick-link';
32734         }
32735         
32736         if(this.bgimage.length){
32737             cls += ' masonry-brick-image';
32738         }
32739         
32740         if(this.maskInverse){
32741             cls += ' mask-inverse';
32742         }
32743         
32744         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32745             cls += ' enable-mask';
32746         }
32747         
32748         if(this.size){
32749             cls += ' masonry-' + this.size + '-brick';
32750         }
32751         
32752         if(this.placetitle.length){
32753             
32754             switch (this.placetitle) {
32755                 case 'center' :
32756                     cls += ' masonry-center-title';
32757                     break;
32758                 case 'bottom' :
32759                     cls += ' masonry-bottom-title';
32760                     break;
32761                 default:
32762                     break;
32763             }
32764             
32765         } else {
32766             if(!this.html.length && !this.bgimage.length){
32767                 cls += ' masonry-center-title';
32768             }
32769
32770             if(!this.html.length && this.bgimage.length){
32771                 cls += ' masonry-bottom-title';
32772             }
32773         }
32774         
32775         if(this.cls){
32776             cls += ' ' + this.cls;
32777         }
32778         
32779         var cfg = {
32780             tag: (this.href.length) ? 'a' : 'div',
32781             cls: cls,
32782             cn: [
32783                 {
32784                     tag: 'div',
32785                     cls: 'masonry-brick-mask'
32786                 },
32787                 {
32788                     tag: 'div',
32789                     cls: 'masonry-brick-paragraph',
32790                     cn: []
32791                 }
32792             ]
32793         };
32794         
32795         if(this.href.length){
32796             cfg.href = this.href;
32797         }
32798         
32799         var cn = cfg.cn[1].cn;
32800         
32801         if(this.title.length){
32802             cn.push({
32803                 tag: 'h4',
32804                 cls: 'masonry-brick-title',
32805                 html: this.title
32806             });
32807         }
32808         
32809         if(this.html.length){
32810             cn.push({
32811                 tag: 'p',
32812                 cls: 'masonry-brick-text',
32813                 html: this.html
32814             });
32815         }
32816         
32817         if (!this.title.length && !this.html.length) {
32818             cfg.cn[1].cls += ' hide';
32819         }
32820         
32821         if(this.bgimage.length){
32822             cfg.cn.push({
32823                 tag: 'img',
32824                 cls: 'masonry-brick-image-view',
32825                 src: this.bgimage
32826             });
32827         }
32828         
32829         if(this.videourl.length){
32830             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32831             // youtube support only?
32832             cfg.cn.push({
32833                 tag: 'iframe',
32834                 cls: 'masonry-brick-image-view',
32835                 src: vurl,
32836                 frameborder : 0,
32837                 allowfullscreen : true
32838             });
32839         }
32840         
32841         return cfg;
32842         
32843     },
32844     
32845     getSplitAutoCreate : function()
32846     {
32847         var cls = 'masonry-brick masonry-brick-split';
32848         
32849         if(this.href.length){
32850             cls += ' masonry-brick-link';
32851         }
32852         
32853         if(this.bgimage.length){
32854             cls += ' masonry-brick-image';
32855         }
32856         
32857         if(this.size){
32858             cls += ' masonry-' + this.size + '-brick';
32859         }
32860         
32861         switch (this.placetitle) {
32862             case 'center' :
32863                 cls += ' masonry-center-title';
32864                 break;
32865             case 'bottom' :
32866                 cls += ' masonry-bottom-title';
32867                 break;
32868             default:
32869                 if(!this.bgimage.length){
32870                     cls += ' masonry-center-title';
32871                 }
32872
32873                 if(this.bgimage.length){
32874                     cls += ' masonry-bottom-title';
32875                 }
32876                 break;
32877         }
32878         
32879         if(this.cls){
32880             cls += ' ' + this.cls;
32881         }
32882         
32883         var cfg = {
32884             tag: (this.href.length) ? 'a' : 'div',
32885             cls: cls,
32886             cn: [
32887                 {
32888                     tag: 'div',
32889                     cls: 'masonry-brick-split-head',
32890                     cn: [
32891                         {
32892                             tag: 'div',
32893                             cls: 'masonry-brick-paragraph',
32894                             cn: []
32895                         }
32896                     ]
32897                 },
32898                 {
32899                     tag: 'div',
32900                     cls: 'masonry-brick-split-body',
32901                     cn: []
32902                 }
32903             ]
32904         };
32905         
32906         if(this.href.length){
32907             cfg.href = this.href;
32908         }
32909         
32910         if(this.title.length){
32911             cfg.cn[0].cn[0].cn.push({
32912                 tag: 'h4',
32913                 cls: 'masonry-brick-title',
32914                 html: this.title
32915             });
32916         }
32917         
32918         if(this.html.length){
32919             cfg.cn[1].cn.push({
32920                 tag: 'p',
32921                 cls: 'masonry-brick-text',
32922                 html: this.html
32923             });
32924         }
32925
32926         if(this.bgimage.length){
32927             cfg.cn[0].cn.push({
32928                 tag: 'img',
32929                 cls: 'masonry-brick-image-view',
32930                 src: this.bgimage
32931             });
32932         }
32933         
32934         if(this.videourl.length){
32935             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32936             // youtube support only?
32937             cfg.cn[0].cn.cn.push({
32938                 tag: 'iframe',
32939                 cls: 'masonry-brick-image-view',
32940                 src: vurl,
32941                 frameborder : 0,
32942                 allowfullscreen : true
32943             });
32944         }
32945         
32946         return cfg;
32947     },
32948     
32949     initEvents: function() 
32950     {
32951         switch (this.size) {
32952             case 'xs' :
32953                 this.x = 1;
32954                 this.y = 1;
32955                 break;
32956             case 'sm' :
32957                 this.x = 2;
32958                 this.y = 2;
32959                 break;
32960             case 'md' :
32961             case 'md-left' :
32962             case 'md-right' :
32963                 this.x = 3;
32964                 this.y = 3;
32965                 break;
32966             case 'tall' :
32967                 this.x = 2;
32968                 this.y = 3;
32969                 break;
32970             case 'wide' :
32971                 this.x = 3;
32972                 this.y = 2;
32973                 break;
32974             case 'wide-thin' :
32975                 this.x = 3;
32976                 this.y = 1;
32977                 break;
32978                         
32979             default :
32980                 break;
32981         }
32982         
32983         if(Roo.isTouch){
32984             this.el.on('touchstart', this.onTouchStart, this);
32985             this.el.on('touchmove', this.onTouchMove, this);
32986             this.el.on('touchend', this.onTouchEnd, this);
32987             this.el.on('contextmenu', this.onContextMenu, this);
32988         } else {
32989             this.el.on('mouseenter'  ,this.enter, this);
32990             this.el.on('mouseleave', this.leave, this);
32991             this.el.on('click', this.onClick, this);
32992         }
32993         
32994         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32995             this.parent().bricks.push(this);   
32996         }
32997         
32998     },
32999     
33000     onClick: function(e, el)
33001     {
33002         var time = this.endTimer - this.startTimer;
33003         // Roo.log(e.preventDefault());
33004         if(Roo.isTouch){
33005             if(time > 1000){
33006                 e.preventDefault();
33007                 return;
33008             }
33009         }
33010         
33011         if(!this.preventDefault){
33012             return;
33013         }
33014         
33015         e.preventDefault();
33016         
33017         if (this.activeClass != '') {
33018             this.selectBrick();
33019         }
33020         
33021         this.fireEvent('click', this, e);
33022     },
33023     
33024     enter: function(e, el)
33025     {
33026         e.preventDefault();
33027         
33028         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33029             return;
33030         }
33031         
33032         if(this.bgimage.length && this.html.length){
33033             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33034         }
33035     },
33036     
33037     leave: function(e, el)
33038     {
33039         e.preventDefault();
33040         
33041         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33042             return;
33043         }
33044         
33045         if(this.bgimage.length && this.html.length){
33046             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33047         }
33048     },
33049     
33050     onTouchStart: function(e, el)
33051     {
33052 //        e.preventDefault();
33053         
33054         this.touchmoved = false;
33055         
33056         if(!this.isFitContainer){
33057             return;
33058         }
33059         
33060         if(!this.bgimage.length || !this.html.length){
33061             return;
33062         }
33063         
33064         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33065         
33066         this.timer = new Date().getTime();
33067         
33068     },
33069     
33070     onTouchMove: function(e, el)
33071     {
33072         this.touchmoved = true;
33073     },
33074     
33075     onContextMenu : function(e,el)
33076     {
33077         e.preventDefault();
33078         e.stopPropagation();
33079         return false;
33080     },
33081     
33082     onTouchEnd: function(e, el)
33083     {
33084 //        e.preventDefault();
33085         
33086         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33087         
33088             this.leave(e,el);
33089             
33090             return;
33091         }
33092         
33093         if(!this.bgimage.length || !this.html.length){
33094             
33095             if(this.href.length){
33096                 window.location.href = this.href;
33097             }
33098             
33099             return;
33100         }
33101         
33102         if(!this.isFitContainer){
33103             return;
33104         }
33105         
33106         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33107         
33108         window.location.href = this.href;
33109     },
33110     
33111     //selection on single brick only
33112     selectBrick : function() {
33113         
33114         if (!this.parentId) {
33115             return;
33116         }
33117         
33118         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33119         var index = m.selectedBrick.indexOf(this.id);
33120         
33121         if ( index > -1) {
33122             m.selectedBrick.splice(index,1);
33123             this.el.removeClass(this.activeClass);
33124             return;
33125         }
33126         
33127         for(var i = 0; i < m.selectedBrick.length; i++) {
33128             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33129             b.el.removeClass(b.activeClass);
33130         }
33131         
33132         m.selectedBrick = [];
33133         
33134         m.selectedBrick.push(this.id);
33135         this.el.addClass(this.activeClass);
33136         return;
33137     },
33138     
33139     isSelected : function(){
33140         return this.el.hasClass(this.activeClass);
33141         
33142     }
33143 });
33144
33145 Roo.apply(Roo.bootstrap.MasonryBrick, {
33146     
33147     //groups: {},
33148     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33149      /**
33150     * register a Masonry Brick
33151     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33152     */
33153     
33154     register : function(brick)
33155     {
33156         //this.groups[brick.id] = brick;
33157         this.groups.add(brick.id, brick);
33158     },
33159     /**
33160     * fetch a  masonry brick based on the masonry brick ID
33161     * @param {string} the masonry brick to add
33162     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33163     */
33164     
33165     get: function(brick_id) 
33166     {
33167         // if (typeof(this.groups[brick_id]) == 'undefined') {
33168         //     return false;
33169         // }
33170         // return this.groups[brick_id] ;
33171         
33172         if(this.groups.key(brick_id)) {
33173             return this.groups.key(brick_id);
33174         }
33175         
33176         return false;
33177     }
33178     
33179     
33180     
33181 });
33182
33183  /*
33184  * - LGPL
33185  *
33186  * element
33187  * 
33188  */
33189
33190 /**
33191  * @class Roo.bootstrap.Brick
33192  * @extends Roo.bootstrap.Component
33193  * Bootstrap Brick class
33194  * 
33195  * @constructor
33196  * Create a new Brick
33197  * @param {Object} config The config object
33198  */
33199
33200 Roo.bootstrap.Brick = function(config){
33201     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33202     
33203     this.addEvents({
33204         // raw events
33205         /**
33206          * @event click
33207          * When a Brick is click
33208          * @param {Roo.bootstrap.Brick} this
33209          * @param {Roo.EventObject} e
33210          */
33211         "click" : true
33212     });
33213 };
33214
33215 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33216     
33217     /**
33218      * @cfg {String} title
33219      */   
33220     title : '',
33221     /**
33222      * @cfg {String} html
33223      */   
33224     html : '',
33225     /**
33226      * @cfg {String} bgimage
33227      */   
33228     bgimage : '',
33229     /**
33230      * @cfg {String} cls
33231      */   
33232     cls : '',
33233     /**
33234      * @cfg {String} href
33235      */   
33236     href : '',
33237     /**
33238      * @cfg {String} video
33239      */   
33240     video : '',
33241     /**
33242      * @cfg {Boolean} square
33243      */   
33244     square : true,
33245     
33246     getAutoCreate : function()
33247     {
33248         var cls = 'roo-brick';
33249         
33250         if(this.href.length){
33251             cls += ' roo-brick-link';
33252         }
33253         
33254         if(this.bgimage.length){
33255             cls += ' roo-brick-image';
33256         }
33257         
33258         if(!this.html.length && !this.bgimage.length){
33259             cls += ' roo-brick-center-title';
33260         }
33261         
33262         if(!this.html.length && this.bgimage.length){
33263             cls += ' roo-brick-bottom-title';
33264         }
33265         
33266         if(this.cls){
33267             cls += ' ' + this.cls;
33268         }
33269         
33270         var cfg = {
33271             tag: (this.href.length) ? 'a' : 'div',
33272             cls: cls,
33273             cn: [
33274                 {
33275                     tag: 'div',
33276                     cls: 'roo-brick-paragraph',
33277                     cn: []
33278                 }
33279             ]
33280         };
33281         
33282         if(this.href.length){
33283             cfg.href = this.href;
33284         }
33285         
33286         var cn = cfg.cn[0].cn;
33287         
33288         if(this.title.length){
33289             cn.push({
33290                 tag: 'h4',
33291                 cls: 'roo-brick-title',
33292                 html: this.title
33293             });
33294         }
33295         
33296         if(this.html.length){
33297             cn.push({
33298                 tag: 'p',
33299                 cls: 'roo-brick-text',
33300                 html: this.html
33301             });
33302         } else {
33303             cn.cls += ' hide';
33304         }
33305         
33306         if(this.bgimage.length){
33307             cfg.cn.push({
33308                 tag: 'img',
33309                 cls: 'roo-brick-image-view',
33310                 src: this.bgimage
33311             });
33312         }
33313         
33314         return cfg;
33315     },
33316     
33317     initEvents: function() 
33318     {
33319         if(this.title.length || this.html.length){
33320             this.el.on('mouseenter'  ,this.enter, this);
33321             this.el.on('mouseleave', this.leave, this);
33322         }
33323         
33324         Roo.EventManager.onWindowResize(this.resize, this); 
33325         
33326         if(this.bgimage.length){
33327             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33328             this.imageEl.on('load', this.onImageLoad, this);
33329             return;
33330         }
33331         
33332         this.resize();
33333     },
33334     
33335     onImageLoad : function()
33336     {
33337         this.resize();
33338     },
33339     
33340     resize : function()
33341     {
33342         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33343         
33344         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33345         
33346         if(this.bgimage.length){
33347             var image = this.el.select('.roo-brick-image-view', true).first();
33348             
33349             image.setWidth(paragraph.getWidth());
33350             
33351             if(this.square){
33352                 image.setHeight(paragraph.getWidth());
33353             }
33354             
33355             this.el.setHeight(image.getHeight());
33356             paragraph.setHeight(image.getHeight());
33357             
33358         }
33359         
33360     },
33361     
33362     enter: function(e, el)
33363     {
33364         e.preventDefault();
33365         
33366         if(this.bgimage.length){
33367             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33368             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33369         }
33370     },
33371     
33372     leave: function(e, el)
33373     {
33374         e.preventDefault();
33375         
33376         if(this.bgimage.length){
33377             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33378             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33379         }
33380     }
33381     
33382 });
33383
33384  
33385
33386  /*
33387  * - LGPL
33388  *
33389  * Number field 
33390  */
33391
33392 /**
33393  * @class Roo.bootstrap.NumberField
33394  * @extends Roo.bootstrap.Input
33395  * Bootstrap NumberField class
33396  * 
33397  * 
33398  * 
33399  * 
33400  * @constructor
33401  * Create a new NumberField
33402  * @param {Object} config The config object
33403  */
33404
33405 Roo.bootstrap.NumberField = function(config){
33406     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33407 };
33408
33409 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33410     
33411     /**
33412      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33413      */
33414     allowDecimals : true,
33415     /**
33416      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33417      */
33418     decimalSeparator : ".",
33419     /**
33420      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33421      */
33422     decimalPrecision : 2,
33423     /**
33424      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33425      */
33426     allowNegative : true,
33427     
33428     /**
33429      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33430      */
33431     allowZero: true,
33432     /**
33433      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33434      */
33435     minValue : Number.NEGATIVE_INFINITY,
33436     /**
33437      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33438      */
33439     maxValue : Number.MAX_VALUE,
33440     /**
33441      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33442      */
33443     minText : "The minimum value for this field is {0}",
33444     /**
33445      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33446      */
33447     maxText : "The maximum value for this field is {0}",
33448     /**
33449      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33450      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33451      */
33452     nanText : "{0} is not a valid number",
33453     /**
33454      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33455      */
33456     thousandsDelimiter : false,
33457     /**
33458      * @cfg {String} valueAlign alignment of value
33459      */
33460     valueAlign : "left",
33461
33462     getAutoCreate : function()
33463     {
33464         var hiddenInput = {
33465             tag: 'input',
33466             type: 'hidden',
33467             id: Roo.id(),
33468             cls: 'hidden-number-input'
33469         };
33470         
33471         if (this.name) {
33472             hiddenInput.name = this.name;
33473         }
33474         
33475         this.name = '';
33476         
33477         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33478         
33479         this.name = hiddenInput.name;
33480         
33481         if(cfg.cn.length > 0) {
33482             cfg.cn.push(hiddenInput);
33483         }
33484         
33485         return cfg;
33486     },
33487
33488     // private
33489     initEvents : function()
33490     {   
33491         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33492         
33493         var allowed = "0123456789";
33494         
33495         if(this.allowDecimals){
33496             allowed += this.decimalSeparator;
33497         }
33498         
33499         if(this.allowNegative){
33500             allowed += "-";
33501         }
33502         
33503         if(this.thousandsDelimiter) {
33504             allowed += ",";
33505         }
33506         
33507         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33508         
33509         var keyPress = function(e){
33510             
33511             var k = e.getKey();
33512             
33513             var c = e.getCharCode();
33514             
33515             if(
33516                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33517                     allowed.indexOf(String.fromCharCode(c)) === -1
33518             ){
33519                 e.stopEvent();
33520                 return;
33521             }
33522             
33523             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33524                 return;
33525             }
33526             
33527             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33528                 e.stopEvent();
33529             }
33530         };
33531         
33532         this.el.on("keypress", keyPress, this);
33533     },
33534     
33535     validateValue : function(value)
33536     {
33537         
33538         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33539             return false;
33540         }
33541         
33542         var num = this.parseValue(value);
33543         
33544         if(isNaN(num)){
33545             this.markInvalid(String.format(this.nanText, value));
33546             return false;
33547         }
33548         
33549         if(num < this.minValue){
33550             this.markInvalid(String.format(this.minText, this.minValue));
33551             return false;
33552         }
33553         
33554         if(num > this.maxValue){
33555             this.markInvalid(String.format(this.maxText, this.maxValue));
33556             return false;
33557         }
33558         
33559         return true;
33560     },
33561
33562     getValue : function()
33563     {
33564         var v = this.hiddenEl().getValue();
33565         
33566         return this.fixPrecision(this.parseValue(v));
33567     },
33568
33569     parseValue : function(value)
33570     {
33571         if(this.thousandsDelimiter) {
33572             value += "";
33573             r = new RegExp(",", "g");
33574             value = value.replace(r, "");
33575         }
33576         
33577         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33578         return isNaN(value) ? '' : value;
33579     },
33580
33581     fixPrecision : function(value)
33582     {
33583         if(this.thousandsDelimiter) {
33584             value += "";
33585             r = new RegExp(",", "g");
33586             value = value.replace(r, "");
33587         }
33588         
33589         var nan = isNaN(value);
33590         
33591         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33592             return nan ? '' : value;
33593         }
33594         return parseFloat(value).toFixed(this.decimalPrecision);
33595     },
33596
33597     setValue : function(v)
33598     {
33599         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33600         
33601         this.value = v;
33602         
33603         if(this.rendered){
33604             
33605             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33606             
33607             this.inputEl().dom.value = (v == '') ? '' :
33608                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33609             
33610             if(!this.allowZero && v === '0') {
33611                 this.hiddenEl().dom.value = '';
33612                 this.inputEl().dom.value = '';
33613             }
33614             
33615             this.validate();
33616         }
33617     },
33618
33619     decimalPrecisionFcn : function(v)
33620     {
33621         return Math.floor(v);
33622     },
33623
33624     beforeBlur : function()
33625     {
33626         var v = this.parseValue(this.getRawValue());
33627         
33628         if(v || v === 0 || v === ''){
33629             this.setValue(v);
33630         }
33631     },
33632     
33633     hiddenEl : function()
33634     {
33635         return this.el.select('input.hidden-number-input',true).first();
33636     }
33637     
33638 });
33639
33640  
33641
33642 /*
33643 * Licence: LGPL
33644 */
33645
33646 /**
33647  * @class Roo.bootstrap.DocumentSlider
33648  * @extends Roo.bootstrap.Component
33649  * Bootstrap DocumentSlider class
33650  * 
33651  * @constructor
33652  * Create a new DocumentViewer
33653  * @param {Object} config The config object
33654  */
33655
33656 Roo.bootstrap.DocumentSlider = function(config){
33657     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33658     
33659     this.files = [];
33660     
33661     this.addEvents({
33662         /**
33663          * @event initial
33664          * Fire after initEvent
33665          * @param {Roo.bootstrap.DocumentSlider} this
33666          */
33667         "initial" : true,
33668         /**
33669          * @event update
33670          * Fire after update
33671          * @param {Roo.bootstrap.DocumentSlider} this
33672          */
33673         "update" : true,
33674         /**
33675          * @event click
33676          * Fire after click
33677          * @param {Roo.bootstrap.DocumentSlider} this
33678          */
33679         "click" : true
33680     });
33681 };
33682
33683 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33684     
33685     files : false,
33686     
33687     indicator : 0,
33688     
33689     getAutoCreate : function()
33690     {
33691         var cfg = {
33692             tag : 'div',
33693             cls : 'roo-document-slider',
33694             cn : [
33695                 {
33696                     tag : 'div',
33697                     cls : 'roo-document-slider-header',
33698                     cn : [
33699                         {
33700                             tag : 'div',
33701                             cls : 'roo-document-slider-header-title'
33702                         }
33703                     ]
33704                 },
33705                 {
33706                     tag : 'div',
33707                     cls : 'roo-document-slider-body',
33708                     cn : [
33709                         {
33710                             tag : 'div',
33711                             cls : 'roo-document-slider-prev',
33712                             cn : [
33713                                 {
33714                                     tag : 'i',
33715                                     cls : 'fa fa-chevron-left'
33716                                 }
33717                             ]
33718                         },
33719                         {
33720                             tag : 'div',
33721                             cls : 'roo-document-slider-thumb',
33722                             cn : [
33723                                 {
33724                                     tag : 'img',
33725                                     cls : 'roo-document-slider-image'
33726                                 }
33727                             ]
33728                         },
33729                         {
33730                             tag : 'div',
33731                             cls : 'roo-document-slider-next',
33732                             cn : [
33733                                 {
33734                                     tag : 'i',
33735                                     cls : 'fa fa-chevron-right'
33736                                 }
33737                             ]
33738                         }
33739                     ]
33740                 }
33741             ]
33742         };
33743         
33744         return cfg;
33745     },
33746     
33747     initEvents : function()
33748     {
33749         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33750         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33751         
33752         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33753         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33754         
33755         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33756         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33757         
33758         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33759         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33760         
33761         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33762         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33763         
33764         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33765         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33766         
33767         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33768         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33769         
33770         this.thumbEl.on('click', this.onClick, this);
33771         
33772         this.prevIndicator.on('click', this.prev, this);
33773         
33774         this.nextIndicator.on('click', this.next, this);
33775         
33776     },
33777     
33778     initial : function()
33779     {
33780         if(this.files.length){
33781             this.indicator = 1;
33782             this.update()
33783         }
33784         
33785         this.fireEvent('initial', this);
33786     },
33787     
33788     update : function()
33789     {
33790         this.imageEl.attr('src', this.files[this.indicator - 1]);
33791         
33792         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33793         
33794         this.prevIndicator.show();
33795         
33796         if(this.indicator == 1){
33797             this.prevIndicator.hide();
33798         }
33799         
33800         this.nextIndicator.show();
33801         
33802         if(this.indicator == this.files.length){
33803             this.nextIndicator.hide();
33804         }
33805         
33806         this.thumbEl.scrollTo('top');
33807         
33808         this.fireEvent('update', this);
33809     },
33810     
33811     onClick : function(e)
33812     {
33813         e.preventDefault();
33814         
33815         this.fireEvent('click', this);
33816     },
33817     
33818     prev : function(e)
33819     {
33820         e.preventDefault();
33821         
33822         this.indicator = Math.max(1, this.indicator - 1);
33823         
33824         this.update();
33825     },
33826     
33827     next : function(e)
33828     {
33829         e.preventDefault();
33830         
33831         this.indicator = Math.min(this.files.length, this.indicator + 1);
33832         
33833         this.update();
33834     }
33835 });
33836 /*
33837  * - LGPL
33838  *
33839  * RadioSet
33840  *
33841  *
33842  */
33843
33844 /**
33845  * @class Roo.bootstrap.RadioSet
33846  * @extends Roo.bootstrap.Input
33847  * Bootstrap RadioSet class
33848  * @cfg {String} indicatorpos (left|right) default left
33849  * @cfg {Boolean} inline (true|false) inline the element (default true)
33850  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33851  * @constructor
33852  * Create a new RadioSet
33853  * @param {Object} config The config object
33854  */
33855
33856 Roo.bootstrap.RadioSet = function(config){
33857     
33858     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33859     
33860     this.radioes = [];
33861     
33862     Roo.bootstrap.RadioSet.register(this);
33863     
33864     this.addEvents({
33865         /**
33866         * @event check
33867         * Fires when the element is checked or unchecked.
33868         * @param {Roo.bootstrap.RadioSet} this This radio
33869         * @param {Roo.bootstrap.Radio} item The checked item
33870         */
33871        check : true,
33872        /**
33873         * @event click
33874         * Fires when the element is click.
33875         * @param {Roo.bootstrap.RadioSet} this This radio set
33876         * @param {Roo.bootstrap.Radio} item The checked item
33877         * @param {Roo.EventObject} e The event object
33878         */
33879        click : true
33880     });
33881     
33882 };
33883
33884 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33885
33886     radioes : false,
33887     
33888     inline : true,
33889     
33890     weight : '',
33891     
33892     indicatorpos : 'left',
33893     
33894     getAutoCreate : function()
33895     {
33896         var label = {
33897             tag : 'label',
33898             cls : 'roo-radio-set-label',
33899             cn : [
33900                 {
33901                     tag : 'span',
33902                     html : this.fieldLabel
33903                 }
33904             ]
33905         };
33906         
33907         if(this.indicatorpos == 'left'){
33908             label.cn.unshift({
33909                 tag : 'i',
33910                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33911                 tooltip : 'This field is required'
33912             });
33913         } else {
33914             label.cn.push({
33915                 tag : 'i',
33916                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33917                 tooltip : 'This field is required'
33918             });
33919         }
33920         
33921         var items = {
33922             tag : 'div',
33923             cls : 'roo-radio-set-items'
33924         };
33925         
33926         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33927         
33928         if (align === 'left' && this.fieldLabel.length) {
33929             
33930             items = {
33931                 cls : "roo-radio-set-right", 
33932                 cn: [
33933                     items
33934                 ]
33935             };
33936             
33937             if(this.labelWidth > 12){
33938                 label.style = "width: " + this.labelWidth + 'px';
33939             }
33940             
33941             if(this.labelWidth < 13 && this.labelmd == 0){
33942                 this.labelmd = this.labelWidth;
33943             }
33944             
33945             if(this.labellg > 0){
33946                 label.cls += ' col-lg-' + this.labellg;
33947                 items.cls += ' col-lg-' + (12 - this.labellg);
33948             }
33949             
33950             if(this.labelmd > 0){
33951                 label.cls += ' col-md-' + this.labelmd;
33952                 items.cls += ' col-md-' + (12 - this.labelmd);
33953             }
33954             
33955             if(this.labelsm > 0){
33956                 label.cls += ' col-sm-' + this.labelsm;
33957                 items.cls += ' col-sm-' + (12 - this.labelsm);
33958             }
33959             
33960             if(this.labelxs > 0){
33961                 label.cls += ' col-xs-' + this.labelxs;
33962                 items.cls += ' col-xs-' + (12 - this.labelxs);
33963             }
33964         }
33965         
33966         var cfg = {
33967             tag : 'div',
33968             cls : 'roo-radio-set',
33969             cn : [
33970                 {
33971                     tag : 'input',
33972                     cls : 'roo-radio-set-input',
33973                     type : 'hidden',
33974                     name : this.name,
33975                     value : this.value ? this.value :  ''
33976                 },
33977                 label,
33978                 items
33979             ]
33980         };
33981         
33982         if(this.weight.length){
33983             cfg.cls += ' roo-radio-' + this.weight;
33984         }
33985         
33986         if(this.inline) {
33987             cfg.cls += ' roo-radio-set-inline';
33988         }
33989         
33990         var settings=this;
33991         ['xs','sm','md','lg'].map(function(size){
33992             if (settings[size]) {
33993                 cfg.cls += ' col-' + size + '-' + settings[size];
33994             }
33995         });
33996         
33997         return cfg;
33998         
33999     },
34000
34001     initEvents : function()
34002     {
34003         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34004         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34005         
34006         if(!this.fieldLabel.length){
34007             this.labelEl.hide();
34008         }
34009         
34010         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34011         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34012         
34013         this.indicator = this.indicatorEl();
34014         
34015         if(this.indicator){
34016             this.indicator.addClass('invisible');
34017         }
34018         
34019         this.originalValue = this.getValue();
34020         
34021     },
34022     
34023     inputEl: function ()
34024     {
34025         return this.el.select('.roo-radio-set-input', true).first();
34026     },
34027     
34028     getChildContainer : function()
34029     {
34030         return this.itemsEl;
34031     },
34032     
34033     register : function(item)
34034     {
34035         this.radioes.push(item);
34036         
34037     },
34038     
34039     validate : function()
34040     {   
34041         if(this.getVisibilityEl().hasClass('hidden')){
34042             return true;
34043         }
34044         
34045         var valid = false;
34046         
34047         Roo.each(this.radioes, function(i){
34048             if(!i.checked){
34049                 return;
34050             }
34051             
34052             valid = true;
34053             return false;
34054         });
34055         
34056         if(this.allowBlank) {
34057             return true;
34058         }
34059         
34060         if(this.disabled || valid){
34061             this.markValid();
34062             return true;
34063         }
34064         
34065         this.markInvalid();
34066         return false;
34067         
34068     },
34069     
34070     markValid : function()
34071     {
34072         if(this.labelEl.isVisible(true)){
34073             this.indicatorEl().removeClass('visible');
34074             this.indicatorEl().addClass('invisible');
34075         }
34076         
34077         this.el.removeClass([this.invalidClass, this.validClass]);
34078         this.el.addClass(this.validClass);
34079         
34080         this.fireEvent('valid', this);
34081     },
34082     
34083     markInvalid : function(msg)
34084     {
34085         if(this.allowBlank || this.disabled){
34086             return;
34087         }
34088         
34089         if(this.labelEl.isVisible(true)){
34090             this.indicatorEl().removeClass('invisible');
34091             this.indicatorEl().addClass('visible');
34092         }
34093         
34094         this.el.removeClass([this.invalidClass, this.validClass]);
34095         this.el.addClass(this.invalidClass);
34096         
34097         this.fireEvent('invalid', this, msg);
34098         
34099     },
34100     
34101     setValue : function(v, suppressEvent)
34102     {   
34103         if(this.value === v){
34104             return;
34105         }
34106         
34107         this.value = v;
34108         
34109         if(this.rendered){
34110             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34111         }
34112         
34113         Roo.each(this.radioes, function(i){
34114             i.checked = false;
34115             i.el.removeClass('checked');
34116         });
34117         
34118         Roo.each(this.radioes, function(i){
34119             
34120             if(i.value === v || i.value.toString() === v.toString()){
34121                 i.checked = true;
34122                 i.el.addClass('checked');
34123                 
34124                 if(suppressEvent !== true){
34125                     this.fireEvent('check', this, i);
34126                 }
34127                 
34128                 return false;
34129             }
34130             
34131         }, this);
34132         
34133         this.validate();
34134     },
34135     
34136     clearInvalid : function(){
34137         
34138         if(!this.el || this.preventMark){
34139             return;
34140         }
34141         
34142         this.el.removeClass([this.invalidClass]);
34143         
34144         this.fireEvent('valid', this);
34145     }
34146     
34147 });
34148
34149 Roo.apply(Roo.bootstrap.RadioSet, {
34150     
34151     groups: {},
34152     
34153     register : function(set)
34154     {
34155         this.groups[set.name] = set;
34156     },
34157     
34158     get: function(name) 
34159     {
34160         if (typeof(this.groups[name]) == 'undefined') {
34161             return false;
34162         }
34163         
34164         return this.groups[name] ;
34165     }
34166     
34167 });
34168 /*
34169  * Based on:
34170  * Ext JS Library 1.1.1
34171  * Copyright(c) 2006-2007, Ext JS, LLC.
34172  *
34173  * Originally Released Under LGPL - original licence link has changed is not relivant.
34174  *
34175  * Fork - LGPL
34176  * <script type="text/javascript">
34177  */
34178
34179
34180 /**
34181  * @class Roo.bootstrap.SplitBar
34182  * @extends Roo.util.Observable
34183  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34184  * <br><br>
34185  * Usage:
34186  * <pre><code>
34187 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34188                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34189 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34190 split.minSize = 100;
34191 split.maxSize = 600;
34192 split.animate = true;
34193 split.on('moved', splitterMoved);
34194 </code></pre>
34195  * @constructor
34196  * Create a new SplitBar
34197  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34198  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34199  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34200  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34201                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34202                         position of the SplitBar).
34203  */
34204 Roo.bootstrap.SplitBar = function(cfg){
34205     
34206     /** @private */
34207     
34208     //{
34209     //  dragElement : elm
34210     //  resizingElement: el,
34211         // optional..
34212     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34213     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34214         // existingProxy ???
34215     //}
34216     
34217     this.el = Roo.get(cfg.dragElement, true);
34218     this.el.dom.unselectable = "on";
34219     /** @private */
34220     this.resizingEl = Roo.get(cfg.resizingElement, true);
34221
34222     /**
34223      * @private
34224      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34225      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34226      * @type Number
34227      */
34228     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34229     
34230     /**
34231      * The minimum size of the resizing element. (Defaults to 0)
34232      * @type Number
34233      */
34234     this.minSize = 0;
34235     
34236     /**
34237      * The maximum size of the resizing element. (Defaults to 2000)
34238      * @type Number
34239      */
34240     this.maxSize = 2000;
34241     
34242     /**
34243      * Whether to animate the transition to the new size
34244      * @type Boolean
34245      */
34246     this.animate = false;
34247     
34248     /**
34249      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34250      * @type Boolean
34251      */
34252     this.useShim = false;
34253     
34254     /** @private */
34255     this.shim = null;
34256     
34257     if(!cfg.existingProxy){
34258         /** @private */
34259         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34260     }else{
34261         this.proxy = Roo.get(cfg.existingProxy).dom;
34262     }
34263     /** @private */
34264     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34265     
34266     /** @private */
34267     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34268     
34269     /** @private */
34270     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34271     
34272     /** @private */
34273     this.dragSpecs = {};
34274     
34275     /**
34276      * @private The adapter to use to positon and resize elements
34277      */
34278     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34279     this.adapter.init(this);
34280     
34281     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34282         /** @private */
34283         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34284         this.el.addClass("roo-splitbar-h");
34285     }else{
34286         /** @private */
34287         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34288         this.el.addClass("roo-splitbar-v");
34289     }
34290     
34291     this.addEvents({
34292         /**
34293          * @event resize
34294          * Fires when the splitter is moved (alias for {@link #event-moved})
34295          * @param {Roo.bootstrap.SplitBar} this
34296          * @param {Number} newSize the new width or height
34297          */
34298         "resize" : true,
34299         /**
34300          * @event moved
34301          * Fires when the splitter is moved
34302          * @param {Roo.bootstrap.SplitBar} this
34303          * @param {Number} newSize the new width or height
34304          */
34305         "moved" : true,
34306         /**
34307          * @event beforeresize
34308          * Fires before the splitter is dragged
34309          * @param {Roo.bootstrap.SplitBar} this
34310          */
34311         "beforeresize" : true,
34312
34313         "beforeapply" : true
34314     });
34315
34316     Roo.util.Observable.call(this);
34317 };
34318
34319 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34320     onStartProxyDrag : function(x, y){
34321         this.fireEvent("beforeresize", this);
34322         if(!this.overlay){
34323             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34324             o.unselectable();
34325             o.enableDisplayMode("block");
34326             // all splitbars share the same overlay
34327             Roo.bootstrap.SplitBar.prototype.overlay = o;
34328         }
34329         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34330         this.overlay.show();
34331         Roo.get(this.proxy).setDisplayed("block");
34332         var size = this.adapter.getElementSize(this);
34333         this.activeMinSize = this.getMinimumSize();;
34334         this.activeMaxSize = this.getMaximumSize();;
34335         var c1 = size - this.activeMinSize;
34336         var c2 = Math.max(this.activeMaxSize - size, 0);
34337         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34338             this.dd.resetConstraints();
34339             this.dd.setXConstraint(
34340                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34341                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34342             );
34343             this.dd.setYConstraint(0, 0);
34344         }else{
34345             this.dd.resetConstraints();
34346             this.dd.setXConstraint(0, 0);
34347             this.dd.setYConstraint(
34348                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34349                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34350             );
34351          }
34352         this.dragSpecs.startSize = size;
34353         this.dragSpecs.startPoint = [x, y];
34354         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34355     },
34356     
34357     /** 
34358      * @private Called after the drag operation by the DDProxy
34359      */
34360     onEndProxyDrag : function(e){
34361         Roo.get(this.proxy).setDisplayed(false);
34362         var endPoint = Roo.lib.Event.getXY(e);
34363         if(this.overlay){
34364             this.overlay.hide();
34365         }
34366         var newSize;
34367         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34368             newSize = this.dragSpecs.startSize + 
34369                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34370                     endPoint[0] - this.dragSpecs.startPoint[0] :
34371                     this.dragSpecs.startPoint[0] - endPoint[0]
34372                 );
34373         }else{
34374             newSize = this.dragSpecs.startSize + 
34375                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34376                     endPoint[1] - this.dragSpecs.startPoint[1] :
34377                     this.dragSpecs.startPoint[1] - endPoint[1]
34378                 );
34379         }
34380         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34381         if(newSize != this.dragSpecs.startSize){
34382             if(this.fireEvent('beforeapply', this, newSize) !== false){
34383                 this.adapter.setElementSize(this, newSize);
34384                 this.fireEvent("moved", this, newSize);
34385                 this.fireEvent("resize", this, newSize);
34386             }
34387         }
34388     },
34389     
34390     /**
34391      * Get the adapter this SplitBar uses
34392      * @return The adapter object
34393      */
34394     getAdapter : function(){
34395         return this.adapter;
34396     },
34397     
34398     /**
34399      * Set the adapter this SplitBar uses
34400      * @param {Object} adapter A SplitBar adapter object
34401      */
34402     setAdapter : function(adapter){
34403         this.adapter = adapter;
34404         this.adapter.init(this);
34405     },
34406     
34407     /**
34408      * Gets the minimum size for the resizing element
34409      * @return {Number} The minimum size
34410      */
34411     getMinimumSize : function(){
34412         return this.minSize;
34413     },
34414     
34415     /**
34416      * Sets the minimum size for the resizing element
34417      * @param {Number} minSize The minimum size
34418      */
34419     setMinimumSize : function(minSize){
34420         this.minSize = minSize;
34421     },
34422     
34423     /**
34424      * Gets the maximum size for the resizing element
34425      * @return {Number} The maximum size
34426      */
34427     getMaximumSize : function(){
34428         return this.maxSize;
34429     },
34430     
34431     /**
34432      * Sets the maximum size for the resizing element
34433      * @param {Number} maxSize The maximum size
34434      */
34435     setMaximumSize : function(maxSize){
34436         this.maxSize = maxSize;
34437     },
34438     
34439     /**
34440      * Sets the initialize size for the resizing element
34441      * @param {Number} size The initial size
34442      */
34443     setCurrentSize : function(size){
34444         var oldAnimate = this.animate;
34445         this.animate = false;
34446         this.adapter.setElementSize(this, size);
34447         this.animate = oldAnimate;
34448     },
34449     
34450     /**
34451      * Destroy this splitbar. 
34452      * @param {Boolean} removeEl True to remove the element
34453      */
34454     destroy : function(removeEl){
34455         if(this.shim){
34456             this.shim.remove();
34457         }
34458         this.dd.unreg();
34459         this.proxy.parentNode.removeChild(this.proxy);
34460         if(removeEl){
34461             this.el.remove();
34462         }
34463     }
34464 });
34465
34466 /**
34467  * @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.
34468  */
34469 Roo.bootstrap.SplitBar.createProxy = function(dir){
34470     var proxy = new Roo.Element(document.createElement("div"));
34471     proxy.unselectable();
34472     var cls = 'roo-splitbar-proxy';
34473     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34474     document.body.appendChild(proxy.dom);
34475     return proxy.dom;
34476 };
34477
34478 /** 
34479  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34480  * Default Adapter. It assumes the splitter and resizing element are not positioned
34481  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34482  */
34483 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34484 };
34485
34486 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34487     // do nothing for now
34488     init : function(s){
34489     
34490     },
34491     /**
34492      * Called before drag operations to get the current size of the resizing element. 
34493      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34494      */
34495      getElementSize : function(s){
34496         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34497             return s.resizingEl.getWidth();
34498         }else{
34499             return s.resizingEl.getHeight();
34500         }
34501     },
34502     
34503     /**
34504      * Called after drag operations to set the size of the resizing element.
34505      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34506      * @param {Number} newSize The new size to set
34507      * @param {Function} onComplete A function to be invoked when resizing is complete
34508      */
34509     setElementSize : function(s, newSize, onComplete){
34510         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34511             if(!s.animate){
34512                 s.resizingEl.setWidth(newSize);
34513                 if(onComplete){
34514                     onComplete(s, newSize);
34515                 }
34516             }else{
34517                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34518             }
34519         }else{
34520             
34521             if(!s.animate){
34522                 s.resizingEl.setHeight(newSize);
34523                 if(onComplete){
34524                     onComplete(s, newSize);
34525                 }
34526             }else{
34527                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34528             }
34529         }
34530     }
34531 };
34532
34533 /** 
34534  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34535  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34536  * Adapter that  moves the splitter element to align with the resized sizing element. 
34537  * Used with an absolute positioned SplitBar.
34538  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34539  * document.body, make sure you assign an id to the body element.
34540  */
34541 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34542     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34543     this.container = Roo.get(container);
34544 };
34545
34546 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34547     init : function(s){
34548         this.basic.init(s);
34549     },
34550     
34551     getElementSize : function(s){
34552         return this.basic.getElementSize(s);
34553     },
34554     
34555     setElementSize : function(s, newSize, onComplete){
34556         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34557     },
34558     
34559     moveSplitter : function(s){
34560         var yes = Roo.bootstrap.SplitBar;
34561         switch(s.placement){
34562             case yes.LEFT:
34563                 s.el.setX(s.resizingEl.getRight());
34564                 break;
34565             case yes.RIGHT:
34566                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34567                 break;
34568             case yes.TOP:
34569                 s.el.setY(s.resizingEl.getBottom());
34570                 break;
34571             case yes.BOTTOM:
34572                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34573                 break;
34574         }
34575     }
34576 };
34577
34578 /**
34579  * Orientation constant - Create a vertical SplitBar
34580  * @static
34581  * @type Number
34582  */
34583 Roo.bootstrap.SplitBar.VERTICAL = 1;
34584
34585 /**
34586  * Orientation constant - Create a horizontal SplitBar
34587  * @static
34588  * @type Number
34589  */
34590 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34591
34592 /**
34593  * Placement constant - The resizing element is to the left of the splitter element
34594  * @static
34595  * @type Number
34596  */
34597 Roo.bootstrap.SplitBar.LEFT = 1;
34598
34599 /**
34600  * Placement constant - The resizing element is to the right of the splitter element
34601  * @static
34602  * @type Number
34603  */
34604 Roo.bootstrap.SplitBar.RIGHT = 2;
34605
34606 /**
34607  * Placement constant - The resizing element is positioned above the splitter element
34608  * @static
34609  * @type Number
34610  */
34611 Roo.bootstrap.SplitBar.TOP = 3;
34612
34613 /**
34614  * Placement constant - The resizing element is positioned under splitter element
34615  * @static
34616  * @type Number
34617  */
34618 Roo.bootstrap.SplitBar.BOTTOM = 4;
34619 Roo.namespace("Roo.bootstrap.layout");/*
34620  * Based on:
34621  * Ext JS Library 1.1.1
34622  * Copyright(c) 2006-2007, Ext JS, LLC.
34623  *
34624  * Originally Released Under LGPL - original licence link has changed is not relivant.
34625  *
34626  * Fork - LGPL
34627  * <script type="text/javascript">
34628  */
34629
34630 /**
34631  * @class Roo.bootstrap.layout.Manager
34632  * @extends Roo.bootstrap.Component
34633  * Base class for layout managers.
34634  */
34635 Roo.bootstrap.layout.Manager = function(config)
34636 {
34637     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34638
34639
34640
34641
34642
34643     /** false to disable window resize monitoring @type Boolean */
34644     this.monitorWindowResize = true;
34645     this.regions = {};
34646     this.addEvents({
34647         /**
34648          * @event layout
34649          * Fires when a layout is performed.
34650          * @param {Roo.LayoutManager} this
34651          */
34652         "layout" : true,
34653         /**
34654          * @event regionresized
34655          * Fires when the user resizes a region.
34656          * @param {Roo.LayoutRegion} region The resized region
34657          * @param {Number} newSize The new size (width for east/west, height for north/south)
34658          */
34659         "regionresized" : true,
34660         /**
34661          * @event regioncollapsed
34662          * Fires when a region is collapsed.
34663          * @param {Roo.LayoutRegion} region The collapsed region
34664          */
34665         "regioncollapsed" : true,
34666         /**
34667          * @event regionexpanded
34668          * Fires when a region is expanded.
34669          * @param {Roo.LayoutRegion} region The expanded region
34670          */
34671         "regionexpanded" : true
34672     });
34673     this.updating = false;
34674
34675     if (config.el) {
34676         this.el = Roo.get(config.el);
34677         this.initEvents();
34678     }
34679
34680 };
34681
34682 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34683
34684
34685     regions : null,
34686
34687     monitorWindowResize : true,
34688
34689
34690     updating : false,
34691
34692
34693     onRender : function(ct, position)
34694     {
34695         if(!this.el){
34696             this.el = Roo.get(ct);
34697             this.initEvents();
34698         }
34699         //this.fireEvent('render',this);
34700     },
34701
34702
34703     initEvents: function()
34704     {
34705
34706
34707         // ie scrollbar fix
34708         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34709             document.body.scroll = "no";
34710         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34711             this.el.position('relative');
34712         }
34713         this.id = this.el.id;
34714         this.el.addClass("roo-layout-container");
34715         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34716         if(this.el.dom != document.body ) {
34717             this.el.on('resize', this.layout,this);
34718             this.el.on('show', this.layout,this);
34719         }
34720
34721     },
34722
34723     /**
34724      * Returns true if this layout is currently being updated
34725      * @return {Boolean}
34726      */
34727     isUpdating : function(){
34728         return this.updating;
34729     },
34730
34731     /**
34732      * Suspend the LayoutManager from doing auto-layouts while
34733      * making multiple add or remove calls
34734      */
34735     beginUpdate : function(){
34736         this.updating = true;
34737     },
34738
34739     /**
34740      * Restore auto-layouts and optionally disable the manager from performing a layout
34741      * @param {Boolean} noLayout true to disable a layout update
34742      */
34743     endUpdate : function(noLayout){
34744         this.updating = false;
34745         if(!noLayout){
34746             this.layout();
34747         }
34748     },
34749
34750     layout: function(){
34751         // abstract...
34752     },
34753
34754     onRegionResized : function(region, newSize){
34755         this.fireEvent("regionresized", region, newSize);
34756         this.layout();
34757     },
34758
34759     onRegionCollapsed : function(region){
34760         this.fireEvent("regioncollapsed", region);
34761     },
34762
34763     onRegionExpanded : function(region){
34764         this.fireEvent("regionexpanded", region);
34765     },
34766
34767     /**
34768      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34769      * performs box-model adjustments.
34770      * @return {Object} The size as an object {width: (the width), height: (the height)}
34771      */
34772     getViewSize : function()
34773     {
34774         var size;
34775         if(this.el.dom != document.body){
34776             size = this.el.getSize();
34777         }else{
34778             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34779         }
34780         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34781         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34782         return size;
34783     },
34784
34785     /**
34786      * Returns the Element this layout is bound to.
34787      * @return {Roo.Element}
34788      */
34789     getEl : function(){
34790         return this.el;
34791     },
34792
34793     /**
34794      * Returns the specified region.
34795      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34796      * @return {Roo.LayoutRegion}
34797      */
34798     getRegion : function(target){
34799         return this.regions[target.toLowerCase()];
34800     },
34801
34802     onWindowResize : function(){
34803         if(this.monitorWindowResize){
34804             this.layout();
34805         }
34806     }
34807 });
34808 /*
34809  * Based on:
34810  * Ext JS Library 1.1.1
34811  * Copyright(c) 2006-2007, Ext JS, LLC.
34812  *
34813  * Originally Released Under LGPL - original licence link has changed is not relivant.
34814  *
34815  * Fork - LGPL
34816  * <script type="text/javascript">
34817  */
34818 /**
34819  * @class Roo.bootstrap.layout.Border
34820  * @extends Roo.bootstrap.layout.Manager
34821  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34822  * please see: examples/bootstrap/nested.html<br><br>
34823  
34824 <b>The container the layout is rendered into can be either the body element or any other element.
34825 If it is not the body element, the container needs to either be an absolute positioned element,
34826 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34827 the container size if it is not the body element.</b>
34828
34829 * @constructor
34830 * Create a new Border
34831 * @param {Object} config Configuration options
34832  */
34833 Roo.bootstrap.layout.Border = function(config){
34834     config = config || {};
34835     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34836     
34837     
34838     
34839     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34840         if(config[region]){
34841             config[region].region = region;
34842             this.addRegion(config[region]);
34843         }
34844     },this);
34845     
34846 };
34847
34848 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34849
34850 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34851     /**
34852      * Creates and adds a new region if it doesn't already exist.
34853      * @param {String} target The target region key (north, south, east, west or center).
34854      * @param {Object} config The regions config object
34855      * @return {BorderLayoutRegion} The new region
34856      */
34857     addRegion : function(config)
34858     {
34859         if(!this.regions[config.region]){
34860             var r = this.factory(config);
34861             this.bindRegion(r);
34862         }
34863         return this.regions[config.region];
34864     },
34865
34866     // private (kinda)
34867     bindRegion : function(r){
34868         this.regions[r.config.region] = r;
34869         
34870         r.on("visibilitychange",    this.layout, this);
34871         r.on("paneladded",          this.layout, this);
34872         r.on("panelremoved",        this.layout, this);
34873         r.on("invalidated",         this.layout, this);
34874         r.on("resized",             this.onRegionResized, this);
34875         r.on("collapsed",           this.onRegionCollapsed, this);
34876         r.on("expanded",            this.onRegionExpanded, this);
34877     },
34878
34879     /**
34880      * Performs a layout update.
34881      */
34882     layout : function()
34883     {
34884         if(this.updating) {
34885             return;
34886         }
34887         
34888         // render all the rebions if they have not been done alreayd?
34889         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34890             if(this.regions[region] && !this.regions[region].bodyEl){
34891                 this.regions[region].onRender(this.el)
34892             }
34893         },this);
34894         
34895         var size = this.getViewSize();
34896         var w = size.width;
34897         var h = size.height;
34898         var centerW = w;
34899         var centerH = h;
34900         var centerY = 0;
34901         var centerX = 0;
34902         //var x = 0, y = 0;
34903
34904         var rs = this.regions;
34905         var north = rs["north"];
34906         var south = rs["south"]; 
34907         var west = rs["west"];
34908         var east = rs["east"];
34909         var center = rs["center"];
34910         //if(this.hideOnLayout){ // not supported anymore
34911             //c.el.setStyle("display", "none");
34912         //}
34913         if(north && north.isVisible()){
34914             var b = north.getBox();
34915             var m = north.getMargins();
34916             b.width = w - (m.left+m.right);
34917             b.x = m.left;
34918             b.y = m.top;
34919             centerY = b.height + b.y + m.bottom;
34920             centerH -= centerY;
34921             north.updateBox(this.safeBox(b));
34922         }
34923         if(south && south.isVisible()){
34924             var b = south.getBox();
34925             var m = south.getMargins();
34926             b.width = w - (m.left+m.right);
34927             b.x = m.left;
34928             var totalHeight = (b.height + m.top + m.bottom);
34929             b.y = h - totalHeight + m.top;
34930             centerH -= totalHeight;
34931             south.updateBox(this.safeBox(b));
34932         }
34933         if(west && west.isVisible()){
34934             var b = west.getBox();
34935             var m = west.getMargins();
34936             b.height = centerH - (m.top+m.bottom);
34937             b.x = m.left;
34938             b.y = centerY + m.top;
34939             var totalWidth = (b.width + m.left + m.right);
34940             centerX += totalWidth;
34941             centerW -= totalWidth;
34942             west.updateBox(this.safeBox(b));
34943         }
34944         if(east && east.isVisible()){
34945             var b = east.getBox();
34946             var m = east.getMargins();
34947             b.height = centerH - (m.top+m.bottom);
34948             var totalWidth = (b.width + m.left + m.right);
34949             b.x = w - totalWidth + m.left;
34950             b.y = centerY + m.top;
34951             centerW -= totalWidth;
34952             east.updateBox(this.safeBox(b));
34953         }
34954         if(center){
34955             var m = center.getMargins();
34956             var centerBox = {
34957                 x: centerX + m.left,
34958                 y: centerY + m.top,
34959                 width: centerW - (m.left+m.right),
34960                 height: centerH - (m.top+m.bottom)
34961             };
34962             //if(this.hideOnLayout){
34963                 //center.el.setStyle("display", "block");
34964             //}
34965             center.updateBox(this.safeBox(centerBox));
34966         }
34967         this.el.repaint();
34968         this.fireEvent("layout", this);
34969     },
34970
34971     // private
34972     safeBox : function(box){
34973         box.width = Math.max(0, box.width);
34974         box.height = Math.max(0, box.height);
34975         return box;
34976     },
34977
34978     /**
34979      * Adds a ContentPanel (or subclass) to this layout.
34980      * @param {String} target The target region key (north, south, east, west or center).
34981      * @param {Roo.ContentPanel} panel The panel to add
34982      * @return {Roo.ContentPanel} The added panel
34983      */
34984     add : function(target, panel){
34985          
34986         target = target.toLowerCase();
34987         return this.regions[target].add(panel);
34988     },
34989
34990     /**
34991      * Remove a ContentPanel (or subclass) to this layout.
34992      * @param {String} target The target region key (north, south, east, west or center).
34993      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34994      * @return {Roo.ContentPanel} The removed panel
34995      */
34996     remove : function(target, panel){
34997         target = target.toLowerCase();
34998         return this.regions[target].remove(panel);
34999     },
35000
35001     /**
35002      * Searches all regions for a panel with the specified id
35003      * @param {String} panelId
35004      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35005      */
35006     findPanel : function(panelId){
35007         var rs = this.regions;
35008         for(var target in rs){
35009             if(typeof rs[target] != "function"){
35010                 var p = rs[target].getPanel(panelId);
35011                 if(p){
35012                     return p;
35013                 }
35014             }
35015         }
35016         return null;
35017     },
35018
35019     /**
35020      * Searches all regions for a panel with the specified id and activates (shows) it.
35021      * @param {String/ContentPanel} panelId The panels id or the panel itself
35022      * @return {Roo.ContentPanel} The shown panel or null
35023      */
35024     showPanel : function(panelId) {
35025       var rs = this.regions;
35026       for(var target in rs){
35027          var r = rs[target];
35028          if(typeof r != "function"){
35029             if(r.hasPanel(panelId)){
35030                return r.showPanel(panelId);
35031             }
35032          }
35033       }
35034       return null;
35035    },
35036
35037    /**
35038      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35039      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35040      */
35041    /*
35042     restoreState : function(provider){
35043         if(!provider){
35044             provider = Roo.state.Manager;
35045         }
35046         var sm = new Roo.LayoutStateManager();
35047         sm.init(this, provider);
35048     },
35049 */
35050  
35051  
35052     /**
35053      * Adds a xtype elements to the layout.
35054      * <pre><code>
35055
35056 layout.addxtype({
35057        xtype : 'ContentPanel',
35058        region: 'west',
35059        items: [ .... ]
35060    }
35061 );
35062
35063 layout.addxtype({
35064         xtype : 'NestedLayoutPanel',
35065         region: 'west',
35066         layout: {
35067            center: { },
35068            west: { }   
35069         },
35070         items : [ ... list of content panels or nested layout panels.. ]
35071    }
35072 );
35073 </code></pre>
35074      * @param {Object} cfg Xtype definition of item to add.
35075      */
35076     addxtype : function(cfg)
35077     {
35078         // basically accepts a pannel...
35079         // can accept a layout region..!?!?
35080         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35081         
35082         
35083         // theory?  children can only be panels??
35084         
35085         //if (!cfg.xtype.match(/Panel$/)) {
35086         //    return false;
35087         //}
35088         var ret = false;
35089         
35090         if (typeof(cfg.region) == 'undefined') {
35091             Roo.log("Failed to add Panel, region was not set");
35092             Roo.log(cfg);
35093             return false;
35094         }
35095         var region = cfg.region;
35096         delete cfg.region;
35097         
35098           
35099         var xitems = [];
35100         if (cfg.items) {
35101             xitems = cfg.items;
35102             delete cfg.items;
35103         }
35104         var nb = false;
35105         
35106         switch(cfg.xtype) 
35107         {
35108             case 'Content':  // ContentPanel (el, cfg)
35109             case 'Scroll':  // ContentPanel (el, cfg)
35110             case 'View': 
35111                 cfg.autoCreate = true;
35112                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35113                 //} else {
35114                 //    var el = this.el.createChild();
35115                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35116                 //}
35117                 
35118                 this.add(region, ret);
35119                 break;
35120             
35121             /*
35122             case 'TreePanel': // our new panel!
35123                 cfg.el = this.el.createChild();
35124                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35125                 this.add(region, ret);
35126                 break;
35127             */
35128             
35129             case 'Nest': 
35130                 // create a new Layout (which is  a Border Layout...
35131                 
35132                 var clayout = cfg.layout;
35133                 clayout.el  = this.el.createChild();
35134                 clayout.items   = clayout.items  || [];
35135                 
35136                 delete cfg.layout;
35137                 
35138                 // replace this exitems with the clayout ones..
35139                 xitems = clayout.items;
35140                  
35141                 // force background off if it's in center...
35142                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35143                     cfg.background = false;
35144                 }
35145                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35146                 
35147                 
35148                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35149                 //console.log('adding nested layout panel '  + cfg.toSource());
35150                 this.add(region, ret);
35151                 nb = {}; /// find first...
35152                 break;
35153             
35154             case 'Grid':
35155                 
35156                 // needs grid and region
35157                 
35158                 //var el = this.getRegion(region).el.createChild();
35159                 /*
35160                  *var el = this.el.createChild();
35161                 // create the grid first...
35162                 cfg.grid.container = el;
35163                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35164                 */
35165                 
35166                 if (region == 'center' && this.active ) {
35167                     cfg.background = false;
35168                 }
35169                 
35170                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35171                 
35172                 this.add(region, ret);
35173                 /*
35174                 if (cfg.background) {
35175                     // render grid on panel activation (if panel background)
35176                     ret.on('activate', function(gp) {
35177                         if (!gp.grid.rendered) {
35178                     //        gp.grid.render(el);
35179                         }
35180                     });
35181                 } else {
35182                   //  cfg.grid.render(el);
35183                 }
35184                 */
35185                 break;
35186            
35187            
35188             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35189                 // it was the old xcomponent building that caused this before.
35190                 // espeically if border is the top element in the tree.
35191                 ret = this;
35192                 break; 
35193                 
35194                     
35195                 
35196                 
35197                 
35198             default:
35199                 /*
35200                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35201                     
35202                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35203                     this.add(region, ret);
35204                 } else {
35205                 */
35206                     Roo.log(cfg);
35207                     throw "Can not add '" + cfg.xtype + "' to Border";
35208                     return null;
35209              
35210                                 
35211              
35212         }
35213         this.beginUpdate();
35214         // add children..
35215         var region = '';
35216         var abn = {};
35217         Roo.each(xitems, function(i)  {
35218             region = nb && i.region ? i.region : false;
35219             
35220             var add = ret.addxtype(i);
35221            
35222             if (region) {
35223                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35224                 if (!i.background) {
35225                     abn[region] = nb[region] ;
35226                 }
35227             }
35228             
35229         });
35230         this.endUpdate();
35231
35232         // make the last non-background panel active..
35233         //if (nb) { Roo.log(abn); }
35234         if (nb) {
35235             
35236             for(var r in abn) {
35237                 region = this.getRegion(r);
35238                 if (region) {
35239                     // tried using nb[r], but it does not work..
35240                      
35241                     region.showPanel(abn[r]);
35242                    
35243                 }
35244             }
35245         }
35246         return ret;
35247         
35248     },
35249     
35250     
35251 // private
35252     factory : function(cfg)
35253     {
35254         
35255         var validRegions = Roo.bootstrap.layout.Border.regions;
35256
35257         var target = cfg.region;
35258         cfg.mgr = this;
35259         
35260         var r = Roo.bootstrap.layout;
35261         Roo.log(target);
35262         switch(target){
35263             case "north":
35264                 return new r.North(cfg);
35265             case "south":
35266                 return new r.South(cfg);
35267             case "east":
35268                 return new r.East(cfg);
35269             case "west":
35270                 return new r.West(cfg);
35271             case "center":
35272                 return new r.Center(cfg);
35273         }
35274         throw 'Layout region "'+target+'" not supported.';
35275     }
35276     
35277     
35278 });
35279  /*
35280  * Based on:
35281  * Ext JS Library 1.1.1
35282  * Copyright(c) 2006-2007, Ext JS, LLC.
35283  *
35284  * Originally Released Under LGPL - original licence link has changed is not relivant.
35285  *
35286  * Fork - LGPL
35287  * <script type="text/javascript">
35288  */
35289  
35290 /**
35291  * @class Roo.bootstrap.layout.Basic
35292  * @extends Roo.util.Observable
35293  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35294  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35295  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35296  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35297  * @cfg {string}   region  the region that it inhabits..
35298  * @cfg {bool}   skipConfig skip config?
35299  * 
35300
35301  */
35302 Roo.bootstrap.layout.Basic = function(config){
35303     
35304     this.mgr = config.mgr;
35305     
35306     this.position = config.region;
35307     
35308     var skipConfig = config.skipConfig;
35309     
35310     this.events = {
35311         /**
35312          * @scope Roo.BasicLayoutRegion
35313          */
35314         
35315         /**
35316          * @event beforeremove
35317          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35318          * @param {Roo.LayoutRegion} this
35319          * @param {Roo.ContentPanel} panel The panel
35320          * @param {Object} e The cancel event object
35321          */
35322         "beforeremove" : true,
35323         /**
35324          * @event invalidated
35325          * Fires when the layout for this region is changed.
35326          * @param {Roo.LayoutRegion} this
35327          */
35328         "invalidated" : true,
35329         /**
35330          * @event visibilitychange
35331          * Fires when this region is shown or hidden 
35332          * @param {Roo.LayoutRegion} this
35333          * @param {Boolean} visibility true or false
35334          */
35335         "visibilitychange" : true,
35336         /**
35337          * @event paneladded
35338          * Fires when a panel is added. 
35339          * @param {Roo.LayoutRegion} this
35340          * @param {Roo.ContentPanel} panel The panel
35341          */
35342         "paneladded" : true,
35343         /**
35344          * @event panelremoved
35345          * Fires when a panel is removed. 
35346          * @param {Roo.LayoutRegion} this
35347          * @param {Roo.ContentPanel} panel The panel
35348          */
35349         "panelremoved" : true,
35350         /**
35351          * @event beforecollapse
35352          * Fires when this region before collapse.
35353          * @param {Roo.LayoutRegion} this
35354          */
35355         "beforecollapse" : true,
35356         /**
35357          * @event collapsed
35358          * Fires when this region is collapsed.
35359          * @param {Roo.LayoutRegion} this
35360          */
35361         "collapsed" : true,
35362         /**
35363          * @event expanded
35364          * Fires when this region is expanded.
35365          * @param {Roo.LayoutRegion} this
35366          */
35367         "expanded" : true,
35368         /**
35369          * @event slideshow
35370          * Fires when this region is slid into view.
35371          * @param {Roo.LayoutRegion} this
35372          */
35373         "slideshow" : true,
35374         /**
35375          * @event slidehide
35376          * Fires when this region slides out of view. 
35377          * @param {Roo.LayoutRegion} this
35378          */
35379         "slidehide" : true,
35380         /**
35381          * @event panelactivated
35382          * Fires when a panel is activated. 
35383          * @param {Roo.LayoutRegion} this
35384          * @param {Roo.ContentPanel} panel The activated panel
35385          */
35386         "panelactivated" : true,
35387         /**
35388          * @event resized
35389          * Fires when the user resizes this region. 
35390          * @param {Roo.LayoutRegion} this
35391          * @param {Number} newSize The new size (width for east/west, height for north/south)
35392          */
35393         "resized" : true
35394     };
35395     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35396     this.panels = new Roo.util.MixedCollection();
35397     this.panels.getKey = this.getPanelId.createDelegate(this);
35398     this.box = null;
35399     this.activePanel = null;
35400     // ensure listeners are added...
35401     
35402     if (config.listeners || config.events) {
35403         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35404             listeners : config.listeners || {},
35405             events : config.events || {}
35406         });
35407     }
35408     
35409     if(skipConfig !== true){
35410         this.applyConfig(config);
35411     }
35412 };
35413
35414 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35415 {
35416     getPanelId : function(p){
35417         return p.getId();
35418     },
35419     
35420     applyConfig : function(config){
35421         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35422         this.config = config;
35423         
35424     },
35425     
35426     /**
35427      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35428      * the width, for horizontal (north, south) the height.
35429      * @param {Number} newSize The new width or height
35430      */
35431     resizeTo : function(newSize){
35432         var el = this.el ? this.el :
35433                  (this.activePanel ? this.activePanel.getEl() : null);
35434         if(el){
35435             switch(this.position){
35436                 case "east":
35437                 case "west":
35438                     el.setWidth(newSize);
35439                     this.fireEvent("resized", this, newSize);
35440                 break;
35441                 case "north":
35442                 case "south":
35443                     el.setHeight(newSize);
35444                     this.fireEvent("resized", this, newSize);
35445                 break;                
35446             }
35447         }
35448     },
35449     
35450     getBox : function(){
35451         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35452     },
35453     
35454     getMargins : function(){
35455         return this.margins;
35456     },
35457     
35458     updateBox : function(box){
35459         this.box = box;
35460         var el = this.activePanel.getEl();
35461         el.dom.style.left = box.x + "px";
35462         el.dom.style.top = box.y + "px";
35463         this.activePanel.setSize(box.width, box.height);
35464     },
35465     
35466     /**
35467      * Returns the container element for this region.
35468      * @return {Roo.Element}
35469      */
35470     getEl : function(){
35471         return this.activePanel;
35472     },
35473     
35474     /**
35475      * Returns true if this region is currently visible.
35476      * @return {Boolean}
35477      */
35478     isVisible : function(){
35479         return this.activePanel ? true : false;
35480     },
35481     
35482     setActivePanel : function(panel){
35483         panel = this.getPanel(panel);
35484         if(this.activePanel && this.activePanel != panel){
35485             this.activePanel.setActiveState(false);
35486             this.activePanel.getEl().setLeftTop(-10000,-10000);
35487         }
35488         this.activePanel = panel;
35489         panel.setActiveState(true);
35490         if(this.box){
35491             panel.setSize(this.box.width, this.box.height);
35492         }
35493         this.fireEvent("panelactivated", this, panel);
35494         this.fireEvent("invalidated");
35495     },
35496     
35497     /**
35498      * Show the specified panel.
35499      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35500      * @return {Roo.ContentPanel} The shown panel or null
35501      */
35502     showPanel : function(panel){
35503         panel = this.getPanel(panel);
35504         if(panel){
35505             this.setActivePanel(panel);
35506         }
35507         return panel;
35508     },
35509     
35510     /**
35511      * Get the active panel for this region.
35512      * @return {Roo.ContentPanel} The active panel or null
35513      */
35514     getActivePanel : function(){
35515         return this.activePanel;
35516     },
35517     
35518     /**
35519      * Add the passed ContentPanel(s)
35520      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35521      * @return {Roo.ContentPanel} The panel added (if only one was added)
35522      */
35523     add : function(panel){
35524         if(arguments.length > 1){
35525             for(var i = 0, len = arguments.length; i < len; i++) {
35526                 this.add(arguments[i]);
35527             }
35528             return null;
35529         }
35530         if(this.hasPanel(panel)){
35531             this.showPanel(panel);
35532             return panel;
35533         }
35534         var el = panel.getEl();
35535         if(el.dom.parentNode != this.mgr.el.dom){
35536             this.mgr.el.dom.appendChild(el.dom);
35537         }
35538         if(panel.setRegion){
35539             panel.setRegion(this);
35540         }
35541         this.panels.add(panel);
35542         el.setStyle("position", "absolute");
35543         if(!panel.background){
35544             this.setActivePanel(panel);
35545             if(this.config.initialSize && this.panels.getCount()==1){
35546                 this.resizeTo(this.config.initialSize);
35547             }
35548         }
35549         this.fireEvent("paneladded", this, panel);
35550         return panel;
35551     },
35552     
35553     /**
35554      * Returns true if the panel is in this region.
35555      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35556      * @return {Boolean}
35557      */
35558     hasPanel : function(panel){
35559         if(typeof panel == "object"){ // must be panel obj
35560             panel = panel.getId();
35561         }
35562         return this.getPanel(panel) ? true : false;
35563     },
35564     
35565     /**
35566      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35567      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35568      * @param {Boolean} preservePanel Overrides the config preservePanel option
35569      * @return {Roo.ContentPanel} The panel that was removed
35570      */
35571     remove : function(panel, preservePanel){
35572         panel = this.getPanel(panel);
35573         if(!panel){
35574             return null;
35575         }
35576         var e = {};
35577         this.fireEvent("beforeremove", this, panel, e);
35578         if(e.cancel === true){
35579             return null;
35580         }
35581         var panelId = panel.getId();
35582         this.panels.removeKey(panelId);
35583         return panel;
35584     },
35585     
35586     /**
35587      * Returns the panel specified or null if it's not in this region.
35588      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35589      * @return {Roo.ContentPanel}
35590      */
35591     getPanel : function(id){
35592         if(typeof id == "object"){ // must be panel obj
35593             return id;
35594         }
35595         return this.panels.get(id);
35596     },
35597     
35598     /**
35599      * Returns this regions position (north/south/east/west/center).
35600      * @return {String} 
35601      */
35602     getPosition: function(){
35603         return this.position;    
35604     }
35605 });/*
35606  * Based on:
35607  * Ext JS Library 1.1.1
35608  * Copyright(c) 2006-2007, Ext JS, LLC.
35609  *
35610  * Originally Released Under LGPL - original licence link has changed is not relivant.
35611  *
35612  * Fork - LGPL
35613  * <script type="text/javascript">
35614  */
35615  
35616 /**
35617  * @class Roo.bootstrap.layout.Region
35618  * @extends Roo.bootstrap.layout.Basic
35619  * This class represents a region in a layout manager.
35620  
35621  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35622  * @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})
35623  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35624  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35625  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35626  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35627  * @cfg {String}    title           The title for the region (overrides panel titles)
35628  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35629  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35630  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35631  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35632  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35633  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35634  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35635  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35636  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35637  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35638
35639  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35640  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35641  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35642  * @cfg {Number}    width           For East/West panels
35643  * @cfg {Number}    height          For North/South panels
35644  * @cfg {Boolean}   split           To show the splitter
35645  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35646  * 
35647  * @cfg {string}   cls             Extra CSS classes to add to region
35648  * 
35649  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35650  * @cfg {string}   region  the region that it inhabits..
35651  *
35652
35653  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35654  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35655
35656  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35657  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35658  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35659  */
35660 Roo.bootstrap.layout.Region = function(config)
35661 {
35662     this.applyConfig(config);
35663
35664     var mgr = config.mgr;
35665     var pos = config.region;
35666     config.skipConfig = true;
35667     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35668     
35669     if (mgr.el) {
35670         this.onRender(mgr.el);   
35671     }
35672      
35673     this.visible = true;
35674     this.collapsed = false;
35675     this.unrendered_panels = [];
35676 };
35677
35678 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35679
35680     position: '', // set by wrapper (eg. north/south etc..)
35681     unrendered_panels : null,  // unrendered panels.
35682     createBody : function(){
35683         /** This region's body element 
35684         * @type Roo.Element */
35685         this.bodyEl = this.el.createChild({
35686                 tag: "div",
35687                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35688         });
35689     },
35690
35691     onRender: function(ctr, pos)
35692     {
35693         var dh = Roo.DomHelper;
35694         /** This region's container element 
35695         * @type Roo.Element */
35696         this.el = dh.append(ctr.dom, {
35697                 tag: "div",
35698                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35699             }, true);
35700         /** This region's title element 
35701         * @type Roo.Element */
35702     
35703         this.titleEl = dh.append(this.el.dom,
35704             {
35705                     tag: "div",
35706                     unselectable: "on",
35707                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35708                     children:[
35709                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35710                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35711                     ]}, true);
35712         
35713         this.titleEl.enableDisplayMode();
35714         /** This region's title text element 
35715         * @type HTMLElement */
35716         this.titleTextEl = this.titleEl.dom.firstChild;
35717         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35718         /*
35719         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35720         this.closeBtn.enableDisplayMode();
35721         this.closeBtn.on("click", this.closeClicked, this);
35722         this.closeBtn.hide();
35723     */
35724         this.createBody(this.config);
35725         if(this.config.hideWhenEmpty){
35726             this.hide();
35727             this.on("paneladded", this.validateVisibility, this);
35728             this.on("panelremoved", this.validateVisibility, this);
35729         }
35730         if(this.autoScroll){
35731             this.bodyEl.setStyle("overflow", "auto");
35732         }else{
35733             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35734         }
35735         //if(c.titlebar !== false){
35736             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35737                 this.titleEl.hide();
35738             }else{
35739                 this.titleEl.show();
35740                 if(this.config.title){
35741                     this.titleTextEl.innerHTML = this.config.title;
35742                 }
35743             }
35744         //}
35745         if(this.config.collapsed){
35746             this.collapse(true);
35747         }
35748         if(this.config.hidden){
35749             this.hide();
35750         }
35751         
35752         if (this.unrendered_panels && this.unrendered_panels.length) {
35753             for (var i =0;i< this.unrendered_panels.length; i++) {
35754                 this.add(this.unrendered_panels[i]);
35755             }
35756             this.unrendered_panels = null;
35757             
35758         }
35759         
35760     },
35761     
35762     applyConfig : function(c)
35763     {
35764         /*
35765          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35766             var dh = Roo.DomHelper;
35767             if(c.titlebar !== false){
35768                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35769                 this.collapseBtn.on("click", this.collapse, this);
35770                 this.collapseBtn.enableDisplayMode();
35771                 /*
35772                 if(c.showPin === true || this.showPin){
35773                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35774                     this.stickBtn.enableDisplayMode();
35775                     this.stickBtn.on("click", this.expand, this);
35776                     this.stickBtn.hide();
35777                 }
35778                 
35779             }
35780             */
35781             /** This region's collapsed element
35782             * @type Roo.Element */
35783             /*
35784              *
35785             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35786                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35787             ]}, true);
35788             
35789             if(c.floatable !== false){
35790                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35791                this.collapsedEl.on("click", this.collapseClick, this);
35792             }
35793
35794             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35795                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35796                    id: "message", unselectable: "on", style:{"float":"left"}});
35797                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35798              }
35799             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35800             this.expandBtn.on("click", this.expand, this);
35801             
35802         }
35803         
35804         if(this.collapseBtn){
35805             this.collapseBtn.setVisible(c.collapsible == true);
35806         }
35807         
35808         this.cmargins = c.cmargins || this.cmargins ||
35809                          (this.position == "west" || this.position == "east" ?
35810                              {top: 0, left: 2, right:2, bottom: 0} :
35811                              {top: 2, left: 0, right:0, bottom: 2});
35812         */
35813         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35814         
35815         
35816         this.bottomTabs = c.tabPosition != "top";
35817         
35818         this.autoScroll = c.autoScroll || false;
35819         
35820         
35821        
35822         
35823         this.duration = c.duration || .30;
35824         this.slideDuration = c.slideDuration || .45;
35825         this.config = c;
35826        
35827     },
35828     /**
35829      * Returns true if this region is currently visible.
35830      * @return {Boolean}
35831      */
35832     isVisible : function(){
35833         return this.visible;
35834     },
35835
35836     /**
35837      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35838      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35839      */
35840     //setCollapsedTitle : function(title){
35841     //    title = title || "&#160;";
35842      //   if(this.collapsedTitleTextEl){
35843       //      this.collapsedTitleTextEl.innerHTML = title;
35844        // }
35845     //},
35846
35847     getBox : function(){
35848         var b;
35849       //  if(!this.collapsed){
35850             b = this.el.getBox(false, true);
35851        // }else{
35852           //  b = this.collapsedEl.getBox(false, true);
35853         //}
35854         return b;
35855     },
35856
35857     getMargins : function(){
35858         return this.margins;
35859         //return this.collapsed ? this.cmargins : this.margins;
35860     },
35861 /*
35862     highlight : function(){
35863         this.el.addClass("x-layout-panel-dragover");
35864     },
35865
35866     unhighlight : function(){
35867         this.el.removeClass("x-layout-panel-dragover");
35868     },
35869 */
35870     updateBox : function(box)
35871     {
35872         if (!this.bodyEl) {
35873             return; // not rendered yet..
35874         }
35875         
35876         this.box = box;
35877         if(!this.collapsed){
35878             this.el.dom.style.left = box.x + "px";
35879             this.el.dom.style.top = box.y + "px";
35880             this.updateBody(box.width, box.height);
35881         }else{
35882             this.collapsedEl.dom.style.left = box.x + "px";
35883             this.collapsedEl.dom.style.top = box.y + "px";
35884             this.collapsedEl.setSize(box.width, box.height);
35885         }
35886         if(this.tabs){
35887             this.tabs.autoSizeTabs();
35888         }
35889     },
35890
35891     updateBody : function(w, h)
35892     {
35893         if(w !== null){
35894             this.el.setWidth(w);
35895             w -= this.el.getBorderWidth("rl");
35896             if(this.config.adjustments){
35897                 w += this.config.adjustments[0];
35898             }
35899         }
35900         if(h !== null && h > 0){
35901             this.el.setHeight(h);
35902             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35903             h -= this.el.getBorderWidth("tb");
35904             if(this.config.adjustments){
35905                 h += this.config.adjustments[1];
35906             }
35907             this.bodyEl.setHeight(h);
35908             if(this.tabs){
35909                 h = this.tabs.syncHeight(h);
35910             }
35911         }
35912         if(this.panelSize){
35913             w = w !== null ? w : this.panelSize.width;
35914             h = h !== null ? h : this.panelSize.height;
35915         }
35916         if(this.activePanel){
35917             var el = this.activePanel.getEl();
35918             w = w !== null ? w : el.getWidth();
35919             h = h !== null ? h : el.getHeight();
35920             this.panelSize = {width: w, height: h};
35921             this.activePanel.setSize(w, h);
35922         }
35923         if(Roo.isIE && this.tabs){
35924             this.tabs.el.repaint();
35925         }
35926     },
35927
35928     /**
35929      * Returns the container element for this region.
35930      * @return {Roo.Element}
35931      */
35932     getEl : function(){
35933         return this.el;
35934     },
35935
35936     /**
35937      * Hides this region.
35938      */
35939     hide : function(){
35940         //if(!this.collapsed){
35941             this.el.dom.style.left = "-2000px";
35942             this.el.hide();
35943         //}else{
35944          //   this.collapsedEl.dom.style.left = "-2000px";
35945          //   this.collapsedEl.hide();
35946        // }
35947         this.visible = false;
35948         this.fireEvent("visibilitychange", this, false);
35949     },
35950
35951     /**
35952      * Shows this region if it was previously hidden.
35953      */
35954     show : function(){
35955         //if(!this.collapsed){
35956             this.el.show();
35957         //}else{
35958         //    this.collapsedEl.show();
35959        // }
35960         this.visible = true;
35961         this.fireEvent("visibilitychange", this, true);
35962     },
35963 /*
35964     closeClicked : function(){
35965         if(this.activePanel){
35966             this.remove(this.activePanel);
35967         }
35968     },
35969
35970     collapseClick : function(e){
35971         if(this.isSlid){
35972            e.stopPropagation();
35973            this.slideIn();
35974         }else{
35975            e.stopPropagation();
35976            this.slideOut();
35977         }
35978     },
35979 */
35980     /**
35981      * Collapses this region.
35982      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35983      */
35984     /*
35985     collapse : function(skipAnim, skipCheck = false){
35986         if(this.collapsed) {
35987             return;
35988         }
35989         
35990         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35991             
35992             this.collapsed = true;
35993             if(this.split){
35994                 this.split.el.hide();
35995             }
35996             if(this.config.animate && skipAnim !== true){
35997                 this.fireEvent("invalidated", this);
35998                 this.animateCollapse();
35999             }else{
36000                 this.el.setLocation(-20000,-20000);
36001                 this.el.hide();
36002                 this.collapsedEl.show();
36003                 this.fireEvent("collapsed", this);
36004                 this.fireEvent("invalidated", this);
36005             }
36006         }
36007         
36008     },
36009 */
36010     animateCollapse : function(){
36011         // overridden
36012     },
36013
36014     /**
36015      * Expands this region if it was previously collapsed.
36016      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36017      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36018      */
36019     /*
36020     expand : function(e, skipAnim){
36021         if(e) {
36022             e.stopPropagation();
36023         }
36024         if(!this.collapsed || this.el.hasActiveFx()) {
36025             return;
36026         }
36027         if(this.isSlid){
36028             this.afterSlideIn();
36029             skipAnim = true;
36030         }
36031         this.collapsed = false;
36032         if(this.config.animate && skipAnim !== true){
36033             this.animateExpand();
36034         }else{
36035             this.el.show();
36036             if(this.split){
36037                 this.split.el.show();
36038             }
36039             this.collapsedEl.setLocation(-2000,-2000);
36040             this.collapsedEl.hide();
36041             this.fireEvent("invalidated", this);
36042             this.fireEvent("expanded", this);
36043         }
36044     },
36045 */
36046     animateExpand : function(){
36047         // overridden
36048     },
36049
36050     initTabs : function()
36051     {
36052         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36053         
36054         var ts = new Roo.bootstrap.panel.Tabs({
36055                 el: this.bodyEl.dom,
36056                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36057                 disableTooltips: this.config.disableTabTips,
36058                 toolbar : this.config.toolbar
36059             });
36060         
36061         if(this.config.hideTabs){
36062             ts.stripWrap.setDisplayed(false);
36063         }
36064         this.tabs = ts;
36065         ts.resizeTabs = this.config.resizeTabs === true;
36066         ts.minTabWidth = this.config.minTabWidth || 40;
36067         ts.maxTabWidth = this.config.maxTabWidth || 250;
36068         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36069         ts.monitorResize = false;
36070         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36071         ts.bodyEl.addClass('roo-layout-tabs-body');
36072         this.panels.each(this.initPanelAsTab, this);
36073     },
36074
36075     initPanelAsTab : function(panel){
36076         var ti = this.tabs.addTab(
36077             panel.getEl().id,
36078             panel.getTitle(),
36079             null,
36080             this.config.closeOnTab && panel.isClosable(),
36081             panel.tpl
36082         );
36083         if(panel.tabTip !== undefined){
36084             ti.setTooltip(panel.tabTip);
36085         }
36086         ti.on("activate", function(){
36087               this.setActivePanel(panel);
36088         }, this);
36089         
36090         if(this.config.closeOnTab){
36091             ti.on("beforeclose", function(t, e){
36092                 e.cancel = true;
36093                 this.remove(panel);
36094             }, this);
36095         }
36096         
36097         panel.tabItem = ti;
36098         
36099         return ti;
36100     },
36101
36102     updatePanelTitle : function(panel, title)
36103     {
36104         if(this.activePanel == panel){
36105             this.updateTitle(title);
36106         }
36107         if(this.tabs){
36108             var ti = this.tabs.getTab(panel.getEl().id);
36109             ti.setText(title);
36110             if(panel.tabTip !== undefined){
36111                 ti.setTooltip(panel.tabTip);
36112             }
36113         }
36114     },
36115
36116     updateTitle : function(title){
36117         if(this.titleTextEl && !this.config.title){
36118             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36119         }
36120     },
36121
36122     setActivePanel : function(panel)
36123     {
36124         panel = this.getPanel(panel);
36125         if(this.activePanel && this.activePanel != panel){
36126             if(this.activePanel.setActiveState(false) === false){
36127                 return;
36128             }
36129         }
36130         this.activePanel = panel;
36131         panel.setActiveState(true);
36132         if(this.panelSize){
36133             panel.setSize(this.panelSize.width, this.panelSize.height);
36134         }
36135         if(this.closeBtn){
36136             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36137         }
36138         this.updateTitle(panel.getTitle());
36139         if(this.tabs){
36140             this.fireEvent("invalidated", this);
36141         }
36142         this.fireEvent("panelactivated", this, panel);
36143     },
36144
36145     /**
36146      * Shows the specified panel.
36147      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36148      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36149      */
36150     showPanel : function(panel)
36151     {
36152         panel = this.getPanel(panel);
36153         if(panel){
36154             if(this.tabs){
36155                 var tab = this.tabs.getTab(panel.getEl().id);
36156                 if(tab.isHidden()){
36157                     this.tabs.unhideTab(tab.id);
36158                 }
36159                 tab.activate();
36160             }else{
36161                 this.setActivePanel(panel);
36162             }
36163         }
36164         return panel;
36165     },
36166
36167     /**
36168      * Get the active panel for this region.
36169      * @return {Roo.ContentPanel} The active panel or null
36170      */
36171     getActivePanel : function(){
36172         return this.activePanel;
36173     },
36174
36175     validateVisibility : function(){
36176         if(this.panels.getCount() < 1){
36177             this.updateTitle("&#160;");
36178             this.closeBtn.hide();
36179             this.hide();
36180         }else{
36181             if(!this.isVisible()){
36182                 this.show();
36183             }
36184         }
36185     },
36186
36187     /**
36188      * Adds the passed ContentPanel(s) to this region.
36189      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36190      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36191      */
36192     add : function(panel)
36193     {
36194         if(arguments.length > 1){
36195             for(var i = 0, len = arguments.length; i < len; i++) {
36196                 this.add(arguments[i]);
36197             }
36198             return null;
36199         }
36200         
36201         // if we have not been rendered yet, then we can not really do much of this..
36202         if (!this.bodyEl) {
36203             this.unrendered_panels.push(panel);
36204             return panel;
36205         }
36206         
36207         
36208         
36209         
36210         if(this.hasPanel(panel)){
36211             this.showPanel(panel);
36212             return panel;
36213         }
36214         panel.setRegion(this);
36215         this.panels.add(panel);
36216        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36217             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36218             // and hide them... ???
36219             this.bodyEl.dom.appendChild(panel.getEl().dom);
36220             if(panel.background !== true){
36221                 this.setActivePanel(panel);
36222             }
36223             this.fireEvent("paneladded", this, panel);
36224             return panel;
36225         }
36226         */
36227         if(!this.tabs){
36228             this.initTabs();
36229         }else{
36230             this.initPanelAsTab(panel);
36231         }
36232         
36233         
36234         if(panel.background !== true){
36235             this.tabs.activate(panel.getEl().id);
36236         }
36237         this.fireEvent("paneladded", this, panel);
36238         return panel;
36239     },
36240
36241     /**
36242      * Hides the tab for the specified panel.
36243      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36244      */
36245     hidePanel : function(panel){
36246         if(this.tabs && (panel = this.getPanel(panel))){
36247             this.tabs.hideTab(panel.getEl().id);
36248         }
36249     },
36250
36251     /**
36252      * Unhides the tab for a previously hidden panel.
36253      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36254      */
36255     unhidePanel : function(panel){
36256         if(this.tabs && (panel = this.getPanel(panel))){
36257             this.tabs.unhideTab(panel.getEl().id);
36258         }
36259     },
36260
36261     clearPanels : function(){
36262         while(this.panels.getCount() > 0){
36263              this.remove(this.panels.first());
36264         }
36265     },
36266
36267     /**
36268      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36269      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36270      * @param {Boolean} preservePanel Overrides the config preservePanel option
36271      * @return {Roo.ContentPanel} The panel that was removed
36272      */
36273     remove : function(panel, preservePanel)
36274     {
36275         panel = this.getPanel(panel);
36276         if(!panel){
36277             return null;
36278         }
36279         var e = {};
36280         this.fireEvent("beforeremove", this, panel, e);
36281         if(e.cancel === true){
36282             return null;
36283         }
36284         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36285         var panelId = panel.getId();
36286         this.panels.removeKey(panelId);
36287         if(preservePanel){
36288             document.body.appendChild(panel.getEl().dom);
36289         }
36290         if(this.tabs){
36291             this.tabs.removeTab(panel.getEl().id);
36292         }else if (!preservePanel){
36293             this.bodyEl.dom.removeChild(panel.getEl().dom);
36294         }
36295         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36296             var p = this.panels.first();
36297             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36298             tempEl.appendChild(p.getEl().dom);
36299             this.bodyEl.update("");
36300             this.bodyEl.dom.appendChild(p.getEl().dom);
36301             tempEl = null;
36302             this.updateTitle(p.getTitle());
36303             this.tabs = null;
36304             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36305             this.setActivePanel(p);
36306         }
36307         panel.setRegion(null);
36308         if(this.activePanel == panel){
36309             this.activePanel = null;
36310         }
36311         if(this.config.autoDestroy !== false && preservePanel !== true){
36312             try{panel.destroy();}catch(e){}
36313         }
36314         this.fireEvent("panelremoved", this, panel);
36315         return panel;
36316     },
36317
36318     /**
36319      * Returns the TabPanel component used by this region
36320      * @return {Roo.TabPanel}
36321      */
36322     getTabs : function(){
36323         return this.tabs;
36324     },
36325
36326     createTool : function(parentEl, className){
36327         var btn = Roo.DomHelper.append(parentEl, {
36328             tag: "div",
36329             cls: "x-layout-tools-button",
36330             children: [ {
36331                 tag: "div",
36332                 cls: "roo-layout-tools-button-inner " + className,
36333                 html: "&#160;"
36334             }]
36335         }, true);
36336         btn.addClassOnOver("roo-layout-tools-button-over");
36337         return btn;
36338     }
36339 });/*
36340  * Based on:
36341  * Ext JS Library 1.1.1
36342  * Copyright(c) 2006-2007, Ext JS, LLC.
36343  *
36344  * Originally Released Under LGPL - original licence link has changed is not relivant.
36345  *
36346  * Fork - LGPL
36347  * <script type="text/javascript">
36348  */
36349  
36350
36351
36352 /**
36353  * @class Roo.SplitLayoutRegion
36354  * @extends Roo.LayoutRegion
36355  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36356  */
36357 Roo.bootstrap.layout.Split = function(config){
36358     this.cursor = config.cursor;
36359     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36360 };
36361
36362 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36363 {
36364     splitTip : "Drag to resize.",
36365     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36366     useSplitTips : false,
36367
36368     applyConfig : function(config){
36369         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36370     },
36371     
36372     onRender : function(ctr,pos) {
36373         
36374         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36375         if(!this.config.split){
36376             return;
36377         }
36378         if(!this.split){
36379             
36380             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36381                             tag: "div",
36382                             id: this.el.id + "-split",
36383                             cls: "roo-layout-split roo-layout-split-"+this.position,
36384                             html: "&#160;"
36385             });
36386             /** The SplitBar for this region 
36387             * @type Roo.SplitBar */
36388             // does not exist yet...
36389             Roo.log([this.position, this.orientation]);
36390             
36391             this.split = new Roo.bootstrap.SplitBar({
36392                 dragElement : splitEl,
36393                 resizingElement: this.el,
36394                 orientation : this.orientation
36395             });
36396             
36397             this.split.on("moved", this.onSplitMove, this);
36398             this.split.useShim = this.config.useShim === true;
36399             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36400             if(this.useSplitTips){
36401                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36402             }
36403             //if(config.collapsible){
36404             //    this.split.el.on("dblclick", this.collapse,  this);
36405             //}
36406         }
36407         if(typeof this.config.minSize != "undefined"){
36408             this.split.minSize = this.config.minSize;
36409         }
36410         if(typeof this.config.maxSize != "undefined"){
36411             this.split.maxSize = this.config.maxSize;
36412         }
36413         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36414             this.hideSplitter();
36415         }
36416         
36417     },
36418
36419     getHMaxSize : function(){
36420          var cmax = this.config.maxSize || 10000;
36421          var center = this.mgr.getRegion("center");
36422          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36423     },
36424
36425     getVMaxSize : function(){
36426          var cmax = this.config.maxSize || 10000;
36427          var center = this.mgr.getRegion("center");
36428          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36429     },
36430
36431     onSplitMove : function(split, newSize){
36432         this.fireEvent("resized", this, newSize);
36433     },
36434     
36435     /** 
36436      * Returns the {@link Roo.SplitBar} for this region.
36437      * @return {Roo.SplitBar}
36438      */
36439     getSplitBar : function(){
36440         return this.split;
36441     },
36442     
36443     hide : function(){
36444         this.hideSplitter();
36445         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36446     },
36447
36448     hideSplitter : function(){
36449         if(this.split){
36450             this.split.el.setLocation(-2000,-2000);
36451             this.split.el.hide();
36452         }
36453     },
36454
36455     show : function(){
36456         if(this.split){
36457             this.split.el.show();
36458         }
36459         Roo.bootstrap.layout.Split.superclass.show.call(this);
36460     },
36461     
36462     beforeSlide: function(){
36463         if(Roo.isGecko){// firefox overflow auto bug workaround
36464             this.bodyEl.clip();
36465             if(this.tabs) {
36466                 this.tabs.bodyEl.clip();
36467             }
36468             if(this.activePanel){
36469                 this.activePanel.getEl().clip();
36470                 
36471                 if(this.activePanel.beforeSlide){
36472                     this.activePanel.beforeSlide();
36473                 }
36474             }
36475         }
36476     },
36477     
36478     afterSlide : function(){
36479         if(Roo.isGecko){// firefox overflow auto bug workaround
36480             this.bodyEl.unclip();
36481             if(this.tabs) {
36482                 this.tabs.bodyEl.unclip();
36483             }
36484             if(this.activePanel){
36485                 this.activePanel.getEl().unclip();
36486                 if(this.activePanel.afterSlide){
36487                     this.activePanel.afterSlide();
36488                 }
36489             }
36490         }
36491     },
36492
36493     initAutoHide : function(){
36494         if(this.autoHide !== false){
36495             if(!this.autoHideHd){
36496                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36497                 this.autoHideHd = {
36498                     "mouseout": function(e){
36499                         if(!e.within(this.el, true)){
36500                             st.delay(500);
36501                         }
36502                     },
36503                     "mouseover" : function(e){
36504                         st.cancel();
36505                     },
36506                     scope : this
36507                 };
36508             }
36509             this.el.on(this.autoHideHd);
36510         }
36511     },
36512
36513     clearAutoHide : function(){
36514         if(this.autoHide !== false){
36515             this.el.un("mouseout", this.autoHideHd.mouseout);
36516             this.el.un("mouseover", this.autoHideHd.mouseover);
36517         }
36518     },
36519
36520     clearMonitor : function(){
36521         Roo.get(document).un("click", this.slideInIf, this);
36522     },
36523
36524     // these names are backwards but not changed for compat
36525     slideOut : function(){
36526         if(this.isSlid || this.el.hasActiveFx()){
36527             return;
36528         }
36529         this.isSlid = true;
36530         if(this.collapseBtn){
36531             this.collapseBtn.hide();
36532         }
36533         this.closeBtnState = this.closeBtn.getStyle('display');
36534         this.closeBtn.hide();
36535         if(this.stickBtn){
36536             this.stickBtn.show();
36537         }
36538         this.el.show();
36539         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36540         this.beforeSlide();
36541         this.el.setStyle("z-index", 10001);
36542         this.el.slideIn(this.getSlideAnchor(), {
36543             callback: function(){
36544                 this.afterSlide();
36545                 this.initAutoHide();
36546                 Roo.get(document).on("click", this.slideInIf, this);
36547                 this.fireEvent("slideshow", this);
36548             },
36549             scope: this,
36550             block: true
36551         });
36552     },
36553
36554     afterSlideIn : function(){
36555         this.clearAutoHide();
36556         this.isSlid = false;
36557         this.clearMonitor();
36558         this.el.setStyle("z-index", "");
36559         if(this.collapseBtn){
36560             this.collapseBtn.show();
36561         }
36562         this.closeBtn.setStyle('display', this.closeBtnState);
36563         if(this.stickBtn){
36564             this.stickBtn.hide();
36565         }
36566         this.fireEvent("slidehide", this);
36567     },
36568
36569     slideIn : function(cb){
36570         if(!this.isSlid || this.el.hasActiveFx()){
36571             Roo.callback(cb);
36572             return;
36573         }
36574         this.isSlid = false;
36575         this.beforeSlide();
36576         this.el.slideOut(this.getSlideAnchor(), {
36577             callback: function(){
36578                 this.el.setLeftTop(-10000, -10000);
36579                 this.afterSlide();
36580                 this.afterSlideIn();
36581                 Roo.callback(cb);
36582             },
36583             scope: this,
36584             block: true
36585         });
36586     },
36587     
36588     slideInIf : function(e){
36589         if(!e.within(this.el)){
36590             this.slideIn();
36591         }
36592     },
36593
36594     animateCollapse : function(){
36595         this.beforeSlide();
36596         this.el.setStyle("z-index", 20000);
36597         var anchor = this.getSlideAnchor();
36598         this.el.slideOut(anchor, {
36599             callback : function(){
36600                 this.el.setStyle("z-index", "");
36601                 this.collapsedEl.slideIn(anchor, {duration:.3});
36602                 this.afterSlide();
36603                 this.el.setLocation(-10000,-10000);
36604                 this.el.hide();
36605                 this.fireEvent("collapsed", this);
36606             },
36607             scope: this,
36608             block: true
36609         });
36610     },
36611
36612     animateExpand : function(){
36613         this.beforeSlide();
36614         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36615         this.el.setStyle("z-index", 20000);
36616         this.collapsedEl.hide({
36617             duration:.1
36618         });
36619         this.el.slideIn(this.getSlideAnchor(), {
36620             callback : function(){
36621                 this.el.setStyle("z-index", "");
36622                 this.afterSlide();
36623                 if(this.split){
36624                     this.split.el.show();
36625                 }
36626                 this.fireEvent("invalidated", this);
36627                 this.fireEvent("expanded", this);
36628             },
36629             scope: this,
36630             block: true
36631         });
36632     },
36633
36634     anchors : {
36635         "west" : "left",
36636         "east" : "right",
36637         "north" : "top",
36638         "south" : "bottom"
36639     },
36640
36641     sanchors : {
36642         "west" : "l",
36643         "east" : "r",
36644         "north" : "t",
36645         "south" : "b"
36646     },
36647
36648     canchors : {
36649         "west" : "tl-tr",
36650         "east" : "tr-tl",
36651         "north" : "tl-bl",
36652         "south" : "bl-tl"
36653     },
36654
36655     getAnchor : function(){
36656         return this.anchors[this.position];
36657     },
36658
36659     getCollapseAnchor : function(){
36660         return this.canchors[this.position];
36661     },
36662
36663     getSlideAnchor : function(){
36664         return this.sanchors[this.position];
36665     },
36666
36667     getAlignAdj : function(){
36668         var cm = this.cmargins;
36669         switch(this.position){
36670             case "west":
36671                 return [0, 0];
36672             break;
36673             case "east":
36674                 return [0, 0];
36675             break;
36676             case "north":
36677                 return [0, 0];
36678             break;
36679             case "south":
36680                 return [0, 0];
36681             break;
36682         }
36683     },
36684
36685     getExpandAdj : function(){
36686         var c = this.collapsedEl, cm = this.cmargins;
36687         switch(this.position){
36688             case "west":
36689                 return [-(cm.right+c.getWidth()+cm.left), 0];
36690             break;
36691             case "east":
36692                 return [cm.right+c.getWidth()+cm.left, 0];
36693             break;
36694             case "north":
36695                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36696             break;
36697             case "south":
36698                 return [0, cm.top+cm.bottom+c.getHeight()];
36699             break;
36700         }
36701     }
36702 });/*
36703  * Based on:
36704  * Ext JS Library 1.1.1
36705  * Copyright(c) 2006-2007, Ext JS, LLC.
36706  *
36707  * Originally Released Under LGPL - original licence link has changed is not relivant.
36708  *
36709  * Fork - LGPL
36710  * <script type="text/javascript">
36711  */
36712 /*
36713  * These classes are private internal classes
36714  */
36715 Roo.bootstrap.layout.Center = function(config){
36716     config.region = "center";
36717     Roo.bootstrap.layout.Region.call(this, config);
36718     this.visible = true;
36719     this.minWidth = config.minWidth || 20;
36720     this.minHeight = config.minHeight || 20;
36721 };
36722
36723 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36724     hide : function(){
36725         // center panel can't be hidden
36726     },
36727     
36728     show : function(){
36729         // center panel can't be hidden
36730     },
36731     
36732     getMinWidth: function(){
36733         return this.minWidth;
36734     },
36735     
36736     getMinHeight: function(){
36737         return this.minHeight;
36738     }
36739 });
36740
36741
36742
36743
36744  
36745
36746
36747
36748
36749
36750 Roo.bootstrap.layout.North = function(config)
36751 {
36752     config.region = 'north';
36753     config.cursor = 'n-resize';
36754     
36755     Roo.bootstrap.layout.Split.call(this, config);
36756     
36757     
36758     if(this.split){
36759         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36760         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36761         this.split.el.addClass("roo-layout-split-v");
36762     }
36763     var size = config.initialSize || config.height;
36764     if(typeof size != "undefined"){
36765         this.el.setHeight(size);
36766     }
36767 };
36768 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36769 {
36770     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36771     
36772     
36773     
36774     getBox : function(){
36775         if(this.collapsed){
36776             return this.collapsedEl.getBox();
36777         }
36778         var box = this.el.getBox();
36779         if(this.split){
36780             box.height += this.split.el.getHeight();
36781         }
36782         return box;
36783     },
36784     
36785     updateBox : function(box){
36786         if(this.split && !this.collapsed){
36787             box.height -= this.split.el.getHeight();
36788             this.split.el.setLeft(box.x);
36789             this.split.el.setTop(box.y+box.height);
36790             this.split.el.setWidth(box.width);
36791         }
36792         if(this.collapsed){
36793             this.updateBody(box.width, null);
36794         }
36795         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36796     }
36797 });
36798
36799
36800
36801
36802
36803 Roo.bootstrap.layout.South = function(config){
36804     config.region = 'south';
36805     config.cursor = 's-resize';
36806     Roo.bootstrap.layout.Split.call(this, config);
36807     if(this.split){
36808         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36809         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36810         this.split.el.addClass("roo-layout-split-v");
36811     }
36812     var size = config.initialSize || config.height;
36813     if(typeof size != "undefined"){
36814         this.el.setHeight(size);
36815     }
36816 };
36817
36818 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36819     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36820     getBox : function(){
36821         if(this.collapsed){
36822             return this.collapsedEl.getBox();
36823         }
36824         var box = this.el.getBox();
36825         if(this.split){
36826             var sh = this.split.el.getHeight();
36827             box.height += sh;
36828             box.y -= sh;
36829         }
36830         return box;
36831     },
36832     
36833     updateBox : function(box){
36834         if(this.split && !this.collapsed){
36835             var sh = this.split.el.getHeight();
36836             box.height -= sh;
36837             box.y += sh;
36838             this.split.el.setLeft(box.x);
36839             this.split.el.setTop(box.y-sh);
36840             this.split.el.setWidth(box.width);
36841         }
36842         if(this.collapsed){
36843             this.updateBody(box.width, null);
36844         }
36845         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36846     }
36847 });
36848
36849 Roo.bootstrap.layout.East = function(config){
36850     config.region = "east";
36851     config.cursor = "e-resize";
36852     Roo.bootstrap.layout.Split.call(this, config);
36853     if(this.split){
36854         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36855         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36856         this.split.el.addClass("roo-layout-split-h");
36857     }
36858     var size = config.initialSize || config.width;
36859     if(typeof size != "undefined"){
36860         this.el.setWidth(size);
36861     }
36862 };
36863 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36864     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36865     getBox : function(){
36866         if(this.collapsed){
36867             return this.collapsedEl.getBox();
36868         }
36869         var box = this.el.getBox();
36870         if(this.split){
36871             var sw = this.split.el.getWidth();
36872             box.width += sw;
36873             box.x -= sw;
36874         }
36875         return box;
36876     },
36877
36878     updateBox : function(box){
36879         if(this.split && !this.collapsed){
36880             var sw = this.split.el.getWidth();
36881             box.width -= sw;
36882             this.split.el.setLeft(box.x);
36883             this.split.el.setTop(box.y);
36884             this.split.el.setHeight(box.height);
36885             box.x += sw;
36886         }
36887         if(this.collapsed){
36888             this.updateBody(null, box.height);
36889         }
36890         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36891     }
36892 });
36893
36894 Roo.bootstrap.layout.West = function(config){
36895     config.region = "west";
36896     config.cursor = "w-resize";
36897     
36898     Roo.bootstrap.layout.Split.call(this, config);
36899     if(this.split){
36900         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36901         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36902         this.split.el.addClass("roo-layout-split-h");
36903     }
36904     
36905 };
36906 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36907     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36908     
36909     onRender: function(ctr, pos)
36910     {
36911         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36912         var size = this.config.initialSize || this.config.width;
36913         if(typeof size != "undefined"){
36914             this.el.setWidth(size);
36915         }
36916     },
36917     
36918     getBox : function(){
36919         if(this.collapsed){
36920             return this.collapsedEl.getBox();
36921         }
36922         var box = this.el.getBox();
36923         if(this.split){
36924             box.width += this.split.el.getWidth();
36925         }
36926         return box;
36927     },
36928     
36929     updateBox : function(box){
36930         if(this.split && !this.collapsed){
36931             var sw = this.split.el.getWidth();
36932             box.width -= sw;
36933             this.split.el.setLeft(box.x+box.width);
36934             this.split.el.setTop(box.y);
36935             this.split.el.setHeight(box.height);
36936         }
36937         if(this.collapsed){
36938             this.updateBody(null, box.height);
36939         }
36940         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36941     }
36942 });
36943 Roo.namespace("Roo.bootstrap.panel");/*
36944  * Based on:
36945  * Ext JS Library 1.1.1
36946  * Copyright(c) 2006-2007, Ext JS, LLC.
36947  *
36948  * Originally Released Under LGPL - original licence link has changed is not relivant.
36949  *
36950  * Fork - LGPL
36951  * <script type="text/javascript">
36952  */
36953 /**
36954  * @class Roo.ContentPanel
36955  * @extends Roo.util.Observable
36956  * A basic ContentPanel element.
36957  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36958  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36959  * @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
36960  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36961  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36962  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36963  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36964  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36965  * @cfg {String} title          The title for this panel
36966  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36967  * @cfg {String} url            Calls {@link #setUrl} with this value
36968  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36969  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36970  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36971  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36972  * @cfg {Boolean} badges render the badges
36973
36974  * @constructor
36975  * Create a new ContentPanel.
36976  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36977  * @param {String/Object} config A string to set only the title or a config object
36978  * @param {String} content (optional) Set the HTML content for this panel
36979  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36980  */
36981 Roo.bootstrap.panel.Content = function( config){
36982     
36983     this.tpl = config.tpl || false;
36984     
36985     var el = config.el;
36986     var content = config.content;
36987
36988     if(config.autoCreate){ // xtype is available if this is called from factory
36989         el = Roo.id();
36990     }
36991     this.el = Roo.get(el);
36992     if(!this.el && config && config.autoCreate){
36993         if(typeof config.autoCreate == "object"){
36994             if(!config.autoCreate.id){
36995                 config.autoCreate.id = config.id||el;
36996             }
36997             this.el = Roo.DomHelper.append(document.body,
36998                         config.autoCreate, true);
36999         }else{
37000             var elcfg =  {   tag: "div",
37001                             cls: "roo-layout-inactive-content",
37002                             id: config.id||el
37003                             };
37004             if (config.html) {
37005                 elcfg.html = config.html;
37006                 
37007             }
37008                         
37009             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37010         }
37011     } 
37012     this.closable = false;
37013     this.loaded = false;
37014     this.active = false;
37015    
37016       
37017     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37018         
37019         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37020         
37021         this.wrapEl = this.el; //this.el.wrap();
37022         var ti = [];
37023         if (config.toolbar.items) {
37024             ti = config.toolbar.items ;
37025             delete config.toolbar.items ;
37026         }
37027         
37028         var nitems = [];
37029         this.toolbar.render(this.wrapEl, 'before');
37030         for(var i =0;i < ti.length;i++) {
37031           //  Roo.log(['add child', items[i]]);
37032             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37033         }
37034         this.toolbar.items = nitems;
37035         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37036         delete config.toolbar;
37037         
37038     }
37039     /*
37040     // xtype created footer. - not sure if will work as we normally have to render first..
37041     if (this.footer && !this.footer.el && this.footer.xtype) {
37042         if (!this.wrapEl) {
37043             this.wrapEl = this.el.wrap();
37044         }
37045     
37046         this.footer.container = this.wrapEl.createChild();
37047          
37048         this.footer = Roo.factory(this.footer, Roo);
37049         
37050     }
37051     */
37052     
37053      if(typeof config == "string"){
37054         this.title = config;
37055     }else{
37056         Roo.apply(this, config);
37057     }
37058     
37059     if(this.resizeEl){
37060         this.resizeEl = Roo.get(this.resizeEl, true);
37061     }else{
37062         this.resizeEl = this.el;
37063     }
37064     // handle view.xtype
37065     
37066  
37067     
37068     
37069     this.addEvents({
37070         /**
37071          * @event activate
37072          * Fires when this panel is activated. 
37073          * @param {Roo.ContentPanel} this
37074          */
37075         "activate" : true,
37076         /**
37077          * @event deactivate
37078          * Fires when this panel is activated. 
37079          * @param {Roo.ContentPanel} this
37080          */
37081         "deactivate" : true,
37082
37083         /**
37084          * @event resize
37085          * Fires when this panel is resized if fitToFrame is true.
37086          * @param {Roo.ContentPanel} this
37087          * @param {Number} width The width after any component adjustments
37088          * @param {Number} height The height after any component adjustments
37089          */
37090         "resize" : true,
37091         
37092          /**
37093          * @event render
37094          * Fires when this tab is created
37095          * @param {Roo.ContentPanel} this
37096          */
37097         "render" : true
37098         
37099         
37100         
37101     });
37102     
37103
37104     
37105     
37106     if(this.autoScroll){
37107         this.resizeEl.setStyle("overflow", "auto");
37108     } else {
37109         // fix randome scrolling
37110         //this.el.on('scroll', function() {
37111         //    Roo.log('fix random scolling');
37112         //    this.scrollTo('top',0); 
37113         //});
37114     }
37115     content = content || this.content;
37116     if(content){
37117         this.setContent(content);
37118     }
37119     if(config && config.url){
37120         this.setUrl(this.url, this.params, this.loadOnce);
37121     }
37122     
37123     
37124     
37125     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37126     
37127     if (this.view && typeof(this.view.xtype) != 'undefined') {
37128         this.view.el = this.el.appendChild(document.createElement("div"));
37129         this.view = Roo.factory(this.view); 
37130         this.view.render  &&  this.view.render(false, '');  
37131     }
37132     
37133     
37134     this.fireEvent('render', this);
37135 };
37136
37137 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37138     
37139     tabTip : '',
37140     
37141     setRegion : function(region){
37142         this.region = region;
37143         this.setActiveClass(region && !this.background);
37144     },
37145     
37146     
37147     setActiveClass: function(state)
37148     {
37149         if(state){
37150            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37151            this.el.setStyle('position','relative');
37152         }else{
37153            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37154            this.el.setStyle('position', 'absolute');
37155         } 
37156     },
37157     
37158     /**
37159      * Returns the toolbar for this Panel if one was configured. 
37160      * @return {Roo.Toolbar} 
37161      */
37162     getToolbar : function(){
37163         return this.toolbar;
37164     },
37165     
37166     setActiveState : function(active)
37167     {
37168         this.active = active;
37169         this.setActiveClass(active);
37170         if(!active){
37171             if(this.fireEvent("deactivate", this) === false){
37172                 return false;
37173             }
37174             return true;
37175         }
37176         this.fireEvent("activate", this);
37177         return true;
37178     },
37179     /**
37180      * Updates this panel's element
37181      * @param {String} content The new content
37182      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37183     */
37184     setContent : function(content, loadScripts){
37185         this.el.update(content, loadScripts);
37186     },
37187
37188     ignoreResize : function(w, h){
37189         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37190             return true;
37191         }else{
37192             this.lastSize = {width: w, height: h};
37193             return false;
37194         }
37195     },
37196     /**
37197      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37198      * @return {Roo.UpdateManager} The UpdateManager
37199      */
37200     getUpdateManager : function(){
37201         return this.el.getUpdateManager();
37202     },
37203      /**
37204      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37205      * @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:
37206 <pre><code>
37207 panel.load({
37208     url: "your-url.php",
37209     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37210     callback: yourFunction,
37211     scope: yourObject, //(optional scope)
37212     discardUrl: false,
37213     nocache: false,
37214     text: "Loading...",
37215     timeout: 30,
37216     scripts: false
37217 });
37218 </code></pre>
37219      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37220      * 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.
37221      * @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}
37222      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37223      * @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.
37224      * @return {Roo.ContentPanel} this
37225      */
37226     load : function(){
37227         var um = this.el.getUpdateManager();
37228         um.update.apply(um, arguments);
37229         return this;
37230     },
37231
37232
37233     /**
37234      * 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.
37235      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37236      * @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)
37237      * @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)
37238      * @return {Roo.UpdateManager} The UpdateManager
37239      */
37240     setUrl : function(url, params, loadOnce){
37241         if(this.refreshDelegate){
37242             this.removeListener("activate", this.refreshDelegate);
37243         }
37244         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37245         this.on("activate", this.refreshDelegate);
37246         return this.el.getUpdateManager();
37247     },
37248     
37249     _handleRefresh : function(url, params, loadOnce){
37250         if(!loadOnce || !this.loaded){
37251             var updater = this.el.getUpdateManager();
37252             updater.update(url, params, this._setLoaded.createDelegate(this));
37253         }
37254     },
37255     
37256     _setLoaded : function(){
37257         this.loaded = true;
37258     }, 
37259     
37260     /**
37261      * Returns this panel's id
37262      * @return {String} 
37263      */
37264     getId : function(){
37265         return this.el.id;
37266     },
37267     
37268     /** 
37269      * Returns this panel's element - used by regiosn to add.
37270      * @return {Roo.Element} 
37271      */
37272     getEl : function(){
37273         return this.wrapEl || this.el;
37274     },
37275     
37276    
37277     
37278     adjustForComponents : function(width, height)
37279     {
37280         //Roo.log('adjustForComponents ');
37281         if(this.resizeEl != this.el){
37282             width -= this.el.getFrameWidth('lr');
37283             height -= this.el.getFrameWidth('tb');
37284         }
37285         if(this.toolbar){
37286             var te = this.toolbar.getEl();
37287             te.setWidth(width);
37288             height -= te.getHeight();
37289         }
37290         if(this.footer){
37291             var te = this.footer.getEl();
37292             te.setWidth(width);
37293             height -= te.getHeight();
37294         }
37295         
37296         
37297         if(this.adjustments){
37298             width += this.adjustments[0];
37299             height += this.adjustments[1];
37300         }
37301         return {"width": width, "height": height};
37302     },
37303     
37304     setSize : function(width, height){
37305         if(this.fitToFrame && !this.ignoreResize(width, height)){
37306             if(this.fitContainer && this.resizeEl != this.el){
37307                 this.el.setSize(width, height);
37308             }
37309             var size = this.adjustForComponents(width, height);
37310             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37311             this.fireEvent('resize', this, size.width, size.height);
37312         }
37313     },
37314     
37315     /**
37316      * Returns this panel's title
37317      * @return {String} 
37318      */
37319     getTitle : function(){
37320         
37321         if (typeof(this.title) != 'object') {
37322             return this.title;
37323         }
37324         
37325         var t = '';
37326         for (var k in this.title) {
37327             if (!this.title.hasOwnProperty(k)) {
37328                 continue;
37329             }
37330             
37331             if (k.indexOf('-') >= 0) {
37332                 var s = k.split('-');
37333                 for (var i = 0; i<s.length; i++) {
37334                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37335                 }
37336             } else {
37337                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37338             }
37339         }
37340         return t;
37341     },
37342     
37343     /**
37344      * Set this panel's title
37345      * @param {String} title
37346      */
37347     setTitle : function(title){
37348         this.title = title;
37349         if(this.region){
37350             this.region.updatePanelTitle(this, title);
37351         }
37352     },
37353     
37354     /**
37355      * Returns true is this panel was configured to be closable
37356      * @return {Boolean} 
37357      */
37358     isClosable : function(){
37359         return this.closable;
37360     },
37361     
37362     beforeSlide : function(){
37363         this.el.clip();
37364         this.resizeEl.clip();
37365     },
37366     
37367     afterSlide : function(){
37368         this.el.unclip();
37369         this.resizeEl.unclip();
37370     },
37371     
37372     /**
37373      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37374      *   Will fail silently if the {@link #setUrl} method has not been called.
37375      *   This does not activate the panel, just updates its content.
37376      */
37377     refresh : function(){
37378         if(this.refreshDelegate){
37379            this.loaded = false;
37380            this.refreshDelegate();
37381         }
37382     },
37383     
37384     /**
37385      * Destroys this panel
37386      */
37387     destroy : function(){
37388         this.el.removeAllListeners();
37389         var tempEl = document.createElement("span");
37390         tempEl.appendChild(this.el.dom);
37391         tempEl.innerHTML = "";
37392         this.el.remove();
37393         this.el = null;
37394     },
37395     
37396     /**
37397      * form - if the content panel contains a form - this is a reference to it.
37398      * @type {Roo.form.Form}
37399      */
37400     form : false,
37401     /**
37402      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37403      *    This contains a reference to it.
37404      * @type {Roo.View}
37405      */
37406     view : false,
37407     
37408       /**
37409      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37410      * <pre><code>
37411
37412 layout.addxtype({
37413        xtype : 'Form',
37414        items: [ .... ]
37415    }
37416 );
37417
37418 </code></pre>
37419      * @param {Object} cfg Xtype definition of item to add.
37420      */
37421     
37422     
37423     getChildContainer: function () {
37424         return this.getEl();
37425     }
37426     
37427     
37428     /*
37429         var  ret = new Roo.factory(cfg);
37430         return ret;
37431         
37432         
37433         // add form..
37434         if (cfg.xtype.match(/^Form$/)) {
37435             
37436             var el;
37437             //if (this.footer) {
37438             //    el = this.footer.container.insertSibling(false, 'before');
37439             //} else {
37440                 el = this.el.createChild();
37441             //}
37442
37443             this.form = new  Roo.form.Form(cfg);
37444             
37445             
37446             if ( this.form.allItems.length) {
37447                 this.form.render(el.dom);
37448             }
37449             return this.form;
37450         }
37451         // should only have one of theses..
37452         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37453             // views.. should not be just added - used named prop 'view''
37454             
37455             cfg.el = this.el.appendChild(document.createElement("div"));
37456             // factory?
37457             
37458             var ret = new Roo.factory(cfg);
37459              
37460              ret.render && ret.render(false, ''); // render blank..
37461             this.view = ret;
37462             return ret;
37463         }
37464         return false;
37465     }
37466     \*/
37467 });
37468  
37469 /**
37470  * @class Roo.bootstrap.panel.Grid
37471  * @extends Roo.bootstrap.panel.Content
37472  * @constructor
37473  * Create a new GridPanel.
37474  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37475  * @param {Object} config A the config object
37476   
37477  */
37478
37479
37480
37481 Roo.bootstrap.panel.Grid = function(config)
37482 {
37483     
37484       
37485     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37486         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37487
37488     config.el = this.wrapper;
37489     //this.el = this.wrapper;
37490     
37491       if (config.container) {
37492         // ctor'ed from a Border/panel.grid
37493         
37494         
37495         this.wrapper.setStyle("overflow", "hidden");
37496         this.wrapper.addClass('roo-grid-container');
37497
37498     }
37499     
37500     
37501     if(config.toolbar){
37502         var tool_el = this.wrapper.createChild();    
37503         this.toolbar = Roo.factory(config.toolbar);
37504         var ti = [];
37505         if (config.toolbar.items) {
37506             ti = config.toolbar.items ;
37507             delete config.toolbar.items ;
37508         }
37509         
37510         var nitems = [];
37511         this.toolbar.render(tool_el);
37512         for(var i =0;i < ti.length;i++) {
37513           //  Roo.log(['add child', items[i]]);
37514             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37515         }
37516         this.toolbar.items = nitems;
37517         
37518         delete config.toolbar;
37519     }
37520     
37521     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37522     config.grid.scrollBody = true;;
37523     config.grid.monitorWindowResize = false; // turn off autosizing
37524     config.grid.autoHeight = false;
37525     config.grid.autoWidth = false;
37526     
37527     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37528     
37529     if (config.background) {
37530         // render grid on panel activation (if panel background)
37531         this.on('activate', function(gp) {
37532             if (!gp.grid.rendered) {
37533                 gp.grid.render(this.wrapper);
37534                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37535             }
37536         });
37537             
37538     } else {
37539         this.grid.render(this.wrapper);
37540         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37541
37542     }
37543     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37544     // ??? needed ??? config.el = this.wrapper;
37545     
37546     
37547     
37548   
37549     // xtype created footer. - not sure if will work as we normally have to render first..
37550     if (this.footer && !this.footer.el && this.footer.xtype) {
37551         
37552         var ctr = this.grid.getView().getFooterPanel(true);
37553         this.footer.dataSource = this.grid.dataSource;
37554         this.footer = Roo.factory(this.footer, Roo);
37555         this.footer.render(ctr);
37556         
37557     }
37558     
37559     
37560     
37561     
37562      
37563 };
37564
37565 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37566     getId : function(){
37567         return this.grid.id;
37568     },
37569     
37570     /**
37571      * Returns the grid for this panel
37572      * @return {Roo.bootstrap.Table} 
37573      */
37574     getGrid : function(){
37575         return this.grid;    
37576     },
37577     
37578     setSize : function(width, height){
37579         if(!this.ignoreResize(width, height)){
37580             var grid = this.grid;
37581             var size = this.adjustForComponents(width, height);
37582             var gridel = grid.getGridEl();
37583             gridel.setSize(size.width, size.height);
37584             /*
37585             var thd = grid.getGridEl().select('thead',true).first();
37586             var tbd = grid.getGridEl().select('tbody', true).first();
37587             if (tbd) {
37588                 tbd.setSize(width, height - thd.getHeight());
37589             }
37590             */
37591             grid.autoSize();
37592         }
37593     },
37594      
37595     
37596     
37597     beforeSlide : function(){
37598         this.grid.getView().scroller.clip();
37599     },
37600     
37601     afterSlide : function(){
37602         this.grid.getView().scroller.unclip();
37603     },
37604     
37605     destroy : function(){
37606         this.grid.destroy();
37607         delete this.grid;
37608         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37609     }
37610 });
37611
37612 /**
37613  * @class Roo.bootstrap.panel.Nest
37614  * @extends Roo.bootstrap.panel.Content
37615  * @constructor
37616  * Create a new Panel, that can contain a layout.Border.
37617  * 
37618  * 
37619  * @param {Roo.BorderLayout} layout The layout for this panel
37620  * @param {String/Object} config A string to set only the title or a config object
37621  */
37622 Roo.bootstrap.panel.Nest = function(config)
37623 {
37624     // construct with only one argument..
37625     /* FIXME - implement nicer consturctors
37626     if (layout.layout) {
37627         config = layout;
37628         layout = config.layout;
37629         delete config.layout;
37630     }
37631     if (layout.xtype && !layout.getEl) {
37632         // then layout needs constructing..
37633         layout = Roo.factory(layout, Roo);
37634     }
37635     */
37636     
37637     config.el =  config.layout.getEl();
37638     
37639     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37640     
37641     config.layout.monitorWindowResize = false; // turn off autosizing
37642     this.layout = config.layout;
37643     this.layout.getEl().addClass("roo-layout-nested-layout");
37644     
37645     
37646     
37647     
37648 };
37649
37650 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37651
37652     setSize : function(width, height){
37653         if(!this.ignoreResize(width, height)){
37654             var size = this.adjustForComponents(width, height);
37655             var el = this.layout.getEl();
37656             if (size.height < 1) {
37657                 el.setWidth(size.width);   
37658             } else {
37659                 el.setSize(size.width, size.height);
37660             }
37661             var touch = el.dom.offsetWidth;
37662             this.layout.layout();
37663             // ie requires a double layout on the first pass
37664             if(Roo.isIE && !this.initialized){
37665                 this.initialized = true;
37666                 this.layout.layout();
37667             }
37668         }
37669     },
37670     
37671     // activate all subpanels if not currently active..
37672     
37673     setActiveState : function(active){
37674         this.active = active;
37675         this.setActiveClass(active);
37676         
37677         if(!active){
37678             this.fireEvent("deactivate", this);
37679             return;
37680         }
37681         
37682         this.fireEvent("activate", this);
37683         // not sure if this should happen before or after..
37684         if (!this.layout) {
37685             return; // should not happen..
37686         }
37687         var reg = false;
37688         for (var r in this.layout.regions) {
37689             reg = this.layout.getRegion(r);
37690             if (reg.getActivePanel()) {
37691                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37692                 reg.setActivePanel(reg.getActivePanel());
37693                 continue;
37694             }
37695             if (!reg.panels.length) {
37696                 continue;
37697             }
37698             reg.showPanel(reg.getPanel(0));
37699         }
37700         
37701         
37702         
37703         
37704     },
37705     
37706     /**
37707      * Returns the nested BorderLayout for this panel
37708      * @return {Roo.BorderLayout} 
37709      */
37710     getLayout : function(){
37711         return this.layout;
37712     },
37713     
37714      /**
37715      * Adds a xtype elements to the layout of the nested panel
37716      * <pre><code>
37717
37718 panel.addxtype({
37719        xtype : 'ContentPanel',
37720        region: 'west',
37721        items: [ .... ]
37722    }
37723 );
37724
37725 panel.addxtype({
37726         xtype : 'NestedLayoutPanel',
37727         region: 'west',
37728         layout: {
37729            center: { },
37730            west: { }   
37731         },
37732         items : [ ... list of content panels or nested layout panels.. ]
37733    }
37734 );
37735 </code></pre>
37736      * @param {Object} cfg Xtype definition of item to add.
37737      */
37738     addxtype : function(cfg) {
37739         return this.layout.addxtype(cfg);
37740     
37741     }
37742 });        /*
37743  * Based on:
37744  * Ext JS Library 1.1.1
37745  * Copyright(c) 2006-2007, Ext JS, LLC.
37746  *
37747  * Originally Released Under LGPL - original licence link has changed is not relivant.
37748  *
37749  * Fork - LGPL
37750  * <script type="text/javascript">
37751  */
37752 /**
37753  * @class Roo.TabPanel
37754  * @extends Roo.util.Observable
37755  * A lightweight tab container.
37756  * <br><br>
37757  * Usage:
37758  * <pre><code>
37759 // basic tabs 1, built from existing content
37760 var tabs = new Roo.TabPanel("tabs1");
37761 tabs.addTab("script", "View Script");
37762 tabs.addTab("markup", "View Markup");
37763 tabs.activate("script");
37764
37765 // more advanced tabs, built from javascript
37766 var jtabs = new Roo.TabPanel("jtabs");
37767 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37768
37769 // set up the UpdateManager
37770 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37771 var updater = tab2.getUpdateManager();
37772 updater.setDefaultUrl("ajax1.htm");
37773 tab2.on('activate', updater.refresh, updater, true);
37774
37775 // Use setUrl for Ajax loading
37776 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37777 tab3.setUrl("ajax2.htm", null, true);
37778
37779 // Disabled tab
37780 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37781 tab4.disable();
37782
37783 jtabs.activate("jtabs-1");
37784  * </code></pre>
37785  * @constructor
37786  * Create a new TabPanel.
37787  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37788  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37789  */
37790 Roo.bootstrap.panel.Tabs = function(config){
37791     /**
37792     * The container element for this TabPanel.
37793     * @type Roo.Element
37794     */
37795     this.el = Roo.get(config.el);
37796     delete config.el;
37797     if(config){
37798         if(typeof config == "boolean"){
37799             this.tabPosition = config ? "bottom" : "top";
37800         }else{
37801             Roo.apply(this, config);
37802         }
37803     }
37804     
37805     if(this.tabPosition == "bottom"){
37806         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37807         this.el.addClass("roo-tabs-bottom");
37808     }
37809     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37810     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37811     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37812     if(Roo.isIE){
37813         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37814     }
37815     if(this.tabPosition != "bottom"){
37816         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37817          * @type Roo.Element
37818          */
37819         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37820         this.el.addClass("roo-tabs-top");
37821     }
37822     this.items = [];
37823
37824     this.bodyEl.setStyle("position", "relative");
37825
37826     this.active = null;
37827     this.activateDelegate = this.activate.createDelegate(this);
37828
37829     this.addEvents({
37830         /**
37831          * @event tabchange
37832          * Fires when the active tab changes
37833          * @param {Roo.TabPanel} this
37834          * @param {Roo.TabPanelItem} activePanel The new active tab
37835          */
37836         "tabchange": true,
37837         /**
37838          * @event beforetabchange
37839          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37840          * @param {Roo.TabPanel} this
37841          * @param {Object} e Set cancel to true on this object to cancel the tab change
37842          * @param {Roo.TabPanelItem} tab The tab being changed to
37843          */
37844         "beforetabchange" : true
37845     });
37846
37847     Roo.EventManager.onWindowResize(this.onResize, this);
37848     this.cpad = this.el.getPadding("lr");
37849     this.hiddenCount = 0;
37850
37851
37852     // toolbar on the tabbar support...
37853     if (this.toolbar) {
37854         alert("no toolbar support yet");
37855         this.toolbar  = false;
37856         /*
37857         var tcfg = this.toolbar;
37858         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37859         this.toolbar = new Roo.Toolbar(tcfg);
37860         if (Roo.isSafari) {
37861             var tbl = tcfg.container.child('table', true);
37862             tbl.setAttribute('width', '100%');
37863         }
37864         */
37865         
37866     }
37867    
37868
37869
37870     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37871 };
37872
37873 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37874     /*
37875      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37876      */
37877     tabPosition : "top",
37878     /*
37879      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37880      */
37881     currentTabWidth : 0,
37882     /*
37883      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37884      */
37885     minTabWidth : 40,
37886     /*
37887      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37888      */
37889     maxTabWidth : 250,
37890     /*
37891      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37892      */
37893     preferredTabWidth : 175,
37894     /*
37895      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37896      */
37897     resizeTabs : false,
37898     /*
37899      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37900      */
37901     monitorResize : true,
37902     /*
37903      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37904      */
37905     toolbar : false,
37906
37907     /**
37908      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37909      * @param {String} id The id of the div to use <b>or create</b>
37910      * @param {String} text The text for the tab
37911      * @param {String} content (optional) Content to put in the TabPanelItem body
37912      * @param {Boolean} closable (optional) True to create a close icon on the tab
37913      * @return {Roo.TabPanelItem} The created TabPanelItem
37914      */
37915     addTab : function(id, text, content, closable, tpl)
37916     {
37917         var item = new Roo.bootstrap.panel.TabItem({
37918             panel: this,
37919             id : id,
37920             text : text,
37921             closable : closable,
37922             tpl : tpl
37923         });
37924         this.addTabItem(item);
37925         if(content){
37926             item.setContent(content);
37927         }
37928         return item;
37929     },
37930
37931     /**
37932      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37933      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37934      * @return {Roo.TabPanelItem}
37935      */
37936     getTab : function(id){
37937         return this.items[id];
37938     },
37939
37940     /**
37941      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37942      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37943      */
37944     hideTab : function(id){
37945         var t = this.items[id];
37946         if(!t.isHidden()){
37947            t.setHidden(true);
37948            this.hiddenCount++;
37949            this.autoSizeTabs();
37950         }
37951     },
37952
37953     /**
37954      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37955      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37956      */
37957     unhideTab : function(id){
37958         var t = this.items[id];
37959         if(t.isHidden()){
37960            t.setHidden(false);
37961            this.hiddenCount--;
37962            this.autoSizeTabs();
37963         }
37964     },
37965
37966     /**
37967      * Adds an existing {@link Roo.TabPanelItem}.
37968      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37969      */
37970     addTabItem : function(item){
37971         this.items[item.id] = item;
37972         this.items.push(item);
37973       //  if(this.resizeTabs){
37974     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37975   //         this.autoSizeTabs();
37976 //        }else{
37977 //            item.autoSize();
37978        // }
37979     },
37980
37981     /**
37982      * Removes a {@link Roo.TabPanelItem}.
37983      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37984      */
37985     removeTab : function(id){
37986         var items = this.items;
37987         var tab = items[id];
37988         if(!tab) { return; }
37989         var index = items.indexOf(tab);
37990         if(this.active == tab && items.length > 1){
37991             var newTab = this.getNextAvailable(index);
37992             if(newTab) {
37993                 newTab.activate();
37994             }
37995         }
37996         this.stripEl.dom.removeChild(tab.pnode.dom);
37997         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37998             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37999         }
38000         items.splice(index, 1);
38001         delete this.items[tab.id];
38002         tab.fireEvent("close", tab);
38003         tab.purgeListeners();
38004         this.autoSizeTabs();
38005     },
38006
38007     getNextAvailable : function(start){
38008         var items = this.items;
38009         var index = start;
38010         // look for a next tab that will slide over to
38011         // replace the one being removed
38012         while(index < items.length){
38013             var item = items[++index];
38014             if(item && !item.isHidden()){
38015                 return item;
38016             }
38017         }
38018         // if one isn't found select the previous tab (on the left)
38019         index = start;
38020         while(index >= 0){
38021             var item = items[--index];
38022             if(item && !item.isHidden()){
38023                 return item;
38024             }
38025         }
38026         return null;
38027     },
38028
38029     /**
38030      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38031      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38032      */
38033     disableTab : function(id){
38034         var tab = this.items[id];
38035         if(tab && this.active != tab){
38036             tab.disable();
38037         }
38038     },
38039
38040     /**
38041      * Enables a {@link Roo.TabPanelItem} that is disabled.
38042      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38043      */
38044     enableTab : function(id){
38045         var tab = this.items[id];
38046         tab.enable();
38047     },
38048
38049     /**
38050      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38051      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38052      * @return {Roo.TabPanelItem} The TabPanelItem.
38053      */
38054     activate : function(id){
38055         var tab = this.items[id];
38056         if(!tab){
38057             return null;
38058         }
38059         if(tab == this.active || tab.disabled){
38060             return tab;
38061         }
38062         var e = {};
38063         this.fireEvent("beforetabchange", this, e, tab);
38064         if(e.cancel !== true && !tab.disabled){
38065             if(this.active){
38066                 this.active.hide();
38067             }
38068             this.active = this.items[id];
38069             this.active.show();
38070             this.fireEvent("tabchange", this, this.active);
38071         }
38072         return tab;
38073     },
38074
38075     /**
38076      * Gets the active {@link Roo.TabPanelItem}.
38077      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38078      */
38079     getActiveTab : function(){
38080         return this.active;
38081     },
38082
38083     /**
38084      * Updates the tab body element to fit the height of the container element
38085      * for overflow scrolling
38086      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38087      */
38088     syncHeight : function(targetHeight){
38089         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38090         var bm = this.bodyEl.getMargins();
38091         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38092         this.bodyEl.setHeight(newHeight);
38093         return newHeight;
38094     },
38095
38096     onResize : function(){
38097         if(this.monitorResize){
38098             this.autoSizeTabs();
38099         }
38100     },
38101
38102     /**
38103      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38104      */
38105     beginUpdate : function(){
38106         this.updating = true;
38107     },
38108
38109     /**
38110      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38111      */
38112     endUpdate : function(){
38113         this.updating = false;
38114         this.autoSizeTabs();
38115     },
38116
38117     /**
38118      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38119      */
38120     autoSizeTabs : function(){
38121         var count = this.items.length;
38122         var vcount = count - this.hiddenCount;
38123         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38124             return;
38125         }
38126         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38127         var availWidth = Math.floor(w / vcount);
38128         var b = this.stripBody;
38129         if(b.getWidth() > w){
38130             var tabs = this.items;
38131             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38132             if(availWidth < this.minTabWidth){
38133                 /*if(!this.sleft){    // incomplete scrolling code
38134                     this.createScrollButtons();
38135                 }
38136                 this.showScroll();
38137                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38138             }
38139         }else{
38140             if(this.currentTabWidth < this.preferredTabWidth){
38141                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38142             }
38143         }
38144     },
38145
38146     /**
38147      * Returns the number of tabs in this TabPanel.
38148      * @return {Number}
38149      */
38150      getCount : function(){
38151          return this.items.length;
38152      },
38153
38154     /**
38155      * Resizes all the tabs to the passed width
38156      * @param {Number} The new width
38157      */
38158     setTabWidth : function(width){
38159         this.currentTabWidth = width;
38160         for(var i = 0, len = this.items.length; i < len; i++) {
38161                 if(!this.items[i].isHidden()) {
38162                 this.items[i].setWidth(width);
38163             }
38164         }
38165     },
38166
38167     /**
38168      * Destroys this TabPanel
38169      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38170      */
38171     destroy : function(removeEl){
38172         Roo.EventManager.removeResizeListener(this.onResize, this);
38173         for(var i = 0, len = this.items.length; i < len; i++){
38174             this.items[i].purgeListeners();
38175         }
38176         if(removeEl === true){
38177             this.el.update("");
38178             this.el.remove();
38179         }
38180     },
38181     
38182     createStrip : function(container)
38183     {
38184         var strip = document.createElement("nav");
38185         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38186         container.appendChild(strip);
38187         return strip;
38188     },
38189     
38190     createStripList : function(strip)
38191     {
38192         // div wrapper for retard IE
38193         // returns the "tr" element.
38194         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38195         //'<div class="x-tabs-strip-wrap">'+
38196           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38197           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38198         return strip.firstChild; //.firstChild.firstChild.firstChild;
38199     },
38200     createBody : function(container)
38201     {
38202         var body = document.createElement("div");
38203         Roo.id(body, "tab-body");
38204         //Roo.fly(body).addClass("x-tabs-body");
38205         Roo.fly(body).addClass("tab-content");
38206         container.appendChild(body);
38207         return body;
38208     },
38209     createItemBody :function(bodyEl, id){
38210         var body = Roo.getDom(id);
38211         if(!body){
38212             body = document.createElement("div");
38213             body.id = id;
38214         }
38215         //Roo.fly(body).addClass("x-tabs-item-body");
38216         Roo.fly(body).addClass("tab-pane");
38217          bodyEl.insertBefore(body, bodyEl.firstChild);
38218         return body;
38219     },
38220     /** @private */
38221     createStripElements :  function(stripEl, text, closable, tpl)
38222     {
38223         var td = document.createElement("li"); // was td..
38224         
38225         
38226         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38227         
38228         
38229         stripEl.appendChild(td);
38230         /*if(closable){
38231             td.className = "x-tabs-closable";
38232             if(!this.closeTpl){
38233                 this.closeTpl = new Roo.Template(
38234                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38235                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38236                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38237                 );
38238             }
38239             var el = this.closeTpl.overwrite(td, {"text": text});
38240             var close = el.getElementsByTagName("div")[0];
38241             var inner = el.getElementsByTagName("em")[0];
38242             return {"el": el, "close": close, "inner": inner};
38243         } else {
38244         */
38245         // not sure what this is..
38246 //            if(!this.tabTpl){
38247                 //this.tabTpl = new Roo.Template(
38248                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38249                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38250                 //);
38251 //                this.tabTpl = new Roo.Template(
38252 //                   '<a href="#">' +
38253 //                   '<span unselectable="on"' +
38254 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38255 //                            ' >{text}</span></a>'
38256 //                );
38257 //                
38258 //            }
38259
38260
38261             var template = tpl || this.tabTpl || false;
38262             
38263             if(!template){
38264                 
38265                 template = new Roo.Template(
38266                    '<a href="#">' +
38267                    '<span unselectable="on"' +
38268                             (this.disableTooltips ? '' : ' title="{text}"') +
38269                             ' >{text}</span></a>'
38270                 );
38271             }
38272             
38273             switch (typeof(template)) {
38274                 case 'object' :
38275                     break;
38276                 case 'string' :
38277                     template = new Roo.Template(template);
38278                     break;
38279                 default :
38280                     break;
38281             }
38282             
38283             var el = template.overwrite(td, {"text": text});
38284             
38285             var inner = el.getElementsByTagName("span")[0];
38286             
38287             return {"el": el, "inner": inner};
38288             
38289     }
38290         
38291     
38292 });
38293
38294 /**
38295  * @class Roo.TabPanelItem
38296  * @extends Roo.util.Observable
38297  * Represents an individual item (tab plus body) in a TabPanel.
38298  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38299  * @param {String} id The id of this TabPanelItem
38300  * @param {String} text The text for the tab of this TabPanelItem
38301  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38302  */
38303 Roo.bootstrap.panel.TabItem = function(config){
38304     /**
38305      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38306      * @type Roo.TabPanel
38307      */
38308     this.tabPanel = config.panel;
38309     /**
38310      * The id for this TabPanelItem
38311      * @type String
38312      */
38313     this.id = config.id;
38314     /** @private */
38315     this.disabled = false;
38316     /** @private */
38317     this.text = config.text;
38318     /** @private */
38319     this.loaded = false;
38320     this.closable = config.closable;
38321
38322     /**
38323      * The body element for this TabPanelItem.
38324      * @type Roo.Element
38325      */
38326     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38327     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38328     this.bodyEl.setStyle("display", "block");
38329     this.bodyEl.setStyle("zoom", "1");
38330     //this.hideAction();
38331
38332     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38333     /** @private */
38334     this.el = Roo.get(els.el);
38335     this.inner = Roo.get(els.inner, true);
38336     this.textEl = Roo.get(this.el.dom.firstChild, true);
38337     this.pnode = Roo.get(els.el.parentNode, true);
38338 //    this.el.on("mousedown", this.onTabMouseDown, this);
38339     this.el.on("click", this.onTabClick, this);
38340     /** @private */
38341     if(config.closable){
38342         var c = Roo.get(els.close, true);
38343         c.dom.title = this.closeText;
38344         c.addClassOnOver("close-over");
38345         c.on("click", this.closeClick, this);
38346      }
38347
38348     this.addEvents({
38349          /**
38350          * @event activate
38351          * Fires when this tab becomes the active tab.
38352          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38353          * @param {Roo.TabPanelItem} this
38354          */
38355         "activate": true,
38356         /**
38357          * @event beforeclose
38358          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38359          * @param {Roo.TabPanelItem} this
38360          * @param {Object} e Set cancel to true on this object to cancel the close.
38361          */
38362         "beforeclose": true,
38363         /**
38364          * @event close
38365          * Fires when this tab is closed.
38366          * @param {Roo.TabPanelItem} this
38367          */
38368          "close": true,
38369         /**
38370          * @event deactivate
38371          * Fires when this tab is no longer the active tab.
38372          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38373          * @param {Roo.TabPanelItem} this
38374          */
38375          "deactivate" : true
38376     });
38377     this.hidden = false;
38378
38379     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38380 };
38381
38382 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38383            {
38384     purgeListeners : function(){
38385        Roo.util.Observable.prototype.purgeListeners.call(this);
38386        this.el.removeAllListeners();
38387     },
38388     /**
38389      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38390      */
38391     show : function(){
38392         this.pnode.addClass("active");
38393         this.showAction();
38394         if(Roo.isOpera){
38395             this.tabPanel.stripWrap.repaint();
38396         }
38397         this.fireEvent("activate", this.tabPanel, this);
38398     },
38399
38400     /**
38401      * Returns true if this tab is the active tab.
38402      * @return {Boolean}
38403      */
38404     isActive : function(){
38405         return this.tabPanel.getActiveTab() == this;
38406     },
38407
38408     /**
38409      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38410      */
38411     hide : function(){
38412         this.pnode.removeClass("active");
38413         this.hideAction();
38414         this.fireEvent("deactivate", this.tabPanel, this);
38415     },
38416
38417     hideAction : function(){
38418         this.bodyEl.hide();
38419         this.bodyEl.setStyle("position", "absolute");
38420         this.bodyEl.setLeft("-20000px");
38421         this.bodyEl.setTop("-20000px");
38422     },
38423
38424     showAction : function(){
38425         this.bodyEl.setStyle("position", "relative");
38426         this.bodyEl.setTop("");
38427         this.bodyEl.setLeft("");
38428         this.bodyEl.show();
38429     },
38430
38431     /**
38432      * Set the tooltip for the tab.
38433      * @param {String} tooltip The tab's tooltip
38434      */
38435     setTooltip : function(text){
38436         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38437             this.textEl.dom.qtip = text;
38438             this.textEl.dom.removeAttribute('title');
38439         }else{
38440             this.textEl.dom.title = text;
38441         }
38442     },
38443
38444     onTabClick : function(e){
38445         e.preventDefault();
38446         this.tabPanel.activate(this.id);
38447     },
38448
38449     onTabMouseDown : function(e){
38450         e.preventDefault();
38451         this.tabPanel.activate(this.id);
38452     },
38453 /*
38454     getWidth : function(){
38455         return this.inner.getWidth();
38456     },
38457
38458     setWidth : function(width){
38459         var iwidth = width - this.pnode.getPadding("lr");
38460         this.inner.setWidth(iwidth);
38461         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38462         this.pnode.setWidth(width);
38463     },
38464 */
38465     /**
38466      * Show or hide the tab
38467      * @param {Boolean} hidden True to hide or false to show.
38468      */
38469     setHidden : function(hidden){
38470         this.hidden = hidden;
38471         this.pnode.setStyle("display", hidden ? "none" : "");
38472     },
38473
38474     /**
38475      * Returns true if this tab is "hidden"
38476      * @return {Boolean}
38477      */
38478     isHidden : function(){
38479         return this.hidden;
38480     },
38481
38482     /**
38483      * Returns the text for this tab
38484      * @return {String}
38485      */
38486     getText : function(){
38487         return this.text;
38488     },
38489     /*
38490     autoSize : function(){
38491         //this.el.beginMeasure();
38492         this.textEl.setWidth(1);
38493         /*
38494          *  #2804 [new] Tabs in Roojs
38495          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38496          */
38497         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38498         //this.el.endMeasure();
38499     //},
38500
38501     /**
38502      * Sets the text for the tab (Note: this also sets the tooltip text)
38503      * @param {String} text The tab's text and tooltip
38504      */
38505     setText : function(text){
38506         this.text = text;
38507         this.textEl.update(text);
38508         this.setTooltip(text);
38509         //if(!this.tabPanel.resizeTabs){
38510         //    this.autoSize();
38511         //}
38512     },
38513     /**
38514      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38515      */
38516     activate : function(){
38517         this.tabPanel.activate(this.id);
38518     },
38519
38520     /**
38521      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38522      */
38523     disable : function(){
38524         if(this.tabPanel.active != this){
38525             this.disabled = true;
38526             this.pnode.addClass("disabled");
38527         }
38528     },
38529
38530     /**
38531      * Enables this TabPanelItem if it was previously disabled.
38532      */
38533     enable : function(){
38534         this.disabled = false;
38535         this.pnode.removeClass("disabled");
38536     },
38537
38538     /**
38539      * Sets the content for this TabPanelItem.
38540      * @param {String} content The content
38541      * @param {Boolean} loadScripts true to look for and load scripts
38542      */
38543     setContent : function(content, loadScripts){
38544         this.bodyEl.update(content, loadScripts);
38545     },
38546
38547     /**
38548      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38549      * @return {Roo.UpdateManager} The UpdateManager
38550      */
38551     getUpdateManager : function(){
38552         return this.bodyEl.getUpdateManager();
38553     },
38554
38555     /**
38556      * Set a URL to be used to load the content for this TabPanelItem.
38557      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38558      * @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)
38559      * @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)
38560      * @return {Roo.UpdateManager} The UpdateManager
38561      */
38562     setUrl : function(url, params, loadOnce){
38563         if(this.refreshDelegate){
38564             this.un('activate', this.refreshDelegate);
38565         }
38566         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38567         this.on("activate", this.refreshDelegate);
38568         return this.bodyEl.getUpdateManager();
38569     },
38570
38571     /** @private */
38572     _handleRefresh : function(url, params, loadOnce){
38573         if(!loadOnce || !this.loaded){
38574             var updater = this.bodyEl.getUpdateManager();
38575             updater.update(url, params, this._setLoaded.createDelegate(this));
38576         }
38577     },
38578
38579     /**
38580      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38581      *   Will fail silently if the setUrl method has not been called.
38582      *   This does not activate the panel, just updates its content.
38583      */
38584     refresh : function(){
38585         if(this.refreshDelegate){
38586            this.loaded = false;
38587            this.refreshDelegate();
38588         }
38589     },
38590
38591     /** @private */
38592     _setLoaded : function(){
38593         this.loaded = true;
38594     },
38595
38596     /** @private */
38597     closeClick : function(e){
38598         var o = {};
38599         e.stopEvent();
38600         this.fireEvent("beforeclose", this, o);
38601         if(o.cancel !== true){
38602             this.tabPanel.removeTab(this.id);
38603         }
38604     },
38605     /**
38606      * The text displayed in the tooltip for the close icon.
38607      * @type String
38608      */
38609     closeText : "Close this tab"
38610 });
38611 /**
38612 *    This script refer to:
38613 *    Title: International Telephone Input
38614 *    Author: Jack O'Connor
38615 *    Code version:  v12.1.12
38616 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38617 **/
38618
38619 Roo.bootstrap.PhoneInputData = function() {
38620     var d = [
38621       [
38622         "Afghanistan (‫افغانستان‬‎)",
38623         "af",
38624         "93"
38625       ],
38626       [
38627         "Albania (Shqipëri)",
38628         "al",
38629         "355"
38630       ],
38631       [
38632         "Algeria (‫الجزائر‬‎)",
38633         "dz",
38634         "213"
38635       ],
38636       [
38637         "American Samoa",
38638         "as",
38639         "1684"
38640       ],
38641       [
38642         "Andorra",
38643         "ad",
38644         "376"
38645       ],
38646       [
38647         "Angola",
38648         "ao",
38649         "244"
38650       ],
38651       [
38652         "Anguilla",
38653         "ai",
38654         "1264"
38655       ],
38656       [
38657         "Antigua and Barbuda",
38658         "ag",
38659         "1268"
38660       ],
38661       [
38662         "Argentina",
38663         "ar",
38664         "54"
38665       ],
38666       [
38667         "Armenia (Հայաստան)",
38668         "am",
38669         "374"
38670       ],
38671       [
38672         "Aruba",
38673         "aw",
38674         "297"
38675       ],
38676       [
38677         "Australia",
38678         "au",
38679         "61",
38680         0
38681       ],
38682       [
38683         "Austria (Österreich)",
38684         "at",
38685         "43"
38686       ],
38687       [
38688         "Azerbaijan (Azərbaycan)",
38689         "az",
38690         "994"
38691       ],
38692       [
38693         "Bahamas",
38694         "bs",
38695         "1242"
38696       ],
38697       [
38698         "Bahrain (‫البحرين‬‎)",
38699         "bh",
38700         "973"
38701       ],
38702       [
38703         "Bangladesh (বাংলাদেশ)",
38704         "bd",
38705         "880"
38706       ],
38707       [
38708         "Barbados",
38709         "bb",
38710         "1246"
38711       ],
38712       [
38713         "Belarus (Беларусь)",
38714         "by",
38715         "375"
38716       ],
38717       [
38718         "Belgium (België)",
38719         "be",
38720         "32"
38721       ],
38722       [
38723         "Belize",
38724         "bz",
38725         "501"
38726       ],
38727       [
38728         "Benin (Bénin)",
38729         "bj",
38730         "229"
38731       ],
38732       [
38733         "Bermuda",
38734         "bm",
38735         "1441"
38736       ],
38737       [
38738         "Bhutan (འབྲུག)",
38739         "bt",
38740         "975"
38741       ],
38742       [
38743         "Bolivia",
38744         "bo",
38745         "591"
38746       ],
38747       [
38748         "Bosnia and Herzegovina (Босна и Херцеговина)",
38749         "ba",
38750         "387"
38751       ],
38752       [
38753         "Botswana",
38754         "bw",
38755         "267"
38756       ],
38757       [
38758         "Brazil (Brasil)",
38759         "br",
38760         "55"
38761       ],
38762       [
38763         "British Indian Ocean Territory",
38764         "io",
38765         "246"
38766       ],
38767       [
38768         "British Virgin Islands",
38769         "vg",
38770         "1284"
38771       ],
38772       [
38773         "Brunei",
38774         "bn",
38775         "673"
38776       ],
38777       [
38778         "Bulgaria (България)",
38779         "bg",
38780         "359"
38781       ],
38782       [
38783         "Burkina Faso",
38784         "bf",
38785         "226"
38786       ],
38787       [
38788         "Burundi (Uburundi)",
38789         "bi",
38790         "257"
38791       ],
38792       [
38793         "Cambodia (កម្ពុជា)",
38794         "kh",
38795         "855"
38796       ],
38797       [
38798         "Cameroon (Cameroun)",
38799         "cm",
38800         "237"
38801       ],
38802       [
38803         "Canada",
38804         "ca",
38805         "1",
38806         1,
38807         ["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"]
38808       ],
38809       [
38810         "Cape Verde (Kabu Verdi)",
38811         "cv",
38812         "238"
38813       ],
38814       [
38815         "Caribbean Netherlands",
38816         "bq",
38817         "599",
38818         1
38819       ],
38820       [
38821         "Cayman Islands",
38822         "ky",
38823         "1345"
38824       ],
38825       [
38826         "Central African Republic (République centrafricaine)",
38827         "cf",
38828         "236"
38829       ],
38830       [
38831         "Chad (Tchad)",
38832         "td",
38833         "235"
38834       ],
38835       [
38836         "Chile",
38837         "cl",
38838         "56"
38839       ],
38840       [
38841         "China (中国)",
38842         "cn",
38843         "86"
38844       ],
38845       [
38846         "Christmas Island",
38847         "cx",
38848         "61",
38849         2
38850       ],
38851       [
38852         "Cocos (Keeling) Islands",
38853         "cc",
38854         "61",
38855         1
38856       ],
38857       [
38858         "Colombia",
38859         "co",
38860         "57"
38861       ],
38862       [
38863         "Comoros (‫جزر القمر‬‎)",
38864         "km",
38865         "269"
38866       ],
38867       [
38868         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38869         "cd",
38870         "243"
38871       ],
38872       [
38873         "Congo (Republic) (Congo-Brazzaville)",
38874         "cg",
38875         "242"
38876       ],
38877       [
38878         "Cook Islands",
38879         "ck",
38880         "682"
38881       ],
38882       [
38883         "Costa Rica",
38884         "cr",
38885         "506"
38886       ],
38887       [
38888         "Côte d’Ivoire",
38889         "ci",
38890         "225"
38891       ],
38892       [
38893         "Croatia (Hrvatska)",
38894         "hr",
38895         "385"
38896       ],
38897       [
38898         "Cuba",
38899         "cu",
38900         "53"
38901       ],
38902       [
38903         "Curaçao",
38904         "cw",
38905         "599",
38906         0
38907       ],
38908       [
38909         "Cyprus (Κύπρος)",
38910         "cy",
38911         "357"
38912       ],
38913       [
38914         "Czech Republic (Česká republika)",
38915         "cz",
38916         "420"
38917       ],
38918       [
38919         "Denmark (Danmark)",
38920         "dk",
38921         "45"
38922       ],
38923       [
38924         "Djibouti",
38925         "dj",
38926         "253"
38927       ],
38928       [
38929         "Dominica",
38930         "dm",
38931         "1767"
38932       ],
38933       [
38934         "Dominican Republic (República Dominicana)",
38935         "do",
38936         "1",
38937         2,
38938         ["809", "829", "849"]
38939       ],
38940       [
38941         "Ecuador",
38942         "ec",
38943         "593"
38944       ],
38945       [
38946         "Egypt (‫مصر‬‎)",
38947         "eg",
38948         "20"
38949       ],
38950       [
38951         "El Salvador",
38952         "sv",
38953         "503"
38954       ],
38955       [
38956         "Equatorial Guinea (Guinea Ecuatorial)",
38957         "gq",
38958         "240"
38959       ],
38960       [
38961         "Eritrea",
38962         "er",
38963         "291"
38964       ],
38965       [
38966         "Estonia (Eesti)",
38967         "ee",
38968         "372"
38969       ],
38970       [
38971         "Ethiopia",
38972         "et",
38973         "251"
38974       ],
38975       [
38976         "Falkland Islands (Islas Malvinas)",
38977         "fk",
38978         "500"
38979       ],
38980       [
38981         "Faroe Islands (Føroyar)",
38982         "fo",
38983         "298"
38984       ],
38985       [
38986         "Fiji",
38987         "fj",
38988         "679"
38989       ],
38990       [
38991         "Finland (Suomi)",
38992         "fi",
38993         "358",
38994         0
38995       ],
38996       [
38997         "France",
38998         "fr",
38999         "33"
39000       ],
39001       [
39002         "French Guiana (Guyane française)",
39003         "gf",
39004         "594"
39005       ],
39006       [
39007         "French Polynesia (Polynésie française)",
39008         "pf",
39009         "689"
39010       ],
39011       [
39012         "Gabon",
39013         "ga",
39014         "241"
39015       ],
39016       [
39017         "Gambia",
39018         "gm",
39019         "220"
39020       ],
39021       [
39022         "Georgia (საქართველო)",
39023         "ge",
39024         "995"
39025       ],
39026       [
39027         "Germany (Deutschland)",
39028         "de",
39029         "49"
39030       ],
39031       [
39032         "Ghana (Gaana)",
39033         "gh",
39034         "233"
39035       ],
39036       [
39037         "Gibraltar",
39038         "gi",
39039         "350"
39040       ],
39041       [
39042         "Greece (Ελλάδα)",
39043         "gr",
39044         "30"
39045       ],
39046       [
39047         "Greenland (Kalaallit Nunaat)",
39048         "gl",
39049         "299"
39050       ],
39051       [
39052         "Grenada",
39053         "gd",
39054         "1473"
39055       ],
39056       [
39057         "Guadeloupe",
39058         "gp",
39059         "590",
39060         0
39061       ],
39062       [
39063         "Guam",
39064         "gu",
39065         "1671"
39066       ],
39067       [
39068         "Guatemala",
39069         "gt",
39070         "502"
39071       ],
39072       [
39073         "Guernsey",
39074         "gg",
39075         "44",
39076         1
39077       ],
39078       [
39079         "Guinea (Guinée)",
39080         "gn",
39081         "224"
39082       ],
39083       [
39084         "Guinea-Bissau (Guiné Bissau)",
39085         "gw",
39086         "245"
39087       ],
39088       [
39089         "Guyana",
39090         "gy",
39091         "592"
39092       ],
39093       [
39094         "Haiti",
39095         "ht",
39096         "509"
39097       ],
39098       [
39099         "Honduras",
39100         "hn",
39101         "504"
39102       ],
39103       [
39104         "Hong Kong (香港)",
39105         "hk",
39106         "852"
39107       ],
39108       [
39109         "Hungary (Magyarország)",
39110         "hu",
39111         "36"
39112       ],
39113       [
39114         "Iceland (Ísland)",
39115         "is",
39116         "354"
39117       ],
39118       [
39119         "India (भारत)",
39120         "in",
39121         "91"
39122       ],
39123       [
39124         "Indonesia",
39125         "id",
39126         "62"
39127       ],
39128       [
39129         "Iran (‫ایران‬‎)",
39130         "ir",
39131         "98"
39132       ],
39133       [
39134         "Iraq (‫العراق‬‎)",
39135         "iq",
39136         "964"
39137       ],
39138       [
39139         "Ireland",
39140         "ie",
39141         "353"
39142       ],
39143       [
39144         "Isle of Man",
39145         "im",
39146         "44",
39147         2
39148       ],
39149       [
39150         "Israel (‫ישראל‬‎)",
39151         "il",
39152         "972"
39153       ],
39154       [
39155         "Italy (Italia)",
39156         "it",
39157         "39",
39158         0
39159       ],
39160       [
39161         "Jamaica",
39162         "jm",
39163         "1876"
39164       ],
39165       [
39166         "Japan (日本)",
39167         "jp",
39168         "81"
39169       ],
39170       [
39171         "Jersey",
39172         "je",
39173         "44",
39174         3
39175       ],
39176       [
39177         "Jordan (‫الأردن‬‎)",
39178         "jo",
39179         "962"
39180       ],
39181       [
39182         "Kazakhstan (Казахстан)",
39183         "kz",
39184         "7",
39185         1
39186       ],
39187       [
39188         "Kenya",
39189         "ke",
39190         "254"
39191       ],
39192       [
39193         "Kiribati",
39194         "ki",
39195         "686"
39196       ],
39197       [
39198         "Kosovo",
39199         "xk",
39200         "383"
39201       ],
39202       [
39203         "Kuwait (‫الكويت‬‎)",
39204         "kw",
39205         "965"
39206       ],
39207       [
39208         "Kyrgyzstan (Кыргызстан)",
39209         "kg",
39210         "996"
39211       ],
39212       [
39213         "Laos (ລາວ)",
39214         "la",
39215         "856"
39216       ],
39217       [
39218         "Latvia (Latvija)",
39219         "lv",
39220         "371"
39221       ],
39222       [
39223         "Lebanon (‫لبنان‬‎)",
39224         "lb",
39225         "961"
39226       ],
39227       [
39228         "Lesotho",
39229         "ls",
39230         "266"
39231       ],
39232       [
39233         "Liberia",
39234         "lr",
39235         "231"
39236       ],
39237       [
39238         "Libya (‫ليبيا‬‎)",
39239         "ly",
39240         "218"
39241       ],
39242       [
39243         "Liechtenstein",
39244         "li",
39245         "423"
39246       ],
39247       [
39248         "Lithuania (Lietuva)",
39249         "lt",
39250         "370"
39251       ],
39252       [
39253         "Luxembourg",
39254         "lu",
39255         "352"
39256       ],
39257       [
39258         "Macau (澳門)",
39259         "mo",
39260         "853"
39261       ],
39262       [
39263         "Macedonia (FYROM) (Македонија)",
39264         "mk",
39265         "389"
39266       ],
39267       [
39268         "Madagascar (Madagasikara)",
39269         "mg",
39270         "261"
39271       ],
39272       [
39273         "Malawi",
39274         "mw",
39275         "265"
39276       ],
39277       [
39278         "Malaysia",
39279         "my",
39280         "60"
39281       ],
39282       [
39283         "Maldives",
39284         "mv",
39285         "960"
39286       ],
39287       [
39288         "Mali",
39289         "ml",
39290         "223"
39291       ],
39292       [
39293         "Malta",
39294         "mt",
39295         "356"
39296       ],
39297       [
39298         "Marshall Islands",
39299         "mh",
39300         "692"
39301       ],
39302       [
39303         "Martinique",
39304         "mq",
39305         "596"
39306       ],
39307       [
39308         "Mauritania (‫موريتانيا‬‎)",
39309         "mr",
39310         "222"
39311       ],
39312       [
39313         "Mauritius (Moris)",
39314         "mu",
39315         "230"
39316       ],
39317       [
39318         "Mayotte",
39319         "yt",
39320         "262",
39321         1
39322       ],
39323       [
39324         "Mexico (México)",
39325         "mx",
39326         "52"
39327       ],
39328       [
39329         "Micronesia",
39330         "fm",
39331         "691"
39332       ],
39333       [
39334         "Moldova (Republica Moldova)",
39335         "md",
39336         "373"
39337       ],
39338       [
39339         "Monaco",
39340         "mc",
39341         "377"
39342       ],
39343       [
39344         "Mongolia (Монгол)",
39345         "mn",
39346         "976"
39347       ],
39348       [
39349         "Montenegro (Crna Gora)",
39350         "me",
39351         "382"
39352       ],
39353       [
39354         "Montserrat",
39355         "ms",
39356         "1664"
39357       ],
39358       [
39359         "Morocco (‫المغرب‬‎)",
39360         "ma",
39361         "212",
39362         0
39363       ],
39364       [
39365         "Mozambique (Moçambique)",
39366         "mz",
39367         "258"
39368       ],
39369       [
39370         "Myanmar (Burma) (မြန်မာ)",
39371         "mm",
39372         "95"
39373       ],
39374       [
39375         "Namibia (Namibië)",
39376         "na",
39377         "264"
39378       ],
39379       [
39380         "Nauru",
39381         "nr",
39382         "674"
39383       ],
39384       [
39385         "Nepal (नेपाल)",
39386         "np",
39387         "977"
39388       ],
39389       [
39390         "Netherlands (Nederland)",
39391         "nl",
39392         "31"
39393       ],
39394       [
39395         "New Caledonia (Nouvelle-Calédonie)",
39396         "nc",
39397         "687"
39398       ],
39399       [
39400         "New Zealand",
39401         "nz",
39402         "64"
39403       ],
39404       [
39405         "Nicaragua",
39406         "ni",
39407         "505"
39408       ],
39409       [
39410         "Niger (Nijar)",
39411         "ne",
39412         "227"
39413       ],
39414       [
39415         "Nigeria",
39416         "ng",
39417         "234"
39418       ],
39419       [
39420         "Niue",
39421         "nu",
39422         "683"
39423       ],
39424       [
39425         "Norfolk Island",
39426         "nf",
39427         "672"
39428       ],
39429       [
39430         "North Korea (조선 민주주의 인민 공화국)",
39431         "kp",
39432         "850"
39433       ],
39434       [
39435         "Northern Mariana Islands",
39436         "mp",
39437         "1670"
39438       ],
39439       [
39440         "Norway (Norge)",
39441         "no",
39442         "47",
39443         0
39444       ],
39445       [
39446         "Oman (‫عُمان‬‎)",
39447         "om",
39448         "968"
39449       ],
39450       [
39451         "Pakistan (‫پاکستان‬‎)",
39452         "pk",
39453         "92"
39454       ],
39455       [
39456         "Palau",
39457         "pw",
39458         "680"
39459       ],
39460       [
39461         "Palestine (‫فلسطين‬‎)",
39462         "ps",
39463         "970"
39464       ],
39465       [
39466         "Panama (Panamá)",
39467         "pa",
39468         "507"
39469       ],
39470       [
39471         "Papua New Guinea",
39472         "pg",
39473         "675"
39474       ],
39475       [
39476         "Paraguay",
39477         "py",
39478         "595"
39479       ],
39480       [
39481         "Peru (Perú)",
39482         "pe",
39483         "51"
39484       ],
39485       [
39486         "Philippines",
39487         "ph",
39488         "63"
39489       ],
39490       [
39491         "Poland (Polska)",
39492         "pl",
39493         "48"
39494       ],
39495       [
39496         "Portugal",
39497         "pt",
39498         "351"
39499       ],
39500       [
39501         "Puerto Rico",
39502         "pr",
39503         "1",
39504         3,
39505         ["787", "939"]
39506       ],
39507       [
39508         "Qatar (‫قطر‬‎)",
39509         "qa",
39510         "974"
39511       ],
39512       [
39513         "Réunion (La Réunion)",
39514         "re",
39515         "262",
39516         0
39517       ],
39518       [
39519         "Romania (România)",
39520         "ro",
39521         "40"
39522       ],
39523       [
39524         "Russia (Россия)",
39525         "ru",
39526         "7",
39527         0
39528       ],
39529       [
39530         "Rwanda",
39531         "rw",
39532         "250"
39533       ],
39534       [
39535         "Saint Barthélemy",
39536         "bl",
39537         "590",
39538         1
39539       ],
39540       [
39541         "Saint Helena",
39542         "sh",
39543         "290"
39544       ],
39545       [
39546         "Saint Kitts and Nevis",
39547         "kn",
39548         "1869"
39549       ],
39550       [
39551         "Saint Lucia",
39552         "lc",
39553         "1758"
39554       ],
39555       [
39556         "Saint Martin (Saint-Martin (partie française))",
39557         "mf",
39558         "590",
39559         2
39560       ],
39561       [
39562         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39563         "pm",
39564         "508"
39565       ],
39566       [
39567         "Saint Vincent and the Grenadines",
39568         "vc",
39569         "1784"
39570       ],
39571       [
39572         "Samoa",
39573         "ws",
39574         "685"
39575       ],
39576       [
39577         "San Marino",
39578         "sm",
39579         "378"
39580       ],
39581       [
39582         "São Tomé and Príncipe (São Tomé e Príncipe)",
39583         "st",
39584         "239"
39585       ],
39586       [
39587         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39588         "sa",
39589         "966"
39590       ],
39591       [
39592         "Senegal (Sénégal)",
39593         "sn",
39594         "221"
39595       ],
39596       [
39597         "Serbia (Србија)",
39598         "rs",
39599         "381"
39600       ],
39601       [
39602         "Seychelles",
39603         "sc",
39604         "248"
39605       ],
39606       [
39607         "Sierra Leone",
39608         "sl",
39609         "232"
39610       ],
39611       [
39612         "Singapore",
39613         "sg",
39614         "65"
39615       ],
39616       [
39617         "Sint Maarten",
39618         "sx",
39619         "1721"
39620       ],
39621       [
39622         "Slovakia (Slovensko)",
39623         "sk",
39624         "421"
39625       ],
39626       [
39627         "Slovenia (Slovenija)",
39628         "si",
39629         "386"
39630       ],
39631       [
39632         "Solomon Islands",
39633         "sb",
39634         "677"
39635       ],
39636       [
39637         "Somalia (Soomaaliya)",
39638         "so",
39639         "252"
39640       ],
39641       [
39642         "South Africa",
39643         "za",
39644         "27"
39645       ],
39646       [
39647         "South Korea (대한민국)",
39648         "kr",
39649         "82"
39650       ],
39651       [
39652         "South Sudan (‫جنوب السودان‬‎)",
39653         "ss",
39654         "211"
39655       ],
39656       [
39657         "Spain (España)",
39658         "es",
39659         "34"
39660       ],
39661       [
39662         "Sri Lanka (ශ්‍රී ලංකාව)",
39663         "lk",
39664         "94"
39665       ],
39666       [
39667         "Sudan (‫السودان‬‎)",
39668         "sd",
39669         "249"
39670       ],
39671       [
39672         "Suriname",
39673         "sr",
39674         "597"
39675       ],
39676       [
39677         "Svalbard and Jan Mayen",
39678         "sj",
39679         "47",
39680         1
39681       ],
39682       [
39683         "Swaziland",
39684         "sz",
39685         "268"
39686       ],
39687       [
39688         "Sweden (Sverige)",
39689         "se",
39690         "46"
39691       ],
39692       [
39693         "Switzerland (Schweiz)",
39694         "ch",
39695         "41"
39696       ],
39697       [
39698         "Syria (‫سوريا‬‎)",
39699         "sy",
39700         "963"
39701       ],
39702       [
39703         "Taiwan (台灣)",
39704         "tw",
39705         "886"
39706       ],
39707       [
39708         "Tajikistan",
39709         "tj",
39710         "992"
39711       ],
39712       [
39713         "Tanzania",
39714         "tz",
39715         "255"
39716       ],
39717       [
39718         "Thailand (ไทย)",
39719         "th",
39720         "66"
39721       ],
39722       [
39723         "Timor-Leste",
39724         "tl",
39725         "670"
39726       ],
39727       [
39728         "Togo",
39729         "tg",
39730         "228"
39731       ],
39732       [
39733         "Tokelau",
39734         "tk",
39735         "690"
39736       ],
39737       [
39738         "Tonga",
39739         "to",
39740         "676"
39741       ],
39742       [
39743         "Trinidad and Tobago",
39744         "tt",
39745         "1868"
39746       ],
39747       [
39748         "Tunisia (‫تونس‬‎)",
39749         "tn",
39750         "216"
39751       ],
39752       [
39753         "Turkey (Türkiye)",
39754         "tr",
39755         "90"
39756       ],
39757       [
39758         "Turkmenistan",
39759         "tm",
39760         "993"
39761       ],
39762       [
39763         "Turks and Caicos Islands",
39764         "tc",
39765         "1649"
39766       ],
39767       [
39768         "Tuvalu",
39769         "tv",
39770         "688"
39771       ],
39772       [
39773         "U.S. Virgin Islands",
39774         "vi",
39775         "1340"
39776       ],
39777       [
39778         "Uganda",
39779         "ug",
39780         "256"
39781       ],
39782       [
39783         "Ukraine (Україна)",
39784         "ua",
39785         "380"
39786       ],
39787       [
39788         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39789         "ae",
39790         "971"
39791       ],
39792       [
39793         "United Kingdom",
39794         "gb",
39795         "44",
39796         0
39797       ],
39798       [
39799         "United States",
39800         "us",
39801         "1",
39802         0
39803       ],
39804       [
39805         "Uruguay",
39806         "uy",
39807         "598"
39808       ],
39809       [
39810         "Uzbekistan (Oʻzbekiston)",
39811         "uz",
39812         "998"
39813       ],
39814       [
39815         "Vanuatu",
39816         "vu",
39817         "678"
39818       ],
39819       [
39820         "Vatican City (Città del Vaticano)",
39821         "va",
39822         "39",
39823         1
39824       ],
39825       [
39826         "Venezuela",
39827         "ve",
39828         "58"
39829       ],
39830       [
39831         "Vietnam (Việt Nam)",
39832         "vn",
39833         "84"
39834       ],
39835       [
39836         "Wallis and Futuna (Wallis-et-Futuna)",
39837         "wf",
39838         "681"
39839       ],
39840       [
39841         "Western Sahara (‫الصحراء الغربية‬‎)",
39842         "eh",
39843         "212",
39844         1
39845       ],
39846       [
39847         "Yemen (‫اليمن‬‎)",
39848         "ye",
39849         "967"
39850       ],
39851       [
39852         "Zambia",
39853         "zm",
39854         "260"
39855       ],
39856       [
39857         "Zimbabwe",
39858         "zw",
39859         "263"
39860       ],
39861       [
39862         "Åland Islands",
39863         "ax",
39864         "358",
39865         1
39866       ]
39867   ];
39868   
39869   return d;
39870 }/**
39871 *    This script refer to:
39872 *    Title: International Telephone Input
39873 *    Author: Jack O'Connor
39874 *    Code version:  v12.1.12
39875 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39876 **/
39877
39878 /**
39879  * @class Roo.bootstrap.PhoneInput
39880  * @extends Roo.bootstrap.TriggerField
39881  * An input with International dial-code selection
39882  
39883  * @cfg {String} defaultDialCode default '+852'
39884  * @cfg {Array} preferedCountries default []
39885   
39886  * @constructor
39887  * Create a new PhoneInput.
39888  * @param {Object} config Configuration options
39889  */
39890
39891 Roo.bootstrap.PhoneInput = function(config) {
39892     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39893 };
39894
39895 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39896         
39897         listWidth: undefined,
39898         
39899         selectedClass: 'active',
39900         
39901         invalidClass : "has-warning",
39902         
39903         validClass: 'has-success',
39904         
39905         allowed: '0123456789',
39906         
39907         max_length: 15,
39908         
39909         /**
39910          * @cfg {String} defaultDialCode The default dial code when initializing the input
39911          */
39912         defaultDialCode: '+852',
39913         
39914         /**
39915          * @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
39916          */
39917         preferedCountries: false,
39918         
39919         getAutoCreate : function()
39920         {
39921             var data = Roo.bootstrap.PhoneInputData();
39922             var align = this.labelAlign || this.parentLabelAlign();
39923             var id = Roo.id();
39924             
39925             this.allCountries = [];
39926             this.dialCodeMapping = [];
39927             
39928             for (var i = 0; i < data.length; i++) {
39929               var c = data[i];
39930               this.allCountries[i] = {
39931                 name: c[0],
39932                 iso2: c[1],
39933                 dialCode: c[2],
39934                 priority: c[3] || 0,
39935                 areaCodes: c[4] || null
39936               };
39937               this.dialCodeMapping[c[2]] = {
39938                   name: c[0],
39939                   iso2: c[1],
39940                   priority: c[3] || 0,
39941                   areaCodes: c[4] || null
39942               };
39943             }
39944             
39945             var cfg = {
39946                 cls: 'form-group',
39947                 cn: []
39948             };
39949             
39950             var input =  {
39951                 tag: 'input',
39952                 id : id,
39953                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39954                 maxlength: this.max_length,
39955                 cls : 'form-control tel-input',
39956                 autocomplete: 'new-password'
39957             };
39958             
39959             var hiddenInput = {
39960                 tag: 'input',
39961                 type: 'hidden',
39962                 cls: 'hidden-tel-input'
39963             };
39964             
39965             if (this.name) {
39966                 hiddenInput.name = this.name;
39967             }
39968             
39969             if (this.disabled) {
39970                 input.disabled = true;
39971             }
39972             
39973             var flag_container = {
39974                 tag: 'div',
39975                 cls: 'flag-box',
39976                 cn: [
39977                     {
39978                         tag: 'div',
39979                         cls: 'flag'
39980                     },
39981                     {
39982                         tag: 'div',
39983                         cls: 'caret'
39984                     }
39985                 ]
39986             };
39987             
39988             var box = {
39989                 tag: 'div',
39990                 cls: this.hasFeedback ? 'has-feedback' : '',
39991                 cn: [
39992                     hiddenInput,
39993                     input,
39994                     {
39995                         tag: 'input',
39996                         cls: 'dial-code-holder',
39997                         disabled: true
39998                     }
39999                 ]
40000             };
40001             
40002             var container = {
40003                 cls: 'roo-select2-container input-group',
40004                 cn: [
40005                     flag_container,
40006                     box
40007                 ]
40008             };
40009             
40010             if (this.fieldLabel.length) {
40011                 var indicator = {
40012                     tag: 'i',
40013                     tooltip: 'This field is required'
40014                 };
40015                 
40016                 var label = {
40017                     tag: 'label',
40018                     'for':  id,
40019                     cls: 'control-label',
40020                     cn: []
40021                 };
40022                 
40023                 var label_text = {
40024                     tag: 'span',
40025                     html: this.fieldLabel
40026                 };
40027                 
40028                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40029                 label.cn = [
40030                     indicator,
40031                     label_text
40032                 ];
40033                 
40034                 if(this.indicatorpos == 'right') {
40035                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40036                     label.cn = [
40037                         label_text,
40038                         indicator
40039                     ];
40040                 }
40041                 
40042                 if(align == 'left') {
40043                     container = {
40044                         tag: 'div',
40045                         cn: [
40046                             container
40047                         ]
40048                     };
40049                     
40050                     if(this.labelWidth > 12){
40051                         label.style = "width: " + this.labelWidth + 'px';
40052                     }
40053                     if(this.labelWidth < 13 && this.labelmd == 0){
40054                         this.labelmd = this.labelWidth;
40055                     }
40056                     if(this.labellg > 0){
40057                         label.cls += ' col-lg-' + this.labellg;
40058                         input.cls += ' col-lg-' + (12 - this.labellg);
40059                     }
40060                     if(this.labelmd > 0){
40061                         label.cls += ' col-md-' + this.labelmd;
40062                         container.cls += ' col-md-' + (12 - this.labelmd);
40063                     }
40064                     if(this.labelsm > 0){
40065                         label.cls += ' col-sm-' + this.labelsm;
40066                         container.cls += ' col-sm-' + (12 - this.labelsm);
40067                     }
40068                     if(this.labelxs > 0){
40069                         label.cls += ' col-xs-' + this.labelxs;
40070                         container.cls += ' col-xs-' + (12 - this.labelxs);
40071                     }
40072                 }
40073             }
40074             
40075             cfg.cn = [
40076                 label,
40077                 container
40078             ];
40079             
40080             var settings = this;
40081             
40082             ['xs','sm','md','lg'].map(function(size){
40083                 if (settings[size]) {
40084                     cfg.cls += ' col-' + size + '-' + settings[size];
40085                 }
40086             });
40087             
40088             this.store = new Roo.data.Store({
40089                 proxy : new Roo.data.MemoryProxy({}),
40090                 reader : new Roo.data.JsonReader({
40091                     fields : [
40092                         {
40093                             'name' : 'name',
40094                             'type' : 'string'
40095                         },
40096                         {
40097                             'name' : 'iso2',
40098                             'type' : 'string'
40099                         },
40100                         {
40101                             'name' : 'dialCode',
40102                             'type' : 'string'
40103                         },
40104                         {
40105                             'name' : 'priority',
40106                             'type' : 'string'
40107                         },
40108                         {
40109                             'name' : 'areaCodes',
40110                             'type' : 'string'
40111                         }
40112                     ]
40113                 })
40114             });
40115             
40116             if(!this.preferedCountries) {
40117                 this.preferedCountries = [
40118                     'hk',
40119                     'gb',
40120                     'us'
40121                 ];
40122             }
40123             
40124             var p = this.preferedCountries.reverse();
40125             
40126             if(p) {
40127                 for (var i = 0; i < p.length; i++) {
40128                     for (var j = 0; j < this.allCountries.length; j++) {
40129                         if(this.allCountries[j].iso2 == p[i]) {
40130                             var t = this.allCountries[j];
40131                             this.allCountries.splice(j,1);
40132                             this.allCountries.unshift(t);
40133                         }
40134                     } 
40135                 }
40136             }
40137             
40138             this.store.proxy.data = {
40139                 success: true,
40140                 data: this.allCountries
40141             };
40142             
40143             return cfg;
40144         },
40145         
40146         initEvents : function()
40147         {
40148             this.createList();
40149             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40150             
40151             this.indicator = this.indicatorEl();
40152             this.flag = this.flagEl();
40153             this.dialCodeHolder = this.dialCodeHolderEl();
40154             
40155             this.trigger = this.el.select('div.flag-box',true).first();
40156             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40157             
40158             var _this = this;
40159             
40160             (function(){
40161                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40162                 _this.list.setWidth(lw);
40163             }).defer(100);
40164             
40165             this.list.on('mouseover', this.onViewOver, this);
40166             this.list.on('mousemove', this.onViewMove, this);
40167             this.inputEl().on("keyup", this.onKeyUp, this);
40168             this.inputEl().on("keypress", this.onKeyPress, this);
40169             
40170             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40171
40172             this.view = new Roo.View(this.list, this.tpl, {
40173                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40174             });
40175             
40176             this.view.on('click', this.onViewClick, this);
40177             this.setValue(this.defaultDialCode);
40178         },
40179         
40180         onTriggerClick : function(e)
40181         {
40182             Roo.log('trigger click');
40183             if(this.disabled){
40184                 return;
40185             }
40186             
40187             if(this.isExpanded()){
40188                 this.collapse();
40189                 this.hasFocus = false;
40190             }else {
40191                 this.store.load({});
40192                 this.hasFocus = true;
40193                 this.expand();
40194             }
40195         },
40196         
40197         isExpanded : function()
40198         {
40199             return this.list.isVisible();
40200         },
40201         
40202         collapse : function()
40203         {
40204             if(!this.isExpanded()){
40205                 return;
40206             }
40207             this.list.hide();
40208             Roo.get(document).un('mousedown', this.collapseIf, this);
40209             Roo.get(document).un('mousewheel', this.collapseIf, this);
40210             this.fireEvent('collapse', this);
40211             this.validate();
40212         },
40213         
40214         expand : function()
40215         {
40216             Roo.log('expand');
40217
40218             if(this.isExpanded() || !this.hasFocus){
40219                 return;
40220             }
40221             
40222             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40223             this.list.setWidth(lw);
40224             
40225             this.list.show();
40226             this.restrictHeight();
40227             
40228             Roo.get(document).on('mousedown', this.collapseIf, this);
40229             Roo.get(document).on('mousewheel', this.collapseIf, this);
40230             
40231             this.fireEvent('expand', this);
40232         },
40233         
40234         restrictHeight : function()
40235         {
40236             this.list.alignTo(this.inputEl(), this.listAlign);
40237             this.list.alignTo(this.inputEl(), this.listAlign);
40238         },
40239         
40240         onViewOver : function(e, t)
40241         {
40242             if(this.inKeyMode){
40243                 return;
40244             }
40245             var item = this.view.findItemFromChild(t);
40246             
40247             if(item){
40248                 var index = this.view.indexOf(item);
40249                 this.select(index, false);
40250             }
40251         },
40252
40253         // private
40254         onViewClick : function(view, doFocus, el, e)
40255         {
40256             var index = this.view.getSelectedIndexes()[0];
40257             
40258             var r = this.store.getAt(index);
40259             
40260             if(r){
40261                 this.onSelect(r, index);
40262             }
40263             if(doFocus !== false && !this.blockFocus){
40264                 this.inputEl().focus();
40265             }
40266         },
40267         
40268         onViewMove : function(e, t)
40269         {
40270             this.inKeyMode = false;
40271         },
40272         
40273         select : function(index, scrollIntoView)
40274         {
40275             this.selectedIndex = index;
40276             this.view.select(index);
40277             if(scrollIntoView !== false){
40278                 var el = this.view.getNode(index);
40279                 if(el){
40280                     this.list.scrollChildIntoView(el, false);
40281                 }
40282             }
40283         },
40284         
40285         createList : function()
40286         {
40287             this.list = Roo.get(document.body).createChild({
40288                 tag: 'ul',
40289                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40290                 style: 'display:none'
40291             });
40292             
40293             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40294         },
40295         
40296         collapseIf : function(e)
40297         {
40298             var in_combo  = e.within(this.el);
40299             var in_list =  e.within(this.list);
40300             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40301             
40302             if (in_combo || in_list || is_list) {
40303                 return;
40304             }
40305             this.collapse();
40306         },
40307         
40308         onSelect : function(record, index)
40309         {
40310             if(this.fireEvent('beforeselect', this, record, index) !== false){
40311                 
40312                 this.setFlagClass(record.data.iso2);
40313                 this.setDialCode(record.data.dialCode);
40314                 this.hasFocus = false;
40315                 this.collapse();
40316                 this.fireEvent('select', this, record, index);
40317             }
40318         },
40319         
40320         flagEl : function()
40321         {
40322             var flag = this.el.select('div.flag',true).first();
40323             if(!flag){
40324                 return false;
40325             }
40326             return flag;
40327         },
40328         
40329         dialCodeHolderEl : function()
40330         {
40331             var d = this.el.select('input.dial-code-holder',true).first();
40332             if(!d){
40333                 return false;
40334             }
40335             return d;
40336         },
40337         
40338         setDialCode : function(v)
40339         {
40340             this.dialCodeHolder.dom.value = '+'+v;
40341         },
40342         
40343         setFlagClass : function(n)
40344         {
40345             this.flag.dom.className = 'flag '+n;
40346         },
40347         
40348         getValue : function()
40349         {
40350             var v = this.inputEl().getValue();
40351             if(this.dialCodeHolder) {
40352                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40353             }
40354             return v;
40355         },
40356         
40357         setValue : function(v)
40358         {
40359             var d = this.getDialCode(v);
40360             
40361             //invalid dial code
40362             if(v.length == 0 || !d || d.length == 0) {
40363                 if(this.rendered){
40364                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40365                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40366                 }
40367                 return;
40368             }
40369             
40370             //valid dial code
40371             this.setFlagClass(this.dialCodeMapping[d].iso2);
40372             this.setDialCode(d);
40373             this.inputEl().dom.value = v.replace('+'+d,'');
40374             this.hiddenEl().dom.value = this.getValue();
40375             
40376             this.validate();
40377         },
40378         
40379         getDialCode : function(v)
40380         {
40381             v = v ||  '';
40382             
40383             if (v.length == 0) {
40384                 return this.dialCodeHolder.dom.value;
40385             }
40386             
40387             var dialCode = "";
40388             if (v.charAt(0) != "+") {
40389                 return false;
40390             }
40391             var numericChars = "";
40392             for (var i = 1; i < v.length; i++) {
40393               var c = v.charAt(i);
40394               if (!isNaN(c)) {
40395                 numericChars += c;
40396                 if (this.dialCodeMapping[numericChars]) {
40397                   dialCode = v.substr(1, i);
40398                 }
40399                 if (numericChars.length == 4) {
40400                   break;
40401                 }
40402               }
40403             }
40404             return dialCode;
40405         },
40406         
40407         reset : function()
40408         {
40409             this.setValue(this.defaultDialCode);
40410             this.validate();
40411         },
40412         
40413         hiddenEl : function()
40414         {
40415             return this.el.select('input.hidden-tel-input',true).first();
40416         },
40417         
40418         // after setting val
40419         onKeyUp : function(e){
40420             this.setValue(this.getValue());
40421         },
40422         
40423         onKeyPress : function(e){
40424             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40425                 e.stopEvent();
40426             }
40427         }
40428         
40429 });
40430 /**
40431  * @class Roo.bootstrap.MoneyField
40432  * @extends Roo.bootstrap.ComboBox
40433  * Bootstrap MoneyField class
40434  * 
40435  * @constructor
40436  * Create a new MoneyField.
40437  * @param {Object} config Configuration options
40438  */
40439
40440 Roo.bootstrap.MoneyField = function(config) {
40441     
40442     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40443     
40444 };
40445
40446 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40447     
40448     /**
40449      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40450      */
40451     allowDecimals : true,
40452     /**
40453      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40454      */
40455     decimalSeparator : ".",
40456     /**
40457      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40458      */
40459     decimalPrecision : 0,
40460     /**
40461      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40462      */
40463     allowNegative : true,
40464     /**
40465      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40466      */
40467     allowZero: true,
40468     /**
40469      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40470      */
40471     minValue : Number.NEGATIVE_INFINITY,
40472     /**
40473      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40474      */
40475     maxValue : Number.MAX_VALUE,
40476     /**
40477      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40478      */
40479     minText : "The minimum value for this field is {0}",
40480     /**
40481      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40482      */
40483     maxText : "The maximum value for this field is {0}",
40484     /**
40485      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40486      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40487      */
40488     nanText : "{0} is not a valid number",
40489     /**
40490      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40491      */
40492     castInt : true,
40493     /**
40494      * @cfg {String} defaults currency of the MoneyField
40495      * value should be in lkey
40496      */
40497     defaultCurrency : false,
40498     /**
40499      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40500      */
40501     thousandsDelimiter : false,
40502     /**
40503      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40504      */
40505     max_length: false,
40506     
40507     inputlg : 9,
40508     inputmd : 9,
40509     inputsm : 9,
40510     inputxs : 6,
40511     
40512     store : false,
40513     
40514     getAutoCreate : function()
40515     {
40516         var align = this.labelAlign || this.parentLabelAlign();
40517         
40518         var id = Roo.id();
40519
40520         var cfg = {
40521             cls: 'form-group',
40522             cn: []
40523         };
40524
40525         var input =  {
40526             tag: 'input',
40527             id : id,
40528             cls : 'form-control roo-money-amount-input',
40529             autocomplete: 'new-password'
40530         };
40531         
40532         var hiddenInput = {
40533             tag: 'input',
40534             type: 'hidden',
40535             id: Roo.id(),
40536             cls: 'hidden-number-input'
40537         };
40538         
40539         if(this.max_length) {
40540             input.maxlength = this.max_length; 
40541         }
40542         
40543         if (this.name) {
40544             hiddenInput.name = this.name;
40545         }
40546
40547         if (this.disabled) {
40548             input.disabled = true;
40549         }
40550
40551         var clg = 12 - this.inputlg;
40552         var cmd = 12 - this.inputmd;
40553         var csm = 12 - this.inputsm;
40554         var cxs = 12 - this.inputxs;
40555         
40556         var container = {
40557             tag : 'div',
40558             cls : 'row roo-money-field',
40559             cn : [
40560                 {
40561                     tag : 'div',
40562                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40563                     cn : [
40564                         {
40565                             tag : 'div',
40566                             cls: 'roo-select2-container input-group',
40567                             cn: [
40568                                 {
40569                                     tag : 'input',
40570                                     cls : 'form-control roo-money-currency-input',
40571                                     autocomplete: 'new-password',
40572                                     readOnly : 1,
40573                                     name : this.currencyName
40574                                 },
40575                                 {
40576                                     tag :'span',
40577                                     cls : 'input-group-addon',
40578                                     cn : [
40579                                         {
40580                                             tag: 'span',
40581                                             cls: 'caret'
40582                                         }
40583                                     ]
40584                                 }
40585                             ]
40586                         }
40587                     ]
40588                 },
40589                 {
40590                     tag : 'div',
40591                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40592                     cn : [
40593                         {
40594                             tag: 'div',
40595                             cls: this.hasFeedback ? 'has-feedback' : '',
40596                             cn: [
40597                                 input
40598                             ]
40599                         }
40600                     ]
40601                 }
40602             ]
40603             
40604         };
40605         
40606         if (this.fieldLabel.length) {
40607             var indicator = {
40608                 tag: 'i',
40609                 tooltip: 'This field is required'
40610             };
40611
40612             var label = {
40613                 tag: 'label',
40614                 'for':  id,
40615                 cls: 'control-label',
40616                 cn: []
40617             };
40618
40619             var label_text = {
40620                 tag: 'span',
40621                 html: this.fieldLabel
40622             };
40623
40624             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40625             label.cn = [
40626                 indicator,
40627                 label_text
40628             ];
40629
40630             if(this.indicatorpos == 'right') {
40631                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40632                 label.cn = [
40633                     label_text,
40634                     indicator
40635                 ];
40636             }
40637
40638             if(align == 'left') {
40639                 container = {
40640                     tag: 'div',
40641                     cn: [
40642                         container
40643                     ]
40644                 };
40645
40646                 if(this.labelWidth > 12){
40647                     label.style = "width: " + this.labelWidth + 'px';
40648                 }
40649                 if(this.labelWidth < 13 && this.labelmd == 0){
40650                     this.labelmd = this.labelWidth;
40651                 }
40652                 if(this.labellg > 0){
40653                     label.cls += ' col-lg-' + this.labellg;
40654                     input.cls += ' col-lg-' + (12 - this.labellg);
40655                 }
40656                 if(this.labelmd > 0){
40657                     label.cls += ' col-md-' + this.labelmd;
40658                     container.cls += ' col-md-' + (12 - this.labelmd);
40659                 }
40660                 if(this.labelsm > 0){
40661                     label.cls += ' col-sm-' + this.labelsm;
40662                     container.cls += ' col-sm-' + (12 - this.labelsm);
40663                 }
40664                 if(this.labelxs > 0){
40665                     label.cls += ' col-xs-' + this.labelxs;
40666                     container.cls += ' col-xs-' + (12 - this.labelxs);
40667                 }
40668             }
40669         }
40670
40671         cfg.cn = [
40672             label,
40673             container,
40674             hiddenInput
40675         ];
40676         
40677         var settings = this;
40678
40679         ['xs','sm','md','lg'].map(function(size){
40680             if (settings[size]) {
40681                 cfg.cls += ' col-' + size + '-' + settings[size];
40682             }
40683         });
40684         
40685         return cfg;
40686     },
40687     
40688     initEvents : function()
40689     {
40690         this.indicator = this.indicatorEl();
40691         
40692         this.initCurrencyEvent();
40693         
40694         this.initNumberEvent();
40695     },
40696     
40697     initCurrencyEvent : function()
40698     {
40699         if (!this.store) {
40700             throw "can not find store for combo";
40701         }
40702         
40703         this.store = Roo.factory(this.store, Roo.data);
40704         this.store.parent = this;
40705         
40706         this.createList();
40707         
40708         this.triggerEl = this.el.select('.input-group-addon', true).first();
40709         
40710         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40711         
40712         var _this = this;
40713         
40714         (function(){
40715             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40716             _this.list.setWidth(lw);
40717         }).defer(100);
40718         
40719         this.list.on('mouseover', this.onViewOver, this);
40720         this.list.on('mousemove', this.onViewMove, this);
40721         this.list.on('scroll', this.onViewScroll, this);
40722         
40723         if(!this.tpl){
40724             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40725         }
40726         
40727         this.view = new Roo.View(this.list, this.tpl, {
40728             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40729         });
40730         
40731         this.view.on('click', this.onViewClick, this);
40732         
40733         this.store.on('beforeload', this.onBeforeLoad, this);
40734         this.store.on('load', this.onLoad, this);
40735         this.store.on('loadexception', this.onLoadException, this);
40736         
40737         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40738             "up" : function(e){
40739                 this.inKeyMode = true;
40740                 this.selectPrev();
40741             },
40742
40743             "down" : function(e){
40744                 if(!this.isExpanded()){
40745                     this.onTriggerClick();
40746                 }else{
40747                     this.inKeyMode = true;
40748                     this.selectNext();
40749                 }
40750             },
40751
40752             "enter" : function(e){
40753                 this.collapse();
40754                 
40755                 if(this.fireEvent("specialkey", this, e)){
40756                     this.onViewClick(false);
40757                 }
40758                 
40759                 return true;
40760             },
40761
40762             "esc" : function(e){
40763                 this.collapse();
40764             },
40765
40766             "tab" : function(e){
40767                 this.collapse();
40768                 
40769                 if(this.fireEvent("specialkey", this, e)){
40770                     this.onViewClick(false);
40771                 }
40772                 
40773                 return true;
40774             },
40775
40776             scope : this,
40777
40778             doRelay : function(foo, bar, hname){
40779                 if(hname == 'down' || this.scope.isExpanded()){
40780                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40781                 }
40782                 return true;
40783             },
40784
40785             forceKeyDown: true
40786         });
40787         
40788         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40789         
40790     },
40791     
40792     initNumberEvent : function(e)
40793     {
40794         this.inputEl().on("keydown" , this.fireKey,  this);
40795         this.inputEl().on("focus", this.onFocus,  this);
40796         this.inputEl().on("blur", this.onBlur,  this);
40797         
40798         this.inputEl().relayEvent('keyup', this);
40799         
40800         if(this.indicator){
40801             this.indicator.addClass('invisible');
40802         }
40803  
40804         this.originalValue = this.getValue();
40805         
40806         if(this.validationEvent == 'keyup'){
40807             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40808             this.inputEl().on('keyup', this.filterValidation, this);
40809         }
40810         else if(this.validationEvent !== false){
40811             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40812         }
40813         
40814         if(this.selectOnFocus){
40815             this.on("focus", this.preFocus, this);
40816             
40817         }
40818         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40819             this.inputEl().on("keypress", this.filterKeys, this);
40820         } else {
40821             this.inputEl().relayEvent('keypress', this);
40822         }
40823         
40824         var allowed = "0123456789";
40825         
40826         if(this.allowDecimals){
40827             allowed += this.decimalSeparator;
40828         }
40829         
40830         if(this.allowNegative){
40831             allowed += "-";
40832         }
40833         
40834         if(this.thousandsDelimiter) {
40835             allowed += ",";
40836         }
40837         
40838         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40839         
40840         var keyPress = function(e){
40841             
40842             var k = e.getKey();
40843             
40844             var c = e.getCharCode();
40845             
40846             if(
40847                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40848                     allowed.indexOf(String.fromCharCode(c)) === -1
40849             ){
40850                 e.stopEvent();
40851                 return;
40852             }
40853             
40854             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40855                 return;
40856             }
40857             
40858             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40859                 e.stopEvent();
40860             }
40861         };
40862         
40863         this.inputEl().on("keypress", keyPress, this);
40864         
40865     },
40866     
40867     onTriggerClick : function(e)
40868     {   
40869         if(this.disabled){
40870             return;
40871         }
40872         
40873         this.page = 0;
40874         this.loadNext = false;
40875         
40876         if(this.isExpanded()){
40877             this.collapse();
40878             return;
40879         }
40880         
40881         this.hasFocus = true;
40882         
40883         if(this.triggerAction == 'all') {
40884             this.doQuery(this.allQuery, true);
40885             return;
40886         }
40887         
40888         this.doQuery(this.getRawValue());
40889     },
40890     
40891     getCurrency : function()
40892     {   
40893         var v = this.currencyEl().getValue();
40894         
40895         return v;
40896     },
40897     
40898     restrictHeight : function()
40899     {
40900         this.list.alignTo(this.currencyEl(), this.listAlign);
40901         this.list.alignTo(this.currencyEl(), this.listAlign);
40902     },
40903     
40904     onViewClick : function(view, doFocus, el, e)
40905     {
40906         var index = this.view.getSelectedIndexes()[0];
40907         
40908         var r = this.store.getAt(index);
40909         
40910         if(r){
40911             this.onSelect(r, index);
40912         }
40913     },
40914     
40915     onSelect : function(record, index){
40916         
40917         if(this.fireEvent('beforeselect', this, record, index) !== false){
40918         
40919             this.setFromCurrencyData(index > -1 ? record.data : false);
40920             
40921             this.collapse();
40922             
40923             this.fireEvent('select', this, record, index);
40924         }
40925     },
40926     
40927     setFromCurrencyData : function(o)
40928     {
40929         var currency = '';
40930         
40931         this.lastCurrency = o;
40932         
40933         if (this.currencyField) {
40934             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40935         } else {
40936             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40937         }
40938         
40939         this.lastSelectionText = currency;
40940         
40941         //setting default currency
40942         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40943             this.setCurrency(this.defaultCurrency);
40944             return;
40945         }
40946         
40947         this.setCurrency(currency);
40948     },
40949     
40950     setFromData : function(o)
40951     {
40952         var c = {};
40953         
40954         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40955         
40956         this.setFromCurrencyData(c);
40957         
40958         var value = '';
40959         
40960         if (this.name) {
40961             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40962         } else {
40963             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40964         }
40965         
40966         this.setValue(value);
40967         
40968     },
40969     
40970     setCurrency : function(v)
40971     {   
40972         this.currencyValue = v;
40973         
40974         if(this.rendered){
40975             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40976             this.validate();
40977         }
40978     },
40979     
40980     setValue : function(v)
40981     {
40982         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40983         
40984         this.value = v;
40985         
40986         if(this.rendered){
40987             
40988             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40989             
40990             this.inputEl().dom.value = (v == '') ? '' :
40991                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40992             
40993             if(!this.allowZero && v === '0') {
40994                 this.hiddenEl().dom.value = '';
40995                 this.inputEl().dom.value = '';
40996             }
40997             
40998             this.validate();
40999         }
41000     },
41001     
41002     getRawValue : function()
41003     {
41004         var v = this.inputEl().getValue();
41005         
41006         return v;
41007     },
41008     
41009     getValue : function()
41010     {
41011         return this.fixPrecision(this.parseValue(this.getRawValue()));
41012     },
41013     
41014     parseValue : function(value)
41015     {
41016         if(this.thousandsDelimiter) {
41017             value += "";
41018             r = new RegExp(",", "g");
41019             value = value.replace(r, "");
41020         }
41021         
41022         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41023         return isNaN(value) ? '' : value;
41024         
41025     },
41026     
41027     fixPrecision : function(value)
41028     {
41029         if(this.thousandsDelimiter) {
41030             value += "";
41031             r = new RegExp(",", "g");
41032             value = value.replace(r, "");
41033         }
41034         
41035         var nan = isNaN(value);
41036         
41037         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41038             return nan ? '' : value;
41039         }
41040         return parseFloat(value).toFixed(this.decimalPrecision);
41041     },
41042     
41043     decimalPrecisionFcn : function(v)
41044     {
41045         return Math.floor(v);
41046     },
41047     
41048     validateValue : function(value)
41049     {
41050         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41051             return false;
41052         }
41053         
41054         var num = this.parseValue(value);
41055         
41056         if(isNaN(num)){
41057             this.markInvalid(String.format(this.nanText, value));
41058             return false;
41059         }
41060         
41061         if(num < this.minValue){
41062             this.markInvalid(String.format(this.minText, this.minValue));
41063             return false;
41064         }
41065         
41066         if(num > this.maxValue){
41067             this.markInvalid(String.format(this.maxText, this.maxValue));
41068             return false;
41069         }
41070         
41071         return true;
41072     },
41073     
41074     validate : function()
41075     {
41076         if(this.disabled || this.allowBlank){
41077             this.markValid();
41078             return true;
41079         }
41080         
41081         var currency = this.getCurrency();
41082         
41083         if(this.validateValue(this.getRawValue()) && currency.length){
41084             this.markValid();
41085             return true;
41086         }
41087         
41088         this.markInvalid();
41089         return false;
41090     },
41091     
41092     getName: function()
41093     {
41094         return this.name;
41095     },
41096     
41097     beforeBlur : function()
41098     {
41099         if(!this.castInt){
41100             return;
41101         }
41102         
41103         var v = this.parseValue(this.getRawValue());
41104         
41105         if(v || v == 0){
41106             this.setValue(v);
41107         }
41108     },
41109     
41110     onBlur : function()
41111     {
41112         this.beforeBlur();
41113         
41114         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41115             //this.el.removeClass(this.focusClass);
41116         }
41117         
41118         this.hasFocus = false;
41119         
41120         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41121             this.validate();
41122         }
41123         
41124         var v = this.getValue();
41125         
41126         if(String(v) !== String(this.startValue)){
41127             this.fireEvent('change', this, v, this.startValue);
41128         }
41129         
41130         this.fireEvent("blur", this);
41131     },
41132     
41133     inputEl : function()
41134     {
41135         return this.el.select('.roo-money-amount-input', true).first();
41136     },
41137     
41138     currencyEl : function()
41139     {
41140         return this.el.select('.roo-money-currency-input', true).first();
41141     },
41142     
41143     hiddenEl : function()
41144     {
41145         return this.el.select('input.hidden-number-input',true).first();
41146     }
41147     
41148 });