roojs-bootstrap.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     this.openClass = Roo.bootstrap.version = 4 ? 'show' : 'open';
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(this.openClass);
2265         
2266         // reassign x when hitting right
2267         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2268             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2269         }
2270         
2271         // reassign y when hitting bottom
2272         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2273             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2274         }
2275         
2276         // but the list may align on trigger left or trigger top... should it be a properity?
2277         
2278         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2279             this.el.setXY(xy);
2280         }
2281         
2282         this.focus();
2283         this.fireEvent("show", this);
2284     },
2285     
2286     focus : function(){
2287         return;
2288         if(!this.hidden){
2289             this.doFocus.defer(50, this);
2290         }
2291     },
2292
2293     doFocus : function(){
2294         if(!this.hidden){
2295             this.focusEl.focus();
2296         }
2297     },
2298
2299     /**
2300      * Hides this menu and optionally all parent menus
2301      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2302      */
2303     hide : function(deep)
2304     {
2305         
2306         this.hideMenuItems();
2307         if(this.el && this.isVisible()){
2308             this.fireEvent("beforehide", this);
2309             if(this.activeItem){
2310                 this.activeItem.deactivate();
2311                 this.activeItem = null;
2312             }
2313             this.triggerEl.removeClass(this.openClass);;
2314             this.hidden = true;
2315             this.fireEvent("hide", this);
2316         }
2317         if(deep === true && this.parentMenu){
2318             this.parentMenu.hide(true);
2319         }
2320     },
2321     
2322     onTriggerClick : function(e)
2323     {
2324         Roo.log('trigger click');
2325         
2326         var target = e.getTarget();
2327         
2328         Roo.log(target.nodeName.toLowerCase());
2329         
2330         if(target.nodeName.toLowerCase() === 'i'){
2331             e.preventDefault();
2332         }
2333         
2334     },
2335     
2336     onTriggerPress  : function(e)
2337     {
2338         Roo.log('trigger press');
2339         //Roo.log(e.getTarget());
2340        // Roo.log(this.triggerEl.dom);
2341        
2342         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2343         var pel = Roo.get(e.getTarget());
2344         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2345             Roo.log('is treeview or dropdown?');
2346             return;
2347         }
2348         
2349         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2350             return;
2351         }
2352         
2353         if (this.isVisible()) {
2354             Roo.log('hide');
2355             this.hide();
2356         } else {
2357             Roo.log('show');
2358             this.show(this.triggerEl, false, false);
2359         }
2360         
2361         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2362             e.stopEvent();
2363         }
2364         
2365     },
2366        
2367     
2368     hideMenuItems : function()
2369     {
2370         Roo.log("hide Menu Items");
2371         if (!this.el) { 
2372             return;
2373         }
2374         //$(backdrop).remove()
2375         this.el.select('.' + this.openClass,true).each(function(aa) {
2376             
2377             aa.removeClass(this.openClass);
2378           //var parent = getParent($(this))
2379           //var relatedTarget = { relatedTarget: this }
2380           
2381            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2382           //if (e.isDefaultPrevented()) return
2383            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2384         });
2385     },
2386     addxtypeChild : function (tree, cntr) {
2387         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2388           
2389         this.menuitems.add(comp);
2390         return comp;
2391
2392     },
2393     getEl : function()
2394     {
2395         Roo.log(this.el);
2396         return this.el;
2397     },
2398     
2399     clear : function()
2400     {
2401         this.getEl().dom.innerHTML = '';
2402         this.menuitems.clear();
2403     }
2404 });
2405
2406  
2407  /*
2408  * - LGPL
2409  *
2410  * menu item
2411  * 
2412  */
2413
2414
2415 /**
2416  * @class Roo.bootstrap.MenuItem
2417  * @extends Roo.bootstrap.Component
2418  * Bootstrap MenuItem class
2419  * @cfg {String} html the menu label
2420  * @cfg {String} href the link
2421  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2422  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2423  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2424  * @cfg {String} fa favicon to show on left of menu item.
2425  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2426  * 
2427  * 
2428  * @constructor
2429  * Create a new MenuItem
2430  * @param {Object} config The config object
2431  */
2432
2433
2434 Roo.bootstrap.MenuItem = function(config){
2435     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2436     this.addEvents({
2437         // raw events
2438         /**
2439          * @event click
2440          * The raw click event for the entire grid.
2441          * @param {Roo.bootstrap.MenuItem} this
2442          * @param {Roo.EventObject} e
2443          */
2444         "click" : true
2445     });
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2449     
2450     href : false,
2451     html : false,
2452     preventDefault: false,
2453     isContainer : false,
2454     active : false,
2455     fa: false,
2456     
2457     getAutoCreate : function(){
2458         
2459         if(this.isContainer){
2460             return {
2461                 tag: 'li',
2462                 cls: 'dropdown-menu-item dropdown-item'
2463             };
2464         }
2465         var ctag = {
2466             tag: 'span',
2467             html: 'Link'
2468         };
2469         
2470         var anc = {
2471             tag : 'a',
2472             href : '#',
2473             cn : [  ]
2474         };
2475         
2476         if (this.fa !== false) {
2477             anc.cn.push({
2478                 tag : 'i',
2479                 cls : 'fa fa-' + this.fa
2480             });
2481         }
2482         
2483         anc.cn.push(ctag);
2484         
2485         
2486         var cfg= {
2487             tag: 'li',
2488             cls: 'dropdown-menu-item dropdown-item',
2489             cn: [ anc ]
2490         };
2491         if (this.parent().type == 'treeview') {
2492             cfg.cls = 'treeview-menu';
2493         }
2494         if (this.active) {
2495             cfg.cls += ' active';
2496         }
2497         
2498         
2499         
2500         anc.href = this.href || cfg.cn[0].href ;
2501         ctag.html = this.html || cfg.cn[0].html ;
2502         return cfg;
2503     },
2504     
2505     initEvents: function()
2506     {
2507         if (this.parent().type == 'treeview') {
2508             this.el.select('a').on('click', this.onClick, this);
2509         }
2510         
2511         if (this.menu) {
2512             this.menu.parentType = this.xtype;
2513             this.menu.triggerEl = this.el;
2514             this.menu = this.addxtype(Roo.apply({}, this.menu));
2515         }
2516         
2517     },
2518     onClick : function(e)
2519     {
2520         Roo.log('item on click ');
2521         
2522         if(this.preventDefault){
2523             e.preventDefault();
2524         }
2525         //this.parent().hideMenuItems();
2526         
2527         this.fireEvent('click', this, e);
2528     },
2529     getEl : function()
2530     {
2531         return this.el;
2532     } 
2533 });
2534
2535  
2536
2537  /*
2538  * - LGPL
2539  *
2540  * menu separator
2541  * 
2542  */
2543
2544
2545 /**
2546  * @class Roo.bootstrap.MenuSeparator
2547  * @extends Roo.bootstrap.Component
2548  * Bootstrap MenuSeparator class
2549  * 
2550  * @constructor
2551  * Create a new MenuItem
2552  * @param {Object} config The config object
2553  */
2554
2555
2556 Roo.bootstrap.MenuSeparator = function(config){
2557     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2558 };
2559
2560 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2561     
2562     getAutoCreate : function(){
2563         var cfg = {
2564             cls: 'divider',
2565             tag : 'li'
2566         };
2567         
2568         return cfg;
2569     }
2570    
2571 });
2572
2573  
2574
2575  
2576 /*
2577 * Licence: LGPL
2578 */
2579
2580 /**
2581  * @class Roo.bootstrap.Modal
2582  * @extends Roo.bootstrap.Component
2583  * Bootstrap Modal class
2584  * @cfg {String} title Title of dialog
2585  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2586  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2587  * @cfg {Boolean} specificTitle default false
2588  * @cfg {Array} buttons Array of buttons or standard button set..
2589  * @cfg {String} buttonPosition (left|right|center) default right
2590  * @cfg {Boolean} animate default true
2591  * @cfg {Boolean} allow_close default true
2592  * @cfg {Boolean} fitwindow default false
2593  * @cfg {String} size (sm|lg) default empty
2594  * @cfg {Number} max_width set the max width of modal
2595  *
2596  *
2597  * @constructor
2598  * Create a new Modal Dialog
2599  * @param {Object} config The config object
2600  */
2601
2602 Roo.bootstrap.Modal = function(config){
2603     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2604     this.addEvents({
2605         // raw events
2606         /**
2607          * @event btnclick
2608          * The raw btnclick event for the button
2609          * @param {Roo.EventObject} e
2610          */
2611         "btnclick" : true,
2612         /**
2613          * @event resize
2614          * Fire when dialog resize
2615          * @param {Roo.bootstrap.Modal} this
2616          * @param {Roo.EventObject} e
2617          */
2618         "resize" : true
2619     });
2620     this.buttons = this.buttons || [];
2621
2622     if (this.tmpl) {
2623         this.tmpl = Roo.factory(this.tmpl);
2624     }
2625
2626 };
2627
2628 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2629
2630     title : 'test dialog',
2631
2632     buttons : false,
2633
2634     // set on load...
2635
2636     html: false,
2637
2638     tmp: false,
2639
2640     specificTitle: false,
2641
2642     buttonPosition: 'right',
2643
2644     allow_close : true,
2645
2646     animate : true,
2647
2648     fitwindow: false,
2649     
2650      // private
2651     dialogEl: false,
2652     bodyEl:  false,
2653     footerEl:  false,
2654     titleEl:  false,
2655     closeEl:  false,
2656
2657     size: '',
2658     
2659     max_width: 0,
2660     
2661     max_height: 0,
2662     
2663     fit_content: false,
2664
2665     onRender : function(ct, position)
2666     {
2667         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2668
2669         if(!this.el){
2670             var cfg = Roo.apply({},  this.getAutoCreate());
2671             cfg.id = Roo.id();
2672             //if(!cfg.name){
2673             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2674             //}
2675             //if (!cfg.name.length) {
2676             //    delete cfg.name;
2677            // }
2678             if (this.cls) {
2679                 cfg.cls += ' ' + this.cls;
2680             }
2681             if (this.style) {
2682                 cfg.style = this.style;
2683             }
2684             this.el = Roo.get(document.body).createChild(cfg, position);
2685         }
2686         //var type = this.el.dom.type;
2687
2688
2689         if(this.tabIndex !== undefined){
2690             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2691         }
2692
2693         this.dialogEl = this.el.select('.modal-dialog',true).first();
2694         this.bodyEl = this.el.select('.modal-body',true).first();
2695         this.closeEl = this.el.select('.modal-header .close', true).first();
2696         this.headerEl = this.el.select('.modal-header',true).first();
2697         this.titleEl = this.el.select('.modal-title',true).first();
2698         this.footerEl = this.el.select('.modal-footer',true).first();
2699
2700         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2701         
2702         //this.el.addClass("x-dlg-modal");
2703
2704         if (this.buttons.length) {
2705             Roo.each(this.buttons, function(bb) {
2706                 var b = Roo.apply({}, bb);
2707                 b.xns = b.xns || Roo.bootstrap;
2708                 b.xtype = b.xtype || 'Button';
2709                 if (typeof(b.listeners) == 'undefined') {
2710                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2711                 }
2712
2713                 var btn = Roo.factory(b);
2714
2715                 btn.render(this.el.select('.modal-footer div').first());
2716
2717             },this);
2718         }
2719         // render the children.
2720         var nitems = [];
2721
2722         if(typeof(this.items) != 'undefined'){
2723             var items = this.items;
2724             delete this.items;
2725
2726             for(var i =0;i < items.length;i++) {
2727                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2728             }
2729         }
2730
2731         this.items = nitems;
2732
2733         // where are these used - they used to be body/close/footer
2734
2735
2736         this.initEvents();
2737         //this.el.addClass([this.fieldClass, this.cls]);
2738
2739     },
2740
2741     getAutoCreate : function()
2742     {
2743         var bdy = {
2744                 cls : 'modal-body',
2745                 html : this.html || ''
2746         };
2747
2748         var title = {
2749             tag: 'h4',
2750             cls : 'modal-title',
2751             html : this.title
2752         };
2753
2754         if(this.specificTitle){
2755             title = this.title;
2756
2757         };
2758
2759         var header = [];
2760         if (this.allow_close) {
2761             header.push({
2762                 tag: 'button',
2763                 cls : 'close',
2764                 html : '&times'
2765             });
2766         }
2767
2768         header.push(title);
2769
2770         var size = '';
2771
2772         if(this.size.length){
2773             size = 'modal-' + this.size;
2774         }
2775
2776         var modal = {
2777             cls: "modal",
2778              cn : [
2779                 {
2780                     cls: "modal-dialog " + size,
2781                     cn : [
2782                         {
2783                             cls : "modal-content",
2784                             cn : [
2785                                 {
2786                                     cls : 'modal-header',
2787                                     cn : header
2788                                 },
2789                                 bdy,
2790                                 {
2791                                     cls : 'modal-footer',
2792                                     cn : [
2793                                         {
2794                                             tag: 'div',
2795                                             cls: 'btn-' + this.buttonPosition
2796                                         }
2797                                     ]
2798
2799                                 }
2800
2801
2802                             ]
2803
2804                         }
2805                     ]
2806
2807                 }
2808             ]
2809         };
2810
2811         if(this.animate){
2812             modal.cls += ' fade';
2813         }
2814
2815         return modal;
2816
2817     },
2818     getChildContainer : function() {
2819
2820          return this.bodyEl;
2821
2822     },
2823     getButtonContainer : function() {
2824          return this.el.select('.modal-footer div',true).first();
2825
2826     },
2827     initEvents : function()
2828     {
2829         if (this.allow_close) {
2830             this.closeEl.on('click', this.hide, this);
2831         }
2832         Roo.EventManager.onWindowResize(this.resize, this, true);
2833
2834
2835     },
2836
2837     resize : function()
2838     {
2839         this.maskEl.setSize(
2840             Roo.lib.Dom.getViewWidth(true),
2841             Roo.lib.Dom.getViewHeight(true)
2842         );
2843         
2844         if (this.fitwindow) {
2845             this.setSize(
2846                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2847                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2848             );
2849             return;
2850         }
2851         
2852         if(this.max_width !== 0) {
2853             
2854             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2855             
2856             if(this.height) {
2857                 this.setSize(w, this.height);
2858                 return;
2859             }
2860             
2861             if(this.max_height) {
2862                 this.setSize(w,Math.min(
2863                     this.max_height,
2864                     Roo.lib.Dom.getViewportHeight(true) - 60
2865                 ));
2866                 
2867                 return;
2868             }
2869             
2870             if(!this.fit_content) {
2871                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2872                 return;
2873             }
2874             
2875             this.setSize(w, Math.min(
2876                 60 +
2877                 this.headerEl.getHeight() + 
2878                 this.footerEl.getHeight() + 
2879                 this.getChildHeight(this.bodyEl.dom.childNodes),
2880                 Roo.lib.Dom.getViewportHeight(true) - 60)
2881             );
2882         }
2883         
2884     },
2885
2886     setSize : function(w,h)
2887     {
2888         if (!w && !h) {
2889             return;
2890         }
2891         
2892         this.resizeTo(w,h);
2893     },
2894
2895     show : function() {
2896
2897         if (!this.rendered) {
2898             this.render();
2899         }
2900
2901         //this.el.setStyle('display', 'block');
2902         this.el.removeClass('hideing');        
2903         this.el.addClass('show');
2904  
2905         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2906             var _this = this;
2907             (function(){
2908                 this.el.addClass('in');
2909             }).defer(50, this);
2910         }else{
2911             this.el.addClass('in');
2912         }
2913
2914         // not sure how we can show data in here..
2915         //if (this.tmpl) {
2916         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2917         //}
2918
2919         Roo.get(document.body).addClass("x-body-masked");
2920         
2921         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2922         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2923         this.maskEl.addClass('show');
2924         
2925         this.resize();
2926         
2927         this.fireEvent('show', this);
2928
2929         // set zindex here - otherwise it appears to be ignored...
2930         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2931
2932         (function () {
2933             this.items.forEach( function(e) {
2934                 e.layout ? e.layout() : false;
2935
2936             });
2937         }).defer(100,this);
2938
2939     },
2940     hide : function()
2941     {
2942         if(this.fireEvent("beforehide", this) !== false){
2943             this.maskEl.removeClass('show');
2944             Roo.get(document.body).removeClass("x-body-masked");
2945             this.el.removeClass('in');
2946             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2947
2948             if(this.animate){ // why
2949                 this.el.addClass('hideing');
2950                 (function(){
2951                     if (!this.el.hasClass('hideing')) {
2952                         return; // it's been shown again...
2953                     }
2954                     this.el.removeClass('show');
2955                     this.el.removeClass('hideing');
2956                 }).defer(150,this);
2957                 
2958             }else{
2959                  this.el.removeClass('show');
2960             }
2961             this.fireEvent('hide', this);
2962         }
2963     },
2964     isVisible : function()
2965     {
2966         
2967         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2968         
2969     },
2970
2971     addButton : function(str, cb)
2972     {
2973
2974
2975         var b = Roo.apply({}, { html : str } );
2976         b.xns = b.xns || Roo.bootstrap;
2977         b.xtype = b.xtype || 'Button';
2978         if (typeof(b.listeners) == 'undefined') {
2979             b.listeners = { click : cb.createDelegate(this)  };
2980         }
2981
2982         var btn = Roo.factory(b);
2983
2984         btn.render(this.el.select('.modal-footer div').first());
2985
2986         return btn;
2987
2988     },
2989
2990     setDefaultButton : function(btn)
2991     {
2992         //this.el.select('.modal-footer').()
2993     },
2994     diff : false,
2995
2996     resizeTo: function(w,h)
2997     {
2998         // skip.. ?? why??
2999
3000         this.dialogEl.setWidth(w);
3001         if (this.diff === false) {
3002             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3003         }
3004
3005         this.bodyEl.setHeight(h - this.diff);
3006
3007         this.fireEvent('resize', this);
3008
3009     },
3010     setContentSize  : function(w, h)
3011     {
3012
3013     },
3014     onButtonClick: function(btn,e)
3015     {
3016         //Roo.log([a,b,c]);
3017         this.fireEvent('btnclick', btn.name, e);
3018     },
3019      /**
3020      * Set the title of the Dialog
3021      * @param {String} str new Title
3022      */
3023     setTitle: function(str) {
3024         this.titleEl.dom.innerHTML = str;
3025     },
3026     /**
3027      * Set the body of the Dialog
3028      * @param {String} str new Title
3029      */
3030     setBody: function(str) {
3031         this.bodyEl.dom.innerHTML = str;
3032     },
3033     /**
3034      * Set the body of the Dialog using the template
3035      * @param {Obj} data - apply this data to the template and replace the body contents.
3036      */
3037     applyBody: function(obj)
3038     {
3039         if (!this.tmpl) {
3040             Roo.log("Error - using apply Body without a template");
3041             //code
3042         }
3043         this.tmpl.overwrite(this.bodyEl, obj);
3044     },
3045     
3046     getChildHeight : function(child_nodes)
3047     {
3048         if(
3049             !child_nodes ||
3050             child_nodes.length == 0
3051         ) {
3052             return;
3053         }
3054         
3055         var child_height = 0;
3056         
3057         for(var i = 0; i < child_nodes.length; i++) {
3058             
3059             /*
3060             * for modal with tabs...
3061             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3062                 
3063                 var layout_childs = child_nodes[i].childNodes;
3064                 
3065                 for(var j = 0; j < layout_childs.length; j++) {
3066                     
3067                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3068                         
3069                         var layout_body_childs = layout_childs[j].childNodes;
3070                         
3071                         for(var k = 0; k < layout_body_childs.length; k++) {
3072                             
3073                             if(layout_body_childs[k].classList.contains('navbar')) {
3074                                 child_height += layout_body_childs[k].offsetHeight;
3075                                 continue;
3076                             }
3077                             
3078                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3079                                 
3080                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3081                                 
3082                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3083                                     
3084                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3085                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3086                                         continue;
3087                                     }
3088                                     
3089                                 }
3090                                 
3091                             }
3092                             
3093                         }
3094                     }
3095                 }
3096                 continue;
3097             }
3098             */
3099             
3100             child_height += child_nodes[i].offsetHeight;
3101             // Roo.log(child_nodes[i].offsetHeight);
3102         }
3103         
3104         return child_height;
3105     }
3106
3107 });
3108
3109
3110 Roo.apply(Roo.bootstrap.Modal,  {
3111     /**
3112          * Button config that displays a single OK button
3113          * @type Object
3114          */
3115         OK :  [{
3116             name : 'ok',
3117             weight : 'primary',
3118             html : 'OK'
3119         }],
3120         /**
3121          * Button config that displays Yes and No buttons
3122          * @type Object
3123          */
3124         YESNO : [
3125             {
3126                 name  : 'no',
3127                 html : 'No'
3128             },
3129             {
3130                 name  :'yes',
3131                 weight : 'primary',
3132                 html : 'Yes'
3133             }
3134         ],
3135
3136         /**
3137          * Button config that displays OK and Cancel buttons
3138          * @type Object
3139          */
3140         OKCANCEL : [
3141             {
3142                name : 'cancel',
3143                 html : 'Cancel'
3144             },
3145             {
3146                 name : 'ok',
3147                 weight : 'primary',
3148                 html : 'OK'
3149             }
3150         ],
3151         /**
3152          * Button config that displays Yes, No and Cancel buttons
3153          * @type Object
3154          */
3155         YESNOCANCEL : [
3156             {
3157                 name : 'yes',
3158                 weight : 'primary',
3159                 html : 'Yes'
3160             },
3161             {
3162                 name : 'no',
3163                 html : 'No'
3164             },
3165             {
3166                 name : 'cancel',
3167                 html : 'Cancel'
3168             }
3169         ],
3170         
3171         zIndex : 10001
3172 });
3173 /*
3174  * - LGPL
3175  *
3176  * messagebox - can be used as a replace
3177  * 
3178  */
3179 /**
3180  * @class Roo.MessageBox
3181  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3182  * Example usage:
3183  *<pre><code>
3184 // Basic alert:
3185 Roo.Msg.alert('Status', 'Changes saved successfully.');
3186
3187 // Prompt for user data:
3188 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3189     if (btn == 'ok'){
3190         // process text value...
3191     }
3192 });
3193
3194 // Show a dialog using config options:
3195 Roo.Msg.show({
3196    title:'Save Changes?',
3197    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3198    buttons: Roo.Msg.YESNOCANCEL,
3199    fn: processResult,
3200    animEl: 'elId'
3201 });
3202 </code></pre>
3203  * @singleton
3204  */
3205 Roo.bootstrap.MessageBox = function(){
3206     var dlg, opt, mask, waitTimer;
3207     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3208     var buttons, activeTextEl, bwidth;
3209
3210     
3211     // private
3212     var handleButton = function(button){
3213         dlg.hide();
3214         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3215     };
3216
3217     // private
3218     var handleHide = function(){
3219         if(opt && opt.cls){
3220             dlg.el.removeClass(opt.cls);
3221         }
3222         //if(waitTimer){
3223         //    Roo.TaskMgr.stop(waitTimer);
3224         //    waitTimer = null;
3225         //}
3226     };
3227
3228     // private
3229     var updateButtons = function(b){
3230         var width = 0;
3231         if(!b){
3232             buttons["ok"].hide();
3233             buttons["cancel"].hide();
3234             buttons["yes"].hide();
3235             buttons["no"].hide();
3236             //dlg.footer.dom.style.display = 'none';
3237             return width;
3238         }
3239         dlg.footerEl.dom.style.display = '';
3240         for(var k in buttons){
3241             if(typeof buttons[k] != "function"){
3242                 if(b[k]){
3243                     buttons[k].show();
3244                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3245                     width += buttons[k].el.getWidth()+15;
3246                 }else{
3247                     buttons[k].hide();
3248                 }
3249             }
3250         }
3251         return width;
3252     };
3253
3254     // private
3255     var handleEsc = function(d, k, e){
3256         if(opt && opt.closable !== false){
3257             dlg.hide();
3258         }
3259         if(e){
3260             e.stopEvent();
3261         }
3262     };
3263
3264     return {
3265         /**
3266          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3267          * @return {Roo.BasicDialog} The BasicDialog element
3268          */
3269         getDialog : function(){
3270            if(!dlg){
3271                 dlg = new Roo.bootstrap.Modal( {
3272                     //draggable: true,
3273                     //resizable:false,
3274                     //constraintoviewport:false,
3275                     //fixedcenter:true,
3276                     //collapsible : false,
3277                     //shim:true,
3278                     //modal: true,
3279                 //    width: 'auto',
3280                   //  height:100,
3281                     //buttonAlign:"center",
3282                     closeClick : function(){
3283                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3284                             handleButton("no");
3285                         }else{
3286                             handleButton("cancel");
3287                         }
3288                     }
3289                 });
3290                 dlg.render();
3291                 dlg.on("hide", handleHide);
3292                 mask = dlg.mask;
3293                 //dlg.addKeyListener(27, handleEsc);
3294                 buttons = {};
3295                 this.buttons = buttons;
3296                 var bt = this.buttonText;
3297                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3298                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3299                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3300                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3301                 //Roo.log(buttons);
3302                 bodyEl = dlg.bodyEl.createChild({
3303
3304                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3305                         '<textarea class="roo-mb-textarea"></textarea>' +
3306                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3307                 });
3308                 msgEl = bodyEl.dom.firstChild;
3309                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3310                 textboxEl.enableDisplayMode();
3311                 textboxEl.addKeyListener([10,13], function(){
3312                     if(dlg.isVisible() && opt && opt.buttons){
3313                         if(opt.buttons.ok){
3314                             handleButton("ok");
3315                         }else if(opt.buttons.yes){
3316                             handleButton("yes");
3317                         }
3318                     }
3319                 });
3320                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3321                 textareaEl.enableDisplayMode();
3322                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3323                 progressEl.enableDisplayMode();
3324                 
3325                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3326                 var pf = progressEl.dom.firstChild;
3327                 if (pf) {
3328                     pp = Roo.get(pf.firstChild);
3329                     pp.setHeight(pf.offsetHeight);
3330                 }
3331                 
3332             }
3333             return dlg;
3334         },
3335
3336         /**
3337          * Updates the message box body text
3338          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3339          * the XHTML-compliant non-breaking space character '&amp;#160;')
3340          * @return {Roo.MessageBox} This message box
3341          */
3342         updateText : function(text)
3343         {
3344             if(!dlg.isVisible() && !opt.width){
3345                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3346                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3347             }
3348             msgEl.innerHTML = text || '&#160;';
3349       
3350             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3351             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3352             var w = Math.max(
3353                     Math.min(opt.width || cw , this.maxWidth), 
3354                     Math.max(opt.minWidth || this.minWidth, bwidth)
3355             );
3356             if(opt.prompt){
3357                 activeTextEl.setWidth(w);
3358             }
3359             if(dlg.isVisible()){
3360                 dlg.fixedcenter = false;
3361             }
3362             // to big, make it scroll. = But as usual stupid IE does not support
3363             // !important..
3364             
3365             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3366                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3367                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3368             } else {
3369                 bodyEl.dom.style.height = '';
3370                 bodyEl.dom.style.overflowY = '';
3371             }
3372             if (cw > w) {
3373                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3374             } else {
3375                 bodyEl.dom.style.overflowX = '';
3376             }
3377             
3378             dlg.setContentSize(w, bodyEl.getHeight());
3379             if(dlg.isVisible()){
3380                 dlg.fixedcenter = true;
3381             }
3382             return this;
3383         },
3384
3385         /**
3386          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3387          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3388          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3389          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         updateProgress : function(value, text){
3393             if(text){
3394                 this.updateText(text);
3395             }
3396             
3397             if (pp) { // weird bug on my firefox - for some reason this is not defined
3398                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3399                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3400             }
3401             return this;
3402         },        
3403
3404         /**
3405          * Returns true if the message box is currently displayed
3406          * @return {Boolean} True if the message box is visible, else false
3407          */
3408         isVisible : function(){
3409             return dlg && dlg.isVisible();  
3410         },
3411
3412         /**
3413          * Hides the message box if it is displayed
3414          */
3415         hide : function(){
3416             if(this.isVisible()){
3417                 dlg.hide();
3418             }  
3419         },
3420
3421         /**
3422          * Displays a new message box, or reinitializes an existing message box, based on the config options
3423          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3424          * The following config object properties are supported:
3425          * <pre>
3426 Property    Type             Description
3427 ----------  ---------------  ------------------------------------------------------------------------------------
3428 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3429                                    closes (defaults to undefined)
3430 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3431                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3432 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3433                                    progress and wait dialogs will ignore this property and always hide the
3434                                    close button as they can only be closed programmatically.
3435 cls               String           A custom CSS class to apply to the message box element
3436 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3437                                    displayed (defaults to 75)
3438 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3439                                    function will be btn (the name of the button that was clicked, if applicable,
3440                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3441                                    Progress and wait dialogs will ignore this option since they do not respond to
3442                                    user actions and can only be closed programmatically, so any required function
3443                                    should be called by the same code after it closes the dialog.
3444 icon              String           A CSS class that provides a background image to be used as an icon for
3445                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3446 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3447 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3448 modal             Boolean          False to allow user interaction with the page while the message box is
3449                                    displayed (defaults to true)
3450 msg               String           A string that will replace the existing message box body text (defaults
3451                                    to the XHTML-compliant non-breaking space character '&#160;')
3452 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3453 progress          Boolean          True to display a progress bar (defaults to false)
3454 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3455 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3456 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3457 title             String           The title text
3458 value             String           The string value to set into the active textbox element if displayed
3459 wait              Boolean          True to display a progress bar (defaults to false)
3460 width             Number           The width of the dialog in pixels
3461 </pre>
3462          *
3463          * Example usage:
3464          * <pre><code>
3465 Roo.Msg.show({
3466    title: 'Address',
3467    msg: 'Please enter your address:',
3468    width: 300,
3469    buttons: Roo.MessageBox.OKCANCEL,
3470    multiline: true,
3471    fn: saveAddress,
3472    animEl: 'addAddressBtn'
3473 });
3474 </code></pre>
3475          * @param {Object} config Configuration options
3476          * @return {Roo.MessageBox} This message box
3477          */
3478         show : function(options)
3479         {
3480             
3481             // this causes nightmares if you show one dialog after another
3482             // especially on callbacks..
3483              
3484             if(this.isVisible()){
3485                 
3486                 this.hide();
3487                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3488                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3489                 Roo.log("New Dialog Message:" +  options.msg )
3490                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3491                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3492                 
3493             }
3494             var d = this.getDialog();
3495             opt = options;
3496             d.setTitle(opt.title || "&#160;");
3497             d.closeEl.setDisplayed(opt.closable !== false);
3498             activeTextEl = textboxEl;
3499             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3500             if(opt.prompt){
3501                 if(opt.multiline){
3502                     textboxEl.hide();
3503                     textareaEl.show();
3504                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3505                         opt.multiline : this.defaultTextHeight);
3506                     activeTextEl = textareaEl;
3507                 }else{
3508                     textboxEl.show();
3509                     textareaEl.hide();
3510                 }
3511             }else{
3512                 textboxEl.hide();
3513                 textareaEl.hide();
3514             }
3515             progressEl.setDisplayed(opt.progress === true);
3516             this.updateProgress(0);
3517             activeTextEl.dom.value = opt.value || "";
3518             if(opt.prompt){
3519                 dlg.setDefaultButton(activeTextEl);
3520             }else{
3521                 var bs = opt.buttons;
3522                 var db = null;
3523                 if(bs && bs.ok){
3524                     db = buttons["ok"];
3525                 }else if(bs && bs.yes){
3526                     db = buttons["yes"];
3527                 }
3528                 dlg.setDefaultButton(db);
3529             }
3530             bwidth = updateButtons(opt.buttons);
3531             this.updateText(opt.msg);
3532             if(opt.cls){
3533                 d.el.addClass(opt.cls);
3534             }
3535             d.proxyDrag = opt.proxyDrag === true;
3536             d.modal = opt.modal !== false;
3537             d.mask = opt.modal !== false ? mask : false;
3538             if(!d.isVisible()){
3539                 // force it to the end of the z-index stack so it gets a cursor in FF
3540                 document.body.appendChild(dlg.el.dom);
3541                 d.animateTarget = null;
3542                 d.show(options.animEl);
3543             }
3544             return this;
3545         },
3546
3547         /**
3548          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3549          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3550          * and closing the message box when the process is complete.
3551          * @param {String} title The title bar text
3552          * @param {String} msg The message box body text
3553          * @return {Roo.MessageBox} This message box
3554          */
3555         progress : function(title, msg){
3556             this.show({
3557                 title : title,
3558                 msg : msg,
3559                 buttons: false,
3560                 progress:true,
3561                 closable:false,
3562                 minWidth: this.minProgressWidth,
3563                 modal : true
3564             });
3565             return this;
3566         },
3567
3568         /**
3569          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3570          * If a callback function is passed it will be called after the user clicks the button, and the
3571          * id of the button that was clicked will be passed as the only parameter to the callback
3572          * (could also be the top-right close button).
3573          * @param {String} title The title bar text
3574          * @param {String} msg The message box body text
3575          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3576          * @param {Object} scope (optional) The scope of the callback function
3577          * @return {Roo.MessageBox} This message box
3578          */
3579         alert : function(title, msg, fn, scope)
3580         {
3581             this.show({
3582                 title : title,
3583                 msg : msg,
3584                 buttons: this.OK,
3585                 fn: fn,
3586                 closable : false,
3587                 scope : scope,
3588                 modal : true
3589             });
3590             return this;
3591         },
3592
3593         /**
3594          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3595          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3596          * You are responsible for closing the message box when the process is complete.
3597          * @param {String} msg The message box body text
3598          * @param {String} title (optional) The title bar text
3599          * @return {Roo.MessageBox} This message box
3600          */
3601         wait : function(msg, title){
3602             this.show({
3603                 title : title,
3604                 msg : msg,
3605                 buttons: false,
3606                 closable:false,
3607                 progress:true,
3608                 modal:true,
3609                 width:300,
3610                 wait:true
3611             });
3612             waitTimer = Roo.TaskMgr.start({
3613                 run: function(i){
3614                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3615                 },
3616                 interval: 1000
3617             });
3618             return this;
3619         },
3620
3621         /**
3622          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3623          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3624          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3625          * @param {String} title The title bar text
3626          * @param {String} msg The message box body text
3627          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3628          * @param {Object} scope (optional) The scope of the callback function
3629          * @return {Roo.MessageBox} This message box
3630          */
3631         confirm : function(title, msg, fn, scope){
3632             this.show({
3633                 title : title,
3634                 msg : msg,
3635                 buttons: this.YESNO,
3636                 fn: fn,
3637                 scope : scope,
3638                 modal : true
3639             });
3640             return this;
3641         },
3642
3643         /**
3644          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3645          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3646          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3647          * (could also be the top-right close button) and the text that was entered will be passed as the two
3648          * parameters to the callback.
3649          * @param {String} title The title bar text
3650          * @param {String} msg The message box body text
3651          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3652          * @param {Object} scope (optional) The scope of the callback function
3653          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3654          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         prompt : function(title, msg, fn, scope, multiline){
3658             this.show({
3659                 title : title,
3660                 msg : msg,
3661                 buttons: this.OKCANCEL,
3662                 fn: fn,
3663                 minWidth:250,
3664                 scope : scope,
3665                 prompt:true,
3666                 multiline: multiline,
3667                 modal : true
3668             });
3669             return this;
3670         },
3671
3672         /**
3673          * Button config that displays a single OK button
3674          * @type Object
3675          */
3676         OK : {ok:true},
3677         /**
3678          * Button config that displays Yes and No buttons
3679          * @type Object
3680          */
3681         YESNO : {yes:true, no:true},
3682         /**
3683          * Button config that displays OK and Cancel buttons
3684          * @type Object
3685          */
3686         OKCANCEL : {ok:true, cancel:true},
3687         /**
3688          * Button config that displays Yes, No and Cancel buttons
3689          * @type Object
3690          */
3691         YESNOCANCEL : {yes:true, no:true, cancel:true},
3692
3693         /**
3694          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3695          * @type Number
3696          */
3697         defaultTextHeight : 75,
3698         /**
3699          * The maximum width in pixels of the message box (defaults to 600)
3700          * @type Number
3701          */
3702         maxWidth : 600,
3703         /**
3704          * The minimum width in pixels of the message box (defaults to 100)
3705          * @type Number
3706          */
3707         minWidth : 100,
3708         /**
3709          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3710          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3711          * @type Number
3712          */
3713         minProgressWidth : 250,
3714         /**
3715          * An object containing the default button text strings that can be overriden for localized language support.
3716          * Supported properties are: ok, cancel, yes and no.
3717          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3718          * @type Object
3719          */
3720         buttonText : {
3721             ok : "OK",
3722             cancel : "Cancel",
3723             yes : "Yes",
3724             no : "No"
3725         }
3726     };
3727 }();
3728
3729 /**
3730  * Shorthand for {@link Roo.MessageBox}
3731  */
3732 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3733 Roo.Msg = Roo.Msg || Roo.MessageBox;
3734 /*
3735  * - LGPL
3736  *
3737  * navbar
3738  * 
3739  */
3740
3741 /**
3742  * @class Roo.bootstrap.Navbar
3743  * @extends Roo.bootstrap.Component
3744  * Bootstrap Navbar class
3745
3746  * @constructor
3747  * Create a new Navbar
3748  * @param {Object} config The config object
3749  */
3750
3751
3752 Roo.bootstrap.Navbar = function(config){
3753     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3754     this.addEvents({
3755         // raw events
3756         /**
3757          * @event beforetoggle
3758          * Fire before toggle the menu
3759          * @param {Roo.EventObject} e
3760          */
3761         "beforetoggle" : true
3762     });
3763 };
3764
3765 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3766     
3767     
3768    
3769     // private
3770     navItems : false,
3771     loadMask : false,
3772     
3773     
3774     getAutoCreate : function(){
3775         
3776         
3777         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3778         
3779     },
3780     
3781     initEvents :function ()
3782     {
3783         //Roo.log(this.el.select('.navbar-toggle',true));
3784         this.el.select('.navbar-toggle',true).on('click', function() {
3785             if(this.fireEvent('beforetoggle', this) !== false){
3786                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3787             }
3788             
3789         }, this);
3790         
3791         var mark = {
3792             tag: "div",
3793             cls:"x-dlg-mask"
3794         };
3795         
3796         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3797         
3798         var size = this.el.getSize();
3799         this.maskEl.setSize(size.width, size.height);
3800         this.maskEl.enableDisplayMode("block");
3801         this.maskEl.hide();
3802         
3803         if(this.loadMask){
3804             this.maskEl.show();
3805         }
3806     },
3807     
3808     
3809     getChildContainer : function()
3810     {
3811         if (this.el.select('.collapse').getCount()) {
3812             return this.el.select('.collapse',true).first();
3813         }
3814         
3815         return this.el;
3816     },
3817     
3818     mask : function()
3819     {
3820         this.maskEl.show();
3821     },
3822     
3823     unmask : function()
3824     {
3825         this.maskEl.hide();
3826     } 
3827     
3828     
3829     
3830     
3831 });
3832
3833
3834
3835  
3836
3837  /*
3838  * - LGPL
3839  *
3840  * navbar
3841  * 
3842  */
3843
3844 /**
3845  * @class Roo.bootstrap.NavSimplebar
3846  * @extends Roo.bootstrap.Navbar
3847  * Bootstrap Sidebar class
3848  *
3849  * @cfg {Boolean} inverse is inverted color
3850  * 
3851  * @cfg {String} type (nav | pills | tabs)
3852  * @cfg {Boolean} arrangement stacked | justified
3853  * @cfg {String} align (left | right) alignment
3854  * 
3855  * @cfg {Boolean} main (true|false) main nav bar? default false
3856  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3857  * 
3858  * @cfg {String} tag (header|footer|nav|div) default is nav 
3859
3860  * 
3861  * 
3862  * 
3863  * @constructor
3864  * Create a new Sidebar
3865  * @param {Object} config The config object
3866  */
3867
3868
3869 Roo.bootstrap.NavSimplebar = function(config){
3870     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3871 };
3872
3873 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3874     
3875     inverse: false,
3876     
3877     type: false,
3878     arrangement: '',
3879     align : false,
3880     
3881     
3882     
3883     main : false,
3884     
3885     
3886     tag : false,
3887     
3888     
3889     getAutoCreate : function(){
3890         
3891         
3892         var cfg = {
3893             tag : this.tag || 'div',
3894             cls : 'navbar'
3895         };
3896           
3897         
3898         cfg.cn = [
3899             {
3900                 cls: 'nav',
3901                 tag : 'ul'
3902             }
3903         ];
3904         
3905          
3906         this.type = this.type || 'nav';
3907         if (['tabs','pills'].indexOf(this.type)!==-1) {
3908             cfg.cn[0].cls += ' nav-' + this.type
3909         
3910         
3911         } else {
3912             if (this.type!=='nav') {
3913                 Roo.log('nav type must be nav/tabs/pills')
3914             }
3915             cfg.cn[0].cls += ' navbar-nav'
3916         }
3917         
3918         
3919         
3920         
3921         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3922             cfg.cn[0].cls += ' nav-' + this.arrangement;
3923         }
3924         
3925         
3926         if (this.align === 'right') {
3927             cfg.cn[0].cls += ' navbar-right';
3928         }
3929         
3930         if (this.inverse) {
3931             cfg.cls += ' navbar-inverse';
3932             
3933         }
3934         
3935         
3936         return cfg;
3937     
3938         
3939     }
3940     
3941     
3942     
3943 });
3944
3945
3946
3947  
3948
3949  
3950        /*
3951  * - LGPL
3952  *
3953  * navbar
3954  * navbar-fixed-top
3955  * navbar-expand-md  fixed-top 
3956  */
3957
3958 /**
3959  * @class Roo.bootstrap.NavHeaderbar
3960  * @extends Roo.bootstrap.NavSimplebar
3961  * Bootstrap Sidebar class
3962  *
3963  * @cfg {String} brand what is brand
3964  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3965  * @cfg {String} brand_href href of the brand
3966  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3967  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3968  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3969  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3970  * 
3971  * @constructor
3972  * Create a new Sidebar
3973  * @param {Object} config The config object
3974  */
3975
3976
3977 Roo.bootstrap.NavHeaderbar = function(config){
3978     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3979       
3980 };
3981
3982 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3983     
3984     position: '',
3985     brand: '',
3986     brand_href: false,
3987     srButton : true,
3988     autohide : false,
3989     desktopCenter : false,
3990    
3991     
3992     getAutoCreate : function(){
3993         
3994         var   cfg = {
3995             tag: this.nav || 'nav',
3996             cls: 'navbar navbar-expand-md',
3997             role: 'navigation',
3998             cn: []
3999         };
4000         
4001         var cn = cfg.cn;
4002         if (this.desktopCenter) {
4003             cn.push({cls : 'container', cn : []});
4004             cn = cn[0].cn;
4005         }
4006         
4007         if(this.srButton){
4008             cn.push({
4009                 tag: 'div',
4010                 cls: 'navbar-header',
4011                 cn: [
4012                     {
4013                         tag: 'button',
4014                         type: 'button',
4015                         cls: 'navbar-toggle navbar-toggler',
4016                         'data-toggle': 'collapse',
4017                         cn: [
4018                             {
4019                                 tag: 'span',
4020                                 cls: 'sr-only',
4021                                 html: 'Toggle navigation'
4022                             },
4023                             {
4024                                 tag: 'span',
4025                                 cls: 'icon-bar navbar-toggler-icon'
4026                             },
4027                             {
4028                                 tag: 'span',
4029                                 cls: 'icon-bar'
4030                             },
4031                             {
4032                                 tag: 'span',
4033                                 cls: 'icon-bar'
4034                             }
4035                         ]
4036                     }
4037                 ]
4038             });
4039         }
4040         
4041         cn.push({
4042             tag: 'div',
4043             cls: 'collapse navbar-collapse',
4044             cn : []
4045         });
4046         
4047         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4048         
4049         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4050             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4051             
4052             // tag can override this..
4053             
4054             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4055         }
4056         
4057         if (this.brand !== '') {
4058             cn[0].cn.push({
4059                 tag: 'a',
4060                 href: this.brand_href ? this.brand_href : '#',
4061                 cls: 'navbar-brand',
4062                 cn: [
4063                 this.brand
4064                 ]
4065             });
4066         }
4067         
4068         if(this.main){
4069             cfg.cls += ' main-nav';
4070         }
4071         
4072         
4073         return cfg;
4074
4075         
4076     },
4077     getHeaderChildContainer : function()
4078     {
4079         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4080             return this.el.select('.navbar-header',true).first();
4081         }
4082         
4083         return this.getChildContainer();
4084     },
4085     
4086     
4087     initEvents : function()
4088     {
4089         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4090         
4091         if (this.autohide) {
4092             
4093             var prevScroll = 0;
4094             var ft = this.el;
4095             
4096             Roo.get(document).on('scroll',function(e) {
4097                 var ns = Roo.get(document).getScroll().top;
4098                 var os = prevScroll;
4099                 prevScroll = ns;
4100                 
4101                 if(ns > os){
4102                     ft.removeClass('slideDown');
4103                     ft.addClass('slideUp');
4104                     return;
4105                 }
4106                 ft.removeClass('slideUp');
4107                 ft.addClass('slideDown');
4108                  
4109               
4110           },this);
4111         }
4112     }    
4113     
4114 });
4115
4116
4117
4118  
4119
4120  /*
4121  * - LGPL
4122  *
4123  * navbar
4124  * 
4125  */
4126
4127 /**
4128  * @class Roo.bootstrap.NavSidebar
4129  * @extends Roo.bootstrap.Navbar
4130  * Bootstrap Sidebar class
4131  * 
4132  * @constructor
4133  * Create a new Sidebar
4134  * @param {Object} config The config object
4135  */
4136
4137
4138 Roo.bootstrap.NavSidebar = function(config){
4139     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4140 };
4141
4142 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4143     
4144     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4145     
4146     getAutoCreate : function(){
4147         
4148         
4149         return  {
4150             tag: 'div',
4151             cls: 'sidebar sidebar-nav'
4152         };
4153     
4154         
4155     }
4156     
4157     
4158     
4159 });
4160
4161
4162
4163  
4164
4165  /*
4166  * - LGPL
4167  *
4168  * nav group
4169  * 
4170  */
4171
4172 /**
4173  * @class Roo.bootstrap.NavGroup
4174  * @extends Roo.bootstrap.Component
4175  * Bootstrap NavGroup class
4176  * @cfg {String} align (left|right)
4177  * @cfg {Boolean} inverse
4178  * @cfg {String} type (nav|pills|tab) default nav
4179  * @cfg {String} navId - reference Id for navbar.
4180
4181  * 
4182  * @constructor
4183  * Create a new nav group
4184  * @param {Object} config The config object
4185  */
4186
4187 Roo.bootstrap.NavGroup = function(config){
4188     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4189     this.navItems = [];
4190    
4191     Roo.bootstrap.NavGroup.register(this);
4192      this.addEvents({
4193         /**
4194              * @event changed
4195              * Fires when the active item changes
4196              * @param {Roo.bootstrap.NavGroup} this
4197              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4198              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4199          */
4200         'changed': true
4201      });
4202     
4203 };
4204
4205 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4206     
4207     align: '',
4208     inverse: false,
4209     form: false,
4210     type: 'nav',
4211     navId : '',
4212     // private
4213     
4214     navItems : false, 
4215     
4216     getAutoCreate : function()
4217     {
4218         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4219         
4220         cfg = {
4221             tag : 'ul',
4222             cls: 'nav' 
4223         };
4224         
4225         if (['tabs','pills'].indexOf(this.type)!==-1) {
4226             cfg.cls += ' nav-' + this.type
4227         } else {
4228             if (this.type!=='nav') {
4229                 Roo.log('nav type must be nav/tabs/pills')
4230             }
4231             cfg.cls += ' navbar-nav mr-auto'
4232         }
4233         
4234         if (this.parent() && this.parent().sidebar) {
4235             cfg = {
4236                 tag: 'ul',
4237                 cls: 'dashboard-menu sidebar-menu'
4238             };
4239             
4240             return cfg;
4241         }
4242         
4243         if (this.form === true) {
4244             cfg = {
4245                 tag: 'form',
4246                 cls: 'navbar-form'
4247             };
4248             
4249             if (this.align === 'right') {
4250                 cfg.cls += ' navbar-right';
4251             } else {
4252                 cfg.cls += ' navbar-left';
4253             }
4254         }
4255         
4256         if (this.align === 'right') {
4257             cfg.cls += ' navbar-right';
4258         }
4259         
4260         if (this.inverse) {
4261             cfg.cls += ' navbar-inverse';
4262             
4263         }
4264         
4265         
4266         return cfg;
4267     },
4268     /**
4269     * sets the active Navigation item
4270     * @param {Roo.bootstrap.NavItem} the new current navitem
4271     */
4272     setActiveItem : function(item)
4273     {
4274         var prev = false;
4275         Roo.each(this.navItems, function(v){
4276             if (v == item) {
4277                 return ;
4278             }
4279             if (v.isActive()) {
4280                 v.setActive(false, true);
4281                 prev = v;
4282                 
4283             }
4284             
4285         });
4286
4287         item.setActive(true, true);
4288         this.fireEvent('changed', this, item, prev);
4289         
4290         
4291     },
4292     /**
4293     * gets the active Navigation item
4294     * @return {Roo.bootstrap.NavItem} the current navitem
4295     */
4296     getActive : function()
4297     {
4298         
4299         var prev = false;
4300         Roo.each(this.navItems, function(v){
4301             
4302             if (v.isActive()) {
4303                 prev = v;
4304                 
4305             }
4306             
4307         });
4308         return prev;
4309     },
4310     
4311     indexOfNav : function()
4312     {
4313         
4314         var prev = false;
4315         Roo.each(this.navItems, function(v,i){
4316             
4317             if (v.isActive()) {
4318                 prev = i;
4319                 
4320             }
4321             
4322         });
4323         return prev;
4324     },
4325     /**
4326     * adds a Navigation item
4327     * @param {Roo.bootstrap.NavItem} the navitem to add
4328     */
4329     addItem : function(cfg)
4330     {
4331         var cn = new Roo.bootstrap.NavItem(cfg);
4332         this.register(cn);
4333         cn.parentId = this.id;
4334         cn.onRender(this.el, null);
4335         return cn;
4336     },
4337     /**
4338     * register a Navigation item
4339     * @param {Roo.bootstrap.NavItem} the navitem to add
4340     */
4341     register : function(item)
4342     {
4343         this.navItems.push( item);
4344         item.navId = this.navId;
4345     
4346     },
4347     
4348     /**
4349     * clear all the Navigation item
4350     */
4351    
4352     clearAll : function()
4353     {
4354         this.navItems = [];
4355         this.el.dom.innerHTML = '';
4356     },
4357     
4358     getNavItem: function(tabId)
4359     {
4360         var ret = false;
4361         Roo.each(this.navItems, function(e) {
4362             if (e.tabId == tabId) {
4363                ret =  e;
4364                return false;
4365             }
4366             return true;
4367             
4368         });
4369         return ret;
4370     },
4371     
4372     setActiveNext : function()
4373     {
4374         var i = this.indexOfNav(this.getActive());
4375         if (i > this.navItems.length) {
4376             return;
4377         }
4378         this.setActiveItem(this.navItems[i+1]);
4379     },
4380     setActivePrev : function()
4381     {
4382         var i = this.indexOfNav(this.getActive());
4383         if (i  < 1) {
4384             return;
4385         }
4386         this.setActiveItem(this.navItems[i-1]);
4387     },
4388     clearWasActive : function(except) {
4389         Roo.each(this.navItems, function(e) {
4390             if (e.tabId != except.tabId && e.was_active) {
4391                e.was_active = false;
4392                return false;
4393             }
4394             return true;
4395             
4396         });
4397     },
4398     getWasActive : function ()
4399     {
4400         var r = false;
4401         Roo.each(this.navItems, function(e) {
4402             if (e.was_active) {
4403                r = e;
4404                return false;
4405             }
4406             return true;
4407             
4408         });
4409         return r;
4410     }
4411     
4412     
4413 });
4414
4415  
4416 Roo.apply(Roo.bootstrap.NavGroup, {
4417     
4418     groups: {},
4419      /**
4420     * register a Navigation Group
4421     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4422     */
4423     register : function(navgrp)
4424     {
4425         this.groups[navgrp.navId] = navgrp;
4426         
4427     },
4428     /**
4429     * fetch a Navigation Group based on the navigation ID
4430     * @param {string} the navgroup to add
4431     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4432     */
4433     get: function(navId) {
4434         if (typeof(this.groups[navId]) == 'undefined') {
4435             return false;
4436             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4437         }
4438         return this.groups[navId] ;
4439     }
4440     
4441     
4442     
4443 });
4444
4445  /*
4446  * - LGPL
4447  *
4448  * row
4449  * 
4450  */
4451
4452 /**
4453  * @class Roo.bootstrap.NavItem
4454  * @extends Roo.bootstrap.Component
4455  * Bootstrap Navbar.NavItem class
4456  * @cfg {String} href  link to
4457  * @cfg {String} html content of button
4458  * @cfg {String} badge text inside badge
4459  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4460  * @cfg {String} glyphicon name of glyphicon
4461  * @cfg {String} icon name of font awesome icon
4462  * @cfg {Boolean} active Is item active
4463  * @cfg {Boolean} disabled Is item disabled
4464  
4465  * @cfg {Boolean} preventDefault (true | false) default false
4466  * @cfg {String} tabId the tab that this item activates.
4467  * @cfg {String} tagtype (a|span) render as a href or span?
4468  * @cfg {Boolean} animateRef (true|false) link to element default false  
4469   
4470  * @constructor
4471  * Create a new Navbar Item
4472  * @param {Object} config The config object
4473  */
4474 Roo.bootstrap.NavItem = function(config){
4475     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4476     this.addEvents({
4477         // raw events
4478         /**
4479          * @event click
4480          * The raw click event for the entire grid.
4481          * @param {Roo.EventObject} e
4482          */
4483         "click" : true,
4484          /**
4485             * @event changed
4486             * Fires when the active item active state changes
4487             * @param {Roo.bootstrap.NavItem} this
4488             * @param {boolean} state the new state
4489              
4490          */
4491         'changed': true,
4492         /**
4493             * @event scrollto
4494             * Fires when scroll to element
4495             * @param {Roo.bootstrap.NavItem} this
4496             * @param {Object} options
4497             * @param {Roo.EventObject} e
4498              
4499          */
4500         'scrollto': true
4501     });
4502    
4503 };
4504
4505 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4506     
4507     href: false,
4508     html: '',
4509     badge: '',
4510     icon: false,
4511     glyphicon: false,
4512     active: false,
4513     preventDefault : false,
4514     tabId : false,
4515     tagtype : 'a',
4516     disabled : false,
4517     animateRef : false,
4518     was_active : false,
4519     
4520     getAutoCreate : function(){
4521          
4522         var cfg = {
4523             tag: 'li',
4524             cls: 'nav-item'
4525             
4526         };
4527         
4528         if (this.active) {
4529             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4530         }
4531         if (this.disabled) {
4532             cfg.cls += ' disabled';
4533         }
4534         
4535         if (this.href || this.html || this.glyphicon || this.icon) {
4536             cfg.cn = [
4537                 {
4538                     tag: this.tagtype,
4539                     href : this.href || "#",
4540                     html: this.html || ''
4541                 }
4542             ];
4543             if (this.tagtype == 'a') {
4544                 cfg.cn[0].cls = 'nav-link';
4545             }
4546             if (this.icon) {
4547                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4548             }
4549
4550             if(this.glyphicon) {
4551                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4552             }
4553             
4554             if (this.menu) {
4555                 
4556                 cfg.cn[0].html += " <span class='caret'></span>";
4557              
4558             }
4559             
4560             if (this.badge !== '') {
4561                  
4562                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4563             }
4564         }
4565         
4566         
4567         
4568         return cfg;
4569     },
4570     initEvents: function() 
4571     {
4572         if (typeof (this.menu) != 'undefined') {
4573             this.menu.parentType = this.xtype;
4574             this.menu.triggerEl = this.el;
4575             this.menu = this.addxtype(Roo.apply({}, this.menu));
4576         }
4577         
4578         this.el.select('a',true).on('click', this.onClick, this);
4579         
4580         if(this.tagtype == 'span'){
4581             this.el.select('span',true).on('click', this.onClick, this);
4582         }
4583        
4584         // at this point parent should be available..
4585         this.parent().register(this);
4586     },
4587     
4588     onClick : function(e)
4589     {
4590         if (e.getTarget('.dropdown-menu-item')) {
4591             // did you click on a menu itemm.... - then don't trigger onclick..
4592             return;
4593         }
4594         
4595         if(
4596                 this.preventDefault || 
4597                 this.href == '#' 
4598         ){
4599             Roo.log("NavItem - prevent Default?");
4600             e.preventDefault();
4601         }
4602         
4603         if (this.disabled) {
4604             return;
4605         }
4606         
4607         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4608         if (tg && tg.transition) {
4609             Roo.log("waiting for the transitionend");
4610             return;
4611         }
4612         
4613         
4614         
4615         //Roo.log("fire event clicked");
4616         if(this.fireEvent('click', this, e) === false){
4617             return;
4618         };
4619         
4620         if(this.tagtype == 'span'){
4621             return;
4622         }
4623         
4624         //Roo.log(this.href);
4625         var ael = this.el.select('a',true).first();
4626         //Roo.log(ael);
4627         
4628         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4629             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4630             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4631                 return; // ignore... - it's a 'hash' to another page.
4632             }
4633             Roo.log("NavItem - prevent Default?");
4634             e.preventDefault();
4635             this.scrollToElement(e);
4636         }
4637         
4638         
4639         var p =  this.parent();
4640    
4641         if (['tabs','pills'].indexOf(p.type)!==-1) {
4642             if (typeof(p.setActiveItem) !== 'undefined') {
4643                 p.setActiveItem(this);
4644             }
4645         }
4646         
4647         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4648         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4649             // remove the collapsed menu expand...
4650             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4651         }
4652     },
4653     
4654     isActive: function () {
4655         return this.active
4656     },
4657     setActive : function(state, fire, is_was_active)
4658     {
4659         if (this.active && !state && this.navId) {
4660             this.was_active = true;
4661             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4662             if (nv) {
4663                 nv.clearWasActive(this);
4664             }
4665             
4666         }
4667         this.active = state;
4668         
4669         if (!state ) {
4670             this.el.removeClass('active');
4671         } else if (!this.el.hasClass('active')) {
4672             this.el.addClass('active');
4673         }
4674         if (fire) {
4675             this.fireEvent('changed', this, state);
4676         }
4677         
4678         // show a panel if it's registered and related..
4679         
4680         if (!this.navId || !this.tabId || !state || is_was_active) {
4681             return;
4682         }
4683         
4684         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4685         if (!tg) {
4686             return;
4687         }
4688         var pan = tg.getPanelByName(this.tabId);
4689         if (!pan) {
4690             return;
4691         }
4692         // if we can not flip to new panel - go back to old nav highlight..
4693         if (false == tg.showPanel(pan)) {
4694             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4695             if (nv) {
4696                 var onav = nv.getWasActive();
4697                 if (onav) {
4698                     onav.setActive(true, false, true);
4699                 }
4700             }
4701             
4702         }
4703         
4704         
4705         
4706     },
4707      // this should not be here...
4708     setDisabled : function(state)
4709     {
4710         this.disabled = state;
4711         if (!state ) {
4712             this.el.removeClass('disabled');
4713         } else if (!this.el.hasClass('disabled')) {
4714             this.el.addClass('disabled');
4715         }
4716         
4717     },
4718     
4719     /**
4720      * Fetch the element to display the tooltip on.
4721      * @return {Roo.Element} defaults to this.el
4722      */
4723     tooltipEl : function()
4724     {
4725         return this.el.select('' + this.tagtype + '', true).first();
4726     },
4727     
4728     scrollToElement : function(e)
4729     {
4730         var c = document.body;
4731         
4732         /*
4733          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4734          */
4735         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4736             c = document.documentElement;
4737         }
4738         
4739         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4740         
4741         if(!target){
4742             return;
4743         }
4744
4745         var o = target.calcOffsetsTo(c);
4746         
4747         var options = {
4748             target : target,
4749             value : o[1]
4750         };
4751         
4752         this.fireEvent('scrollto', this, options, e);
4753         
4754         Roo.get(c).scrollTo('top', options.value, true);
4755         
4756         return;
4757     }
4758 });
4759  
4760
4761  /*
4762  * - LGPL
4763  *
4764  * sidebar item
4765  *
4766  *  li
4767  *    <span> icon </span>
4768  *    <span> text </span>
4769  *    <span>badge </span>
4770  */
4771
4772 /**
4773  * @class Roo.bootstrap.NavSidebarItem
4774  * @extends Roo.bootstrap.NavItem
4775  * Bootstrap Navbar.NavSidebarItem class
4776  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4777  * {Boolean} open is the menu open
4778  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4779  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4780  * {String} buttonSize (sm|md|lg)the extra classes for the button
4781  * {Boolean} showArrow show arrow next to the text (default true)
4782  * @constructor
4783  * Create a new Navbar Button
4784  * @param {Object} config The config object
4785  */
4786 Roo.bootstrap.NavSidebarItem = function(config){
4787     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4788     this.addEvents({
4789         // raw events
4790         /**
4791          * @event click
4792          * The raw click event for the entire grid.
4793          * @param {Roo.EventObject} e
4794          */
4795         "click" : true,
4796          /**
4797             * @event changed
4798             * Fires when the active item active state changes
4799             * @param {Roo.bootstrap.NavSidebarItem} this
4800             * @param {boolean} state the new state
4801              
4802          */
4803         'changed': true
4804     });
4805    
4806 };
4807
4808 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4809     
4810     badgeWeight : 'default',
4811     
4812     open: false,
4813     
4814     buttonView : false,
4815     
4816     buttonWeight : 'default',
4817     
4818     buttonSize : 'md',
4819     
4820     showArrow : true,
4821     
4822     getAutoCreate : function(){
4823         
4824         
4825         var a = {
4826                 tag: 'a',
4827                 href : this.href || '#',
4828                 cls: '',
4829                 html : '',
4830                 cn : []
4831         };
4832         
4833         if(this.buttonView){
4834             a = {
4835                 tag: 'button',
4836                 href : this.href || '#',
4837                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4838                 html : this.html,
4839                 cn : []
4840             };
4841         }
4842         
4843         var cfg = {
4844             tag: 'li',
4845             cls: '',
4846             cn: [ a ]
4847         };
4848         
4849         if (this.active) {
4850             cfg.cls += ' active';
4851         }
4852         
4853         if (this.disabled) {
4854             cfg.cls += ' disabled';
4855         }
4856         if (this.open) {
4857             cfg.cls += ' open x-open';
4858         }
4859         // left icon..
4860         if (this.glyphicon || this.icon) {
4861             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4862             a.cn.push({ tag : 'i', cls : c }) ;
4863         }
4864         
4865         if(!this.buttonView){
4866             var span = {
4867                 tag: 'span',
4868                 html : this.html || ''
4869             };
4870
4871             a.cn.push(span);
4872             
4873         }
4874         
4875         if (this.badge !== '') {
4876             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4877         }
4878         
4879         if (this.menu) {
4880             
4881             if(this.showArrow){
4882                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4883             }
4884             
4885             a.cls += ' dropdown-toggle treeview' ;
4886         }
4887         
4888         return cfg;
4889     },
4890     
4891     initEvents : function()
4892     { 
4893         if (typeof (this.menu) != 'undefined') {
4894             this.menu.parentType = this.xtype;
4895             this.menu.triggerEl = this.el;
4896             this.menu = this.addxtype(Roo.apply({}, this.menu));
4897         }
4898         
4899         this.el.on('click', this.onClick, this);
4900         
4901         if(this.badge !== ''){
4902             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4903         }
4904         
4905     },
4906     
4907     onClick : function(e)
4908     {
4909         if(this.disabled){
4910             e.preventDefault();
4911             return;
4912         }
4913         
4914         if(this.preventDefault){
4915             e.preventDefault();
4916         }
4917         
4918         this.fireEvent('click', this);
4919     },
4920     
4921     disable : function()
4922     {
4923         this.setDisabled(true);
4924     },
4925     
4926     enable : function()
4927     {
4928         this.setDisabled(false);
4929     },
4930     
4931     setDisabled : function(state)
4932     {
4933         if(this.disabled == state){
4934             return;
4935         }
4936         
4937         this.disabled = state;
4938         
4939         if (state) {
4940             this.el.addClass('disabled');
4941             return;
4942         }
4943         
4944         this.el.removeClass('disabled');
4945         
4946         return;
4947     },
4948     
4949     setActive : function(state)
4950     {
4951         if(this.active == state){
4952             return;
4953         }
4954         
4955         this.active = state;
4956         
4957         if (state) {
4958             this.el.addClass('active');
4959             return;
4960         }
4961         
4962         this.el.removeClass('active');
4963         
4964         return;
4965     },
4966     
4967     isActive: function () 
4968     {
4969         return this.active;
4970     },
4971     
4972     setBadge : function(str)
4973     {
4974         if(!this.badgeEl){
4975             return;
4976         }
4977         
4978         this.badgeEl.dom.innerHTML = str;
4979     }
4980     
4981    
4982      
4983  
4984 });
4985  
4986
4987  /*
4988  * - LGPL
4989  *
4990  * row
4991  * 
4992  */
4993
4994 /**
4995  * @class Roo.bootstrap.Row
4996  * @extends Roo.bootstrap.Component
4997  * Bootstrap Row class (contains columns...)
4998  * 
4999  * @constructor
5000  * Create a new Row
5001  * @param {Object} config The config object
5002  */
5003
5004 Roo.bootstrap.Row = function(config){
5005     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5006 };
5007
5008 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5009     
5010     getAutoCreate : function(){
5011        return {
5012             cls: 'row clearfix'
5013        };
5014     }
5015     
5016     
5017 });
5018
5019  
5020
5021  /*
5022  * - LGPL
5023  *
5024  * element
5025  * 
5026  */
5027
5028 /**
5029  * @class Roo.bootstrap.Element
5030  * @extends Roo.bootstrap.Component
5031  * Bootstrap Element class
5032  * @cfg {String} html contents of the element
5033  * @cfg {String} tag tag of the element
5034  * @cfg {String} cls class of the element
5035  * @cfg {Boolean} preventDefault (true|false) default false
5036  * @cfg {Boolean} clickable (true|false) default false
5037  * 
5038  * @constructor
5039  * Create a new Element
5040  * @param {Object} config The config object
5041  */
5042
5043 Roo.bootstrap.Element = function(config){
5044     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5045     
5046     this.addEvents({
5047         // raw events
5048         /**
5049          * @event click
5050          * When a element is chick
5051          * @param {Roo.bootstrap.Element} this
5052          * @param {Roo.EventObject} e
5053          */
5054         "click" : true
5055     });
5056 };
5057
5058 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5059     
5060     tag: 'div',
5061     cls: '',
5062     html: '',
5063     preventDefault: false, 
5064     clickable: false,
5065     
5066     getAutoCreate : function(){
5067         
5068         var cfg = {
5069             tag: this.tag,
5070             // cls: this.cls, double assign in parent class Component.js :: onRender
5071             html: this.html
5072         };
5073         
5074         return cfg;
5075     },
5076     
5077     initEvents: function() 
5078     {
5079         Roo.bootstrap.Element.superclass.initEvents.call(this);
5080         
5081         if(this.clickable){
5082             this.el.on('click', this.onClick, this);
5083         }
5084         
5085     },
5086     
5087     onClick : function(e)
5088     {
5089         if(this.preventDefault){
5090             e.preventDefault();
5091         }
5092         
5093         this.fireEvent('click', this, e);
5094     },
5095     
5096     getValue : function()
5097     {
5098         return this.el.dom.innerHTML;
5099     },
5100     
5101     setValue : function(value)
5102     {
5103         this.el.dom.innerHTML = value;
5104     }
5105    
5106 });
5107
5108  
5109
5110  /*
5111  * - LGPL
5112  *
5113  * pagination
5114  * 
5115  */
5116
5117 /**
5118  * @class Roo.bootstrap.Pagination
5119  * @extends Roo.bootstrap.Component
5120  * Bootstrap Pagination class
5121  * @cfg {String} size xs | sm | md | lg
5122  * @cfg {Boolean} inverse false | true
5123  * 
5124  * @constructor
5125  * Create a new Pagination
5126  * @param {Object} config The config object
5127  */
5128
5129 Roo.bootstrap.Pagination = function(config){
5130     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5131 };
5132
5133 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5134     
5135     cls: false,
5136     size: false,
5137     inverse: false,
5138     
5139     getAutoCreate : function(){
5140         var cfg = {
5141             tag: 'ul',
5142                 cls: 'pagination'
5143         };
5144         if (this.inverse) {
5145             cfg.cls += ' inverse';
5146         }
5147         if (this.html) {
5148             cfg.html=this.html;
5149         }
5150         if (this.cls) {
5151             cfg.cls += " " + this.cls;
5152         }
5153         return cfg;
5154     }
5155    
5156 });
5157
5158  
5159
5160  /*
5161  * - LGPL
5162  *
5163  * Pagination item
5164  * 
5165  */
5166
5167
5168 /**
5169  * @class Roo.bootstrap.PaginationItem
5170  * @extends Roo.bootstrap.Component
5171  * Bootstrap PaginationItem class
5172  * @cfg {String} html text
5173  * @cfg {String} href the link
5174  * @cfg {Boolean} preventDefault (true | false) default true
5175  * @cfg {Boolean} active (true | false) default false
5176  * @cfg {Boolean} disabled default false
5177  * 
5178  * 
5179  * @constructor
5180  * Create a new PaginationItem
5181  * @param {Object} config The config object
5182  */
5183
5184
5185 Roo.bootstrap.PaginationItem = function(config){
5186     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5187     this.addEvents({
5188         // raw events
5189         /**
5190          * @event click
5191          * The raw click event for the entire grid.
5192          * @param {Roo.EventObject} e
5193          */
5194         "click" : true
5195     });
5196 };
5197
5198 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5199     
5200     href : false,
5201     html : false,
5202     preventDefault: true,
5203     active : false,
5204     cls : false,
5205     disabled: false,
5206     
5207     getAutoCreate : function(){
5208         var cfg= {
5209             tag: 'li',
5210             cn: [
5211                 {
5212                     tag : 'a',
5213                     href : this.href ? this.href : '#',
5214                     html : this.html ? this.html : ''
5215                 }
5216             ]
5217         };
5218         
5219         if(this.cls){
5220             cfg.cls = this.cls;
5221         }
5222         
5223         if(this.disabled){
5224             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5225         }
5226         
5227         if(this.active){
5228             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5229         }
5230         
5231         return cfg;
5232     },
5233     
5234     initEvents: function() {
5235         
5236         this.el.on('click', this.onClick, this);
5237         
5238     },
5239     onClick : function(e)
5240     {
5241         Roo.log('PaginationItem on click ');
5242         if(this.preventDefault){
5243             e.preventDefault();
5244         }
5245         
5246         if(this.disabled){
5247             return;
5248         }
5249         
5250         this.fireEvent('click', this, e);
5251     }
5252    
5253 });
5254
5255  
5256
5257  /*
5258  * - LGPL
5259  *
5260  * slider
5261  * 
5262  */
5263
5264
5265 /**
5266  * @class Roo.bootstrap.Slider
5267  * @extends Roo.bootstrap.Component
5268  * Bootstrap Slider class
5269  *    
5270  * @constructor
5271  * Create a new Slider
5272  * @param {Object} config The config object
5273  */
5274
5275 Roo.bootstrap.Slider = function(config){
5276     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5277 };
5278
5279 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5280     
5281     getAutoCreate : function(){
5282         
5283         var cfg = {
5284             tag: 'div',
5285             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5286             cn: [
5287                 {
5288                     tag: 'a',
5289                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5290                 }
5291             ]
5292         };
5293         
5294         return cfg;
5295     }
5296    
5297 });
5298
5299  /*
5300  * Based on:
5301  * Ext JS Library 1.1.1
5302  * Copyright(c) 2006-2007, Ext JS, LLC.
5303  *
5304  * Originally Released Under LGPL - original licence link has changed is not relivant.
5305  *
5306  * Fork - LGPL
5307  * <script type="text/javascript">
5308  */
5309  
5310
5311 /**
5312  * @class Roo.grid.ColumnModel
5313  * @extends Roo.util.Observable
5314  * This is the default implementation of a ColumnModel used by the Grid. It defines
5315  * the columns in the grid.
5316  * <br>Usage:<br>
5317  <pre><code>
5318  var colModel = new Roo.grid.ColumnModel([
5319         {header: "Ticker", width: 60, sortable: true, locked: true},
5320         {header: "Company Name", width: 150, sortable: true},
5321         {header: "Market Cap.", width: 100, sortable: true},
5322         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5323         {header: "Employees", width: 100, sortable: true, resizable: false}
5324  ]);
5325  </code></pre>
5326  * <p>
5327  
5328  * The config options listed for this class are options which may appear in each
5329  * individual column definition.
5330  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5331  * @constructor
5332  * @param {Object} config An Array of column config objects. See this class's
5333  * config objects for details.
5334 */
5335 Roo.grid.ColumnModel = function(config){
5336         /**
5337      * The config passed into the constructor
5338      */
5339     this.config = config;
5340     this.lookup = {};
5341
5342     // if no id, create one
5343     // if the column does not have a dataIndex mapping,
5344     // map it to the order it is in the config
5345     for(var i = 0, len = config.length; i < len; i++){
5346         var c = config[i];
5347         if(typeof c.dataIndex == "undefined"){
5348             c.dataIndex = i;
5349         }
5350         if(typeof c.renderer == "string"){
5351             c.renderer = Roo.util.Format[c.renderer];
5352         }
5353         if(typeof c.id == "undefined"){
5354             c.id = Roo.id();
5355         }
5356         if(c.editor && c.editor.xtype){
5357             c.editor  = Roo.factory(c.editor, Roo.grid);
5358         }
5359         if(c.editor && c.editor.isFormField){
5360             c.editor = new Roo.grid.GridEditor(c.editor);
5361         }
5362         this.lookup[c.id] = c;
5363     }
5364
5365     /**
5366      * The width of columns which have no width specified (defaults to 100)
5367      * @type Number
5368      */
5369     this.defaultWidth = 100;
5370
5371     /**
5372      * Default sortable of columns which have no sortable specified (defaults to false)
5373      * @type Boolean
5374      */
5375     this.defaultSortable = false;
5376
5377     this.addEvents({
5378         /**
5379              * @event widthchange
5380              * Fires when the width of a column changes.
5381              * @param {ColumnModel} this
5382              * @param {Number} columnIndex The column index
5383              * @param {Number} newWidth The new width
5384              */
5385             "widthchange": true,
5386         /**
5387              * @event headerchange
5388              * Fires when the text of a header changes.
5389              * @param {ColumnModel} this
5390              * @param {Number} columnIndex The column index
5391              * @param {Number} newText The new header text
5392              */
5393             "headerchange": true,
5394         /**
5395              * @event hiddenchange
5396              * Fires when a column is hidden or "unhidden".
5397              * @param {ColumnModel} this
5398              * @param {Number} columnIndex The column index
5399              * @param {Boolean} hidden true if hidden, false otherwise
5400              */
5401             "hiddenchange": true,
5402             /**
5403          * @event columnmoved
5404          * Fires when a column is moved.
5405          * @param {ColumnModel} this
5406          * @param {Number} oldIndex
5407          * @param {Number} newIndex
5408          */
5409         "columnmoved" : true,
5410         /**
5411          * @event columlockchange
5412          * Fires when a column's locked state is changed
5413          * @param {ColumnModel} this
5414          * @param {Number} colIndex
5415          * @param {Boolean} locked true if locked
5416          */
5417         "columnlockchange" : true
5418     });
5419     Roo.grid.ColumnModel.superclass.constructor.call(this);
5420 };
5421 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5422     /**
5423      * @cfg {String} header The header text to display in the Grid view.
5424      */
5425     /**
5426      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5427      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5428      * specified, the column's index is used as an index into the Record's data Array.
5429      */
5430     /**
5431      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5432      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5433      */
5434     /**
5435      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5436      * Defaults to the value of the {@link #defaultSortable} property.
5437      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5438      */
5439     /**
5440      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5441      */
5442     /**
5443      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5444      */
5445     /**
5446      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5447      */
5448     /**
5449      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5450      */
5451     /**
5452      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5453      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5454      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5455      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5456      */
5457        /**
5458      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5459      */
5460     /**
5461      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5462      */
5463     /**
5464      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5465      */
5466     /**
5467      * @cfg {String} cursor (Optional)
5468      */
5469     /**
5470      * @cfg {String} tooltip (Optional)
5471      */
5472     /**
5473      * @cfg {Number} xs (Optional)
5474      */
5475     /**
5476      * @cfg {Number} sm (Optional)
5477      */
5478     /**
5479      * @cfg {Number} md (Optional)
5480      */
5481     /**
5482      * @cfg {Number} lg (Optional)
5483      */
5484     /**
5485      * Returns the id of the column at the specified index.
5486      * @param {Number} index The column index
5487      * @return {String} the id
5488      */
5489     getColumnId : function(index){
5490         return this.config[index].id;
5491     },
5492
5493     /**
5494      * Returns the column for a specified id.
5495      * @param {String} id The column id
5496      * @return {Object} the column
5497      */
5498     getColumnById : function(id){
5499         return this.lookup[id];
5500     },
5501
5502     
5503     /**
5504      * Returns the column for a specified dataIndex.
5505      * @param {String} dataIndex The column dataIndex
5506      * @return {Object|Boolean} the column or false if not found
5507      */
5508     getColumnByDataIndex: function(dataIndex){
5509         var index = this.findColumnIndex(dataIndex);
5510         return index > -1 ? this.config[index] : false;
5511     },
5512     
5513     /**
5514      * Returns the index for a specified column id.
5515      * @param {String} id The column id
5516      * @return {Number} the index, or -1 if not found
5517      */
5518     getIndexById : function(id){
5519         for(var i = 0, len = this.config.length; i < len; i++){
5520             if(this.config[i].id == id){
5521                 return i;
5522             }
5523         }
5524         return -1;
5525     },
5526     
5527     /**
5528      * Returns the index for a specified column dataIndex.
5529      * @param {String} dataIndex The column dataIndex
5530      * @return {Number} the index, or -1 if not found
5531      */
5532     
5533     findColumnIndex : function(dataIndex){
5534         for(var i = 0, len = this.config.length; i < len; i++){
5535             if(this.config[i].dataIndex == dataIndex){
5536                 return i;
5537             }
5538         }
5539         return -1;
5540     },
5541     
5542     
5543     moveColumn : function(oldIndex, newIndex){
5544         var c = this.config[oldIndex];
5545         this.config.splice(oldIndex, 1);
5546         this.config.splice(newIndex, 0, c);
5547         this.dataMap = null;
5548         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5549     },
5550
5551     isLocked : function(colIndex){
5552         return this.config[colIndex].locked === true;
5553     },
5554
5555     setLocked : function(colIndex, value, suppressEvent){
5556         if(this.isLocked(colIndex) == value){
5557             return;
5558         }
5559         this.config[colIndex].locked = value;
5560         if(!suppressEvent){
5561             this.fireEvent("columnlockchange", this, colIndex, value);
5562         }
5563     },
5564
5565     getTotalLockedWidth : function(){
5566         var totalWidth = 0;
5567         for(var i = 0; i < this.config.length; i++){
5568             if(this.isLocked(i) && !this.isHidden(i)){
5569                 this.totalWidth += this.getColumnWidth(i);
5570             }
5571         }
5572         return totalWidth;
5573     },
5574
5575     getLockedCount : function(){
5576         for(var i = 0, len = this.config.length; i < len; i++){
5577             if(!this.isLocked(i)){
5578                 return i;
5579             }
5580         }
5581         
5582         return this.config.length;
5583     },
5584
5585     /**
5586      * Returns the number of columns.
5587      * @return {Number}
5588      */
5589     getColumnCount : function(visibleOnly){
5590         if(visibleOnly === true){
5591             var c = 0;
5592             for(var i = 0, len = this.config.length; i < len; i++){
5593                 if(!this.isHidden(i)){
5594                     c++;
5595                 }
5596             }
5597             return c;
5598         }
5599         return this.config.length;
5600     },
5601
5602     /**
5603      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5604      * @param {Function} fn
5605      * @param {Object} scope (optional)
5606      * @return {Array} result
5607      */
5608     getColumnsBy : function(fn, scope){
5609         var r = [];
5610         for(var i = 0, len = this.config.length; i < len; i++){
5611             var c = this.config[i];
5612             if(fn.call(scope||this, c, i) === true){
5613                 r[r.length] = c;
5614             }
5615         }
5616         return r;
5617     },
5618
5619     /**
5620      * Returns true if the specified column is sortable.
5621      * @param {Number} col The column index
5622      * @return {Boolean}
5623      */
5624     isSortable : function(col){
5625         if(typeof this.config[col].sortable == "undefined"){
5626             return this.defaultSortable;
5627         }
5628         return this.config[col].sortable;
5629     },
5630
5631     /**
5632      * Returns the rendering (formatting) function defined for the column.
5633      * @param {Number} col The column index.
5634      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5635      */
5636     getRenderer : function(col){
5637         if(!this.config[col].renderer){
5638             return Roo.grid.ColumnModel.defaultRenderer;
5639         }
5640         return this.config[col].renderer;
5641     },
5642
5643     /**
5644      * Sets the rendering (formatting) function for a column.
5645      * @param {Number} col The column index
5646      * @param {Function} fn The function to use to process the cell's raw data
5647      * to return HTML markup for the grid view. The render function is called with
5648      * the following parameters:<ul>
5649      * <li>Data value.</li>
5650      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5651      * <li>css A CSS style string to apply to the table cell.</li>
5652      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5653      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5654      * <li>Row index</li>
5655      * <li>Column index</li>
5656      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5657      */
5658     setRenderer : function(col, fn){
5659         this.config[col].renderer = fn;
5660     },
5661
5662     /**
5663      * Returns the width for the specified column.
5664      * @param {Number} col The column index
5665      * @return {Number}
5666      */
5667     getColumnWidth : function(col){
5668         return this.config[col].width * 1 || this.defaultWidth;
5669     },
5670
5671     /**
5672      * Sets the width for a column.
5673      * @param {Number} col The column index
5674      * @param {Number} width The new width
5675      */
5676     setColumnWidth : function(col, width, suppressEvent){
5677         this.config[col].width = width;
5678         this.totalWidth = null;
5679         if(!suppressEvent){
5680              this.fireEvent("widthchange", this, col, width);
5681         }
5682     },
5683
5684     /**
5685      * Returns the total width of all columns.
5686      * @param {Boolean} includeHidden True to include hidden column widths
5687      * @return {Number}
5688      */
5689     getTotalWidth : function(includeHidden){
5690         if(!this.totalWidth){
5691             this.totalWidth = 0;
5692             for(var i = 0, len = this.config.length; i < len; i++){
5693                 if(includeHidden || !this.isHidden(i)){
5694                     this.totalWidth += this.getColumnWidth(i);
5695                 }
5696             }
5697         }
5698         return this.totalWidth;
5699     },
5700
5701     /**
5702      * Returns the header for the specified column.
5703      * @param {Number} col The column index
5704      * @return {String}
5705      */
5706     getColumnHeader : function(col){
5707         return this.config[col].header;
5708     },
5709
5710     /**
5711      * Sets the header for a column.
5712      * @param {Number} col The column index
5713      * @param {String} header The new header
5714      */
5715     setColumnHeader : function(col, header){
5716         this.config[col].header = header;
5717         this.fireEvent("headerchange", this, col, header);
5718     },
5719
5720     /**
5721      * Returns the tooltip for the specified column.
5722      * @param {Number} col The column index
5723      * @return {String}
5724      */
5725     getColumnTooltip : function(col){
5726             return this.config[col].tooltip;
5727     },
5728     /**
5729      * Sets the tooltip for a column.
5730      * @param {Number} col The column index
5731      * @param {String} tooltip The new tooltip
5732      */
5733     setColumnTooltip : function(col, tooltip){
5734             this.config[col].tooltip = tooltip;
5735     },
5736
5737     /**
5738      * Returns the dataIndex for the specified column.
5739      * @param {Number} col The column index
5740      * @return {Number}
5741      */
5742     getDataIndex : function(col){
5743         return this.config[col].dataIndex;
5744     },
5745
5746     /**
5747      * Sets the dataIndex for a column.
5748      * @param {Number} col The column index
5749      * @param {Number} dataIndex The new dataIndex
5750      */
5751     setDataIndex : function(col, dataIndex){
5752         this.config[col].dataIndex = dataIndex;
5753     },
5754
5755     
5756     
5757     /**
5758      * Returns true if the cell is editable.
5759      * @param {Number} colIndex The column index
5760      * @param {Number} rowIndex The row index - this is nto actually used..?
5761      * @return {Boolean}
5762      */
5763     isCellEditable : function(colIndex, rowIndex){
5764         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5765     },
5766
5767     /**
5768      * Returns the editor defined for the cell/column.
5769      * return false or null to disable editing.
5770      * @param {Number} colIndex The column index
5771      * @param {Number} rowIndex The row index
5772      * @return {Object}
5773      */
5774     getCellEditor : function(colIndex, rowIndex){
5775         return this.config[colIndex].editor;
5776     },
5777
5778     /**
5779      * Sets if a column is editable.
5780      * @param {Number} col The column index
5781      * @param {Boolean} editable True if the column is editable
5782      */
5783     setEditable : function(col, editable){
5784         this.config[col].editable = editable;
5785     },
5786
5787
5788     /**
5789      * Returns true if the column is hidden.
5790      * @param {Number} colIndex The column index
5791      * @return {Boolean}
5792      */
5793     isHidden : function(colIndex){
5794         return this.config[colIndex].hidden;
5795     },
5796
5797
5798     /**
5799      * Returns true if the column width cannot be changed
5800      */
5801     isFixed : function(colIndex){
5802         return this.config[colIndex].fixed;
5803     },
5804
5805     /**
5806      * Returns true if the column can be resized
5807      * @return {Boolean}
5808      */
5809     isResizable : function(colIndex){
5810         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5811     },
5812     /**
5813      * Sets if a column is hidden.
5814      * @param {Number} colIndex The column index
5815      * @param {Boolean} hidden True if the column is hidden
5816      */
5817     setHidden : function(colIndex, hidden){
5818         this.config[colIndex].hidden = hidden;
5819         this.totalWidth = null;
5820         this.fireEvent("hiddenchange", this, colIndex, hidden);
5821     },
5822
5823     /**
5824      * Sets the editor for a column.
5825      * @param {Number} col The column index
5826      * @param {Object} editor The editor object
5827      */
5828     setEditor : function(col, editor){
5829         this.config[col].editor = editor;
5830     }
5831 });
5832
5833 Roo.grid.ColumnModel.defaultRenderer = function(value)
5834 {
5835     if(typeof value == "object") {
5836         return value;
5837     }
5838         if(typeof value == "string" && value.length < 1){
5839             return "&#160;";
5840         }
5841     
5842         return String.format("{0}", value);
5843 };
5844
5845 // Alias for backwards compatibility
5846 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5847 /*
5848  * Based on:
5849  * Ext JS Library 1.1.1
5850  * Copyright(c) 2006-2007, Ext JS, LLC.
5851  *
5852  * Originally Released Under LGPL - original licence link has changed is not relivant.
5853  *
5854  * Fork - LGPL
5855  * <script type="text/javascript">
5856  */
5857  
5858 /**
5859  * @class Roo.LoadMask
5860  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5861  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5862  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5863  * element's UpdateManager load indicator and will be destroyed after the initial load.
5864  * @constructor
5865  * Create a new LoadMask
5866  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5867  * @param {Object} config The config object
5868  */
5869 Roo.LoadMask = function(el, config){
5870     this.el = Roo.get(el);
5871     Roo.apply(this, config);
5872     if(this.store){
5873         this.store.on('beforeload', this.onBeforeLoad, this);
5874         this.store.on('load', this.onLoad, this);
5875         this.store.on('loadexception', this.onLoadException, this);
5876         this.removeMask = false;
5877     }else{
5878         var um = this.el.getUpdateManager();
5879         um.showLoadIndicator = false; // disable the default indicator
5880         um.on('beforeupdate', this.onBeforeLoad, this);
5881         um.on('update', this.onLoad, this);
5882         um.on('failure', this.onLoad, this);
5883         this.removeMask = true;
5884     }
5885 };
5886
5887 Roo.LoadMask.prototype = {
5888     /**
5889      * @cfg {Boolean} removeMask
5890      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5891      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5892      */
5893     /**
5894      * @cfg {String} msg
5895      * The text to display in a centered loading message box (defaults to 'Loading...')
5896      */
5897     msg : 'Loading...',
5898     /**
5899      * @cfg {String} msgCls
5900      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5901      */
5902     msgCls : 'x-mask-loading',
5903
5904     /**
5905      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5906      * @type Boolean
5907      */
5908     disabled: false,
5909
5910     /**
5911      * Disables the mask to prevent it from being displayed
5912      */
5913     disable : function(){
5914        this.disabled = true;
5915     },
5916
5917     /**
5918      * Enables the mask so that it can be displayed
5919      */
5920     enable : function(){
5921         this.disabled = false;
5922     },
5923     
5924     onLoadException : function()
5925     {
5926         Roo.log(arguments);
5927         
5928         if (typeof(arguments[3]) != 'undefined') {
5929             Roo.MessageBox.alert("Error loading",arguments[3]);
5930         } 
5931         /*
5932         try {
5933             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5934                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5935             }   
5936         } catch(e) {
5937             
5938         }
5939         */
5940     
5941         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5942     },
5943     // private
5944     onLoad : function()
5945     {
5946         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5947     },
5948
5949     // private
5950     onBeforeLoad : function(){
5951         if(!this.disabled){
5952             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5953         }
5954     },
5955
5956     // private
5957     destroy : function(){
5958         if(this.store){
5959             this.store.un('beforeload', this.onBeforeLoad, this);
5960             this.store.un('load', this.onLoad, this);
5961             this.store.un('loadexception', this.onLoadException, this);
5962         }else{
5963             var um = this.el.getUpdateManager();
5964             um.un('beforeupdate', this.onBeforeLoad, this);
5965             um.un('update', this.onLoad, this);
5966             um.un('failure', this.onLoad, this);
5967         }
5968     }
5969 };/*
5970  * - LGPL
5971  *
5972  * table
5973  * 
5974  */
5975
5976 /**
5977  * @class Roo.bootstrap.Table
5978  * @extends Roo.bootstrap.Component
5979  * Bootstrap Table class
5980  * @cfg {String} cls table class
5981  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5982  * @cfg {String} bgcolor Specifies the background color for a table
5983  * @cfg {Number} border Specifies whether the table cells should have borders or not
5984  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5985  * @cfg {Number} cellspacing Specifies the space between cells
5986  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5987  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5988  * @cfg {String} sortable Specifies that the table should be sortable
5989  * @cfg {String} summary Specifies a summary of the content of a table
5990  * @cfg {Number} width Specifies the width of a table
5991  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5992  * 
5993  * @cfg {boolean} striped Should the rows be alternative striped
5994  * @cfg {boolean} bordered Add borders to the table
5995  * @cfg {boolean} hover Add hover highlighting
5996  * @cfg {boolean} condensed Format condensed
5997  * @cfg {boolean} responsive Format condensed
5998  * @cfg {Boolean} loadMask (true|false) default false
5999  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6000  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6001  * @cfg {Boolean} rowSelection (true|false) default false
6002  * @cfg {Boolean} cellSelection (true|false) default false
6003  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6004  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6005  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6006  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6007  
6008  * 
6009  * @constructor
6010  * Create a new Table
6011  * @param {Object} config The config object
6012  */
6013
6014 Roo.bootstrap.Table = function(config){
6015     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6016     
6017   
6018     
6019     // BC...
6020     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6021     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6022     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6023     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6024     
6025     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6026     if (this.sm) {
6027         this.sm.grid = this;
6028         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6029         this.sm = this.selModel;
6030         this.sm.xmodule = this.xmodule || false;
6031     }
6032     
6033     if (this.cm && typeof(this.cm.config) == 'undefined') {
6034         this.colModel = new Roo.grid.ColumnModel(this.cm);
6035         this.cm = this.colModel;
6036         this.cm.xmodule = this.xmodule || false;
6037     }
6038     if (this.store) {
6039         this.store= Roo.factory(this.store, Roo.data);
6040         this.ds = this.store;
6041         this.ds.xmodule = this.xmodule || false;
6042          
6043     }
6044     if (this.footer && this.store) {
6045         this.footer.dataSource = this.ds;
6046         this.footer = Roo.factory(this.footer);
6047     }
6048     
6049     /** @private */
6050     this.addEvents({
6051         /**
6052          * @event cellclick
6053          * Fires when a cell is clicked
6054          * @param {Roo.bootstrap.Table} this
6055          * @param {Roo.Element} el
6056          * @param {Number} rowIndex
6057          * @param {Number} columnIndex
6058          * @param {Roo.EventObject} e
6059          */
6060         "cellclick" : true,
6061         /**
6062          * @event celldblclick
6063          * Fires when a cell is double clicked
6064          * @param {Roo.bootstrap.Table} this
6065          * @param {Roo.Element} el
6066          * @param {Number} rowIndex
6067          * @param {Number} columnIndex
6068          * @param {Roo.EventObject} e
6069          */
6070         "celldblclick" : true,
6071         /**
6072          * @event rowclick
6073          * Fires when a row is clicked
6074          * @param {Roo.bootstrap.Table} this
6075          * @param {Roo.Element} el
6076          * @param {Number} rowIndex
6077          * @param {Roo.EventObject} e
6078          */
6079         "rowclick" : true,
6080         /**
6081          * @event rowdblclick
6082          * Fires when a row is double clicked
6083          * @param {Roo.bootstrap.Table} this
6084          * @param {Roo.Element} el
6085          * @param {Number} rowIndex
6086          * @param {Roo.EventObject} e
6087          */
6088         "rowdblclick" : true,
6089         /**
6090          * @event mouseover
6091          * Fires when a mouseover occur
6092          * @param {Roo.bootstrap.Table} this
6093          * @param {Roo.Element} el
6094          * @param {Number} rowIndex
6095          * @param {Number} columnIndex
6096          * @param {Roo.EventObject} e
6097          */
6098         "mouseover" : true,
6099         /**
6100          * @event mouseout
6101          * Fires when a mouseout occur
6102          * @param {Roo.bootstrap.Table} this
6103          * @param {Roo.Element} el
6104          * @param {Number} rowIndex
6105          * @param {Number} columnIndex
6106          * @param {Roo.EventObject} e
6107          */
6108         "mouseout" : true,
6109         /**
6110          * @event rowclass
6111          * Fires when a row is rendered, so you can change add a style to it.
6112          * @param {Roo.bootstrap.Table} this
6113          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6114          */
6115         'rowclass' : true,
6116           /**
6117          * @event rowsrendered
6118          * Fires when all the  rows have been rendered
6119          * @param {Roo.bootstrap.Table} this
6120          */
6121         'rowsrendered' : true,
6122         /**
6123          * @event contextmenu
6124          * The raw contextmenu event for the entire grid.
6125          * @param {Roo.EventObject} e
6126          */
6127         "contextmenu" : true,
6128         /**
6129          * @event rowcontextmenu
6130          * Fires when a row is right clicked
6131          * @param {Roo.bootstrap.Table} this
6132          * @param {Number} rowIndex
6133          * @param {Roo.EventObject} e
6134          */
6135         "rowcontextmenu" : true,
6136         /**
6137          * @event cellcontextmenu
6138          * Fires when a cell is right clicked
6139          * @param {Roo.bootstrap.Table} this
6140          * @param {Number} rowIndex
6141          * @param {Number} cellIndex
6142          * @param {Roo.EventObject} e
6143          */
6144          "cellcontextmenu" : true,
6145          /**
6146          * @event headercontextmenu
6147          * Fires when a header is right clicked
6148          * @param {Roo.bootstrap.Table} this
6149          * @param {Number} columnIndex
6150          * @param {Roo.EventObject} e
6151          */
6152         "headercontextmenu" : true
6153     });
6154 };
6155
6156 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6157     
6158     cls: false,
6159     align: false,
6160     bgcolor: false,
6161     border: false,
6162     cellpadding: false,
6163     cellspacing: false,
6164     frame: false,
6165     rules: false,
6166     sortable: false,
6167     summary: false,
6168     width: false,
6169     striped : false,
6170     scrollBody : false,
6171     bordered: false,
6172     hover:  false,
6173     condensed : false,
6174     responsive : false,
6175     sm : false,
6176     cm : false,
6177     store : false,
6178     loadMask : false,
6179     footerShow : true,
6180     headerShow : true,
6181   
6182     rowSelection : false,
6183     cellSelection : false,
6184     layout : false,
6185     
6186     // Roo.Element - the tbody
6187     mainBody: false,
6188     // Roo.Element - thead element
6189     mainHead: false,
6190     
6191     container: false, // used by gridpanel...
6192     
6193     lazyLoad : false,
6194     
6195     CSS : Roo.util.CSS,
6196     
6197     auto_hide_footer : false,
6198     
6199     getAutoCreate : function()
6200     {
6201         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6202         
6203         cfg = {
6204             tag: 'table',
6205             cls : 'table',
6206             cn : []
6207         };
6208         if (this.scrollBody) {
6209             cfg.cls += ' table-body-fixed';
6210         }    
6211         if (this.striped) {
6212             cfg.cls += ' table-striped';
6213         }
6214         
6215         if (this.hover) {
6216             cfg.cls += ' table-hover';
6217         }
6218         if (this.bordered) {
6219             cfg.cls += ' table-bordered';
6220         }
6221         if (this.condensed) {
6222             cfg.cls += ' table-condensed';
6223         }
6224         if (this.responsive) {
6225             cfg.cls += ' table-responsive';
6226         }
6227         
6228         if (this.cls) {
6229             cfg.cls+=  ' ' +this.cls;
6230         }
6231         
6232         // this lot should be simplifed...
6233         var _t = this;
6234         var cp = [
6235             'align',
6236             'bgcolor',
6237             'border',
6238             'cellpadding',
6239             'cellspacing',
6240             'frame',
6241             'rules',
6242             'sortable',
6243             'summary',
6244             'width'
6245         ].forEach(function(k) {
6246             if (_t[k]) {
6247                 cfg[k] = _t[k];
6248             }
6249         });
6250         
6251         
6252         if (this.layout) {
6253             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6254         }
6255         
6256         if(this.store || this.cm){
6257             if(this.headerShow){
6258                 cfg.cn.push(this.renderHeader());
6259             }
6260             
6261             cfg.cn.push(this.renderBody());
6262             
6263             if(this.footerShow){
6264                 cfg.cn.push(this.renderFooter());
6265             }
6266             // where does this come from?
6267             //cfg.cls+=  ' TableGrid';
6268         }
6269         
6270         return { cn : [ cfg ] };
6271     },
6272     
6273     initEvents : function()
6274     {   
6275         if(!this.store || !this.cm){
6276             return;
6277         }
6278         if (this.selModel) {
6279             this.selModel.initEvents();
6280         }
6281         
6282         
6283         //Roo.log('initEvents with ds!!!!');
6284         
6285         this.mainBody = this.el.select('tbody', true).first();
6286         this.mainHead = this.el.select('thead', true).first();
6287         this.mainFoot = this.el.select('tfoot', true).first();
6288         
6289         
6290         
6291         var _this = this;
6292         
6293         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6294             e.on('click', _this.sort, _this);
6295         });
6296         
6297         this.mainBody.on("click", this.onClick, this);
6298         this.mainBody.on("dblclick", this.onDblClick, this);
6299         
6300         // why is this done????? = it breaks dialogs??
6301         //this.parent().el.setStyle('position', 'relative');
6302         
6303         
6304         if (this.footer) {
6305             this.footer.parentId = this.id;
6306             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6307             
6308             if(this.lazyLoad){
6309                 this.el.select('tfoot tr td').first().addClass('hide');
6310             }
6311         } 
6312         
6313         if(this.loadMask) {
6314             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6315         }
6316         
6317         this.store.on('load', this.onLoad, this);
6318         this.store.on('beforeload', this.onBeforeLoad, this);
6319         this.store.on('update', this.onUpdate, this);
6320         this.store.on('add', this.onAdd, this);
6321         this.store.on("clear", this.clear, this);
6322         
6323         this.el.on("contextmenu", this.onContextMenu, this);
6324         
6325         this.mainBody.on('scroll', this.onBodyScroll, this);
6326         
6327         this.cm.on("headerchange", this.onHeaderChange, this);
6328         
6329         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6330         
6331     },
6332     
6333     onContextMenu : function(e, t)
6334     {
6335         this.processEvent("contextmenu", e);
6336     },
6337     
6338     processEvent : function(name, e)
6339     {
6340         if (name != 'touchstart' ) {
6341             this.fireEvent(name, e);    
6342         }
6343         
6344         var t = e.getTarget();
6345         
6346         var cell = Roo.get(t);
6347         
6348         if(!cell){
6349             return;
6350         }
6351         
6352         if(cell.findParent('tfoot', false, true)){
6353             return;
6354         }
6355         
6356         if(cell.findParent('thead', false, true)){
6357             
6358             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6359                 cell = Roo.get(t).findParent('th', false, true);
6360                 if (!cell) {
6361                     Roo.log("failed to find th in thead?");
6362                     Roo.log(e.getTarget());
6363                     return;
6364                 }
6365             }
6366             
6367             var cellIndex = cell.dom.cellIndex;
6368             
6369             var ename = name == 'touchstart' ? 'click' : name;
6370             this.fireEvent("header" + ename, this, cellIndex, e);
6371             
6372             return;
6373         }
6374         
6375         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6376             cell = Roo.get(t).findParent('td', false, true);
6377             if (!cell) {
6378                 Roo.log("failed to find th in tbody?");
6379                 Roo.log(e.getTarget());
6380                 return;
6381             }
6382         }
6383         
6384         var row = cell.findParent('tr', false, true);
6385         var cellIndex = cell.dom.cellIndex;
6386         var rowIndex = row.dom.rowIndex - 1;
6387         
6388         if(row !== false){
6389             
6390             this.fireEvent("row" + name, this, rowIndex, e);
6391             
6392             if(cell !== false){
6393             
6394                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6395             }
6396         }
6397         
6398     },
6399     
6400     onMouseover : function(e, el)
6401     {
6402         var cell = Roo.get(el);
6403         
6404         if(!cell){
6405             return;
6406         }
6407         
6408         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6409             cell = cell.findParent('td', false, true);
6410         }
6411         
6412         var row = cell.findParent('tr', false, true);
6413         var cellIndex = cell.dom.cellIndex;
6414         var rowIndex = row.dom.rowIndex - 1; // start from 0
6415         
6416         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6417         
6418     },
6419     
6420     onMouseout : function(e, el)
6421     {
6422         var cell = Roo.get(el);
6423         
6424         if(!cell){
6425             return;
6426         }
6427         
6428         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6429             cell = cell.findParent('td', false, true);
6430         }
6431         
6432         var row = cell.findParent('tr', false, true);
6433         var cellIndex = cell.dom.cellIndex;
6434         var rowIndex = row.dom.rowIndex - 1; // start from 0
6435         
6436         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6437         
6438     },
6439     
6440     onClick : function(e, el)
6441     {
6442         var cell = Roo.get(el);
6443         
6444         if(!cell || (!this.cellSelection && !this.rowSelection)){
6445             return;
6446         }
6447         
6448         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6449             cell = cell.findParent('td', false, true);
6450         }
6451         
6452         if(!cell || typeof(cell) == 'undefined'){
6453             return;
6454         }
6455         
6456         var row = cell.findParent('tr', false, true);
6457         
6458         if(!row || typeof(row) == 'undefined'){
6459             return;
6460         }
6461         
6462         var cellIndex = cell.dom.cellIndex;
6463         var rowIndex = this.getRowIndex(row);
6464         
6465         // why??? - should these not be based on SelectionModel?
6466         if(this.cellSelection){
6467             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6468         }
6469         
6470         if(this.rowSelection){
6471             this.fireEvent('rowclick', this, row, rowIndex, e);
6472         }
6473         
6474         
6475     },
6476         
6477     onDblClick : function(e,el)
6478     {
6479         var cell = Roo.get(el);
6480         
6481         if(!cell || (!this.cellSelection && !this.rowSelection)){
6482             return;
6483         }
6484         
6485         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6486             cell = cell.findParent('td', false, true);
6487         }
6488         
6489         if(!cell || typeof(cell) == 'undefined'){
6490             return;
6491         }
6492         
6493         var row = cell.findParent('tr', false, true);
6494         
6495         if(!row || typeof(row) == 'undefined'){
6496             return;
6497         }
6498         
6499         var cellIndex = cell.dom.cellIndex;
6500         var rowIndex = this.getRowIndex(row);
6501         
6502         if(this.cellSelection){
6503             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6504         }
6505         
6506         if(this.rowSelection){
6507             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6508         }
6509     },
6510     
6511     sort : function(e,el)
6512     {
6513         var col = Roo.get(el);
6514         
6515         if(!col.hasClass('sortable')){
6516             return;
6517         }
6518         
6519         var sort = col.attr('sort');
6520         var dir = 'ASC';
6521         
6522         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6523             dir = 'DESC';
6524         }
6525         
6526         this.store.sortInfo = {field : sort, direction : dir};
6527         
6528         if (this.footer) {
6529             Roo.log("calling footer first");
6530             this.footer.onClick('first');
6531         } else {
6532         
6533             this.store.load({ params : { start : 0 } });
6534         }
6535     },
6536     
6537     renderHeader : function()
6538     {
6539         var header = {
6540             tag: 'thead',
6541             cn : []
6542         };
6543         
6544         var cm = this.cm;
6545         this.totalWidth = 0;
6546         
6547         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6548             
6549             var config = cm.config[i];
6550             
6551             var c = {
6552                 tag: 'th',
6553                 cls : 'x-hcol-' + i,
6554                 style : '',
6555                 html: cm.getColumnHeader(i)
6556             };
6557             
6558             var hh = '';
6559             
6560             if(typeof(config.sortable) != 'undefined' && config.sortable){
6561                 c.cls = 'sortable';
6562                 c.html = '<i class="glyphicon"></i>' + c.html;
6563             }
6564             
6565             if(typeof(config.lgHeader) != 'undefined'){
6566                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6567             }
6568             
6569             if(typeof(config.mdHeader) != 'undefined'){
6570                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6571             }
6572             
6573             if(typeof(config.smHeader) != 'undefined'){
6574                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6575             }
6576             
6577             if(typeof(config.xsHeader) != 'undefined'){
6578                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6579             }
6580             
6581             if(hh.length){
6582                 c.html = hh;
6583             }
6584             
6585             if(typeof(config.tooltip) != 'undefined'){
6586                 c.tooltip = config.tooltip;
6587             }
6588             
6589             if(typeof(config.colspan) != 'undefined'){
6590                 c.colspan = config.colspan;
6591             }
6592             
6593             if(typeof(config.hidden) != 'undefined' && config.hidden){
6594                 c.style += ' display:none;';
6595             }
6596             
6597             if(typeof(config.dataIndex) != 'undefined'){
6598                 c.sort = config.dataIndex;
6599             }
6600             
6601            
6602             
6603             if(typeof(config.align) != 'undefined' && config.align.length){
6604                 c.style += ' text-align:' + config.align + ';';
6605             }
6606             
6607             if(typeof(config.width) != 'undefined'){
6608                 c.style += ' width:' + config.width + 'px;';
6609                 this.totalWidth += config.width;
6610             } else {
6611                 this.totalWidth += 100; // assume minimum of 100 per column?
6612             }
6613             
6614             if(typeof(config.cls) != 'undefined'){
6615                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6616             }
6617             
6618             ['xs','sm','md','lg'].map(function(size){
6619                 
6620                 if(typeof(config[size]) == 'undefined'){
6621                     return;
6622                 }
6623                 
6624                 if (!config[size]) { // 0 = hidden
6625                     c.cls += ' hidden-' + size;
6626                     return;
6627                 }
6628                 
6629                 c.cls += ' col-' + size + '-' + config[size];
6630
6631             });
6632             
6633             header.cn.push(c)
6634         }
6635         
6636         return header;
6637     },
6638     
6639     renderBody : function()
6640     {
6641         var body = {
6642             tag: 'tbody',
6643             cn : [
6644                 {
6645                     tag: 'tr',
6646                     cn : [
6647                         {
6648                             tag : 'td',
6649                             colspan :  this.cm.getColumnCount()
6650                         }
6651                     ]
6652                 }
6653             ]
6654         };
6655         
6656         return body;
6657     },
6658     
6659     renderFooter : function()
6660     {
6661         var footer = {
6662             tag: 'tfoot',
6663             cn : [
6664                 {
6665                     tag: 'tr',
6666                     cn : [
6667                         {
6668                             tag : 'td',
6669                             colspan :  this.cm.getColumnCount()
6670                         }
6671                     ]
6672                 }
6673             ]
6674         };
6675         
6676         return footer;
6677     },
6678     
6679     
6680     
6681     onLoad : function()
6682     {
6683 //        Roo.log('ds onload');
6684         this.clear();
6685         
6686         var _this = this;
6687         var cm = this.cm;
6688         var ds = this.store;
6689         
6690         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6691             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6692             if (_this.store.sortInfo) {
6693                     
6694                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6695                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6696                 }
6697                 
6698                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6699                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6700                 }
6701             }
6702         });
6703         
6704         var tbody =  this.mainBody;
6705               
6706         if(ds.getCount() > 0){
6707             ds.data.each(function(d,rowIndex){
6708                 var row =  this.renderRow(cm, ds, rowIndex);
6709                 
6710                 tbody.createChild(row);
6711                 
6712                 var _this = this;
6713                 
6714                 if(row.cellObjects.length){
6715                     Roo.each(row.cellObjects, function(r){
6716                         _this.renderCellObject(r);
6717                     })
6718                 }
6719                 
6720             }, this);
6721         }
6722         
6723         var tfoot = this.el.select('tfoot', true).first();
6724         
6725         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6726             
6727             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6728             
6729             var total = this.ds.getTotalCount();
6730             
6731             if(this.footer.pageSize < total){
6732                 this.mainFoot.show();
6733             }
6734         }
6735         
6736         Roo.each(this.el.select('tbody td', true).elements, function(e){
6737             e.on('mouseover', _this.onMouseover, _this);
6738         });
6739         
6740         Roo.each(this.el.select('tbody td', true).elements, function(e){
6741             e.on('mouseout', _this.onMouseout, _this);
6742         });
6743         this.fireEvent('rowsrendered', this);
6744         
6745         this.autoSize();
6746     },
6747     
6748     
6749     onUpdate : function(ds,record)
6750     {
6751         this.refreshRow(record);
6752         this.autoSize();
6753     },
6754     
6755     onRemove : function(ds, record, index, isUpdate){
6756         if(isUpdate !== true){
6757             this.fireEvent("beforerowremoved", this, index, record);
6758         }
6759         var bt = this.mainBody.dom;
6760         
6761         var rows = this.el.select('tbody > tr', true).elements;
6762         
6763         if(typeof(rows[index]) != 'undefined'){
6764             bt.removeChild(rows[index].dom);
6765         }
6766         
6767 //        if(bt.rows[index]){
6768 //            bt.removeChild(bt.rows[index]);
6769 //        }
6770         
6771         if(isUpdate !== true){
6772             //this.stripeRows(index);
6773             //this.syncRowHeights(index, index);
6774             //this.layout();
6775             this.fireEvent("rowremoved", this, index, record);
6776         }
6777     },
6778     
6779     onAdd : function(ds, records, rowIndex)
6780     {
6781         //Roo.log('on Add called');
6782         // - note this does not handle multiple adding very well..
6783         var bt = this.mainBody.dom;
6784         for (var i =0 ; i < records.length;i++) {
6785             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6786             //Roo.log(records[i]);
6787             //Roo.log(this.store.getAt(rowIndex+i));
6788             this.insertRow(this.store, rowIndex + i, false);
6789             return;
6790         }
6791         
6792     },
6793     
6794     
6795     refreshRow : function(record){
6796         var ds = this.store, index;
6797         if(typeof record == 'number'){
6798             index = record;
6799             record = ds.getAt(index);
6800         }else{
6801             index = ds.indexOf(record);
6802         }
6803         this.insertRow(ds, index, true);
6804         this.autoSize();
6805         this.onRemove(ds, record, index+1, true);
6806         this.autoSize();
6807         //this.syncRowHeights(index, index);
6808         //this.layout();
6809         this.fireEvent("rowupdated", this, index, record);
6810     },
6811     
6812     insertRow : function(dm, rowIndex, isUpdate){
6813         
6814         if(!isUpdate){
6815             this.fireEvent("beforerowsinserted", this, rowIndex);
6816         }
6817             //var s = this.getScrollState();
6818         var row = this.renderRow(this.cm, this.store, rowIndex);
6819         // insert before rowIndex..
6820         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6821         
6822         var _this = this;
6823                 
6824         if(row.cellObjects.length){
6825             Roo.each(row.cellObjects, function(r){
6826                 _this.renderCellObject(r);
6827             })
6828         }
6829             
6830         if(!isUpdate){
6831             this.fireEvent("rowsinserted", this, rowIndex);
6832             //this.syncRowHeights(firstRow, lastRow);
6833             //this.stripeRows(firstRow);
6834             //this.layout();
6835         }
6836         
6837     },
6838     
6839     
6840     getRowDom : function(rowIndex)
6841     {
6842         var rows = this.el.select('tbody > tr', true).elements;
6843         
6844         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6845         
6846     },
6847     // returns the object tree for a tr..
6848   
6849     
6850     renderRow : function(cm, ds, rowIndex) 
6851     {
6852         var d = ds.getAt(rowIndex);
6853         
6854         var row = {
6855             tag : 'tr',
6856             cls : 'x-row-' + rowIndex,
6857             cn : []
6858         };
6859             
6860         var cellObjects = [];
6861         
6862         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6863             var config = cm.config[i];
6864             
6865             var renderer = cm.getRenderer(i);
6866             var value = '';
6867             var id = false;
6868             
6869             if(typeof(renderer) !== 'undefined'){
6870                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6871             }
6872             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6873             // and are rendered into the cells after the row is rendered - using the id for the element.
6874             
6875             if(typeof(value) === 'object'){
6876                 id = Roo.id();
6877                 cellObjects.push({
6878                     container : id,
6879                     cfg : value 
6880                 })
6881             }
6882             
6883             var rowcfg = {
6884                 record: d,
6885                 rowIndex : rowIndex,
6886                 colIndex : i,
6887                 rowClass : ''
6888             };
6889
6890             this.fireEvent('rowclass', this, rowcfg);
6891             
6892             var td = {
6893                 tag: 'td',
6894                 cls : rowcfg.rowClass + ' x-col-' + i,
6895                 style: '',
6896                 html: (typeof(value) === 'object') ? '' : value
6897             };
6898             
6899             if (id) {
6900                 td.id = id;
6901             }
6902             
6903             if(typeof(config.colspan) != 'undefined'){
6904                 td.colspan = config.colspan;
6905             }
6906             
6907             if(typeof(config.hidden) != 'undefined' && config.hidden){
6908                 td.style += ' display:none;';
6909             }
6910             
6911             if(typeof(config.align) != 'undefined' && config.align.length){
6912                 td.style += ' text-align:' + config.align + ';';
6913             }
6914             if(typeof(config.valign) != 'undefined' && config.valign.length){
6915                 td.style += ' vertical-align:' + config.valign + ';';
6916             }
6917             
6918             if(typeof(config.width) != 'undefined'){
6919                 td.style += ' width:' +  config.width + 'px;';
6920             }
6921             
6922             if(typeof(config.cursor) != 'undefined'){
6923                 td.style += ' cursor:' +  config.cursor + ';';
6924             }
6925             
6926             if(typeof(config.cls) != 'undefined'){
6927                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6928             }
6929             
6930             ['xs','sm','md','lg'].map(function(size){
6931                 
6932                 if(typeof(config[size]) == 'undefined'){
6933                     return;
6934                 }
6935                 
6936                 if (!config[size]) { // 0 = hidden
6937                     td.cls += ' hidden-' + size;
6938                     return;
6939                 }
6940                 
6941                 td.cls += ' col-' + size + '-' + config[size];
6942
6943             });
6944             
6945             row.cn.push(td);
6946            
6947         }
6948         
6949         row.cellObjects = cellObjects;
6950         
6951         return row;
6952           
6953     },
6954     
6955     
6956     
6957     onBeforeLoad : function()
6958     {
6959         
6960     },
6961      /**
6962      * Remove all rows
6963      */
6964     clear : function()
6965     {
6966         this.el.select('tbody', true).first().dom.innerHTML = '';
6967     },
6968     /**
6969      * Show or hide a row.
6970      * @param {Number} rowIndex to show or hide
6971      * @param {Boolean} state hide
6972      */
6973     setRowVisibility : function(rowIndex, state)
6974     {
6975         var bt = this.mainBody.dom;
6976         
6977         var rows = this.el.select('tbody > tr', true).elements;
6978         
6979         if(typeof(rows[rowIndex]) == 'undefined'){
6980             return;
6981         }
6982         rows[rowIndex].dom.style.display = state ? '' : 'none';
6983     },
6984     
6985     
6986     getSelectionModel : function(){
6987         if(!this.selModel){
6988             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6989         }
6990         return this.selModel;
6991     },
6992     /*
6993      * Render the Roo.bootstrap object from renderder
6994      */
6995     renderCellObject : function(r)
6996     {
6997         var _this = this;
6998         
6999         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7000         
7001         var t = r.cfg.render(r.container);
7002         
7003         if(r.cfg.cn){
7004             Roo.each(r.cfg.cn, function(c){
7005                 var child = {
7006                     container: t.getChildContainer(),
7007                     cfg: c
7008                 };
7009                 _this.renderCellObject(child);
7010             })
7011         }
7012     },
7013     
7014     getRowIndex : function(row)
7015     {
7016         var rowIndex = -1;
7017         
7018         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7019             if(el != row){
7020                 return;
7021             }
7022             
7023             rowIndex = index;
7024         });
7025         
7026         return rowIndex;
7027     },
7028      /**
7029      * Returns the grid's underlying element = used by panel.Grid
7030      * @return {Element} The element
7031      */
7032     getGridEl : function(){
7033         return this.el;
7034     },
7035      /**
7036      * Forces a resize - used by panel.Grid
7037      * @return {Element} The element
7038      */
7039     autoSize : function()
7040     {
7041         //var ctr = Roo.get(this.container.dom.parentElement);
7042         var ctr = Roo.get(this.el.dom);
7043         
7044         var thd = this.getGridEl().select('thead',true).first();
7045         var tbd = this.getGridEl().select('tbody', true).first();
7046         var tfd = this.getGridEl().select('tfoot', true).first();
7047         
7048         var cw = ctr.getWidth();
7049         
7050         if (tbd) {
7051             
7052             tbd.setSize(ctr.getWidth(),
7053                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7054             );
7055             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7056             cw -= barsize;
7057         }
7058         cw = Math.max(cw, this.totalWidth);
7059         this.getGridEl().select('tr',true).setWidth(cw);
7060         // resize 'expandable coloumn?
7061         
7062         return; // we doe not have a view in this design..
7063         
7064     },
7065     onBodyScroll: function()
7066     {
7067         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7068         if(this.mainHead){
7069             this.mainHead.setStyle({
7070                 'position' : 'relative',
7071                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7072             });
7073         }
7074         
7075         if(this.lazyLoad){
7076             
7077             var scrollHeight = this.mainBody.dom.scrollHeight;
7078             
7079             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7080             
7081             var height = this.mainBody.getHeight();
7082             
7083             if(scrollHeight - height == scrollTop) {
7084                 
7085                 var total = this.ds.getTotalCount();
7086                 
7087                 if(this.footer.cursor + this.footer.pageSize < total){
7088                     
7089                     this.footer.ds.load({
7090                         params : {
7091                             start : this.footer.cursor + this.footer.pageSize,
7092                             limit : this.footer.pageSize
7093                         },
7094                         add : true
7095                     });
7096                 }
7097             }
7098             
7099         }
7100     },
7101     
7102     onHeaderChange : function()
7103     {
7104         var header = this.renderHeader();
7105         var table = this.el.select('table', true).first();
7106         
7107         this.mainHead.remove();
7108         this.mainHead = table.createChild(header, this.mainBody, false);
7109     },
7110     
7111     onHiddenChange : function(colModel, colIndex, hidden)
7112     {
7113         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7114         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7115         
7116         this.CSS.updateRule(thSelector, "display", "");
7117         this.CSS.updateRule(tdSelector, "display", "");
7118         
7119         if(hidden){
7120             this.CSS.updateRule(thSelector, "display", "none");
7121             this.CSS.updateRule(tdSelector, "display", "none");
7122         }
7123         
7124         this.onHeaderChange();
7125         this.onLoad();
7126     },
7127     
7128     setColumnWidth: function(col_index, width)
7129     {
7130         // width = "md-2 xs-2..."
7131         if(!this.colModel.config[col_index]) {
7132             return;
7133         }
7134         
7135         var w = width.split(" ");
7136         
7137         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7138         
7139         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7140         
7141         
7142         for(var j = 0; j < w.length; j++) {
7143             
7144             if(!w[j]) {
7145                 continue;
7146             }
7147             
7148             var size_cls = w[j].split("-");
7149             
7150             if(!Number.isInteger(size_cls[1] * 1)) {
7151                 continue;
7152             }
7153             
7154             if(!this.colModel.config[col_index][size_cls[0]]) {
7155                 continue;
7156             }
7157             
7158             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7159                 continue;
7160             }
7161             
7162             h_row[0].classList.replace(
7163                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7164                 "col-"+size_cls[0]+"-"+size_cls[1]
7165             );
7166             
7167             for(var i = 0; i < rows.length; i++) {
7168                 
7169                 var size_cls = w[j].split("-");
7170                 
7171                 if(!Number.isInteger(size_cls[1] * 1)) {
7172                     continue;
7173                 }
7174                 
7175                 if(!this.colModel.config[col_index][size_cls[0]]) {
7176                     continue;
7177                 }
7178                 
7179                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7180                     continue;
7181                 }
7182                 
7183                 rows[i].classList.replace(
7184                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7185                     "col-"+size_cls[0]+"-"+size_cls[1]
7186                 );
7187             }
7188             
7189             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7190         }
7191     }
7192 });
7193
7194  
7195
7196  /*
7197  * - LGPL
7198  *
7199  * table cell
7200  * 
7201  */
7202
7203 /**
7204  * @class Roo.bootstrap.TableCell
7205  * @extends Roo.bootstrap.Component
7206  * Bootstrap TableCell class
7207  * @cfg {String} html cell contain text
7208  * @cfg {String} cls cell class
7209  * @cfg {String} tag cell tag (td|th) default td
7210  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7211  * @cfg {String} align Aligns the content in a cell
7212  * @cfg {String} axis Categorizes cells
7213  * @cfg {String} bgcolor Specifies the background color of a cell
7214  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7215  * @cfg {Number} colspan Specifies the number of columns a cell should span
7216  * @cfg {String} headers Specifies one or more header cells a cell is related to
7217  * @cfg {Number} height Sets the height of a cell
7218  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7219  * @cfg {Number} rowspan Sets the number of rows a cell should span
7220  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7221  * @cfg {String} valign Vertical aligns the content in a cell
7222  * @cfg {Number} width Specifies the width of a cell
7223  * 
7224  * @constructor
7225  * Create a new TableCell
7226  * @param {Object} config The config object
7227  */
7228
7229 Roo.bootstrap.TableCell = function(config){
7230     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7231 };
7232
7233 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7234     
7235     html: false,
7236     cls: false,
7237     tag: false,
7238     abbr: false,
7239     align: false,
7240     axis: false,
7241     bgcolor: false,
7242     charoff: false,
7243     colspan: false,
7244     headers: false,
7245     height: false,
7246     nowrap: false,
7247     rowspan: false,
7248     scope: false,
7249     valign: false,
7250     width: false,
7251     
7252     
7253     getAutoCreate : function(){
7254         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7255         
7256         cfg = {
7257             tag: 'td'
7258         };
7259         
7260         if(this.tag){
7261             cfg.tag = this.tag;
7262         }
7263         
7264         if (this.html) {
7265             cfg.html=this.html
7266         }
7267         if (this.cls) {
7268             cfg.cls=this.cls
7269         }
7270         if (this.abbr) {
7271             cfg.abbr=this.abbr
7272         }
7273         if (this.align) {
7274             cfg.align=this.align
7275         }
7276         if (this.axis) {
7277             cfg.axis=this.axis
7278         }
7279         if (this.bgcolor) {
7280             cfg.bgcolor=this.bgcolor
7281         }
7282         if (this.charoff) {
7283             cfg.charoff=this.charoff
7284         }
7285         if (this.colspan) {
7286             cfg.colspan=this.colspan
7287         }
7288         if (this.headers) {
7289             cfg.headers=this.headers
7290         }
7291         if (this.height) {
7292             cfg.height=this.height
7293         }
7294         if (this.nowrap) {
7295             cfg.nowrap=this.nowrap
7296         }
7297         if (this.rowspan) {
7298             cfg.rowspan=this.rowspan
7299         }
7300         if (this.scope) {
7301             cfg.scope=this.scope
7302         }
7303         if (this.valign) {
7304             cfg.valign=this.valign
7305         }
7306         if (this.width) {
7307             cfg.width=this.width
7308         }
7309         
7310         
7311         return cfg;
7312     }
7313    
7314 });
7315
7316  
7317
7318  /*
7319  * - LGPL
7320  *
7321  * table row
7322  * 
7323  */
7324
7325 /**
7326  * @class Roo.bootstrap.TableRow
7327  * @extends Roo.bootstrap.Component
7328  * Bootstrap TableRow class
7329  * @cfg {String} cls row class
7330  * @cfg {String} align Aligns the content in a table row
7331  * @cfg {String} bgcolor Specifies a background color for a table row
7332  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7333  * @cfg {String} valign Vertical aligns the content in a table row
7334  * 
7335  * @constructor
7336  * Create a new TableRow
7337  * @param {Object} config The config object
7338  */
7339
7340 Roo.bootstrap.TableRow = function(config){
7341     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7342 };
7343
7344 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7345     
7346     cls: false,
7347     align: false,
7348     bgcolor: false,
7349     charoff: false,
7350     valign: false,
7351     
7352     getAutoCreate : function(){
7353         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7354         
7355         cfg = {
7356             tag: 'tr'
7357         };
7358             
7359         if(this.cls){
7360             cfg.cls = this.cls;
7361         }
7362         if(this.align){
7363             cfg.align = this.align;
7364         }
7365         if(this.bgcolor){
7366             cfg.bgcolor = this.bgcolor;
7367         }
7368         if(this.charoff){
7369             cfg.charoff = this.charoff;
7370         }
7371         if(this.valign){
7372             cfg.valign = this.valign;
7373         }
7374         
7375         return cfg;
7376     }
7377    
7378 });
7379
7380  
7381
7382  /*
7383  * - LGPL
7384  *
7385  * table body
7386  * 
7387  */
7388
7389 /**
7390  * @class Roo.bootstrap.TableBody
7391  * @extends Roo.bootstrap.Component
7392  * Bootstrap TableBody class
7393  * @cfg {String} cls element class
7394  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7395  * @cfg {String} align Aligns the content inside the element
7396  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7397  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7398  * 
7399  * @constructor
7400  * Create a new TableBody
7401  * @param {Object} config The config object
7402  */
7403
7404 Roo.bootstrap.TableBody = function(config){
7405     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7406 };
7407
7408 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7409     
7410     cls: false,
7411     tag: false,
7412     align: false,
7413     charoff: false,
7414     valign: false,
7415     
7416     getAutoCreate : function(){
7417         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7418         
7419         cfg = {
7420             tag: 'tbody'
7421         };
7422             
7423         if (this.cls) {
7424             cfg.cls=this.cls
7425         }
7426         if(this.tag){
7427             cfg.tag = this.tag;
7428         }
7429         
7430         if(this.align){
7431             cfg.align = this.align;
7432         }
7433         if(this.charoff){
7434             cfg.charoff = this.charoff;
7435         }
7436         if(this.valign){
7437             cfg.valign = this.valign;
7438         }
7439         
7440         return cfg;
7441     }
7442     
7443     
7444 //    initEvents : function()
7445 //    {
7446 //        
7447 //        if(!this.store){
7448 //            return;
7449 //        }
7450 //        
7451 //        this.store = Roo.factory(this.store, Roo.data);
7452 //        this.store.on('load', this.onLoad, this);
7453 //        
7454 //        this.store.load();
7455 //        
7456 //    },
7457 //    
7458 //    onLoad: function () 
7459 //    {   
7460 //        this.fireEvent('load', this);
7461 //    }
7462 //    
7463 //   
7464 });
7465
7466  
7467
7468  /*
7469  * Based on:
7470  * Ext JS Library 1.1.1
7471  * Copyright(c) 2006-2007, Ext JS, LLC.
7472  *
7473  * Originally Released Under LGPL - original licence link has changed is not relivant.
7474  *
7475  * Fork - LGPL
7476  * <script type="text/javascript">
7477  */
7478
7479 // as we use this in bootstrap.
7480 Roo.namespace('Roo.form');
7481  /**
7482  * @class Roo.form.Action
7483  * Internal Class used to handle form actions
7484  * @constructor
7485  * @param {Roo.form.BasicForm} el The form element or its id
7486  * @param {Object} config Configuration options
7487  */
7488
7489  
7490  
7491 // define the action interface
7492 Roo.form.Action = function(form, options){
7493     this.form = form;
7494     this.options = options || {};
7495 };
7496 /**
7497  * Client Validation Failed
7498  * @const 
7499  */
7500 Roo.form.Action.CLIENT_INVALID = 'client';
7501 /**
7502  * Server Validation Failed
7503  * @const 
7504  */
7505 Roo.form.Action.SERVER_INVALID = 'server';
7506  /**
7507  * Connect to Server Failed
7508  * @const 
7509  */
7510 Roo.form.Action.CONNECT_FAILURE = 'connect';
7511 /**
7512  * Reading Data from Server Failed
7513  * @const 
7514  */
7515 Roo.form.Action.LOAD_FAILURE = 'load';
7516
7517 Roo.form.Action.prototype = {
7518     type : 'default',
7519     failureType : undefined,
7520     response : undefined,
7521     result : undefined,
7522
7523     // interface method
7524     run : function(options){
7525
7526     },
7527
7528     // interface method
7529     success : function(response){
7530
7531     },
7532
7533     // interface method
7534     handleResponse : function(response){
7535
7536     },
7537
7538     // default connection failure
7539     failure : function(response){
7540         
7541         this.response = response;
7542         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7543         this.form.afterAction(this, false);
7544     },
7545
7546     processResponse : function(response){
7547         this.response = response;
7548         if(!response.responseText){
7549             return true;
7550         }
7551         this.result = this.handleResponse(response);
7552         return this.result;
7553     },
7554
7555     // utility functions used internally
7556     getUrl : function(appendParams){
7557         var url = this.options.url || this.form.url || this.form.el.dom.action;
7558         if(appendParams){
7559             var p = this.getParams();
7560             if(p){
7561                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7562             }
7563         }
7564         return url;
7565     },
7566
7567     getMethod : function(){
7568         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7569     },
7570
7571     getParams : function(){
7572         var bp = this.form.baseParams;
7573         var p = this.options.params;
7574         if(p){
7575             if(typeof p == "object"){
7576                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7577             }else if(typeof p == 'string' && bp){
7578                 p += '&' + Roo.urlEncode(bp);
7579             }
7580         }else if(bp){
7581             p = Roo.urlEncode(bp);
7582         }
7583         return p;
7584     },
7585
7586     createCallback : function(){
7587         return {
7588             success: this.success,
7589             failure: this.failure,
7590             scope: this,
7591             timeout: (this.form.timeout*1000),
7592             upload: this.form.fileUpload ? this.success : undefined
7593         };
7594     }
7595 };
7596
7597 Roo.form.Action.Submit = function(form, options){
7598     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7599 };
7600
7601 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7602     type : 'submit',
7603
7604     haveProgress : false,
7605     uploadComplete : false,
7606     
7607     // uploadProgress indicator.
7608     uploadProgress : function()
7609     {
7610         if (!this.form.progressUrl) {
7611             return;
7612         }
7613         
7614         if (!this.haveProgress) {
7615             Roo.MessageBox.progress("Uploading", "Uploading");
7616         }
7617         if (this.uploadComplete) {
7618            Roo.MessageBox.hide();
7619            return;
7620         }
7621         
7622         this.haveProgress = true;
7623    
7624         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7625         
7626         var c = new Roo.data.Connection();
7627         c.request({
7628             url : this.form.progressUrl,
7629             params: {
7630                 id : uid
7631             },
7632             method: 'GET',
7633             success : function(req){
7634                //console.log(data);
7635                 var rdata = false;
7636                 var edata;
7637                 try  {
7638                    rdata = Roo.decode(req.responseText)
7639                 } catch (e) {
7640                     Roo.log("Invalid data from server..");
7641                     Roo.log(edata);
7642                     return;
7643                 }
7644                 if (!rdata || !rdata.success) {
7645                     Roo.log(rdata);
7646                     Roo.MessageBox.alert(Roo.encode(rdata));
7647                     return;
7648                 }
7649                 var data = rdata.data;
7650                 
7651                 if (this.uploadComplete) {
7652                    Roo.MessageBox.hide();
7653                    return;
7654                 }
7655                    
7656                 if (data){
7657                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7658                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7659                     );
7660                 }
7661                 this.uploadProgress.defer(2000,this);
7662             },
7663        
7664             failure: function(data) {
7665                 Roo.log('progress url failed ');
7666                 Roo.log(data);
7667             },
7668             scope : this
7669         });
7670            
7671     },
7672     
7673     
7674     run : function()
7675     {
7676         // run get Values on the form, so it syncs any secondary forms.
7677         this.form.getValues();
7678         
7679         var o = this.options;
7680         var method = this.getMethod();
7681         var isPost = method == 'POST';
7682         if(o.clientValidation === false || this.form.isValid()){
7683             
7684             if (this.form.progressUrl) {
7685                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7686                     (new Date() * 1) + '' + Math.random());
7687                     
7688             } 
7689             
7690             
7691             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7692                 form:this.form.el.dom,
7693                 url:this.getUrl(!isPost),
7694                 method: method,
7695                 params:isPost ? this.getParams() : null,
7696                 isUpload: this.form.fileUpload
7697             }));
7698             
7699             this.uploadProgress();
7700
7701         }else if (o.clientValidation !== false){ // client validation failed
7702             this.failureType = Roo.form.Action.CLIENT_INVALID;
7703             this.form.afterAction(this, false);
7704         }
7705     },
7706
7707     success : function(response)
7708     {
7709         this.uploadComplete= true;
7710         if (this.haveProgress) {
7711             Roo.MessageBox.hide();
7712         }
7713         
7714         
7715         var result = this.processResponse(response);
7716         if(result === true || result.success){
7717             this.form.afterAction(this, true);
7718             return;
7719         }
7720         if(result.errors){
7721             this.form.markInvalid(result.errors);
7722             this.failureType = Roo.form.Action.SERVER_INVALID;
7723         }
7724         this.form.afterAction(this, false);
7725     },
7726     failure : function(response)
7727     {
7728         this.uploadComplete= true;
7729         if (this.haveProgress) {
7730             Roo.MessageBox.hide();
7731         }
7732         
7733         this.response = response;
7734         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7735         this.form.afterAction(this, false);
7736     },
7737     
7738     handleResponse : function(response){
7739         if(this.form.errorReader){
7740             var rs = this.form.errorReader.read(response);
7741             var errors = [];
7742             if(rs.records){
7743                 for(var i = 0, len = rs.records.length; i < len; i++) {
7744                     var r = rs.records[i];
7745                     errors[i] = r.data;
7746                 }
7747             }
7748             if(errors.length < 1){
7749                 errors = null;
7750             }
7751             return {
7752                 success : rs.success,
7753                 errors : errors
7754             };
7755         }
7756         var ret = false;
7757         try {
7758             ret = Roo.decode(response.responseText);
7759         } catch (e) {
7760             ret = {
7761                 success: false,
7762                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7763                 errors : []
7764             };
7765         }
7766         return ret;
7767         
7768     }
7769 });
7770
7771
7772 Roo.form.Action.Load = function(form, options){
7773     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7774     this.reader = this.form.reader;
7775 };
7776
7777 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7778     type : 'load',
7779
7780     run : function(){
7781         
7782         Roo.Ajax.request(Roo.apply(
7783                 this.createCallback(), {
7784                     method:this.getMethod(),
7785                     url:this.getUrl(false),
7786                     params:this.getParams()
7787         }));
7788     },
7789
7790     success : function(response){
7791         
7792         var result = this.processResponse(response);
7793         if(result === true || !result.success || !result.data){
7794             this.failureType = Roo.form.Action.LOAD_FAILURE;
7795             this.form.afterAction(this, false);
7796             return;
7797         }
7798         this.form.clearInvalid();
7799         this.form.setValues(result.data);
7800         this.form.afterAction(this, true);
7801     },
7802
7803     handleResponse : function(response){
7804         if(this.form.reader){
7805             var rs = this.form.reader.read(response);
7806             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7807             return {
7808                 success : rs.success,
7809                 data : data
7810             };
7811         }
7812         return Roo.decode(response.responseText);
7813     }
7814 });
7815
7816 Roo.form.Action.ACTION_TYPES = {
7817     'load' : Roo.form.Action.Load,
7818     'submit' : Roo.form.Action.Submit
7819 };/*
7820  * - LGPL
7821  *
7822  * form
7823  *
7824  */
7825
7826 /**
7827  * @class Roo.bootstrap.Form
7828  * @extends Roo.bootstrap.Component
7829  * Bootstrap Form class
7830  * @cfg {String} method  GET | POST (default POST)
7831  * @cfg {String} labelAlign top | left (default top)
7832  * @cfg {String} align left  | right - for navbars
7833  * @cfg {Boolean} loadMask load mask when submit (default true)
7834
7835  *
7836  * @constructor
7837  * Create a new Form
7838  * @param {Object} config The config object
7839  */
7840
7841
7842 Roo.bootstrap.Form = function(config){
7843     
7844     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7845     
7846     Roo.bootstrap.Form.popover.apply();
7847     
7848     this.addEvents({
7849         /**
7850          * @event clientvalidation
7851          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7852          * @param {Form} this
7853          * @param {Boolean} valid true if the form has passed client-side validation
7854          */
7855         clientvalidation: true,
7856         /**
7857          * @event beforeaction
7858          * Fires before any action is performed. Return false to cancel the action.
7859          * @param {Form} this
7860          * @param {Action} action The action to be performed
7861          */
7862         beforeaction: true,
7863         /**
7864          * @event actionfailed
7865          * Fires when an action fails.
7866          * @param {Form} this
7867          * @param {Action} action The action that failed
7868          */
7869         actionfailed : true,
7870         /**
7871          * @event actioncomplete
7872          * Fires when an action is completed.
7873          * @param {Form} this
7874          * @param {Action} action The action that completed
7875          */
7876         actioncomplete : true
7877     });
7878 };
7879
7880 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7881
7882      /**
7883      * @cfg {String} method
7884      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7885      */
7886     method : 'POST',
7887     /**
7888      * @cfg {String} url
7889      * The URL to use for form actions if one isn't supplied in the action options.
7890      */
7891     /**
7892      * @cfg {Boolean} fileUpload
7893      * Set to true if this form is a file upload.
7894      */
7895
7896     /**
7897      * @cfg {Object} baseParams
7898      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7899      */
7900
7901     /**
7902      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7903      */
7904     timeout: 30,
7905     /**
7906      * @cfg {Sting} align (left|right) for navbar forms
7907      */
7908     align : 'left',
7909
7910     // private
7911     activeAction : null,
7912
7913     /**
7914      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7915      * element by passing it or its id or mask the form itself by passing in true.
7916      * @type Mixed
7917      */
7918     waitMsgTarget : false,
7919
7920     loadMask : true,
7921     
7922     /**
7923      * @cfg {Boolean} errorMask (true|false) default false
7924      */
7925     errorMask : false,
7926     
7927     /**
7928      * @cfg {Number} maskOffset Default 100
7929      */
7930     maskOffset : 100,
7931     
7932     /**
7933      * @cfg {Boolean} maskBody
7934      */
7935     maskBody : false,
7936
7937     getAutoCreate : function(){
7938
7939         var cfg = {
7940             tag: 'form',
7941             method : this.method || 'POST',
7942             id : this.id || Roo.id(),
7943             cls : ''
7944         };
7945         if (this.parent().xtype.match(/^Nav/)) {
7946             cfg.cls = 'navbar-form navbar-' + this.align;
7947
7948         }
7949
7950         if (this.labelAlign == 'left' ) {
7951             cfg.cls += ' form-horizontal';
7952         }
7953
7954
7955         return cfg;
7956     },
7957     initEvents : function()
7958     {
7959         this.el.on('submit', this.onSubmit, this);
7960         // this was added as random key presses on the form where triggering form submit.
7961         this.el.on('keypress', function(e) {
7962             if (e.getCharCode() != 13) {
7963                 return true;
7964             }
7965             // we might need to allow it for textareas.. and some other items.
7966             // check e.getTarget().
7967
7968             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7969                 return true;
7970             }
7971
7972             Roo.log("keypress blocked");
7973
7974             e.preventDefault();
7975             return false;
7976         });
7977         
7978     },
7979     // private
7980     onSubmit : function(e){
7981         e.stopEvent();
7982     },
7983
7984      /**
7985      * Returns true if client-side validation on the form is successful.
7986      * @return Boolean
7987      */
7988     isValid : function(){
7989         var items = this.getItems();
7990         var valid = true;
7991         var target = false;
7992         
7993         items.each(function(f){
7994             
7995             if(f.validate()){
7996                 return;
7997             }
7998             
7999             Roo.log('invalid field: ' + f.name);
8000             
8001             valid = false;
8002
8003             if(!target && f.el.isVisible(true)){
8004                 target = f;
8005             }
8006            
8007         });
8008         
8009         if(this.errorMask && !valid){
8010             Roo.bootstrap.Form.popover.mask(this, target);
8011         }
8012         
8013         return valid;
8014     },
8015     
8016     /**
8017      * Returns true if any fields in this form have changed since their original load.
8018      * @return Boolean
8019      */
8020     isDirty : function(){
8021         var dirty = false;
8022         var items = this.getItems();
8023         items.each(function(f){
8024            if(f.isDirty()){
8025                dirty = true;
8026                return false;
8027            }
8028            return true;
8029         });
8030         return dirty;
8031     },
8032      /**
8033      * Performs a predefined action (submit or load) or custom actions you define on this form.
8034      * @param {String} actionName The name of the action type
8035      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8036      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8037      * accept other config options):
8038      * <pre>
8039 Property          Type             Description
8040 ----------------  ---------------  ----------------------------------------------------------------------------------
8041 url               String           The url for the action (defaults to the form's url)
8042 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8043 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8044 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8045                                    validate the form on the client (defaults to false)
8046      * </pre>
8047      * @return {BasicForm} this
8048      */
8049     doAction : function(action, options){
8050         if(typeof action == 'string'){
8051             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8052         }
8053         if(this.fireEvent('beforeaction', this, action) !== false){
8054             this.beforeAction(action);
8055             action.run.defer(100, action);
8056         }
8057         return this;
8058     },
8059
8060     // private
8061     beforeAction : function(action){
8062         var o = action.options;
8063         
8064         if(this.loadMask){
8065             
8066             if(this.maskBody){
8067                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8068             } else {
8069                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8070             }
8071         }
8072         // not really supported yet.. ??
8073
8074         //if(this.waitMsgTarget === true){
8075         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8076         //}else if(this.waitMsgTarget){
8077         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8078         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8079         //}else {
8080         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8081        // }
8082
8083     },
8084
8085     // private
8086     afterAction : function(action, success){
8087         this.activeAction = null;
8088         var o = action.options;
8089
8090         if(this.loadMask){
8091             
8092             if(this.maskBody){
8093                 Roo.get(document.body).unmask();
8094             } else {
8095                 this.el.unmask();
8096             }
8097         }
8098         
8099         //if(this.waitMsgTarget === true){
8100 //            this.el.unmask();
8101         //}else if(this.waitMsgTarget){
8102         //    this.waitMsgTarget.unmask();
8103         //}else{
8104         //    Roo.MessageBox.updateProgress(1);
8105         //    Roo.MessageBox.hide();
8106        // }
8107         //
8108         if(success){
8109             if(o.reset){
8110                 this.reset();
8111             }
8112             Roo.callback(o.success, o.scope, [this, action]);
8113             this.fireEvent('actioncomplete', this, action);
8114
8115         }else{
8116
8117             // failure condition..
8118             // we have a scenario where updates need confirming.
8119             // eg. if a locking scenario exists..
8120             // we look for { errors : { needs_confirm : true }} in the response.
8121             if (
8122                 (typeof(action.result) != 'undefined')  &&
8123                 (typeof(action.result.errors) != 'undefined')  &&
8124                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8125            ){
8126                 var _t = this;
8127                 Roo.log("not supported yet");
8128                  /*
8129
8130                 Roo.MessageBox.confirm(
8131                     "Change requires confirmation",
8132                     action.result.errorMsg,
8133                     function(r) {
8134                         if (r != 'yes') {
8135                             return;
8136                         }
8137                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8138                     }
8139
8140                 );
8141                 */
8142
8143
8144                 return;
8145             }
8146
8147             Roo.callback(o.failure, o.scope, [this, action]);
8148             // show an error message if no failed handler is set..
8149             if (!this.hasListener('actionfailed')) {
8150                 Roo.log("need to add dialog support");
8151                 /*
8152                 Roo.MessageBox.alert("Error",
8153                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8154                         action.result.errorMsg :
8155                         "Saving Failed, please check your entries or try again"
8156                 );
8157                 */
8158             }
8159
8160             this.fireEvent('actionfailed', this, action);
8161         }
8162
8163     },
8164     /**
8165      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8166      * @param {String} id The value to search for
8167      * @return Field
8168      */
8169     findField : function(id){
8170         var items = this.getItems();
8171         var field = items.get(id);
8172         if(!field){
8173              items.each(function(f){
8174                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8175                     field = f;
8176                     return false;
8177                 }
8178                 return true;
8179             });
8180         }
8181         return field || null;
8182     },
8183      /**
8184      * Mark fields in this form invalid in bulk.
8185      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8186      * @return {BasicForm} this
8187      */
8188     markInvalid : function(errors){
8189         if(errors instanceof Array){
8190             for(var i = 0, len = errors.length; i < len; i++){
8191                 var fieldError = errors[i];
8192                 var f = this.findField(fieldError.id);
8193                 if(f){
8194                     f.markInvalid(fieldError.msg);
8195                 }
8196             }
8197         }else{
8198             var field, id;
8199             for(id in errors){
8200                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8201                     field.markInvalid(errors[id]);
8202                 }
8203             }
8204         }
8205         //Roo.each(this.childForms || [], function (f) {
8206         //    f.markInvalid(errors);
8207         //});
8208
8209         return this;
8210     },
8211
8212     /**
8213      * Set values for fields in this form in bulk.
8214      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8215      * @return {BasicForm} this
8216      */
8217     setValues : function(values){
8218         if(values instanceof Array){ // array of objects
8219             for(var i = 0, len = values.length; i < len; i++){
8220                 var v = values[i];
8221                 var f = this.findField(v.id);
8222                 if(f){
8223                     f.setValue(v.value);
8224                     if(this.trackResetOnLoad){
8225                         f.originalValue = f.getValue();
8226                     }
8227                 }
8228             }
8229         }else{ // object hash
8230             var field, id;
8231             for(id in values){
8232                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8233
8234                     if (field.setFromData &&
8235                         field.valueField &&
8236                         field.displayField &&
8237                         // combos' with local stores can
8238                         // be queried via setValue()
8239                         // to set their value..
8240                         (field.store && !field.store.isLocal)
8241                         ) {
8242                         // it's a combo
8243                         var sd = { };
8244                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8245                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8246                         field.setFromData(sd);
8247
8248                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8249                         
8250                         field.setFromData(values);
8251                         
8252                     } else {
8253                         field.setValue(values[id]);
8254                     }
8255
8256
8257                     if(this.trackResetOnLoad){
8258                         field.originalValue = field.getValue();
8259                     }
8260                 }
8261             }
8262         }
8263
8264         //Roo.each(this.childForms || [], function (f) {
8265         //    f.setValues(values);
8266         //});
8267
8268         return this;
8269     },
8270
8271     /**
8272      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8273      * they are returned as an array.
8274      * @param {Boolean} asString
8275      * @return {Object}
8276      */
8277     getValues : function(asString){
8278         //if (this.childForms) {
8279             // copy values from the child forms
8280         //    Roo.each(this.childForms, function (f) {
8281         //        this.setValues(f.getValues());
8282         //    }, this);
8283         //}
8284
8285
8286
8287         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8288         if(asString === true){
8289             return fs;
8290         }
8291         return Roo.urlDecode(fs);
8292     },
8293
8294     /**
8295      * Returns the fields in this form as an object with key/value pairs.
8296      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8297      * @return {Object}
8298      */
8299     getFieldValues : function(with_hidden)
8300     {
8301         var items = this.getItems();
8302         var ret = {};
8303         items.each(function(f){
8304             
8305             if (!f.getName()) {
8306                 return;
8307             }
8308             
8309             var v = f.getValue();
8310             
8311             if (f.inputType =='radio') {
8312                 if (typeof(ret[f.getName()]) == 'undefined') {
8313                     ret[f.getName()] = ''; // empty..
8314                 }
8315
8316                 if (!f.el.dom.checked) {
8317                     return;
8318
8319                 }
8320                 v = f.el.dom.value;
8321
8322             }
8323             
8324             if(f.xtype == 'MoneyField'){
8325                 ret[f.currencyName] = f.getCurrency();
8326             }
8327
8328             // not sure if this supported any more..
8329             if ((typeof(v) == 'object') && f.getRawValue) {
8330                 v = f.getRawValue() ; // dates..
8331             }
8332             // combo boxes where name != hiddenName...
8333             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8334                 ret[f.name] = f.getRawValue();
8335             }
8336             ret[f.getName()] = v;
8337         });
8338
8339         return ret;
8340     },
8341
8342     /**
8343      * Clears all invalid messages in this form.
8344      * @return {BasicForm} this
8345      */
8346     clearInvalid : function(){
8347         var items = this.getItems();
8348
8349         items.each(function(f){
8350            f.clearInvalid();
8351         });
8352
8353         return this;
8354     },
8355
8356     /**
8357      * Resets this form.
8358      * @return {BasicForm} this
8359      */
8360     reset : function(){
8361         var items = this.getItems();
8362         items.each(function(f){
8363             f.reset();
8364         });
8365
8366         Roo.each(this.childForms || [], function (f) {
8367             f.reset();
8368         });
8369
8370
8371         return this;
8372     },
8373     
8374     getItems : function()
8375     {
8376         var r=new Roo.util.MixedCollection(false, function(o){
8377             return o.id || (o.id = Roo.id());
8378         });
8379         var iter = function(el) {
8380             if (el.inputEl) {
8381                 r.add(el);
8382             }
8383             if (!el.items) {
8384                 return;
8385             }
8386             Roo.each(el.items,function(e) {
8387                 iter(e);
8388             });
8389         };
8390
8391         iter(this);
8392         return r;
8393     },
8394     
8395     hideFields : function(items)
8396     {
8397         Roo.each(items, function(i){
8398             
8399             var f = this.findField(i);
8400             
8401             if(!f){
8402                 return;
8403             }
8404             
8405             f.hide();
8406             
8407         }, this);
8408     },
8409     
8410     showFields : function(items)
8411     {
8412         Roo.each(items, function(i){
8413             
8414             var f = this.findField(i);
8415             
8416             if(!f){
8417                 return;
8418             }
8419             
8420             f.show();
8421             
8422         }, this);
8423     }
8424
8425 });
8426
8427 Roo.apply(Roo.bootstrap.Form, {
8428     
8429     popover : {
8430         
8431         padding : 5,
8432         
8433         isApplied : false,
8434         
8435         isMasked : false,
8436         
8437         form : false,
8438         
8439         target : false,
8440         
8441         toolTip : false,
8442         
8443         intervalID : false,
8444         
8445         maskEl : false,
8446         
8447         apply : function()
8448         {
8449             if(this.isApplied){
8450                 return;
8451             }
8452             
8453             this.maskEl = {
8454                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8455                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8456                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8457                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8458             };
8459             
8460             this.maskEl.top.enableDisplayMode("block");
8461             this.maskEl.left.enableDisplayMode("block");
8462             this.maskEl.bottom.enableDisplayMode("block");
8463             this.maskEl.right.enableDisplayMode("block");
8464             
8465             this.toolTip = new Roo.bootstrap.Tooltip({
8466                 cls : 'roo-form-error-popover',
8467                 alignment : {
8468                     'left' : ['r-l', [-2,0], 'right'],
8469                     'right' : ['l-r', [2,0], 'left'],
8470                     'bottom' : ['tl-bl', [0,2], 'top'],
8471                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8472                 }
8473             });
8474             
8475             this.toolTip.render(Roo.get(document.body));
8476
8477             this.toolTip.el.enableDisplayMode("block");
8478             
8479             Roo.get(document.body).on('click', function(){
8480                 this.unmask();
8481             }, this);
8482             
8483             Roo.get(document.body).on('touchstart', function(){
8484                 this.unmask();
8485             }, this);
8486             
8487             this.isApplied = true
8488         },
8489         
8490         mask : function(form, target)
8491         {
8492             this.form = form;
8493             
8494             this.target = target;
8495             
8496             if(!this.form.errorMask || !target.el){
8497                 return;
8498             }
8499             
8500             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8501             
8502             Roo.log(scrollable);
8503             
8504             var ot = this.target.el.calcOffsetsTo(scrollable);
8505             
8506             var scrollTo = ot[1] - this.form.maskOffset;
8507             
8508             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8509             
8510             scrollable.scrollTo('top', scrollTo);
8511             
8512             var box = this.target.el.getBox();
8513             Roo.log(box);
8514             var zIndex = Roo.bootstrap.Modal.zIndex++;
8515
8516             
8517             this.maskEl.top.setStyle('position', 'absolute');
8518             this.maskEl.top.setStyle('z-index', zIndex);
8519             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8520             this.maskEl.top.setLeft(0);
8521             this.maskEl.top.setTop(0);
8522             this.maskEl.top.show();
8523             
8524             this.maskEl.left.setStyle('position', 'absolute');
8525             this.maskEl.left.setStyle('z-index', zIndex);
8526             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8527             this.maskEl.left.setLeft(0);
8528             this.maskEl.left.setTop(box.y - this.padding);
8529             this.maskEl.left.show();
8530
8531             this.maskEl.bottom.setStyle('position', 'absolute');
8532             this.maskEl.bottom.setStyle('z-index', zIndex);
8533             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8534             this.maskEl.bottom.setLeft(0);
8535             this.maskEl.bottom.setTop(box.bottom + this.padding);
8536             this.maskEl.bottom.show();
8537
8538             this.maskEl.right.setStyle('position', 'absolute');
8539             this.maskEl.right.setStyle('z-index', zIndex);
8540             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8541             this.maskEl.right.setLeft(box.right + this.padding);
8542             this.maskEl.right.setTop(box.y - this.padding);
8543             this.maskEl.right.show();
8544
8545             this.toolTip.bindEl = this.target.el;
8546
8547             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8548
8549             var tip = this.target.blankText;
8550
8551             if(this.target.getValue() !== '' ) {
8552                 
8553                 if (this.target.invalidText.length) {
8554                     tip = this.target.invalidText;
8555                 } else if (this.target.regexText.length){
8556                     tip = this.target.regexText;
8557                 }
8558             }
8559
8560             this.toolTip.show(tip);
8561
8562             this.intervalID = window.setInterval(function() {
8563                 Roo.bootstrap.Form.popover.unmask();
8564             }, 10000);
8565
8566             window.onwheel = function(){ return false;};
8567             
8568             (function(){ this.isMasked = true; }).defer(500, this);
8569             
8570         },
8571         
8572         unmask : function()
8573         {
8574             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8575                 return;
8576             }
8577             
8578             this.maskEl.top.setStyle('position', 'absolute');
8579             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8580             this.maskEl.top.hide();
8581
8582             this.maskEl.left.setStyle('position', 'absolute');
8583             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8584             this.maskEl.left.hide();
8585
8586             this.maskEl.bottom.setStyle('position', 'absolute');
8587             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8588             this.maskEl.bottom.hide();
8589
8590             this.maskEl.right.setStyle('position', 'absolute');
8591             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8592             this.maskEl.right.hide();
8593             
8594             this.toolTip.hide();
8595             
8596             this.toolTip.el.hide();
8597             
8598             window.onwheel = function(){ return true;};
8599             
8600             if(this.intervalID){
8601                 window.clearInterval(this.intervalID);
8602                 this.intervalID = false;
8603             }
8604             
8605             this.isMasked = false;
8606             
8607         }
8608         
8609     }
8610     
8611 });
8612
8613 /*
8614  * Based on:
8615  * Ext JS Library 1.1.1
8616  * Copyright(c) 2006-2007, Ext JS, LLC.
8617  *
8618  * Originally Released Under LGPL - original licence link has changed is not relivant.
8619  *
8620  * Fork - LGPL
8621  * <script type="text/javascript">
8622  */
8623 /**
8624  * @class Roo.form.VTypes
8625  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8626  * @singleton
8627  */
8628 Roo.form.VTypes = function(){
8629     // closure these in so they are only created once.
8630     var alpha = /^[a-zA-Z_]+$/;
8631     var alphanum = /^[a-zA-Z0-9_]+$/;
8632     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8633     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8634
8635     // All these messages and functions are configurable
8636     return {
8637         /**
8638          * The function used to validate email addresses
8639          * @param {String} value The email address
8640          */
8641         'email' : function(v){
8642             return email.test(v);
8643         },
8644         /**
8645          * The error text to display when the email validation function returns false
8646          * @type String
8647          */
8648         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8649         /**
8650          * The keystroke filter mask to be applied on email input
8651          * @type RegExp
8652          */
8653         'emailMask' : /[a-z0-9_\.\-@]/i,
8654
8655         /**
8656          * The function used to validate URLs
8657          * @param {String} value The URL
8658          */
8659         'url' : function(v){
8660             return url.test(v);
8661         },
8662         /**
8663          * The error text to display when the url validation function returns false
8664          * @type String
8665          */
8666         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8667         
8668         /**
8669          * The function used to validate alpha values
8670          * @param {String} value The value
8671          */
8672         'alpha' : function(v){
8673             return alpha.test(v);
8674         },
8675         /**
8676          * The error text to display when the alpha validation function returns false
8677          * @type String
8678          */
8679         'alphaText' : 'This field should only contain letters and _',
8680         /**
8681          * The keystroke filter mask to be applied on alpha input
8682          * @type RegExp
8683          */
8684         'alphaMask' : /[a-z_]/i,
8685
8686         /**
8687          * The function used to validate alphanumeric values
8688          * @param {String} value The value
8689          */
8690         'alphanum' : function(v){
8691             return alphanum.test(v);
8692         },
8693         /**
8694          * The error text to display when the alphanumeric validation function returns false
8695          * @type String
8696          */
8697         'alphanumText' : 'This field should only contain letters, numbers and _',
8698         /**
8699          * The keystroke filter mask to be applied on alphanumeric input
8700          * @type RegExp
8701          */
8702         'alphanumMask' : /[a-z0-9_]/i
8703     };
8704 }();/*
8705  * - LGPL
8706  *
8707  * Input
8708  * 
8709  */
8710
8711 /**
8712  * @class Roo.bootstrap.Input
8713  * @extends Roo.bootstrap.Component
8714  * Bootstrap Input class
8715  * @cfg {Boolean} disabled is it disabled
8716  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8717  * @cfg {String} name name of the input
8718  * @cfg {string} fieldLabel - the label associated
8719  * @cfg {string} placeholder - placeholder to put in text.
8720  * @cfg {string}  before - input group add on before
8721  * @cfg {string} after - input group add on after
8722  * @cfg {string} size - (lg|sm) or leave empty..
8723  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8724  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8725  * @cfg {Number} md colspan out of 12 for computer-sized screens
8726  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8727  * @cfg {string} value default value of the input
8728  * @cfg {Number} labelWidth set the width of label 
8729  * @cfg {Number} labellg set the width of label (1-12)
8730  * @cfg {Number} labelmd set the width of label (1-12)
8731  * @cfg {Number} labelsm set the width of label (1-12)
8732  * @cfg {Number} labelxs set the width of label (1-12)
8733  * @cfg {String} labelAlign (top|left)
8734  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8735  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8736  * @cfg {String} indicatorpos (left|right) default left
8737  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8738  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8739
8740  * @cfg {String} align (left|center|right) Default left
8741  * @cfg {Boolean} forceFeedback (true|false) Default false
8742  * 
8743  * @constructor
8744  * Create a new Input
8745  * @param {Object} config The config object
8746  */
8747
8748 Roo.bootstrap.Input = function(config){
8749     
8750     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8751     
8752     this.addEvents({
8753         /**
8754          * @event focus
8755          * Fires when this field receives input focus.
8756          * @param {Roo.form.Field} this
8757          */
8758         focus : true,
8759         /**
8760          * @event blur
8761          * Fires when this field loses input focus.
8762          * @param {Roo.form.Field} this
8763          */
8764         blur : true,
8765         /**
8766          * @event specialkey
8767          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8768          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8769          * @param {Roo.form.Field} this
8770          * @param {Roo.EventObject} e The event object
8771          */
8772         specialkey : true,
8773         /**
8774          * @event change
8775          * Fires just before the field blurs if the field value has changed.
8776          * @param {Roo.form.Field} this
8777          * @param {Mixed} newValue The new value
8778          * @param {Mixed} oldValue The original value
8779          */
8780         change : true,
8781         /**
8782          * @event invalid
8783          * Fires after the field has been marked as invalid.
8784          * @param {Roo.form.Field} this
8785          * @param {String} msg The validation message
8786          */
8787         invalid : true,
8788         /**
8789          * @event valid
8790          * Fires after the field has been validated with no errors.
8791          * @param {Roo.form.Field} this
8792          */
8793         valid : true,
8794          /**
8795          * @event keyup
8796          * Fires after the key up
8797          * @param {Roo.form.Field} this
8798          * @param {Roo.EventObject}  e The event Object
8799          */
8800         keyup : true
8801     });
8802 };
8803
8804 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8805      /**
8806      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8807       automatic validation (defaults to "keyup").
8808      */
8809     validationEvent : "keyup",
8810      /**
8811      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8812      */
8813     validateOnBlur : true,
8814     /**
8815      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8816      */
8817     validationDelay : 250,
8818      /**
8819      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8820      */
8821     focusClass : "x-form-focus",  // not needed???
8822     
8823        
8824     /**
8825      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8826      */
8827     invalidClass : "has-warning",
8828     
8829     /**
8830      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8831      */
8832     validClass : "has-success",
8833     
8834     /**
8835      * @cfg {Boolean} hasFeedback (true|false) default true
8836      */
8837     hasFeedback : true,
8838     
8839     /**
8840      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8841      */
8842     invalidFeedbackClass : "glyphicon-warning-sign",
8843     
8844     /**
8845      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8846      */
8847     validFeedbackClass : "glyphicon-ok",
8848     
8849     /**
8850      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8851      */
8852     selectOnFocus : false,
8853     
8854      /**
8855      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8856      */
8857     maskRe : null,
8858        /**
8859      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8860      */
8861     vtype : null,
8862     
8863       /**
8864      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8865      */
8866     disableKeyFilter : false,
8867     
8868        /**
8869      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8870      */
8871     disabled : false,
8872      /**
8873      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8874      */
8875     allowBlank : true,
8876     /**
8877      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8878      */
8879     blankText : "Please complete this mandatory field",
8880     
8881      /**
8882      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8883      */
8884     minLength : 0,
8885     /**
8886      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8887      */
8888     maxLength : Number.MAX_VALUE,
8889     /**
8890      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8891      */
8892     minLengthText : "The minimum length for this field is {0}",
8893     /**
8894      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8895      */
8896     maxLengthText : "The maximum length for this field is {0}",
8897   
8898     
8899     /**
8900      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8901      * If available, this function will be called only after the basic validators all return true, and will be passed the
8902      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8903      */
8904     validator : null,
8905     /**
8906      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8907      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8908      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8909      */
8910     regex : null,
8911     /**
8912      * @cfg {String} regexText -- Depricated - use Invalid Text
8913      */
8914     regexText : "",
8915     
8916     /**
8917      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8918      */
8919     invalidText : "",
8920     
8921     
8922     
8923     autocomplete: false,
8924     
8925     
8926     fieldLabel : '',
8927     inputType : 'text',
8928     
8929     name : false,
8930     placeholder: false,
8931     before : false,
8932     after : false,
8933     size : false,
8934     hasFocus : false,
8935     preventMark: false,
8936     isFormField : true,
8937     value : '',
8938     labelWidth : 2,
8939     labelAlign : false,
8940     readOnly : false,
8941     align : false,
8942     formatedValue : false,
8943     forceFeedback : false,
8944     
8945     indicatorpos : 'left',
8946     
8947     labellg : 0,
8948     labelmd : 0,
8949     labelsm : 0,
8950     labelxs : 0,
8951     
8952     capture : '',
8953     accept : '',
8954     
8955     parentLabelAlign : function()
8956     {
8957         var parent = this;
8958         while (parent.parent()) {
8959             parent = parent.parent();
8960             if (typeof(parent.labelAlign) !='undefined') {
8961                 return parent.labelAlign;
8962             }
8963         }
8964         return 'left';
8965         
8966     },
8967     
8968     getAutoCreate : function()
8969     {
8970         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8971         
8972         var id = Roo.id();
8973         
8974         var cfg = {};
8975         
8976         if(this.inputType != 'hidden'){
8977             cfg.cls = 'form-group' //input-group
8978         }
8979         
8980         var input =  {
8981             tag: 'input',
8982             id : id,
8983             type : this.inputType,
8984             value : this.value,
8985             cls : 'form-control',
8986             placeholder : this.placeholder || '',
8987             autocomplete : this.autocomplete || 'new-password'
8988         };
8989         
8990         if(this.capture.length){
8991             input.capture = this.capture;
8992         }
8993         
8994         if(this.accept.length){
8995             input.accept = this.accept + "/*";
8996         }
8997         
8998         if(this.align){
8999             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9000         }
9001         
9002         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9003             input.maxLength = this.maxLength;
9004         }
9005         
9006         if (this.disabled) {
9007             input.disabled=true;
9008         }
9009         
9010         if (this.readOnly) {
9011             input.readonly=true;
9012         }
9013         
9014         if (this.name) {
9015             input.name = this.name;
9016         }
9017         
9018         if (this.size) {
9019             input.cls += ' input-' + this.size;
9020         }
9021         
9022         var settings=this;
9023         ['xs','sm','md','lg'].map(function(size){
9024             if (settings[size]) {
9025                 cfg.cls += ' col-' + size + '-' + settings[size];
9026             }
9027         });
9028         
9029         var inputblock = input;
9030         
9031         var feedback = {
9032             tag: 'span',
9033             cls: 'glyphicon form-control-feedback'
9034         };
9035             
9036         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9037             
9038             inputblock = {
9039                 cls : 'has-feedback',
9040                 cn :  [
9041                     input,
9042                     feedback
9043                 ] 
9044             };  
9045         }
9046         
9047         if (this.before || this.after) {
9048             
9049             inputblock = {
9050                 cls : 'input-group',
9051                 cn :  [] 
9052             };
9053             
9054             if (this.before && typeof(this.before) == 'string') {
9055                 
9056                 inputblock.cn.push({
9057                     tag :'span',
9058                     cls : 'roo-input-before input-group-addon',
9059                     html : this.before
9060                 });
9061             }
9062             if (this.before && typeof(this.before) == 'object') {
9063                 this.before = Roo.factory(this.before);
9064                 
9065                 inputblock.cn.push({
9066                     tag :'span',
9067                     cls : 'roo-input-before input-group-' +
9068                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9069                 });
9070             }
9071             
9072             inputblock.cn.push(input);
9073             
9074             if (this.after && typeof(this.after) == 'string') {
9075                 inputblock.cn.push({
9076                     tag :'span',
9077                     cls : 'roo-input-after input-group-addon',
9078                     html : this.after
9079                 });
9080             }
9081             if (this.after && typeof(this.after) == 'object') {
9082                 this.after = Roo.factory(this.after);
9083                 
9084                 inputblock.cn.push({
9085                     tag :'span',
9086                     cls : 'roo-input-after input-group-' +
9087                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9088                 });
9089             }
9090             
9091             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9092                 inputblock.cls += ' has-feedback';
9093                 inputblock.cn.push(feedback);
9094             }
9095         };
9096         
9097         if (align ==='left' && this.fieldLabel.length) {
9098             
9099             cfg.cls += ' roo-form-group-label-left';
9100             
9101             cfg.cn = [
9102                 {
9103                     tag : 'i',
9104                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9105                     tooltip : 'This field is required'
9106                 },
9107                 {
9108                     tag: 'label',
9109                     'for' :  id,
9110                     cls : 'control-label',
9111                     html : this.fieldLabel
9112
9113                 },
9114                 {
9115                     cls : "", 
9116                     cn: [
9117                         inputblock
9118                     ]
9119                 }
9120             ];
9121             
9122             var labelCfg = cfg.cn[1];
9123             var contentCfg = cfg.cn[2];
9124             
9125             if(this.indicatorpos == 'right'){
9126                 cfg.cn = [
9127                     {
9128                         tag: 'label',
9129                         'for' :  id,
9130                         cls : 'control-label',
9131                         cn : [
9132                             {
9133                                 tag : 'span',
9134                                 html : this.fieldLabel
9135                             },
9136                             {
9137                                 tag : 'i',
9138                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9139                                 tooltip : 'This field is required'
9140                             }
9141                         ]
9142                     },
9143                     {
9144                         cls : "",
9145                         cn: [
9146                             inputblock
9147                         ]
9148                     }
9149
9150                 ];
9151                 
9152                 labelCfg = cfg.cn[0];
9153                 contentCfg = cfg.cn[1];
9154             
9155             }
9156             
9157             if(this.labelWidth > 12){
9158                 labelCfg.style = "width: " + this.labelWidth + 'px';
9159             }
9160             
9161             if(this.labelWidth < 13 && this.labelmd == 0){
9162                 this.labelmd = this.labelWidth;
9163             }
9164             
9165             if(this.labellg > 0){
9166                 labelCfg.cls += ' col-lg-' + this.labellg;
9167                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9168             }
9169             
9170             if(this.labelmd > 0){
9171                 labelCfg.cls += ' col-md-' + this.labelmd;
9172                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9173             }
9174             
9175             if(this.labelsm > 0){
9176                 labelCfg.cls += ' col-sm-' + this.labelsm;
9177                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9178             }
9179             
9180             if(this.labelxs > 0){
9181                 labelCfg.cls += ' col-xs-' + this.labelxs;
9182                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9183             }
9184             
9185             
9186         } else if ( this.fieldLabel.length) {
9187                 
9188             cfg.cn = [
9189                 {
9190                     tag : 'i',
9191                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9192                     tooltip : 'This field is required'
9193                 },
9194                 {
9195                     tag: 'label',
9196                    //cls : 'input-group-addon',
9197                     html : this.fieldLabel
9198
9199                 },
9200
9201                inputblock
9202
9203            ];
9204            
9205            if(this.indicatorpos == 'right'){
9206                 
9207                 cfg.cn = [
9208                     {
9209                         tag: 'label',
9210                        //cls : 'input-group-addon',
9211                         html : this.fieldLabel
9212
9213                     },
9214                     {
9215                         tag : 'i',
9216                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9217                         tooltip : 'This field is required'
9218                     },
9219
9220                    inputblock
9221
9222                ];
9223
9224             }
9225
9226         } else {
9227             
9228             cfg.cn = [
9229
9230                     inputblock
9231
9232             ];
9233                 
9234                 
9235         };
9236         
9237         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9238            cfg.cls += ' navbar-form';
9239         }
9240         
9241         if (this.parentType === 'NavGroup') {
9242            cfg.cls += ' navbar-form';
9243            cfg.tag = 'li';
9244         }
9245         
9246         return cfg;
9247         
9248     },
9249     /**
9250      * return the real input element.
9251      */
9252     inputEl: function ()
9253     {
9254         return this.el.select('input.form-control',true).first();
9255     },
9256     
9257     tooltipEl : function()
9258     {
9259         return this.inputEl();
9260     },
9261     
9262     indicatorEl : function()
9263     {
9264         var indicator = this.el.select('i.roo-required-indicator',true).first();
9265         
9266         if(!indicator){
9267             return false;
9268         }
9269         
9270         return indicator;
9271         
9272     },
9273     
9274     setDisabled : function(v)
9275     {
9276         var i  = this.inputEl().dom;
9277         if (!v) {
9278             i.removeAttribute('disabled');
9279             return;
9280             
9281         }
9282         i.setAttribute('disabled','true');
9283     },
9284     initEvents : function()
9285     {
9286           
9287         this.inputEl().on("keydown" , this.fireKey,  this);
9288         this.inputEl().on("focus", this.onFocus,  this);
9289         this.inputEl().on("blur", this.onBlur,  this);
9290         
9291         this.inputEl().relayEvent('keyup', this);
9292         
9293         this.indicator = this.indicatorEl();
9294         
9295         if(this.indicator){
9296             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9297         }
9298  
9299         // reference to original value for reset
9300         this.originalValue = this.getValue();
9301         //Roo.form.TextField.superclass.initEvents.call(this);
9302         if(this.validationEvent == 'keyup'){
9303             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9304             this.inputEl().on('keyup', this.filterValidation, this);
9305         }
9306         else if(this.validationEvent !== false){
9307             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9308         }
9309         
9310         if(this.selectOnFocus){
9311             this.on("focus", this.preFocus, this);
9312             
9313         }
9314         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9315             this.inputEl().on("keypress", this.filterKeys, this);
9316         } else {
9317             this.inputEl().relayEvent('keypress', this);
9318         }
9319        /* if(this.grow){
9320             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9321             this.el.on("click", this.autoSize,  this);
9322         }
9323         */
9324         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9325             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9326         }
9327         
9328         if (typeof(this.before) == 'object') {
9329             this.before.render(this.el.select('.roo-input-before',true).first());
9330         }
9331         if (typeof(this.after) == 'object') {
9332             this.after.render(this.el.select('.roo-input-after',true).first());
9333         }
9334         
9335         this.inputEl().on('change', this.onChange, this);
9336         
9337     },
9338     filterValidation : function(e){
9339         if(!e.isNavKeyPress()){
9340             this.validationTask.delay(this.validationDelay);
9341         }
9342     },
9343      /**
9344      * Validates the field value
9345      * @return {Boolean} True if the value is valid, else false
9346      */
9347     validate : function(){
9348         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9349         if(this.disabled || this.validateValue(this.getRawValue())){
9350             this.markValid();
9351             return true;
9352         }
9353         
9354         this.markInvalid();
9355         return false;
9356     },
9357     
9358     
9359     /**
9360      * Validates a value according to the field's validation rules and marks the field as invalid
9361      * if the validation fails
9362      * @param {Mixed} value The value to validate
9363      * @return {Boolean} True if the value is valid, else false
9364      */
9365     validateValue : function(value)
9366     {
9367         if(this.getVisibilityEl().hasClass('hidden')){
9368             return true;
9369         }
9370         
9371         if(value.length < 1)  { // if it's blank
9372             if(this.allowBlank){
9373                 return true;
9374             }
9375             return false;
9376         }
9377         
9378         if(value.length < this.minLength){
9379             return false;
9380         }
9381         if(value.length > this.maxLength){
9382             return false;
9383         }
9384         if(this.vtype){
9385             var vt = Roo.form.VTypes;
9386             if(!vt[this.vtype](value, this)){
9387                 return false;
9388             }
9389         }
9390         if(typeof this.validator == "function"){
9391             var msg = this.validator(value);
9392             if(msg !== true){
9393                 return false;
9394             }
9395             if (typeof(msg) == 'string') {
9396                 this.invalidText = msg;
9397             }
9398         }
9399         
9400         if(this.regex && !this.regex.test(value)){
9401             return false;
9402         }
9403         
9404         return true;
9405     },
9406     
9407      // private
9408     fireKey : function(e){
9409         //Roo.log('field ' + e.getKey());
9410         if(e.isNavKeyPress()){
9411             this.fireEvent("specialkey", this, e);
9412         }
9413     },
9414     focus : function (selectText){
9415         if(this.rendered){
9416             this.inputEl().focus();
9417             if(selectText === true){
9418                 this.inputEl().dom.select();
9419             }
9420         }
9421         return this;
9422     } ,
9423     
9424     onFocus : function(){
9425         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9426            // this.el.addClass(this.focusClass);
9427         }
9428         if(!this.hasFocus){
9429             this.hasFocus = true;
9430             this.startValue = this.getValue();
9431             this.fireEvent("focus", this);
9432         }
9433     },
9434     
9435     beforeBlur : Roo.emptyFn,
9436
9437     
9438     // private
9439     onBlur : function(){
9440         this.beforeBlur();
9441         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9442             //this.el.removeClass(this.focusClass);
9443         }
9444         this.hasFocus = false;
9445         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9446             this.validate();
9447         }
9448         var v = this.getValue();
9449         if(String(v) !== String(this.startValue)){
9450             this.fireEvent('change', this, v, this.startValue);
9451         }
9452         this.fireEvent("blur", this);
9453     },
9454     
9455     onChange : function(e)
9456     {
9457         var v = this.getValue();
9458         if(String(v) !== String(this.startValue)){
9459             this.fireEvent('change', this, v, this.startValue);
9460         }
9461         
9462     },
9463     
9464     /**
9465      * Resets the current field value to the originally loaded value and clears any validation messages
9466      */
9467     reset : function(){
9468         this.setValue(this.originalValue);
9469         this.validate();
9470     },
9471      /**
9472      * Returns the name of the field
9473      * @return {Mixed} name The name field
9474      */
9475     getName: function(){
9476         return this.name;
9477     },
9478      /**
9479      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9480      * @return {Mixed} value The field value
9481      */
9482     getValue : function(){
9483         
9484         var v = this.inputEl().getValue();
9485         
9486         return v;
9487     },
9488     /**
9489      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9490      * @return {Mixed} value The field value
9491      */
9492     getRawValue : function(){
9493         var v = this.inputEl().getValue();
9494         
9495         return v;
9496     },
9497     
9498     /**
9499      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9500      * @param {Mixed} value The value to set
9501      */
9502     setRawValue : function(v){
9503         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9504     },
9505     
9506     selectText : function(start, end){
9507         var v = this.getRawValue();
9508         if(v.length > 0){
9509             start = start === undefined ? 0 : start;
9510             end = end === undefined ? v.length : end;
9511             var d = this.inputEl().dom;
9512             if(d.setSelectionRange){
9513                 d.setSelectionRange(start, end);
9514             }else if(d.createTextRange){
9515                 var range = d.createTextRange();
9516                 range.moveStart("character", start);
9517                 range.moveEnd("character", v.length-end);
9518                 range.select();
9519             }
9520         }
9521     },
9522     
9523     /**
9524      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9525      * @param {Mixed} value The value to set
9526      */
9527     setValue : function(v){
9528         this.value = v;
9529         if(this.rendered){
9530             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9531             this.validate();
9532         }
9533     },
9534     
9535     /*
9536     processValue : function(value){
9537         if(this.stripCharsRe){
9538             var newValue = value.replace(this.stripCharsRe, '');
9539             if(newValue !== value){
9540                 this.setRawValue(newValue);
9541                 return newValue;
9542             }
9543         }
9544         return value;
9545     },
9546   */
9547     preFocus : function(){
9548         
9549         if(this.selectOnFocus){
9550             this.inputEl().dom.select();
9551         }
9552     },
9553     filterKeys : function(e){
9554         var k = e.getKey();
9555         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9556             return;
9557         }
9558         var c = e.getCharCode(), cc = String.fromCharCode(c);
9559         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9560             return;
9561         }
9562         if(!this.maskRe.test(cc)){
9563             e.stopEvent();
9564         }
9565     },
9566      /**
9567      * Clear any invalid styles/messages for this field
9568      */
9569     clearInvalid : function(){
9570         
9571         if(!this.el || this.preventMark){ // not rendered
9572             return;
9573         }
9574         
9575      
9576         this.el.removeClass(this.invalidClass);
9577         
9578         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9579             
9580             var feedback = this.el.select('.form-control-feedback', true).first();
9581             
9582             if(feedback){
9583                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9584             }
9585             
9586         }
9587         
9588         if(this.indicator){
9589             this.indicator.removeClass('visible');
9590             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9591         }
9592         
9593         this.fireEvent('valid', this);
9594     },
9595     
9596      /**
9597      * Mark this field as valid
9598      */
9599     markValid : function()
9600     {
9601         if(!this.el  || this.preventMark){ // not rendered...
9602             return;
9603         }
9604         
9605         this.el.removeClass([this.invalidClass, this.validClass]);
9606         
9607         var feedback = this.el.select('.form-control-feedback', true).first();
9608             
9609         if(feedback){
9610             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9611         }
9612         
9613         if(this.indicator){
9614             this.indicator.removeClass('visible');
9615             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9616         }
9617         
9618         if(this.disabled){
9619             return;
9620         }
9621         
9622         if(this.allowBlank && !this.getRawValue().length){
9623             return;
9624         }
9625         
9626         this.el.addClass(this.validClass);
9627         
9628         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9629             
9630             var feedback = this.el.select('.form-control-feedback', true).first();
9631             
9632             if(feedback){
9633                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9634                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9635             }
9636             
9637         }
9638         
9639         this.fireEvent('valid', this);
9640     },
9641     
9642      /**
9643      * Mark this field as invalid
9644      * @param {String} msg The validation message
9645      */
9646     markInvalid : function(msg)
9647     {
9648         if(!this.el  || this.preventMark){ // not rendered
9649             return;
9650         }
9651         
9652         this.el.removeClass([this.invalidClass, this.validClass]);
9653         
9654         var feedback = this.el.select('.form-control-feedback', true).first();
9655             
9656         if(feedback){
9657             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9658         }
9659
9660         if(this.disabled){
9661             return;
9662         }
9663         
9664         if(this.allowBlank && !this.getRawValue().length){
9665             return;
9666         }
9667         
9668         if(this.indicator){
9669             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9670             this.indicator.addClass('visible');
9671         }
9672         
9673         this.el.addClass(this.invalidClass);
9674         
9675         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9676             
9677             var feedback = this.el.select('.form-control-feedback', true).first();
9678             
9679             if(feedback){
9680                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9681                 
9682                 if(this.getValue().length || this.forceFeedback){
9683                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9684                 }
9685                 
9686             }
9687             
9688         }
9689         
9690         this.fireEvent('invalid', this, msg);
9691     },
9692     // private
9693     SafariOnKeyDown : function(event)
9694     {
9695         // this is a workaround for a password hang bug on chrome/ webkit.
9696         if (this.inputEl().dom.type != 'password') {
9697             return;
9698         }
9699         
9700         var isSelectAll = false;
9701         
9702         if(this.inputEl().dom.selectionEnd > 0){
9703             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9704         }
9705         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9706             event.preventDefault();
9707             this.setValue('');
9708             return;
9709         }
9710         
9711         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9712             
9713             event.preventDefault();
9714             // this is very hacky as keydown always get's upper case.
9715             //
9716             var cc = String.fromCharCode(event.getCharCode());
9717             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9718             
9719         }
9720     },
9721     adjustWidth : function(tag, w){
9722         tag = tag.toLowerCase();
9723         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9724             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9725                 if(tag == 'input'){
9726                     return w + 2;
9727                 }
9728                 if(tag == 'textarea'){
9729                     return w-2;
9730                 }
9731             }else if(Roo.isOpera){
9732                 if(tag == 'input'){
9733                     return w + 2;
9734                 }
9735                 if(tag == 'textarea'){
9736                     return w-2;
9737                 }
9738             }
9739         }
9740         return w;
9741     },
9742     
9743     setFieldLabel : function(v)
9744     {
9745         if(!this.rendered){
9746             return;
9747         }
9748         
9749         if(this.indicator){
9750             var ar = this.el.select('label > span',true);
9751             
9752             if (ar.elements.length) {
9753                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9754                 this.fieldLabel = v;
9755                 return;
9756             }
9757             
9758             var br = this.el.select('label',true);
9759             
9760             if(br.elements.length) {
9761                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9762                 this.fieldLabel = v;
9763                 return;
9764             }
9765             
9766             Roo.log('Cannot Found any of label > span || label in input');
9767             return;
9768         }
9769         
9770         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9771         this.fieldLabel = v;
9772         
9773         
9774     }
9775 });
9776
9777  
9778 /*
9779  * - LGPL
9780  *
9781  * Input
9782  * 
9783  */
9784
9785 /**
9786  * @class Roo.bootstrap.TextArea
9787  * @extends Roo.bootstrap.Input
9788  * Bootstrap TextArea class
9789  * @cfg {Number} cols Specifies the visible width of a text area
9790  * @cfg {Number} rows Specifies the visible number of lines in a text area
9791  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9792  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9793  * @cfg {string} html text
9794  * 
9795  * @constructor
9796  * Create a new TextArea
9797  * @param {Object} config The config object
9798  */
9799
9800 Roo.bootstrap.TextArea = function(config){
9801     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9802    
9803 };
9804
9805 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9806      
9807     cols : false,
9808     rows : 5,
9809     readOnly : false,
9810     warp : 'soft',
9811     resize : false,
9812     value: false,
9813     html: false,
9814     
9815     getAutoCreate : function(){
9816         
9817         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9818         
9819         var id = Roo.id();
9820         
9821         var cfg = {};
9822         
9823         if(this.inputType != 'hidden'){
9824             cfg.cls = 'form-group' //input-group
9825         }
9826         
9827         var input =  {
9828             tag: 'textarea',
9829             id : id,
9830             warp : this.warp,
9831             rows : this.rows,
9832             value : this.value || '',
9833             html: this.html || '',
9834             cls : 'form-control',
9835             placeholder : this.placeholder || '' 
9836             
9837         };
9838         
9839         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9840             input.maxLength = this.maxLength;
9841         }
9842         
9843         if(this.resize){
9844             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9845         }
9846         
9847         if(this.cols){
9848             input.cols = this.cols;
9849         }
9850         
9851         if (this.readOnly) {
9852             input.readonly = true;
9853         }
9854         
9855         if (this.name) {
9856             input.name = this.name;
9857         }
9858         
9859         if (this.size) {
9860             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9861         }
9862         
9863         var settings=this;
9864         ['xs','sm','md','lg'].map(function(size){
9865             if (settings[size]) {
9866                 cfg.cls += ' col-' + size + '-' + settings[size];
9867             }
9868         });
9869         
9870         var inputblock = input;
9871         
9872         if(this.hasFeedback && !this.allowBlank){
9873             
9874             var feedback = {
9875                 tag: 'span',
9876                 cls: 'glyphicon form-control-feedback'
9877             };
9878
9879             inputblock = {
9880                 cls : 'has-feedback',
9881                 cn :  [
9882                     input,
9883                     feedback
9884                 ] 
9885             };  
9886         }
9887         
9888         
9889         if (this.before || this.after) {
9890             
9891             inputblock = {
9892                 cls : 'input-group',
9893                 cn :  [] 
9894             };
9895             if (this.before) {
9896                 inputblock.cn.push({
9897                     tag :'span',
9898                     cls : 'input-group-addon',
9899                     html : this.before
9900                 });
9901             }
9902             
9903             inputblock.cn.push(input);
9904             
9905             if(this.hasFeedback && !this.allowBlank){
9906                 inputblock.cls += ' has-feedback';
9907                 inputblock.cn.push(feedback);
9908             }
9909             
9910             if (this.after) {
9911                 inputblock.cn.push({
9912                     tag :'span',
9913                     cls : 'input-group-addon',
9914                     html : this.after
9915                 });
9916             }
9917             
9918         }
9919         
9920         if (align ==='left' && this.fieldLabel.length) {
9921             cfg.cn = [
9922                 {
9923                     tag: 'label',
9924                     'for' :  id,
9925                     cls : 'control-label',
9926                     html : this.fieldLabel
9927                 },
9928                 {
9929                     cls : "",
9930                     cn: [
9931                         inputblock
9932                     ]
9933                 }
9934
9935             ];
9936             
9937             if(this.labelWidth > 12){
9938                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9939             }
9940
9941             if(this.labelWidth < 13 && this.labelmd == 0){
9942                 this.labelmd = this.labelWidth;
9943             }
9944
9945             if(this.labellg > 0){
9946                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9947                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9948             }
9949
9950             if(this.labelmd > 0){
9951                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9952                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9953             }
9954
9955             if(this.labelsm > 0){
9956                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9957                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9958             }
9959
9960             if(this.labelxs > 0){
9961                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9962                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9963             }
9964             
9965         } else if ( this.fieldLabel.length) {
9966             cfg.cn = [
9967
9968                {
9969                    tag: 'label',
9970                    //cls : 'input-group-addon',
9971                    html : this.fieldLabel
9972
9973                },
9974
9975                inputblock
9976
9977            ];
9978
9979         } else {
9980
9981             cfg.cn = [
9982
9983                 inputblock
9984
9985             ];
9986                 
9987         }
9988         
9989         if (this.disabled) {
9990             input.disabled=true;
9991         }
9992         
9993         return cfg;
9994         
9995     },
9996     /**
9997      * return the real textarea element.
9998      */
9999     inputEl: function ()
10000     {
10001         return this.el.select('textarea.form-control',true).first();
10002     },
10003     
10004     /**
10005      * Clear any invalid styles/messages for this field
10006      */
10007     clearInvalid : function()
10008     {
10009         
10010         if(!this.el || this.preventMark){ // not rendered
10011             return;
10012         }
10013         
10014         var label = this.el.select('label', true).first();
10015         var icon = this.el.select('i.fa-star', true).first();
10016         
10017         if(label && icon){
10018             icon.remove();
10019         }
10020         
10021         this.el.removeClass(this.invalidClass);
10022         
10023         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10024             
10025             var feedback = this.el.select('.form-control-feedback', true).first();
10026             
10027             if(feedback){
10028                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10029             }
10030             
10031         }
10032         
10033         this.fireEvent('valid', this);
10034     },
10035     
10036      /**
10037      * Mark this field as valid
10038      */
10039     markValid : function()
10040     {
10041         if(!this.el  || this.preventMark){ // not rendered
10042             return;
10043         }
10044         
10045         this.el.removeClass([this.invalidClass, this.validClass]);
10046         
10047         var feedback = this.el.select('.form-control-feedback', true).first();
10048             
10049         if(feedback){
10050             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10051         }
10052
10053         if(this.disabled || this.allowBlank){
10054             return;
10055         }
10056         
10057         var label = this.el.select('label', true).first();
10058         var icon = this.el.select('i.fa-star', true).first();
10059         
10060         if(label && icon){
10061             icon.remove();
10062         }
10063         
10064         this.el.addClass(this.validClass);
10065         
10066         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10067             
10068             var feedback = this.el.select('.form-control-feedback', true).first();
10069             
10070             if(feedback){
10071                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10072                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10073             }
10074             
10075         }
10076         
10077         this.fireEvent('valid', this);
10078     },
10079     
10080      /**
10081      * Mark this field as invalid
10082      * @param {String} msg The validation message
10083      */
10084     markInvalid : function(msg)
10085     {
10086         if(!this.el  || this.preventMark){ // not rendered
10087             return;
10088         }
10089         
10090         this.el.removeClass([this.invalidClass, this.validClass]);
10091         
10092         var feedback = this.el.select('.form-control-feedback', true).first();
10093             
10094         if(feedback){
10095             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10096         }
10097
10098         if(this.disabled || this.allowBlank){
10099             return;
10100         }
10101         
10102         var label = this.el.select('label', true).first();
10103         var icon = this.el.select('i.fa-star', true).first();
10104         
10105         if(!this.getValue().length && label && !icon){
10106             this.el.createChild({
10107                 tag : 'i',
10108                 cls : 'text-danger fa fa-lg fa-star',
10109                 tooltip : 'This field is required',
10110                 style : 'margin-right:5px;'
10111             }, label, true);
10112         }
10113
10114         this.el.addClass(this.invalidClass);
10115         
10116         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10117             
10118             var feedback = this.el.select('.form-control-feedback', true).first();
10119             
10120             if(feedback){
10121                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10122                 
10123                 if(this.getValue().length || this.forceFeedback){
10124                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10125                 }
10126                 
10127             }
10128             
10129         }
10130         
10131         this.fireEvent('invalid', this, msg);
10132     }
10133 });
10134
10135  
10136 /*
10137  * - LGPL
10138  *
10139  * trigger field - base class for combo..
10140  * 
10141  */
10142  
10143 /**
10144  * @class Roo.bootstrap.TriggerField
10145  * @extends Roo.bootstrap.Input
10146  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10147  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10148  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10149  * for which you can provide a custom implementation.  For example:
10150  * <pre><code>
10151 var trigger = new Roo.bootstrap.TriggerField();
10152 trigger.onTriggerClick = myTriggerFn;
10153 trigger.applyTo('my-field');
10154 </code></pre>
10155  *
10156  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10157  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10158  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10159  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10160  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10161
10162  * @constructor
10163  * Create a new TriggerField.
10164  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10165  * to the base TextField)
10166  */
10167 Roo.bootstrap.TriggerField = function(config){
10168     this.mimicing = false;
10169     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10170 };
10171
10172 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10173     /**
10174      * @cfg {String} triggerClass A CSS class to apply to the trigger
10175      */
10176      /**
10177      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10178      */
10179     hideTrigger:false,
10180
10181     /**
10182      * @cfg {Boolean} removable (true|false) special filter default false
10183      */
10184     removable : false,
10185     
10186     /** @cfg {Boolean} grow @hide */
10187     /** @cfg {Number} growMin @hide */
10188     /** @cfg {Number} growMax @hide */
10189
10190     /**
10191      * @hide 
10192      * @method
10193      */
10194     autoSize: Roo.emptyFn,
10195     // private
10196     monitorTab : true,
10197     // private
10198     deferHeight : true,
10199
10200     
10201     actionMode : 'wrap',
10202     
10203     caret : false,
10204     
10205     
10206     getAutoCreate : function(){
10207        
10208         var align = this.labelAlign || this.parentLabelAlign();
10209         
10210         var id = Roo.id();
10211         
10212         var cfg = {
10213             cls: 'form-group' //input-group
10214         };
10215         
10216         
10217         var input =  {
10218             tag: 'input',
10219             id : id,
10220             type : this.inputType,
10221             cls : 'form-control',
10222             autocomplete: 'new-password',
10223             placeholder : this.placeholder || '' 
10224             
10225         };
10226         if (this.name) {
10227             input.name = this.name;
10228         }
10229         if (this.size) {
10230             input.cls += ' input-' + this.size;
10231         }
10232         
10233         if (this.disabled) {
10234             input.disabled=true;
10235         }
10236         
10237         var inputblock = input;
10238         
10239         if(this.hasFeedback && !this.allowBlank){
10240             
10241             var feedback = {
10242                 tag: 'span',
10243                 cls: 'glyphicon form-control-feedback'
10244             };
10245             
10246             if(this.removable && !this.editable && !this.tickable){
10247                 inputblock = {
10248                     cls : 'has-feedback',
10249                     cn :  [
10250                         inputblock,
10251                         {
10252                             tag: 'button',
10253                             html : 'x',
10254                             cls : 'roo-combo-removable-btn close'
10255                         },
10256                         feedback
10257                     ] 
10258                 };
10259             } else {
10260                 inputblock = {
10261                     cls : 'has-feedback',
10262                     cn :  [
10263                         inputblock,
10264                         feedback
10265                     ] 
10266                 };
10267             }
10268
10269         } else {
10270             if(this.removable && !this.editable && !this.tickable){
10271                 inputblock = {
10272                     cls : 'roo-removable',
10273                     cn :  [
10274                         inputblock,
10275                         {
10276                             tag: 'button',
10277                             html : 'x',
10278                             cls : 'roo-combo-removable-btn close'
10279                         }
10280                     ] 
10281                 };
10282             }
10283         }
10284         
10285         if (this.before || this.after) {
10286             
10287             inputblock = {
10288                 cls : 'input-group',
10289                 cn :  [] 
10290             };
10291             if (this.before) {
10292                 inputblock.cn.push({
10293                     tag :'span',
10294                     cls : 'input-group-addon',
10295                     html : this.before
10296                 });
10297             }
10298             
10299             inputblock.cn.push(input);
10300             
10301             if(this.hasFeedback && !this.allowBlank){
10302                 inputblock.cls += ' has-feedback';
10303                 inputblock.cn.push(feedback);
10304             }
10305             
10306             if (this.after) {
10307                 inputblock.cn.push({
10308                     tag :'span',
10309                     cls : 'input-group-addon',
10310                     html : this.after
10311                 });
10312             }
10313             
10314         };
10315         
10316         var box = {
10317             tag: 'div',
10318             cn: [
10319                 {
10320                     tag: 'input',
10321                     type : 'hidden',
10322                     cls: 'form-hidden-field'
10323                 },
10324                 inputblock
10325             ]
10326             
10327         };
10328         
10329         if(this.multiple){
10330             box = {
10331                 tag: 'div',
10332                 cn: [
10333                     {
10334                         tag: 'input',
10335                         type : 'hidden',
10336                         cls: 'form-hidden-field'
10337                     },
10338                     {
10339                         tag: 'ul',
10340                         cls: 'roo-select2-choices',
10341                         cn:[
10342                             {
10343                                 tag: 'li',
10344                                 cls: 'roo-select2-search-field',
10345                                 cn: [
10346
10347                                     inputblock
10348                                 ]
10349                             }
10350                         ]
10351                     }
10352                 ]
10353             }
10354         };
10355         
10356         var combobox = {
10357             cls: 'roo-select2-container input-group',
10358             cn: [
10359                 box
10360 //                {
10361 //                    tag: 'ul',
10362 //                    cls: 'typeahead typeahead-long dropdown-menu',
10363 //                    style: 'display:none'
10364 //                }
10365             ]
10366         };
10367         
10368         if(!this.multiple && this.showToggleBtn){
10369             
10370             var caret = {
10371                         tag: 'span',
10372                         cls: 'caret'
10373              };
10374             if (this.caret != false) {
10375                 caret = {
10376                      tag: 'i',
10377                      cls: 'fa fa-' + this.caret
10378                 };
10379                 
10380             }
10381             
10382             combobox.cn.push({
10383                 tag :'span',
10384                 cls : 'input-group-addon btn dropdown-toggle',
10385                 cn : [
10386                     caret,
10387                     {
10388                         tag: 'span',
10389                         cls: 'combobox-clear',
10390                         cn  : [
10391                             {
10392                                 tag : 'i',
10393                                 cls: 'icon-remove'
10394                             }
10395                         ]
10396                     }
10397                 ]
10398
10399             })
10400         }
10401         
10402         if(this.multiple){
10403             combobox.cls += ' roo-select2-container-multi';
10404         }
10405         
10406         if (align ==='left' && this.fieldLabel.length) {
10407             
10408             cfg.cls += ' roo-form-group-label-left';
10409
10410             cfg.cn = [
10411                 {
10412                     tag : 'i',
10413                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10414                     tooltip : 'This field is required'
10415                 },
10416                 {
10417                     tag: 'label',
10418                     'for' :  id,
10419                     cls : 'control-label',
10420                     html : this.fieldLabel
10421
10422                 },
10423                 {
10424                     cls : "", 
10425                     cn: [
10426                         combobox
10427                     ]
10428                 }
10429
10430             ];
10431             
10432             var labelCfg = cfg.cn[1];
10433             var contentCfg = cfg.cn[2];
10434             
10435             if(this.indicatorpos == 'right'){
10436                 cfg.cn = [
10437                     {
10438                         tag: 'label',
10439                         'for' :  id,
10440                         cls : 'control-label',
10441                         cn : [
10442                             {
10443                                 tag : 'span',
10444                                 html : this.fieldLabel
10445                             },
10446                             {
10447                                 tag : 'i',
10448                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10449                                 tooltip : 'This field is required'
10450                             }
10451                         ]
10452                     },
10453                     {
10454                         cls : "", 
10455                         cn: [
10456                             combobox
10457                         ]
10458                     }
10459
10460                 ];
10461                 
10462                 labelCfg = cfg.cn[0];
10463                 contentCfg = cfg.cn[1];
10464             }
10465             
10466             if(this.labelWidth > 12){
10467                 labelCfg.style = "width: " + this.labelWidth + 'px';
10468             }
10469             
10470             if(this.labelWidth < 13 && this.labelmd == 0){
10471                 this.labelmd = this.labelWidth;
10472             }
10473             
10474             if(this.labellg > 0){
10475                 labelCfg.cls += ' col-lg-' + this.labellg;
10476                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10477             }
10478             
10479             if(this.labelmd > 0){
10480                 labelCfg.cls += ' col-md-' + this.labelmd;
10481                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10482             }
10483             
10484             if(this.labelsm > 0){
10485                 labelCfg.cls += ' col-sm-' + this.labelsm;
10486                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10487             }
10488             
10489             if(this.labelxs > 0){
10490                 labelCfg.cls += ' col-xs-' + this.labelxs;
10491                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10492             }
10493             
10494         } else if ( this.fieldLabel.length) {
10495 //                Roo.log(" label");
10496             cfg.cn = [
10497                 {
10498                    tag : 'i',
10499                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10500                    tooltip : 'This field is required'
10501                },
10502                {
10503                    tag: 'label',
10504                    //cls : 'input-group-addon',
10505                    html : this.fieldLabel
10506
10507                },
10508
10509                combobox
10510
10511             ];
10512             
10513             if(this.indicatorpos == 'right'){
10514                 
10515                 cfg.cn = [
10516                     {
10517                        tag: 'label',
10518                        cn : [
10519                            {
10520                                tag : 'span',
10521                                html : this.fieldLabel
10522                            },
10523                            {
10524                               tag : 'i',
10525                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10526                               tooltip : 'This field is required'
10527                            }
10528                        ]
10529
10530                     },
10531                     combobox
10532
10533                 ];
10534
10535             }
10536
10537         } else {
10538             
10539 //                Roo.log(" no label && no align");
10540                 cfg = combobox
10541                      
10542                 
10543         }
10544         
10545         var settings=this;
10546         ['xs','sm','md','lg'].map(function(size){
10547             if (settings[size]) {
10548                 cfg.cls += ' col-' + size + '-' + settings[size];
10549             }
10550         });
10551         
10552         return cfg;
10553         
10554     },
10555     
10556     
10557     
10558     // private
10559     onResize : function(w, h){
10560 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10561 //        if(typeof w == 'number'){
10562 //            var x = w - this.trigger.getWidth();
10563 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10564 //            this.trigger.setStyle('left', x+'px');
10565 //        }
10566     },
10567
10568     // private
10569     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10570
10571     // private
10572     getResizeEl : function(){
10573         return this.inputEl();
10574     },
10575
10576     // private
10577     getPositionEl : function(){
10578         return this.inputEl();
10579     },
10580
10581     // private
10582     alignErrorIcon : function(){
10583         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10584     },
10585
10586     // private
10587     initEvents : function(){
10588         
10589         this.createList();
10590         
10591         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10592         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10593         if(!this.multiple && this.showToggleBtn){
10594             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10595             if(this.hideTrigger){
10596                 this.trigger.setDisplayed(false);
10597             }
10598             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10599         }
10600         
10601         if(this.multiple){
10602             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10603         }
10604         
10605         if(this.removable && !this.editable && !this.tickable){
10606             var close = this.closeTriggerEl();
10607             
10608             if(close){
10609                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10610                 close.on('click', this.removeBtnClick, this, close);
10611             }
10612         }
10613         
10614         //this.trigger.addClassOnOver('x-form-trigger-over');
10615         //this.trigger.addClassOnClick('x-form-trigger-click');
10616         
10617         //if(!this.width){
10618         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10619         //}
10620     },
10621     
10622     closeTriggerEl : function()
10623     {
10624         var close = this.el.select('.roo-combo-removable-btn', true).first();
10625         return close ? close : false;
10626     },
10627     
10628     removeBtnClick : function(e, h, el)
10629     {
10630         e.preventDefault();
10631         
10632         if(this.fireEvent("remove", this) !== false){
10633             this.reset();
10634             this.fireEvent("afterremove", this)
10635         }
10636     },
10637     
10638     createList : function()
10639     {
10640         this.list = Roo.get(document.body).createChild({
10641             tag: 'ul',
10642             cls: 'typeahead typeahead-long dropdown-menu',
10643             style: 'display:none'
10644         });
10645         
10646         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10647         
10648     },
10649
10650     // private
10651     initTrigger : function(){
10652        
10653     },
10654
10655     // private
10656     onDestroy : function(){
10657         if(this.trigger){
10658             this.trigger.removeAllListeners();
10659           //  this.trigger.remove();
10660         }
10661         //if(this.wrap){
10662         //    this.wrap.remove();
10663         //}
10664         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10665     },
10666
10667     // private
10668     onFocus : function(){
10669         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10670         /*
10671         if(!this.mimicing){
10672             this.wrap.addClass('x-trigger-wrap-focus');
10673             this.mimicing = true;
10674             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10675             if(this.monitorTab){
10676                 this.el.on("keydown", this.checkTab, this);
10677             }
10678         }
10679         */
10680     },
10681
10682     // private
10683     checkTab : function(e){
10684         if(e.getKey() == e.TAB){
10685             this.triggerBlur();
10686         }
10687     },
10688
10689     // private
10690     onBlur : function(){
10691         // do nothing
10692     },
10693
10694     // private
10695     mimicBlur : function(e, t){
10696         /*
10697         if(!this.wrap.contains(t) && this.validateBlur()){
10698             this.triggerBlur();
10699         }
10700         */
10701     },
10702
10703     // private
10704     triggerBlur : function(){
10705         this.mimicing = false;
10706         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10707         if(this.monitorTab){
10708             this.el.un("keydown", this.checkTab, this);
10709         }
10710         //this.wrap.removeClass('x-trigger-wrap-focus');
10711         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10712     },
10713
10714     // private
10715     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10716     validateBlur : function(e, t){
10717         return true;
10718     },
10719
10720     // private
10721     onDisable : function(){
10722         this.inputEl().dom.disabled = true;
10723         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10724         //if(this.wrap){
10725         //    this.wrap.addClass('x-item-disabled');
10726         //}
10727     },
10728
10729     // private
10730     onEnable : function(){
10731         this.inputEl().dom.disabled = false;
10732         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10733         //if(this.wrap){
10734         //    this.el.removeClass('x-item-disabled');
10735         //}
10736     },
10737
10738     // private
10739     onShow : function(){
10740         var ae = this.getActionEl();
10741         
10742         if(ae){
10743             ae.dom.style.display = '';
10744             ae.dom.style.visibility = 'visible';
10745         }
10746     },
10747
10748     // private
10749     
10750     onHide : function(){
10751         var ae = this.getActionEl();
10752         ae.dom.style.display = 'none';
10753     },
10754
10755     /**
10756      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10757      * by an implementing function.
10758      * @method
10759      * @param {EventObject} e
10760      */
10761     onTriggerClick : Roo.emptyFn
10762 });
10763  /*
10764  * Based on:
10765  * Ext JS Library 1.1.1
10766  * Copyright(c) 2006-2007, Ext JS, LLC.
10767  *
10768  * Originally Released Under LGPL - original licence link has changed is not relivant.
10769  *
10770  * Fork - LGPL
10771  * <script type="text/javascript">
10772  */
10773
10774
10775 /**
10776  * @class Roo.data.SortTypes
10777  * @singleton
10778  * Defines the default sorting (casting?) comparison functions used when sorting data.
10779  */
10780 Roo.data.SortTypes = {
10781     /**
10782      * Default sort that does nothing
10783      * @param {Mixed} s The value being converted
10784      * @return {Mixed} The comparison value
10785      */
10786     none : function(s){
10787         return s;
10788     },
10789     
10790     /**
10791      * The regular expression used to strip tags
10792      * @type {RegExp}
10793      * @property
10794      */
10795     stripTagsRE : /<\/?[^>]+>/gi,
10796     
10797     /**
10798      * Strips all HTML tags to sort on text only
10799      * @param {Mixed} s The value being converted
10800      * @return {String} The comparison value
10801      */
10802     asText : function(s){
10803         return String(s).replace(this.stripTagsRE, "");
10804     },
10805     
10806     /**
10807      * Strips all HTML tags to sort on text only - Case insensitive
10808      * @param {Mixed} s The value being converted
10809      * @return {String} The comparison value
10810      */
10811     asUCText : function(s){
10812         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10813     },
10814     
10815     /**
10816      * Case insensitive string
10817      * @param {Mixed} s The value being converted
10818      * @return {String} The comparison value
10819      */
10820     asUCString : function(s) {
10821         return String(s).toUpperCase();
10822     },
10823     
10824     /**
10825      * Date sorting
10826      * @param {Mixed} s The value being converted
10827      * @return {Number} The comparison value
10828      */
10829     asDate : function(s) {
10830         if(!s){
10831             return 0;
10832         }
10833         if(s instanceof Date){
10834             return s.getTime();
10835         }
10836         return Date.parse(String(s));
10837     },
10838     
10839     /**
10840      * Float sorting
10841      * @param {Mixed} s The value being converted
10842      * @return {Float} The comparison value
10843      */
10844     asFloat : function(s) {
10845         var val = parseFloat(String(s).replace(/,/g, ""));
10846         if(isNaN(val)) {
10847             val = 0;
10848         }
10849         return val;
10850     },
10851     
10852     /**
10853      * Integer sorting
10854      * @param {Mixed} s The value being converted
10855      * @return {Number} The comparison value
10856      */
10857     asInt : function(s) {
10858         var val = parseInt(String(s).replace(/,/g, ""));
10859         if(isNaN(val)) {
10860             val = 0;
10861         }
10862         return val;
10863     }
10864 };/*
10865  * Based on:
10866  * Ext JS Library 1.1.1
10867  * Copyright(c) 2006-2007, Ext JS, LLC.
10868  *
10869  * Originally Released Under LGPL - original licence link has changed is not relivant.
10870  *
10871  * Fork - LGPL
10872  * <script type="text/javascript">
10873  */
10874
10875 /**
10876 * @class Roo.data.Record
10877  * Instances of this class encapsulate both record <em>definition</em> information, and record
10878  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10879  * to access Records cached in an {@link Roo.data.Store} object.<br>
10880  * <p>
10881  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10882  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10883  * objects.<br>
10884  * <p>
10885  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10886  * @constructor
10887  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10888  * {@link #create}. The parameters are the same.
10889  * @param {Array} data An associative Array of data values keyed by the field name.
10890  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10891  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10892  * not specified an integer id is generated.
10893  */
10894 Roo.data.Record = function(data, id){
10895     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10896     this.data = data;
10897 };
10898
10899 /**
10900  * Generate a constructor for a specific record layout.
10901  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10902  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10903  * Each field definition object may contain the following properties: <ul>
10904  * <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,
10905  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10906  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10907  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10908  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10909  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10910  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10911  * this may be omitted.</p></li>
10912  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10913  * <ul><li>auto (Default, implies no conversion)</li>
10914  * <li>string</li>
10915  * <li>int</li>
10916  * <li>float</li>
10917  * <li>boolean</li>
10918  * <li>date</li></ul></p></li>
10919  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10920  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10921  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10922  * by the Reader into an object that will be stored in the Record. It is passed the
10923  * following parameters:<ul>
10924  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10925  * </ul></p></li>
10926  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10927  * </ul>
10928  * <br>usage:<br><pre><code>
10929 var TopicRecord = Roo.data.Record.create(
10930     {name: 'title', mapping: 'topic_title'},
10931     {name: 'author', mapping: 'username'},
10932     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10933     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10934     {name: 'lastPoster', mapping: 'user2'},
10935     {name: 'excerpt', mapping: 'post_text'}
10936 );
10937
10938 var myNewRecord = new TopicRecord({
10939     title: 'Do my job please',
10940     author: 'noobie',
10941     totalPosts: 1,
10942     lastPost: new Date(),
10943     lastPoster: 'Animal',
10944     excerpt: 'No way dude!'
10945 });
10946 myStore.add(myNewRecord);
10947 </code></pre>
10948  * @method create
10949  * @static
10950  */
10951 Roo.data.Record.create = function(o){
10952     var f = function(){
10953         f.superclass.constructor.apply(this, arguments);
10954     };
10955     Roo.extend(f, Roo.data.Record);
10956     var p = f.prototype;
10957     p.fields = new Roo.util.MixedCollection(false, function(field){
10958         return field.name;
10959     });
10960     for(var i = 0, len = o.length; i < len; i++){
10961         p.fields.add(new Roo.data.Field(o[i]));
10962     }
10963     f.getField = function(name){
10964         return p.fields.get(name);  
10965     };
10966     return f;
10967 };
10968
10969 Roo.data.Record.AUTO_ID = 1000;
10970 Roo.data.Record.EDIT = 'edit';
10971 Roo.data.Record.REJECT = 'reject';
10972 Roo.data.Record.COMMIT = 'commit';
10973
10974 Roo.data.Record.prototype = {
10975     /**
10976      * Readonly flag - true if this record has been modified.
10977      * @type Boolean
10978      */
10979     dirty : false,
10980     editing : false,
10981     error: null,
10982     modified: null,
10983
10984     // private
10985     join : function(store){
10986         this.store = store;
10987     },
10988
10989     /**
10990      * Set the named field to the specified value.
10991      * @param {String} name The name of the field to set.
10992      * @param {Object} value The value to set the field to.
10993      */
10994     set : function(name, value){
10995         if(this.data[name] == value){
10996             return;
10997         }
10998         this.dirty = true;
10999         if(!this.modified){
11000             this.modified = {};
11001         }
11002         if(typeof this.modified[name] == 'undefined'){
11003             this.modified[name] = this.data[name];
11004         }
11005         this.data[name] = value;
11006         if(!this.editing && this.store){
11007             this.store.afterEdit(this);
11008         }       
11009     },
11010
11011     /**
11012      * Get the value of the named field.
11013      * @param {String} name The name of the field to get the value of.
11014      * @return {Object} The value of the field.
11015      */
11016     get : function(name){
11017         return this.data[name]; 
11018     },
11019
11020     // private
11021     beginEdit : function(){
11022         this.editing = true;
11023         this.modified = {}; 
11024     },
11025
11026     // private
11027     cancelEdit : function(){
11028         this.editing = false;
11029         delete this.modified;
11030     },
11031
11032     // private
11033     endEdit : function(){
11034         this.editing = false;
11035         if(this.dirty && this.store){
11036             this.store.afterEdit(this);
11037         }
11038     },
11039
11040     /**
11041      * Usually called by the {@link Roo.data.Store} which owns the Record.
11042      * Rejects all changes made to the Record since either creation, or the last commit operation.
11043      * Modified fields are reverted to their original values.
11044      * <p>
11045      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11046      * of reject operations.
11047      */
11048     reject : function(){
11049         var m = this.modified;
11050         for(var n in m){
11051             if(typeof m[n] != "function"){
11052                 this.data[n] = m[n];
11053             }
11054         }
11055         this.dirty = false;
11056         delete this.modified;
11057         this.editing = false;
11058         if(this.store){
11059             this.store.afterReject(this);
11060         }
11061     },
11062
11063     /**
11064      * Usually called by the {@link Roo.data.Store} which owns the Record.
11065      * Commits all changes made to the Record since either creation, or the last commit operation.
11066      * <p>
11067      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11068      * of commit operations.
11069      */
11070     commit : function(){
11071         this.dirty = false;
11072         delete this.modified;
11073         this.editing = false;
11074         if(this.store){
11075             this.store.afterCommit(this);
11076         }
11077     },
11078
11079     // private
11080     hasError : function(){
11081         return this.error != null;
11082     },
11083
11084     // private
11085     clearError : function(){
11086         this.error = null;
11087     },
11088
11089     /**
11090      * Creates a copy of this record.
11091      * @param {String} id (optional) A new record id if you don't want to use this record's id
11092      * @return {Record}
11093      */
11094     copy : function(newId) {
11095         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11096     }
11097 };/*
11098  * Based on:
11099  * Ext JS Library 1.1.1
11100  * Copyright(c) 2006-2007, Ext JS, LLC.
11101  *
11102  * Originally Released Under LGPL - original licence link has changed is not relivant.
11103  *
11104  * Fork - LGPL
11105  * <script type="text/javascript">
11106  */
11107
11108
11109
11110 /**
11111  * @class Roo.data.Store
11112  * @extends Roo.util.Observable
11113  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11114  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11115  * <p>
11116  * 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
11117  * has no knowledge of the format of the data returned by the Proxy.<br>
11118  * <p>
11119  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11120  * instances from the data object. These records are cached and made available through accessor functions.
11121  * @constructor
11122  * Creates a new Store.
11123  * @param {Object} config A config object containing the objects needed for the Store to access data,
11124  * and read the data into Records.
11125  */
11126 Roo.data.Store = function(config){
11127     this.data = new Roo.util.MixedCollection(false);
11128     this.data.getKey = function(o){
11129         return o.id;
11130     };
11131     this.baseParams = {};
11132     // private
11133     this.paramNames = {
11134         "start" : "start",
11135         "limit" : "limit",
11136         "sort" : "sort",
11137         "dir" : "dir",
11138         "multisort" : "_multisort"
11139     };
11140
11141     if(config && config.data){
11142         this.inlineData = config.data;
11143         delete config.data;
11144     }
11145
11146     Roo.apply(this, config);
11147     
11148     if(this.reader){ // reader passed
11149         this.reader = Roo.factory(this.reader, Roo.data);
11150         this.reader.xmodule = this.xmodule || false;
11151         if(!this.recordType){
11152             this.recordType = this.reader.recordType;
11153         }
11154         if(this.reader.onMetaChange){
11155             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11156         }
11157     }
11158
11159     if(this.recordType){
11160         this.fields = this.recordType.prototype.fields;
11161     }
11162     this.modified = [];
11163
11164     this.addEvents({
11165         /**
11166          * @event datachanged
11167          * Fires when the data cache has changed, and a widget which is using this Store
11168          * as a Record cache should refresh its view.
11169          * @param {Store} this
11170          */
11171         datachanged : true,
11172         /**
11173          * @event metachange
11174          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11175          * @param {Store} this
11176          * @param {Object} meta The JSON metadata
11177          */
11178         metachange : true,
11179         /**
11180          * @event add
11181          * Fires when Records have been added to the Store
11182          * @param {Store} this
11183          * @param {Roo.data.Record[]} records The array of Records added
11184          * @param {Number} index The index at which the record(s) were added
11185          */
11186         add : true,
11187         /**
11188          * @event remove
11189          * Fires when a Record has been removed from the Store
11190          * @param {Store} this
11191          * @param {Roo.data.Record} record The Record that was removed
11192          * @param {Number} index The index at which the record was removed
11193          */
11194         remove : true,
11195         /**
11196          * @event update
11197          * Fires when a Record has been updated
11198          * @param {Store} this
11199          * @param {Roo.data.Record} record The Record that was updated
11200          * @param {String} operation The update operation being performed.  Value may be one of:
11201          * <pre><code>
11202  Roo.data.Record.EDIT
11203  Roo.data.Record.REJECT
11204  Roo.data.Record.COMMIT
11205          * </code></pre>
11206          */
11207         update : true,
11208         /**
11209          * @event clear
11210          * Fires when the data cache has been cleared.
11211          * @param {Store} this
11212          */
11213         clear : true,
11214         /**
11215          * @event beforeload
11216          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11217          * the load action will be canceled.
11218          * @param {Store} this
11219          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11220          */
11221         beforeload : true,
11222         /**
11223          * @event beforeloadadd
11224          * Fires after a new set of Records has been loaded.
11225          * @param {Store} this
11226          * @param {Roo.data.Record[]} records The Records that were loaded
11227          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11228          */
11229         beforeloadadd : true,
11230         /**
11231          * @event load
11232          * Fires after a new set of Records has been loaded, before they are added to the store.
11233          * @param {Store} this
11234          * @param {Roo.data.Record[]} records The Records that were loaded
11235          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11236          * @params {Object} return from reader
11237          */
11238         load : true,
11239         /**
11240          * @event loadexception
11241          * Fires if an exception occurs in the Proxy during loading.
11242          * Called with the signature of the Proxy's "loadexception" event.
11243          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11244          * 
11245          * @param {Proxy} 
11246          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11247          * @param {Object} load options 
11248          * @param {Object} jsonData from your request (normally this contains the Exception)
11249          */
11250         loadexception : true
11251     });
11252     
11253     if(this.proxy){
11254         this.proxy = Roo.factory(this.proxy, Roo.data);
11255         this.proxy.xmodule = this.xmodule || false;
11256         this.relayEvents(this.proxy,  ["loadexception"]);
11257     }
11258     this.sortToggle = {};
11259     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11260
11261     Roo.data.Store.superclass.constructor.call(this);
11262
11263     if(this.inlineData){
11264         this.loadData(this.inlineData);
11265         delete this.inlineData;
11266     }
11267 };
11268
11269 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11270      /**
11271     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11272     * without a remote query - used by combo/forms at present.
11273     */
11274     
11275     /**
11276     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11277     */
11278     /**
11279     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11280     */
11281     /**
11282     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11283     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11284     */
11285     /**
11286     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11287     * on any HTTP request
11288     */
11289     /**
11290     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11291     */
11292     /**
11293     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11294     */
11295     multiSort: false,
11296     /**
11297     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11298     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11299     */
11300     remoteSort : false,
11301
11302     /**
11303     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11304      * loaded or when a record is removed. (defaults to false).
11305     */
11306     pruneModifiedRecords : false,
11307
11308     // private
11309     lastOptions : null,
11310
11311     /**
11312      * Add Records to the Store and fires the add event.
11313      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11314      */
11315     add : function(records){
11316         records = [].concat(records);
11317         for(var i = 0, len = records.length; i < len; i++){
11318             records[i].join(this);
11319         }
11320         var index = this.data.length;
11321         this.data.addAll(records);
11322         this.fireEvent("add", this, records, index);
11323     },
11324
11325     /**
11326      * Remove a Record from the Store and fires the remove event.
11327      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11328      */
11329     remove : function(record){
11330         var index = this.data.indexOf(record);
11331         this.data.removeAt(index);
11332  
11333         if(this.pruneModifiedRecords){
11334             this.modified.remove(record);
11335         }
11336         this.fireEvent("remove", this, record, index);
11337     },
11338
11339     /**
11340      * Remove all Records from the Store and fires the clear event.
11341      */
11342     removeAll : function(){
11343         this.data.clear();
11344         if(this.pruneModifiedRecords){
11345             this.modified = [];
11346         }
11347         this.fireEvent("clear", this);
11348     },
11349
11350     /**
11351      * Inserts Records to the Store at the given index and fires the add event.
11352      * @param {Number} index The start index at which to insert the passed Records.
11353      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11354      */
11355     insert : function(index, records){
11356         records = [].concat(records);
11357         for(var i = 0, len = records.length; i < len; i++){
11358             this.data.insert(index, records[i]);
11359             records[i].join(this);
11360         }
11361         this.fireEvent("add", this, records, index);
11362     },
11363
11364     /**
11365      * Get the index within the cache of the passed Record.
11366      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11367      * @return {Number} The index of the passed Record. Returns -1 if not found.
11368      */
11369     indexOf : function(record){
11370         return this.data.indexOf(record);
11371     },
11372
11373     /**
11374      * Get the index within the cache of the Record with the passed id.
11375      * @param {String} id The id of the Record to find.
11376      * @return {Number} The index of the Record. Returns -1 if not found.
11377      */
11378     indexOfId : function(id){
11379         return this.data.indexOfKey(id);
11380     },
11381
11382     /**
11383      * Get the Record with the specified id.
11384      * @param {String} id The id of the Record to find.
11385      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11386      */
11387     getById : function(id){
11388         return this.data.key(id);
11389     },
11390
11391     /**
11392      * Get the Record at the specified index.
11393      * @param {Number} index The index of the Record to find.
11394      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11395      */
11396     getAt : function(index){
11397         return this.data.itemAt(index);
11398     },
11399
11400     /**
11401      * Returns a range of Records between specified indices.
11402      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11403      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11404      * @return {Roo.data.Record[]} An array of Records
11405      */
11406     getRange : function(start, end){
11407         return this.data.getRange(start, end);
11408     },
11409
11410     // private
11411     storeOptions : function(o){
11412         o = Roo.apply({}, o);
11413         delete o.callback;
11414         delete o.scope;
11415         this.lastOptions = o;
11416     },
11417
11418     /**
11419      * Loads the Record cache from the configured Proxy using the configured Reader.
11420      * <p>
11421      * If using remote paging, then the first load call must specify the <em>start</em>
11422      * and <em>limit</em> properties in the options.params property to establish the initial
11423      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11424      * <p>
11425      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11426      * and this call will return before the new data has been loaded. Perform any post-processing
11427      * in a callback function, or in a "load" event handler.</strong>
11428      * <p>
11429      * @param {Object} options An object containing properties which control loading options:<ul>
11430      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11431      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11432      * passed the following arguments:<ul>
11433      * <li>r : Roo.data.Record[]</li>
11434      * <li>options: Options object from the load call</li>
11435      * <li>success: Boolean success indicator</li></ul></li>
11436      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11437      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11438      * </ul>
11439      */
11440     load : function(options){
11441         options = options || {};
11442         if(this.fireEvent("beforeload", this, options) !== false){
11443             this.storeOptions(options);
11444             var p = Roo.apply(options.params || {}, this.baseParams);
11445             // if meta was not loaded from remote source.. try requesting it.
11446             if (!this.reader.metaFromRemote) {
11447                 p._requestMeta = 1;
11448             }
11449             if(this.sortInfo && this.remoteSort){
11450                 var pn = this.paramNames;
11451                 p[pn["sort"]] = this.sortInfo.field;
11452                 p[pn["dir"]] = this.sortInfo.direction;
11453             }
11454             if (this.multiSort) {
11455                 var pn = this.paramNames;
11456                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11457             }
11458             
11459             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11460         }
11461     },
11462
11463     /**
11464      * Reloads the Record cache from the configured Proxy using the configured Reader and
11465      * the options from the last load operation performed.
11466      * @param {Object} options (optional) An object containing properties which may override the options
11467      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11468      * the most recently used options are reused).
11469      */
11470     reload : function(options){
11471         this.load(Roo.applyIf(options||{}, this.lastOptions));
11472     },
11473
11474     // private
11475     // Called as a callback by the Reader during a load operation.
11476     loadRecords : function(o, options, success){
11477         if(!o || success === false){
11478             if(success !== false){
11479                 this.fireEvent("load", this, [], options, o);
11480             }
11481             if(options.callback){
11482                 options.callback.call(options.scope || this, [], options, false);
11483             }
11484             return;
11485         }
11486         // if data returned failure - throw an exception.
11487         if (o.success === false) {
11488             // show a message if no listener is registered.
11489             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11490                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11491             }
11492             // loadmask wil be hooked into this..
11493             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11494             return;
11495         }
11496         var r = o.records, t = o.totalRecords || r.length;
11497         
11498         this.fireEvent("beforeloadadd", this, r, options, o);
11499         
11500         if(!options || options.add !== true){
11501             if(this.pruneModifiedRecords){
11502                 this.modified = [];
11503             }
11504             for(var i = 0, len = r.length; i < len; i++){
11505                 r[i].join(this);
11506             }
11507             if(this.snapshot){
11508                 this.data = this.snapshot;
11509                 delete this.snapshot;
11510             }
11511             this.data.clear();
11512             this.data.addAll(r);
11513             this.totalLength = t;
11514             this.applySort();
11515             this.fireEvent("datachanged", this);
11516         }else{
11517             this.totalLength = Math.max(t, this.data.length+r.length);
11518             this.add(r);
11519         }
11520         
11521         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11522                 
11523             var e = new Roo.data.Record({});
11524
11525             e.set(this.parent.displayField, this.parent.emptyTitle);
11526             e.set(this.parent.valueField, '');
11527
11528             this.insert(0, e);
11529         }
11530             
11531         this.fireEvent("load", this, r, options, o);
11532         if(options.callback){
11533             options.callback.call(options.scope || this, r, options, true);
11534         }
11535     },
11536
11537
11538     /**
11539      * Loads data from a passed data block. A Reader which understands the format of the data
11540      * must have been configured in the constructor.
11541      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11542      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11543      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11544      */
11545     loadData : function(o, append){
11546         var r = this.reader.readRecords(o);
11547         this.loadRecords(r, {add: append}, true);
11548     },
11549
11550     /**
11551      * Gets the number of cached records.
11552      * <p>
11553      * <em>If using paging, this may not be the total size of the dataset. If the data object
11554      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11555      * the data set size</em>
11556      */
11557     getCount : function(){
11558         return this.data.length || 0;
11559     },
11560
11561     /**
11562      * Gets the total number of records in the dataset as returned by the server.
11563      * <p>
11564      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11565      * the dataset size</em>
11566      */
11567     getTotalCount : function(){
11568         return this.totalLength || 0;
11569     },
11570
11571     /**
11572      * Returns the sort state of the Store as an object with two properties:
11573      * <pre><code>
11574  field {String} The name of the field by which the Records are sorted
11575  direction {String} The sort order, "ASC" or "DESC"
11576      * </code></pre>
11577      */
11578     getSortState : function(){
11579         return this.sortInfo;
11580     },
11581
11582     // private
11583     applySort : function(){
11584         if(this.sortInfo && !this.remoteSort){
11585             var s = this.sortInfo, f = s.field;
11586             var st = this.fields.get(f).sortType;
11587             var fn = function(r1, r2){
11588                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11589                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11590             };
11591             this.data.sort(s.direction, fn);
11592             if(this.snapshot && this.snapshot != this.data){
11593                 this.snapshot.sort(s.direction, fn);
11594             }
11595         }
11596     },
11597
11598     /**
11599      * Sets the default sort column and order to be used by the next load operation.
11600      * @param {String} fieldName The name of the field to sort by.
11601      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11602      */
11603     setDefaultSort : function(field, dir){
11604         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11605     },
11606
11607     /**
11608      * Sort the Records.
11609      * If remote sorting is used, the sort is performed on the server, and the cache is
11610      * reloaded. If local sorting is used, the cache is sorted internally.
11611      * @param {String} fieldName The name of the field to sort by.
11612      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11613      */
11614     sort : function(fieldName, dir){
11615         var f = this.fields.get(fieldName);
11616         if(!dir){
11617             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11618             
11619             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11620                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11621             }else{
11622                 dir = f.sortDir;
11623             }
11624         }
11625         this.sortToggle[f.name] = dir;
11626         this.sortInfo = {field: f.name, direction: dir};
11627         if(!this.remoteSort){
11628             this.applySort();
11629             this.fireEvent("datachanged", this);
11630         }else{
11631             this.load(this.lastOptions);
11632         }
11633     },
11634
11635     /**
11636      * Calls the specified function for each of the Records in the cache.
11637      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11638      * Returning <em>false</em> aborts and exits the iteration.
11639      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11640      */
11641     each : function(fn, scope){
11642         this.data.each(fn, scope);
11643     },
11644
11645     /**
11646      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11647      * (e.g., during paging).
11648      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11649      */
11650     getModifiedRecords : function(){
11651         return this.modified;
11652     },
11653
11654     // private
11655     createFilterFn : function(property, value, anyMatch){
11656         if(!value.exec){ // not a regex
11657             value = String(value);
11658             if(value.length == 0){
11659                 return false;
11660             }
11661             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11662         }
11663         return function(r){
11664             return value.test(r.data[property]);
11665         };
11666     },
11667
11668     /**
11669      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11670      * @param {String} property A field on your records
11671      * @param {Number} start The record index to start at (defaults to 0)
11672      * @param {Number} end The last record index to include (defaults to length - 1)
11673      * @return {Number} The sum
11674      */
11675     sum : function(property, start, end){
11676         var rs = this.data.items, v = 0;
11677         start = start || 0;
11678         end = (end || end === 0) ? end : rs.length-1;
11679
11680         for(var i = start; i <= end; i++){
11681             v += (rs[i].data[property] || 0);
11682         }
11683         return v;
11684     },
11685
11686     /**
11687      * Filter the records by a specified property.
11688      * @param {String} field A field on your records
11689      * @param {String/RegExp} value Either a string that the field
11690      * should start with or a RegExp to test against the field
11691      * @param {Boolean} anyMatch True to match any part not just the beginning
11692      */
11693     filter : function(property, value, anyMatch){
11694         var fn = this.createFilterFn(property, value, anyMatch);
11695         return fn ? this.filterBy(fn) : this.clearFilter();
11696     },
11697
11698     /**
11699      * Filter by a function. The specified function will be called with each
11700      * record in this data source. If the function returns true the record is included,
11701      * otherwise it is filtered.
11702      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11703      * @param {Object} scope (optional) The scope of the function (defaults to this)
11704      */
11705     filterBy : function(fn, scope){
11706         this.snapshot = this.snapshot || this.data;
11707         this.data = this.queryBy(fn, scope||this);
11708         this.fireEvent("datachanged", this);
11709     },
11710
11711     /**
11712      * Query the records by a specified property.
11713      * @param {String} field A field on your records
11714      * @param {String/RegExp} value Either a string that the field
11715      * should start with or a RegExp to test against the field
11716      * @param {Boolean} anyMatch True to match any part not just the beginning
11717      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11718      */
11719     query : function(property, value, anyMatch){
11720         var fn = this.createFilterFn(property, value, anyMatch);
11721         return fn ? this.queryBy(fn) : this.data.clone();
11722     },
11723
11724     /**
11725      * Query by a function. The specified function will be called with each
11726      * record in this data source. If the function returns true the record is included
11727      * in the results.
11728      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11729      * @param {Object} scope (optional) The scope of the function (defaults to this)
11730       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11731      **/
11732     queryBy : function(fn, scope){
11733         var data = this.snapshot || this.data;
11734         return data.filterBy(fn, scope||this);
11735     },
11736
11737     /**
11738      * Collects unique values for a particular dataIndex from this store.
11739      * @param {String} dataIndex The property to collect
11740      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11741      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11742      * @return {Array} An array of the unique values
11743      **/
11744     collect : function(dataIndex, allowNull, bypassFilter){
11745         var d = (bypassFilter === true && this.snapshot) ?
11746                 this.snapshot.items : this.data.items;
11747         var v, sv, r = [], l = {};
11748         for(var i = 0, len = d.length; i < len; i++){
11749             v = d[i].data[dataIndex];
11750             sv = String(v);
11751             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11752                 l[sv] = true;
11753                 r[r.length] = v;
11754             }
11755         }
11756         return r;
11757     },
11758
11759     /**
11760      * Revert to a view of the Record cache with no filtering applied.
11761      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11762      */
11763     clearFilter : function(suppressEvent){
11764         if(this.snapshot && this.snapshot != this.data){
11765             this.data = this.snapshot;
11766             delete this.snapshot;
11767             if(suppressEvent !== true){
11768                 this.fireEvent("datachanged", this);
11769             }
11770         }
11771     },
11772
11773     // private
11774     afterEdit : function(record){
11775         if(this.modified.indexOf(record) == -1){
11776             this.modified.push(record);
11777         }
11778         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11779     },
11780     
11781     // private
11782     afterReject : function(record){
11783         this.modified.remove(record);
11784         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11785     },
11786
11787     // private
11788     afterCommit : function(record){
11789         this.modified.remove(record);
11790         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11791     },
11792
11793     /**
11794      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11795      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11796      */
11797     commitChanges : function(){
11798         var m = this.modified.slice(0);
11799         this.modified = [];
11800         for(var i = 0, len = m.length; i < len; i++){
11801             m[i].commit();
11802         }
11803     },
11804
11805     /**
11806      * Cancel outstanding changes on all changed records.
11807      */
11808     rejectChanges : function(){
11809         var m = this.modified.slice(0);
11810         this.modified = [];
11811         for(var i = 0, len = m.length; i < len; i++){
11812             m[i].reject();
11813         }
11814     },
11815
11816     onMetaChange : function(meta, rtype, o){
11817         this.recordType = rtype;
11818         this.fields = rtype.prototype.fields;
11819         delete this.snapshot;
11820         this.sortInfo = meta.sortInfo || this.sortInfo;
11821         this.modified = [];
11822         this.fireEvent('metachange', this, this.reader.meta);
11823     },
11824     
11825     moveIndex : function(data, type)
11826     {
11827         var index = this.indexOf(data);
11828         
11829         var newIndex = index + type;
11830         
11831         this.remove(data);
11832         
11833         this.insert(newIndex, data);
11834         
11835     }
11836 });/*
11837  * Based on:
11838  * Ext JS Library 1.1.1
11839  * Copyright(c) 2006-2007, Ext JS, LLC.
11840  *
11841  * Originally Released Under LGPL - original licence link has changed is not relivant.
11842  *
11843  * Fork - LGPL
11844  * <script type="text/javascript">
11845  */
11846
11847 /**
11848  * @class Roo.data.SimpleStore
11849  * @extends Roo.data.Store
11850  * Small helper class to make creating Stores from Array data easier.
11851  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11852  * @cfg {Array} fields An array of field definition objects, or field name strings.
11853  * @cfg {Array} data The multi-dimensional array of data
11854  * @constructor
11855  * @param {Object} config
11856  */
11857 Roo.data.SimpleStore = function(config){
11858     Roo.data.SimpleStore.superclass.constructor.call(this, {
11859         isLocal : true,
11860         reader: new Roo.data.ArrayReader({
11861                 id: config.id
11862             },
11863             Roo.data.Record.create(config.fields)
11864         ),
11865         proxy : new Roo.data.MemoryProxy(config.data)
11866     });
11867     this.load();
11868 };
11869 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11870  * Based on:
11871  * Ext JS Library 1.1.1
11872  * Copyright(c) 2006-2007, Ext JS, LLC.
11873  *
11874  * Originally Released Under LGPL - original licence link has changed is not relivant.
11875  *
11876  * Fork - LGPL
11877  * <script type="text/javascript">
11878  */
11879
11880 /**
11881 /**
11882  * @extends Roo.data.Store
11883  * @class Roo.data.JsonStore
11884  * Small helper class to make creating Stores for JSON data easier. <br/>
11885 <pre><code>
11886 var store = new Roo.data.JsonStore({
11887     url: 'get-images.php',
11888     root: 'images',
11889     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11890 });
11891 </code></pre>
11892  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11893  * JsonReader and HttpProxy (unless inline data is provided).</b>
11894  * @cfg {Array} fields An array of field definition objects, or field name strings.
11895  * @constructor
11896  * @param {Object} config
11897  */
11898 Roo.data.JsonStore = function(c){
11899     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11900         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11901         reader: new Roo.data.JsonReader(c, c.fields)
11902     }));
11903 };
11904 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11905  * Based on:
11906  * Ext JS Library 1.1.1
11907  * Copyright(c) 2006-2007, Ext JS, LLC.
11908  *
11909  * Originally Released Under LGPL - original licence link has changed is not relivant.
11910  *
11911  * Fork - LGPL
11912  * <script type="text/javascript">
11913  */
11914
11915  
11916 Roo.data.Field = function(config){
11917     if(typeof config == "string"){
11918         config = {name: config};
11919     }
11920     Roo.apply(this, config);
11921     
11922     if(!this.type){
11923         this.type = "auto";
11924     }
11925     
11926     var st = Roo.data.SortTypes;
11927     // named sortTypes are supported, here we look them up
11928     if(typeof this.sortType == "string"){
11929         this.sortType = st[this.sortType];
11930     }
11931     
11932     // set default sortType for strings and dates
11933     if(!this.sortType){
11934         switch(this.type){
11935             case "string":
11936                 this.sortType = st.asUCString;
11937                 break;
11938             case "date":
11939                 this.sortType = st.asDate;
11940                 break;
11941             default:
11942                 this.sortType = st.none;
11943         }
11944     }
11945
11946     // define once
11947     var stripRe = /[\$,%]/g;
11948
11949     // prebuilt conversion function for this field, instead of
11950     // switching every time we're reading a value
11951     if(!this.convert){
11952         var cv, dateFormat = this.dateFormat;
11953         switch(this.type){
11954             case "":
11955             case "auto":
11956             case undefined:
11957                 cv = function(v){ return v; };
11958                 break;
11959             case "string":
11960                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11961                 break;
11962             case "int":
11963                 cv = function(v){
11964                     return v !== undefined && v !== null && v !== '' ?
11965                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11966                     };
11967                 break;
11968             case "float":
11969                 cv = function(v){
11970                     return v !== undefined && v !== null && v !== '' ?
11971                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11972                     };
11973                 break;
11974             case "bool":
11975             case "boolean":
11976                 cv = function(v){ return v === true || v === "true" || v == 1; };
11977                 break;
11978             case "date":
11979                 cv = function(v){
11980                     if(!v){
11981                         return '';
11982                     }
11983                     if(v instanceof Date){
11984                         return v;
11985                     }
11986                     if(dateFormat){
11987                         if(dateFormat == "timestamp"){
11988                             return new Date(v*1000);
11989                         }
11990                         return Date.parseDate(v, dateFormat);
11991                     }
11992                     var parsed = Date.parse(v);
11993                     return parsed ? new Date(parsed) : null;
11994                 };
11995              break;
11996             
11997         }
11998         this.convert = cv;
11999     }
12000 };
12001
12002 Roo.data.Field.prototype = {
12003     dateFormat: null,
12004     defaultValue: "",
12005     mapping: null,
12006     sortType : null,
12007     sortDir : "ASC"
12008 };/*
12009  * Based on:
12010  * Ext JS Library 1.1.1
12011  * Copyright(c) 2006-2007, Ext JS, LLC.
12012  *
12013  * Originally Released Under LGPL - original licence link has changed is not relivant.
12014  *
12015  * Fork - LGPL
12016  * <script type="text/javascript">
12017  */
12018  
12019 // Base class for reading structured data from a data source.  This class is intended to be
12020 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12021
12022 /**
12023  * @class Roo.data.DataReader
12024  * Base class for reading structured data from a data source.  This class is intended to be
12025  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12026  */
12027
12028 Roo.data.DataReader = function(meta, recordType){
12029     
12030     this.meta = meta;
12031     
12032     this.recordType = recordType instanceof Array ? 
12033         Roo.data.Record.create(recordType) : recordType;
12034 };
12035
12036 Roo.data.DataReader.prototype = {
12037      /**
12038      * Create an empty record
12039      * @param {Object} data (optional) - overlay some values
12040      * @return {Roo.data.Record} record created.
12041      */
12042     newRow :  function(d) {
12043         var da =  {};
12044         this.recordType.prototype.fields.each(function(c) {
12045             switch( c.type) {
12046                 case 'int' : da[c.name] = 0; break;
12047                 case 'date' : da[c.name] = new Date(); break;
12048                 case 'float' : da[c.name] = 0.0; break;
12049                 case 'boolean' : da[c.name] = false; break;
12050                 default : da[c.name] = ""; break;
12051             }
12052             
12053         });
12054         return new this.recordType(Roo.apply(da, d));
12055     }
12056     
12057 };/*
12058  * Based on:
12059  * Ext JS Library 1.1.1
12060  * Copyright(c) 2006-2007, Ext JS, LLC.
12061  *
12062  * Originally Released Under LGPL - original licence link has changed is not relivant.
12063  *
12064  * Fork - LGPL
12065  * <script type="text/javascript">
12066  */
12067
12068 /**
12069  * @class Roo.data.DataProxy
12070  * @extends Roo.data.Observable
12071  * This class is an abstract base class for implementations which provide retrieval of
12072  * unformatted data objects.<br>
12073  * <p>
12074  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12075  * (of the appropriate type which knows how to parse the data object) to provide a block of
12076  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12077  * <p>
12078  * Custom implementations must implement the load method as described in
12079  * {@link Roo.data.HttpProxy#load}.
12080  */
12081 Roo.data.DataProxy = function(){
12082     this.addEvents({
12083         /**
12084          * @event beforeload
12085          * Fires before a network request is made to retrieve a data object.
12086          * @param {Object} This DataProxy object.
12087          * @param {Object} params The params parameter to the load function.
12088          */
12089         beforeload : true,
12090         /**
12091          * @event load
12092          * Fires before the load method's callback is called.
12093          * @param {Object} This DataProxy object.
12094          * @param {Object} o The data object.
12095          * @param {Object} arg The callback argument object passed to the load function.
12096          */
12097         load : true,
12098         /**
12099          * @event loadexception
12100          * Fires if an Exception occurs during data retrieval.
12101          * @param {Object} This DataProxy object.
12102          * @param {Object} o The data object.
12103          * @param {Object} arg The callback argument object passed to the load function.
12104          * @param {Object} e The Exception.
12105          */
12106         loadexception : true
12107     });
12108     Roo.data.DataProxy.superclass.constructor.call(this);
12109 };
12110
12111 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12112
12113     /**
12114      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12115      */
12116 /*
12117  * Based on:
12118  * Ext JS Library 1.1.1
12119  * Copyright(c) 2006-2007, Ext JS, LLC.
12120  *
12121  * Originally Released Under LGPL - original licence link has changed is not relivant.
12122  *
12123  * Fork - LGPL
12124  * <script type="text/javascript">
12125  */
12126 /**
12127  * @class Roo.data.MemoryProxy
12128  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12129  * to the Reader when its load method is called.
12130  * @constructor
12131  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12132  */
12133 Roo.data.MemoryProxy = function(data){
12134     if (data.data) {
12135         data = data.data;
12136     }
12137     Roo.data.MemoryProxy.superclass.constructor.call(this);
12138     this.data = data;
12139 };
12140
12141 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12142     
12143     /**
12144      * Load data from the requested source (in this case an in-memory
12145      * data object passed to the constructor), read the data object into
12146      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12147      * process that block using the passed callback.
12148      * @param {Object} params This parameter is not used by the MemoryProxy class.
12149      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12150      * object into a block of Roo.data.Records.
12151      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12152      * The function must be passed <ul>
12153      * <li>The Record block object</li>
12154      * <li>The "arg" argument from the load function</li>
12155      * <li>A boolean success indicator</li>
12156      * </ul>
12157      * @param {Object} scope The scope in which to call the callback
12158      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12159      */
12160     load : function(params, reader, callback, scope, arg){
12161         params = params || {};
12162         var result;
12163         try {
12164             result = reader.readRecords(this.data);
12165         }catch(e){
12166             this.fireEvent("loadexception", this, arg, null, e);
12167             callback.call(scope, null, arg, false);
12168             return;
12169         }
12170         callback.call(scope, result, arg, true);
12171     },
12172     
12173     // private
12174     update : function(params, records){
12175         
12176     }
12177 });/*
12178  * Based on:
12179  * Ext JS Library 1.1.1
12180  * Copyright(c) 2006-2007, Ext JS, LLC.
12181  *
12182  * Originally Released Under LGPL - original licence link has changed is not relivant.
12183  *
12184  * Fork - LGPL
12185  * <script type="text/javascript">
12186  */
12187 /**
12188  * @class Roo.data.HttpProxy
12189  * @extends Roo.data.DataProxy
12190  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12191  * configured to reference a certain URL.<br><br>
12192  * <p>
12193  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12194  * from which the running page was served.<br><br>
12195  * <p>
12196  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12197  * <p>
12198  * Be aware that to enable the browser to parse an XML document, the server must set
12199  * the Content-Type header in the HTTP response to "text/xml".
12200  * @constructor
12201  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12202  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12203  * will be used to make the request.
12204  */
12205 Roo.data.HttpProxy = function(conn){
12206     Roo.data.HttpProxy.superclass.constructor.call(this);
12207     // is conn a conn config or a real conn?
12208     this.conn = conn;
12209     this.useAjax = !conn || !conn.events;
12210   
12211 };
12212
12213 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12214     // thse are take from connection...
12215     
12216     /**
12217      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12218      */
12219     /**
12220      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12221      * extra parameters to each request made by this object. (defaults to undefined)
12222      */
12223     /**
12224      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12225      *  to each request made by this object. (defaults to undefined)
12226      */
12227     /**
12228      * @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)
12229      */
12230     /**
12231      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12232      */
12233      /**
12234      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12235      * @type Boolean
12236      */
12237   
12238
12239     /**
12240      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12241      * @type Boolean
12242      */
12243     /**
12244      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12245      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12246      * a finer-grained basis than the DataProxy events.
12247      */
12248     getConnection : function(){
12249         return this.useAjax ? Roo.Ajax : this.conn;
12250     },
12251
12252     /**
12253      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12254      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12255      * process that block using the passed callback.
12256      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12257      * for the request to the remote server.
12258      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12259      * object into a block of Roo.data.Records.
12260      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12261      * The function must be passed <ul>
12262      * <li>The Record block object</li>
12263      * <li>The "arg" argument from the load function</li>
12264      * <li>A boolean success indicator</li>
12265      * </ul>
12266      * @param {Object} scope The scope in which to call the callback
12267      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12268      */
12269     load : function(params, reader, callback, scope, arg){
12270         if(this.fireEvent("beforeload", this, params) !== false){
12271             var  o = {
12272                 params : params || {},
12273                 request: {
12274                     callback : callback,
12275                     scope : scope,
12276                     arg : arg
12277                 },
12278                 reader: reader,
12279                 callback : this.loadResponse,
12280                 scope: this
12281             };
12282             if(this.useAjax){
12283                 Roo.applyIf(o, this.conn);
12284                 if(this.activeRequest){
12285                     Roo.Ajax.abort(this.activeRequest);
12286                 }
12287                 this.activeRequest = Roo.Ajax.request(o);
12288             }else{
12289                 this.conn.request(o);
12290             }
12291         }else{
12292             callback.call(scope||this, null, arg, false);
12293         }
12294     },
12295
12296     // private
12297     loadResponse : function(o, success, response){
12298         delete this.activeRequest;
12299         if(!success){
12300             this.fireEvent("loadexception", this, o, response);
12301             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12302             return;
12303         }
12304         var result;
12305         try {
12306             result = o.reader.read(response);
12307         }catch(e){
12308             this.fireEvent("loadexception", this, o, response, e);
12309             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12310             return;
12311         }
12312         
12313         this.fireEvent("load", this, o, o.request.arg);
12314         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12315     },
12316
12317     // private
12318     update : function(dataSet){
12319
12320     },
12321
12322     // private
12323     updateResponse : function(dataSet){
12324
12325     }
12326 });/*
12327  * Based on:
12328  * Ext JS Library 1.1.1
12329  * Copyright(c) 2006-2007, Ext JS, LLC.
12330  *
12331  * Originally Released Under LGPL - original licence link has changed is not relivant.
12332  *
12333  * Fork - LGPL
12334  * <script type="text/javascript">
12335  */
12336
12337 /**
12338  * @class Roo.data.ScriptTagProxy
12339  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12340  * other than the originating domain of the running page.<br><br>
12341  * <p>
12342  * <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
12343  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12344  * <p>
12345  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12346  * source code that is used as the source inside a &lt;script> tag.<br><br>
12347  * <p>
12348  * In order for the browser to process the returned data, the server must wrap the data object
12349  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12350  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12351  * depending on whether the callback name was passed:
12352  * <p>
12353  * <pre><code>
12354 boolean scriptTag = false;
12355 String cb = request.getParameter("callback");
12356 if (cb != null) {
12357     scriptTag = true;
12358     response.setContentType("text/javascript");
12359 } else {
12360     response.setContentType("application/x-json");
12361 }
12362 Writer out = response.getWriter();
12363 if (scriptTag) {
12364     out.write(cb + "(");
12365 }
12366 out.print(dataBlock.toJsonString());
12367 if (scriptTag) {
12368     out.write(");");
12369 }
12370 </pre></code>
12371  *
12372  * @constructor
12373  * @param {Object} config A configuration object.
12374  */
12375 Roo.data.ScriptTagProxy = function(config){
12376     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12377     Roo.apply(this, config);
12378     this.head = document.getElementsByTagName("head")[0];
12379 };
12380
12381 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12382
12383 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12384     /**
12385      * @cfg {String} url The URL from which to request the data object.
12386      */
12387     /**
12388      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12389      */
12390     timeout : 30000,
12391     /**
12392      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12393      * the server the name of the callback function set up by the load call to process the returned data object.
12394      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12395      * javascript output which calls this named function passing the data object as its only parameter.
12396      */
12397     callbackParam : "callback",
12398     /**
12399      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12400      * name to the request.
12401      */
12402     nocache : true,
12403
12404     /**
12405      * Load data from the configured URL, read the data object into
12406      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12407      * process that block using the passed callback.
12408      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12409      * for the request to the remote server.
12410      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12411      * object into a block of Roo.data.Records.
12412      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12413      * The function must be passed <ul>
12414      * <li>The Record block object</li>
12415      * <li>The "arg" argument from the load function</li>
12416      * <li>A boolean success indicator</li>
12417      * </ul>
12418      * @param {Object} scope The scope in which to call the callback
12419      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12420      */
12421     load : function(params, reader, callback, scope, arg){
12422         if(this.fireEvent("beforeload", this, params) !== false){
12423
12424             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12425
12426             var url = this.url;
12427             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12428             if(this.nocache){
12429                 url += "&_dc=" + (new Date().getTime());
12430             }
12431             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12432             var trans = {
12433                 id : transId,
12434                 cb : "stcCallback"+transId,
12435                 scriptId : "stcScript"+transId,
12436                 params : params,
12437                 arg : arg,
12438                 url : url,
12439                 callback : callback,
12440                 scope : scope,
12441                 reader : reader
12442             };
12443             var conn = this;
12444
12445             window[trans.cb] = function(o){
12446                 conn.handleResponse(o, trans);
12447             };
12448
12449             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12450
12451             if(this.autoAbort !== false){
12452                 this.abort();
12453             }
12454
12455             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12456
12457             var script = document.createElement("script");
12458             script.setAttribute("src", url);
12459             script.setAttribute("type", "text/javascript");
12460             script.setAttribute("id", trans.scriptId);
12461             this.head.appendChild(script);
12462
12463             this.trans = trans;
12464         }else{
12465             callback.call(scope||this, null, arg, false);
12466         }
12467     },
12468
12469     // private
12470     isLoading : function(){
12471         return this.trans ? true : false;
12472     },
12473
12474     /**
12475      * Abort the current server request.
12476      */
12477     abort : function(){
12478         if(this.isLoading()){
12479             this.destroyTrans(this.trans);
12480         }
12481     },
12482
12483     // private
12484     destroyTrans : function(trans, isLoaded){
12485         this.head.removeChild(document.getElementById(trans.scriptId));
12486         clearTimeout(trans.timeoutId);
12487         if(isLoaded){
12488             window[trans.cb] = undefined;
12489             try{
12490                 delete window[trans.cb];
12491             }catch(e){}
12492         }else{
12493             // if hasn't been loaded, wait for load to remove it to prevent script error
12494             window[trans.cb] = function(){
12495                 window[trans.cb] = undefined;
12496                 try{
12497                     delete window[trans.cb];
12498                 }catch(e){}
12499             };
12500         }
12501     },
12502
12503     // private
12504     handleResponse : function(o, trans){
12505         this.trans = false;
12506         this.destroyTrans(trans, true);
12507         var result;
12508         try {
12509             result = trans.reader.readRecords(o);
12510         }catch(e){
12511             this.fireEvent("loadexception", this, o, trans.arg, e);
12512             trans.callback.call(trans.scope||window, null, trans.arg, false);
12513             return;
12514         }
12515         this.fireEvent("load", this, o, trans.arg);
12516         trans.callback.call(trans.scope||window, result, trans.arg, true);
12517     },
12518
12519     // private
12520     handleFailure : function(trans){
12521         this.trans = false;
12522         this.destroyTrans(trans, false);
12523         this.fireEvent("loadexception", this, null, trans.arg);
12524         trans.callback.call(trans.scope||window, null, trans.arg, false);
12525     }
12526 });/*
12527  * Based on:
12528  * Ext JS Library 1.1.1
12529  * Copyright(c) 2006-2007, Ext JS, LLC.
12530  *
12531  * Originally Released Under LGPL - original licence link has changed is not relivant.
12532  *
12533  * Fork - LGPL
12534  * <script type="text/javascript">
12535  */
12536
12537 /**
12538  * @class Roo.data.JsonReader
12539  * @extends Roo.data.DataReader
12540  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12541  * based on mappings in a provided Roo.data.Record constructor.
12542  * 
12543  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12544  * in the reply previously. 
12545  * 
12546  * <p>
12547  * Example code:
12548  * <pre><code>
12549 var RecordDef = Roo.data.Record.create([
12550     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12551     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12552 ]);
12553 var myReader = new Roo.data.JsonReader({
12554     totalProperty: "results",    // The property which contains the total dataset size (optional)
12555     root: "rows",                // The property which contains an Array of row objects
12556     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12557 }, RecordDef);
12558 </code></pre>
12559  * <p>
12560  * This would consume a JSON file like this:
12561  * <pre><code>
12562 { 'results': 2, 'rows': [
12563     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12564     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12565 }
12566 </code></pre>
12567  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12568  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12569  * paged from the remote server.
12570  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12571  * @cfg {String} root name of the property which contains the Array of row objects.
12572  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12573  * @cfg {Array} fields Array of field definition objects
12574  * @constructor
12575  * Create a new JsonReader
12576  * @param {Object} meta Metadata configuration options
12577  * @param {Object} recordType Either an Array of field definition objects,
12578  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12579  */
12580 Roo.data.JsonReader = function(meta, recordType){
12581     
12582     meta = meta || {};
12583     // set some defaults:
12584     Roo.applyIf(meta, {
12585         totalProperty: 'total',
12586         successProperty : 'success',
12587         root : 'data',
12588         id : 'id'
12589     });
12590     
12591     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12592 };
12593 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12594     
12595     /**
12596      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12597      * Used by Store query builder to append _requestMeta to params.
12598      * 
12599      */
12600     metaFromRemote : false,
12601     /**
12602      * This method is only used by a DataProxy which has retrieved data from a remote server.
12603      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12604      * @return {Object} data A data block which is used by an Roo.data.Store object as
12605      * a cache of Roo.data.Records.
12606      */
12607     read : function(response){
12608         var json = response.responseText;
12609        
12610         var o = /* eval:var:o */ eval("("+json+")");
12611         if(!o) {
12612             throw {message: "JsonReader.read: Json object not found"};
12613         }
12614         
12615         if(o.metaData){
12616             
12617             delete this.ef;
12618             this.metaFromRemote = true;
12619             this.meta = o.metaData;
12620             this.recordType = Roo.data.Record.create(o.metaData.fields);
12621             this.onMetaChange(this.meta, this.recordType, o);
12622         }
12623         return this.readRecords(o);
12624     },
12625
12626     // private function a store will implement
12627     onMetaChange : function(meta, recordType, o){
12628
12629     },
12630
12631     /**
12632          * @ignore
12633          */
12634     simpleAccess: function(obj, subsc) {
12635         return obj[subsc];
12636     },
12637
12638         /**
12639          * @ignore
12640          */
12641     getJsonAccessor: function(){
12642         var re = /[\[\.]/;
12643         return function(expr) {
12644             try {
12645                 return(re.test(expr))
12646                     ? new Function("obj", "return obj." + expr)
12647                     : function(obj){
12648                         return obj[expr];
12649                     };
12650             } catch(e){}
12651             return Roo.emptyFn;
12652         };
12653     }(),
12654
12655     /**
12656      * Create a data block containing Roo.data.Records from an XML document.
12657      * @param {Object} o An object which contains an Array of row objects in the property specified
12658      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12659      * which contains the total size of the dataset.
12660      * @return {Object} data A data block which is used by an Roo.data.Store object as
12661      * a cache of Roo.data.Records.
12662      */
12663     readRecords : function(o){
12664         /**
12665          * After any data loads, the raw JSON data is available for further custom processing.
12666          * @type Object
12667          */
12668         this.o = o;
12669         var s = this.meta, Record = this.recordType,
12670             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12671
12672 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12673         if (!this.ef) {
12674             if(s.totalProperty) {
12675                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12676                 }
12677                 if(s.successProperty) {
12678                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12679                 }
12680                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12681                 if (s.id) {
12682                         var g = this.getJsonAccessor(s.id);
12683                         this.getId = function(rec) {
12684                                 var r = g(rec);  
12685                                 return (r === undefined || r === "") ? null : r;
12686                         };
12687                 } else {
12688                         this.getId = function(){return null;};
12689                 }
12690             this.ef = [];
12691             for(var jj = 0; jj < fl; jj++){
12692                 f = fi[jj];
12693                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12694                 this.ef[jj] = this.getJsonAccessor(map);
12695             }
12696         }
12697
12698         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12699         if(s.totalProperty){
12700             var vt = parseInt(this.getTotal(o), 10);
12701             if(!isNaN(vt)){
12702                 totalRecords = vt;
12703             }
12704         }
12705         if(s.successProperty){
12706             var vs = this.getSuccess(o);
12707             if(vs === false || vs === 'false'){
12708                 success = false;
12709             }
12710         }
12711         var records = [];
12712         for(var i = 0; i < c; i++){
12713                 var n = root[i];
12714             var values = {};
12715             var id = this.getId(n);
12716             for(var j = 0; j < fl; j++){
12717                 f = fi[j];
12718             var v = this.ef[j](n);
12719             if (!f.convert) {
12720                 Roo.log('missing convert for ' + f.name);
12721                 Roo.log(f);
12722                 continue;
12723             }
12724             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12725             }
12726             var record = new Record(values, id);
12727             record.json = n;
12728             records[i] = record;
12729         }
12730         return {
12731             raw : o,
12732             success : success,
12733             records : records,
12734             totalRecords : totalRecords
12735         };
12736     }
12737 });/*
12738  * Based on:
12739  * Ext JS Library 1.1.1
12740  * Copyright(c) 2006-2007, Ext JS, LLC.
12741  *
12742  * Originally Released Under LGPL - original licence link has changed is not relivant.
12743  *
12744  * Fork - LGPL
12745  * <script type="text/javascript">
12746  */
12747
12748 /**
12749  * @class Roo.data.ArrayReader
12750  * @extends Roo.data.DataReader
12751  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12752  * Each element of that Array represents a row of data fields. The
12753  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12754  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12755  * <p>
12756  * Example code:.
12757  * <pre><code>
12758 var RecordDef = Roo.data.Record.create([
12759     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12760     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12761 ]);
12762 var myReader = new Roo.data.ArrayReader({
12763     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12764 }, RecordDef);
12765 </code></pre>
12766  * <p>
12767  * This would consume an Array like this:
12768  * <pre><code>
12769 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12770   </code></pre>
12771  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12772  * @constructor
12773  * Create a new JsonReader
12774  * @param {Object} meta Metadata configuration options.
12775  * @param {Object} recordType Either an Array of field definition objects
12776  * as specified to {@link Roo.data.Record#create},
12777  * or an {@link Roo.data.Record} object
12778  * created using {@link Roo.data.Record#create}.
12779  */
12780 Roo.data.ArrayReader = function(meta, recordType){
12781     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12782 };
12783
12784 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12785     /**
12786      * Create a data block containing Roo.data.Records from an XML document.
12787      * @param {Object} o An Array of row objects which represents the dataset.
12788      * @return {Object} data A data block which is used by an Roo.data.Store object as
12789      * a cache of Roo.data.Records.
12790      */
12791     readRecords : function(o){
12792         var sid = this.meta ? this.meta.id : null;
12793         var recordType = this.recordType, fields = recordType.prototype.fields;
12794         var records = [];
12795         var root = o;
12796             for(var i = 0; i < root.length; i++){
12797                     var n = root[i];
12798                 var values = {};
12799                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12800                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12801                 var f = fields.items[j];
12802                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12803                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12804                 v = f.convert(v);
12805                 values[f.name] = v;
12806             }
12807                 var record = new recordType(values, id);
12808                 record.json = n;
12809                 records[records.length] = record;
12810             }
12811             return {
12812                 records : records,
12813                 totalRecords : records.length
12814             };
12815     }
12816 });/*
12817  * - LGPL
12818  * * 
12819  */
12820
12821 /**
12822  * @class Roo.bootstrap.ComboBox
12823  * @extends Roo.bootstrap.TriggerField
12824  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12825  * @cfg {Boolean} append (true|false) default false
12826  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12827  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12828  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12829  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12830  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12831  * @cfg {Boolean} animate default true
12832  * @cfg {Boolean} emptyResultText only for touch device
12833  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12834  * @cfg {String} emptyTitle default ''
12835  * @constructor
12836  * Create a new ComboBox.
12837  * @param {Object} config Configuration options
12838  */
12839 Roo.bootstrap.ComboBox = function(config){
12840     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12841     this.addEvents({
12842         /**
12843          * @event expand
12844          * Fires when the dropdown list is expanded
12845         * @param {Roo.bootstrap.ComboBox} combo This combo box
12846         */
12847         'expand' : true,
12848         /**
12849          * @event collapse
12850          * Fires when the dropdown list is collapsed
12851         * @param {Roo.bootstrap.ComboBox} combo This combo box
12852         */
12853         'collapse' : true,
12854         /**
12855          * @event beforeselect
12856          * Fires before a list item is selected. Return false to cancel the selection.
12857         * @param {Roo.bootstrap.ComboBox} combo This combo box
12858         * @param {Roo.data.Record} record The data record returned from the underlying store
12859         * @param {Number} index The index of the selected item in the dropdown list
12860         */
12861         'beforeselect' : true,
12862         /**
12863          * @event select
12864          * Fires when a list item is selected
12865         * @param {Roo.bootstrap.ComboBox} combo This combo box
12866         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12867         * @param {Number} index The index of the selected item in the dropdown list
12868         */
12869         'select' : true,
12870         /**
12871          * @event beforequery
12872          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12873          * The event object passed has these properties:
12874         * @param {Roo.bootstrap.ComboBox} combo This combo box
12875         * @param {String} query The query
12876         * @param {Boolean} forceAll true to force "all" query
12877         * @param {Boolean} cancel true to cancel the query
12878         * @param {Object} e The query event object
12879         */
12880         'beforequery': true,
12881          /**
12882          * @event add
12883          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12884         * @param {Roo.bootstrap.ComboBox} combo This combo box
12885         */
12886         'add' : true,
12887         /**
12888          * @event edit
12889          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12890         * @param {Roo.bootstrap.ComboBox} combo This combo box
12891         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12892         */
12893         'edit' : true,
12894         /**
12895          * @event remove
12896          * Fires when the remove value from the combobox array
12897         * @param {Roo.bootstrap.ComboBox} combo This combo box
12898         */
12899         'remove' : true,
12900         /**
12901          * @event afterremove
12902          * Fires when the remove value from the combobox array
12903         * @param {Roo.bootstrap.ComboBox} combo This combo box
12904         */
12905         'afterremove' : true,
12906         /**
12907          * @event specialfilter
12908          * Fires when specialfilter
12909             * @param {Roo.bootstrap.ComboBox} combo This combo box
12910             */
12911         'specialfilter' : true,
12912         /**
12913          * @event tick
12914          * Fires when tick the element
12915             * @param {Roo.bootstrap.ComboBox} combo This combo box
12916             */
12917         'tick' : true,
12918         /**
12919          * @event touchviewdisplay
12920          * Fires when touch view require special display (default is using displayField)
12921             * @param {Roo.bootstrap.ComboBox} combo This combo box
12922             * @param {Object} cfg set html .
12923             */
12924         'touchviewdisplay' : true
12925         
12926     });
12927     
12928     this.item = [];
12929     this.tickItems = [];
12930     
12931     this.selectedIndex = -1;
12932     if(this.mode == 'local'){
12933         if(config.queryDelay === undefined){
12934             this.queryDelay = 10;
12935         }
12936         if(config.minChars === undefined){
12937             this.minChars = 0;
12938         }
12939     }
12940 };
12941
12942 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12943      
12944     /**
12945      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12946      * rendering into an Roo.Editor, defaults to false)
12947      */
12948     /**
12949      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12950      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12951      */
12952     /**
12953      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12954      */
12955     /**
12956      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12957      * the dropdown list (defaults to undefined, with no header element)
12958      */
12959
12960      /**
12961      * @cfg {String/Roo.Template} tpl The template to use to render the output
12962      */
12963      
12964      /**
12965      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12966      */
12967     listWidth: undefined,
12968     /**
12969      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12970      * mode = 'remote' or 'text' if mode = 'local')
12971      */
12972     displayField: undefined,
12973     
12974     /**
12975      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12976      * mode = 'remote' or 'value' if mode = 'local'). 
12977      * Note: use of a valueField requires the user make a selection
12978      * in order for a value to be mapped.
12979      */
12980     valueField: undefined,
12981     /**
12982      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12983      */
12984     modalTitle : '',
12985     
12986     /**
12987      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12988      * field's data value (defaults to the underlying DOM element's name)
12989      */
12990     hiddenName: undefined,
12991     /**
12992      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12993      */
12994     listClass: '',
12995     /**
12996      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12997      */
12998     selectedClass: 'active',
12999     
13000     /**
13001      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13002      */
13003     shadow:'sides',
13004     /**
13005      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13006      * anchor positions (defaults to 'tl-bl')
13007      */
13008     listAlign: 'tl-bl?',
13009     /**
13010      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13011      */
13012     maxHeight: 300,
13013     /**
13014      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13015      * query specified by the allQuery config option (defaults to 'query')
13016      */
13017     triggerAction: 'query',
13018     /**
13019      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13020      * (defaults to 4, does not apply if editable = false)
13021      */
13022     minChars : 4,
13023     /**
13024      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13025      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13026      */
13027     typeAhead: false,
13028     /**
13029      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13030      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13031      */
13032     queryDelay: 500,
13033     /**
13034      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13035      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13036      */
13037     pageSize: 0,
13038     /**
13039      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13040      * when editable = true (defaults to false)
13041      */
13042     selectOnFocus:false,
13043     /**
13044      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13045      */
13046     queryParam: 'query',
13047     /**
13048      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13049      * when mode = 'remote' (defaults to 'Loading...')
13050      */
13051     loadingText: 'Loading...',
13052     /**
13053      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13054      */
13055     resizable: false,
13056     /**
13057      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13058      */
13059     handleHeight : 8,
13060     /**
13061      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13062      * traditional select (defaults to true)
13063      */
13064     editable: true,
13065     /**
13066      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13067      */
13068     allQuery: '',
13069     /**
13070      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13071      */
13072     mode: 'remote',
13073     /**
13074      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13075      * listWidth has a higher value)
13076      */
13077     minListWidth : 70,
13078     /**
13079      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13080      * allow the user to set arbitrary text into the field (defaults to false)
13081      */
13082     forceSelection:false,
13083     /**
13084      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13085      * if typeAhead = true (defaults to 250)
13086      */
13087     typeAheadDelay : 250,
13088     /**
13089      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13090      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13091      */
13092     valueNotFoundText : undefined,
13093     /**
13094      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13095      */
13096     blockFocus : false,
13097     
13098     /**
13099      * @cfg {Boolean} disableClear Disable showing of clear button.
13100      */
13101     disableClear : false,
13102     /**
13103      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13104      */
13105     alwaysQuery : false,
13106     
13107     /**
13108      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13109      */
13110     multiple : false,
13111     
13112     /**
13113      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13114      */
13115     invalidClass : "has-warning",
13116     
13117     /**
13118      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13119      */
13120     validClass : "has-success",
13121     
13122     /**
13123      * @cfg {Boolean} specialFilter (true|false) special filter default false
13124      */
13125     specialFilter : false,
13126     
13127     /**
13128      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13129      */
13130     mobileTouchView : true,
13131     
13132     /**
13133      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13134      */
13135     useNativeIOS : false,
13136     
13137     /**
13138      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13139      */
13140     mobile_restrict_height : false,
13141     
13142     ios_options : false,
13143     
13144     //private
13145     addicon : false,
13146     editicon: false,
13147     
13148     page: 0,
13149     hasQuery: false,
13150     append: false,
13151     loadNext: false,
13152     autoFocus : true,
13153     tickable : false,
13154     btnPosition : 'right',
13155     triggerList : true,
13156     showToggleBtn : true,
13157     animate : true,
13158     emptyResultText: 'Empty',
13159     triggerText : 'Select',
13160     emptyTitle : '',
13161     
13162     // element that contains real text value.. (when hidden is used..)
13163     
13164     getAutoCreate : function()
13165     {   
13166         var cfg = false;
13167         //render
13168         /*
13169          * Render classic select for iso
13170          */
13171         
13172         if(Roo.isIOS && this.useNativeIOS){
13173             cfg = this.getAutoCreateNativeIOS();
13174             return cfg;
13175         }
13176         
13177         /*
13178          * Touch Devices
13179          */
13180         
13181         if(Roo.isTouch && this.mobileTouchView){
13182             cfg = this.getAutoCreateTouchView();
13183             return cfg;;
13184         }
13185         
13186         /*
13187          *  Normal ComboBox
13188          */
13189         if(!this.tickable){
13190             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13191             return cfg;
13192         }
13193         
13194         /*
13195          *  ComboBox with tickable selections
13196          */
13197              
13198         var align = this.labelAlign || this.parentLabelAlign();
13199         
13200         cfg = {
13201             cls : 'form-group roo-combobox-tickable' //input-group
13202         };
13203         
13204         var btn_text_select = '';
13205         var btn_text_done = '';
13206         var btn_text_cancel = '';
13207         
13208         if (this.btn_text_show) {
13209             btn_text_select = 'Select';
13210             btn_text_done = 'Done';
13211             btn_text_cancel = 'Cancel'; 
13212         }
13213         
13214         var buttons = {
13215             tag : 'div',
13216             cls : 'tickable-buttons',
13217             cn : [
13218                 {
13219                     tag : 'button',
13220                     type : 'button',
13221                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13222                     //html : this.triggerText
13223                     html: btn_text_select
13224                 },
13225                 {
13226                     tag : 'button',
13227                     type : 'button',
13228                     name : 'ok',
13229                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13230                     //html : 'Done'
13231                     html: btn_text_done
13232                 },
13233                 {
13234                     tag : 'button',
13235                     type : 'button',
13236                     name : 'cancel',
13237                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13238                     //html : 'Cancel'
13239                     html: btn_text_cancel
13240                 }
13241             ]
13242         };
13243         
13244         if(this.editable){
13245             buttons.cn.unshift({
13246                 tag: 'input',
13247                 cls: 'roo-select2-search-field-input'
13248             });
13249         }
13250         
13251         var _this = this;
13252         
13253         Roo.each(buttons.cn, function(c){
13254             if (_this.size) {
13255                 c.cls += ' btn-' + _this.size;
13256             }
13257
13258             if (_this.disabled) {
13259                 c.disabled = true;
13260             }
13261         });
13262         
13263         var box = {
13264             tag: 'div',
13265             cn: [
13266                 {
13267                     tag: 'input',
13268                     type : 'hidden',
13269                     cls: 'form-hidden-field'
13270                 },
13271                 {
13272                     tag: 'ul',
13273                     cls: 'roo-select2-choices',
13274                     cn:[
13275                         {
13276                             tag: 'li',
13277                             cls: 'roo-select2-search-field',
13278                             cn: [
13279                                 buttons
13280                             ]
13281                         }
13282                     ]
13283                 }
13284             ]
13285         };
13286         
13287         var combobox = {
13288             cls: 'roo-select2-container input-group roo-select2-container-multi',
13289             cn: [
13290                 box
13291 //                {
13292 //                    tag: 'ul',
13293 //                    cls: 'typeahead typeahead-long dropdown-menu',
13294 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13295 //                }
13296             ]
13297         };
13298         
13299         if(this.hasFeedback && !this.allowBlank){
13300             
13301             var feedback = {
13302                 tag: 'span',
13303                 cls: 'glyphicon form-control-feedback'
13304             };
13305
13306             combobox.cn.push(feedback);
13307         }
13308         
13309         
13310         if (align ==='left' && this.fieldLabel.length) {
13311             
13312             cfg.cls += ' roo-form-group-label-left';
13313             
13314             cfg.cn = [
13315                 {
13316                     tag : 'i',
13317                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13318                     tooltip : 'This field is required'
13319                 },
13320                 {
13321                     tag: 'label',
13322                     'for' :  id,
13323                     cls : 'control-label',
13324                     html : this.fieldLabel
13325
13326                 },
13327                 {
13328                     cls : "", 
13329                     cn: [
13330                         combobox
13331                     ]
13332                 }
13333
13334             ];
13335             
13336             var labelCfg = cfg.cn[1];
13337             var contentCfg = cfg.cn[2];
13338             
13339
13340             if(this.indicatorpos == 'right'){
13341                 
13342                 cfg.cn = [
13343                     {
13344                         tag: 'label',
13345                         'for' :  id,
13346                         cls : 'control-label',
13347                         cn : [
13348                             {
13349                                 tag : 'span',
13350                                 html : this.fieldLabel
13351                             },
13352                             {
13353                                 tag : 'i',
13354                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13355                                 tooltip : 'This field is required'
13356                             }
13357                         ]
13358                     },
13359                     {
13360                         cls : "",
13361                         cn: [
13362                             combobox
13363                         ]
13364                     }
13365
13366                 ];
13367                 
13368                 
13369                 
13370                 labelCfg = cfg.cn[0];
13371                 contentCfg = cfg.cn[1];
13372             
13373             }
13374             
13375             if(this.labelWidth > 12){
13376                 labelCfg.style = "width: " + this.labelWidth + 'px';
13377             }
13378             
13379             if(this.labelWidth < 13 && this.labelmd == 0){
13380                 this.labelmd = this.labelWidth;
13381             }
13382             
13383             if(this.labellg > 0){
13384                 labelCfg.cls += ' col-lg-' + this.labellg;
13385                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13386             }
13387             
13388             if(this.labelmd > 0){
13389                 labelCfg.cls += ' col-md-' + this.labelmd;
13390                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13391             }
13392             
13393             if(this.labelsm > 0){
13394                 labelCfg.cls += ' col-sm-' + this.labelsm;
13395                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13396             }
13397             
13398             if(this.labelxs > 0){
13399                 labelCfg.cls += ' col-xs-' + this.labelxs;
13400                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13401             }
13402                 
13403                 
13404         } else if ( this.fieldLabel.length) {
13405 //                Roo.log(" label");
13406                  cfg.cn = [
13407                     {
13408                         tag : 'i',
13409                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13410                         tooltip : 'This field is required'
13411                     },
13412                     {
13413                         tag: 'label',
13414                         //cls : 'input-group-addon',
13415                         html : this.fieldLabel
13416                     },
13417                     combobox
13418                 ];
13419                 
13420                 if(this.indicatorpos == 'right'){
13421                     cfg.cn = [
13422                         {
13423                             tag: 'label',
13424                             //cls : 'input-group-addon',
13425                             html : this.fieldLabel
13426                         },
13427                         {
13428                             tag : 'i',
13429                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13430                             tooltip : 'This field is required'
13431                         },
13432                         combobox
13433                     ];
13434                     
13435                 }
13436
13437         } else {
13438             
13439 //                Roo.log(" no label && no align");
13440                 cfg = combobox
13441                      
13442                 
13443         }
13444          
13445         var settings=this;
13446         ['xs','sm','md','lg'].map(function(size){
13447             if (settings[size]) {
13448                 cfg.cls += ' col-' + size + '-' + settings[size];
13449             }
13450         });
13451         
13452         return cfg;
13453         
13454     },
13455     
13456     _initEventsCalled : false,
13457     
13458     // private
13459     initEvents: function()
13460     {   
13461         if (this._initEventsCalled) { // as we call render... prevent looping...
13462             return;
13463         }
13464         this._initEventsCalled = true;
13465         
13466         if (!this.store) {
13467             throw "can not find store for combo";
13468         }
13469         
13470         this.indicator = this.indicatorEl();
13471         
13472         this.store = Roo.factory(this.store, Roo.data);
13473         this.store.parent = this;
13474         
13475         // if we are building from html. then this element is so complex, that we can not really
13476         // use the rendered HTML.
13477         // so we have to trash and replace the previous code.
13478         if (Roo.XComponent.build_from_html) {
13479             // remove this element....
13480             var e = this.el.dom, k=0;
13481             while (e ) { e = e.previousSibling;  ++k;}
13482
13483             this.el.remove();
13484             
13485             this.el=false;
13486             this.rendered = false;
13487             
13488             this.render(this.parent().getChildContainer(true), k);
13489         }
13490         
13491         if(Roo.isIOS && this.useNativeIOS){
13492             this.initIOSView();
13493             return;
13494         }
13495         
13496         /*
13497          * Touch Devices
13498          */
13499         
13500         if(Roo.isTouch && this.mobileTouchView){
13501             this.initTouchView();
13502             return;
13503         }
13504         
13505         if(this.tickable){
13506             this.initTickableEvents();
13507             return;
13508         }
13509         
13510         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13511         
13512         if(this.hiddenName){
13513             
13514             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13515             
13516             this.hiddenField.dom.value =
13517                 this.hiddenValue !== undefined ? this.hiddenValue :
13518                 this.value !== undefined ? this.value : '';
13519
13520             // prevent input submission
13521             this.el.dom.removeAttribute('name');
13522             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13523              
13524              
13525         }
13526         //if(Roo.isGecko){
13527         //    this.el.dom.setAttribute('autocomplete', 'off');
13528         //}
13529         
13530         var cls = 'x-combo-list';
13531         
13532         //this.list = new Roo.Layer({
13533         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13534         //});
13535         
13536         var _this = this;
13537         
13538         (function(){
13539             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13540             _this.list.setWidth(lw);
13541         }).defer(100);
13542         
13543         this.list.on('mouseover', this.onViewOver, this);
13544         this.list.on('mousemove', this.onViewMove, this);
13545         this.list.on('scroll', this.onViewScroll, this);
13546         
13547         /*
13548         this.list.swallowEvent('mousewheel');
13549         this.assetHeight = 0;
13550
13551         if(this.title){
13552             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13553             this.assetHeight += this.header.getHeight();
13554         }
13555
13556         this.innerList = this.list.createChild({cls:cls+'-inner'});
13557         this.innerList.on('mouseover', this.onViewOver, this);
13558         this.innerList.on('mousemove', this.onViewMove, this);
13559         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13560         
13561         if(this.allowBlank && !this.pageSize && !this.disableClear){
13562             this.footer = this.list.createChild({cls:cls+'-ft'});
13563             this.pageTb = new Roo.Toolbar(this.footer);
13564            
13565         }
13566         if(this.pageSize){
13567             this.footer = this.list.createChild({cls:cls+'-ft'});
13568             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13569                     {pageSize: this.pageSize});
13570             
13571         }
13572         
13573         if (this.pageTb && this.allowBlank && !this.disableClear) {
13574             var _this = this;
13575             this.pageTb.add(new Roo.Toolbar.Fill(), {
13576                 cls: 'x-btn-icon x-btn-clear',
13577                 text: '&#160;',
13578                 handler: function()
13579                 {
13580                     _this.collapse();
13581                     _this.clearValue();
13582                     _this.onSelect(false, -1);
13583                 }
13584             });
13585         }
13586         if (this.footer) {
13587             this.assetHeight += this.footer.getHeight();
13588         }
13589         */
13590             
13591         if(!this.tpl){
13592             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13593         }
13594
13595         this.view = new Roo.View(this.list, this.tpl, {
13596             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13597         });
13598         //this.view.wrapEl.setDisplayed(false);
13599         this.view.on('click', this.onViewClick, this);
13600         
13601         
13602         this.store.on('beforeload', this.onBeforeLoad, this);
13603         this.store.on('load', this.onLoad, this);
13604         this.store.on('loadexception', this.onLoadException, this);
13605         /*
13606         if(this.resizable){
13607             this.resizer = new Roo.Resizable(this.list,  {
13608                pinned:true, handles:'se'
13609             });
13610             this.resizer.on('resize', function(r, w, h){
13611                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13612                 this.listWidth = w;
13613                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13614                 this.restrictHeight();
13615             }, this);
13616             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13617         }
13618         */
13619         if(!this.editable){
13620             this.editable = true;
13621             this.setEditable(false);
13622         }
13623         
13624         /*
13625         
13626         if (typeof(this.events.add.listeners) != 'undefined') {
13627             
13628             this.addicon = this.wrap.createChild(
13629                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13630        
13631             this.addicon.on('click', function(e) {
13632                 this.fireEvent('add', this);
13633             }, this);
13634         }
13635         if (typeof(this.events.edit.listeners) != 'undefined') {
13636             
13637             this.editicon = this.wrap.createChild(
13638                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13639             if (this.addicon) {
13640                 this.editicon.setStyle('margin-left', '40px');
13641             }
13642             this.editicon.on('click', function(e) {
13643                 
13644                 // we fire even  if inothing is selected..
13645                 this.fireEvent('edit', this, this.lastData );
13646                 
13647             }, this);
13648         }
13649         */
13650         
13651         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13652             "up" : function(e){
13653                 this.inKeyMode = true;
13654                 this.selectPrev();
13655             },
13656
13657             "down" : function(e){
13658                 if(!this.isExpanded()){
13659                     this.onTriggerClick();
13660                 }else{
13661                     this.inKeyMode = true;
13662                     this.selectNext();
13663                 }
13664             },
13665
13666             "enter" : function(e){
13667 //                this.onViewClick();
13668                 //return true;
13669                 this.collapse();
13670                 
13671                 if(this.fireEvent("specialkey", this, e)){
13672                     this.onViewClick(false);
13673                 }
13674                 
13675                 return true;
13676             },
13677
13678             "esc" : function(e){
13679                 this.collapse();
13680             },
13681
13682             "tab" : function(e){
13683                 this.collapse();
13684                 
13685                 if(this.fireEvent("specialkey", this, e)){
13686                     this.onViewClick(false);
13687                 }
13688                 
13689                 return true;
13690             },
13691
13692             scope : this,
13693
13694             doRelay : function(foo, bar, hname){
13695                 if(hname == 'down' || this.scope.isExpanded()){
13696                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13697                 }
13698                 return true;
13699             },
13700
13701             forceKeyDown: true
13702         });
13703         
13704         
13705         this.queryDelay = Math.max(this.queryDelay || 10,
13706                 this.mode == 'local' ? 10 : 250);
13707         
13708         
13709         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13710         
13711         if(this.typeAhead){
13712             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13713         }
13714         if(this.editable !== false){
13715             this.inputEl().on("keyup", this.onKeyUp, this);
13716         }
13717         if(this.forceSelection){
13718             this.inputEl().on('blur', this.doForce, this);
13719         }
13720         
13721         if(this.multiple){
13722             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13723             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13724         }
13725     },
13726     
13727     initTickableEvents: function()
13728     {   
13729         this.createList();
13730         
13731         if(this.hiddenName){
13732             
13733             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13734             
13735             this.hiddenField.dom.value =
13736                 this.hiddenValue !== undefined ? this.hiddenValue :
13737                 this.value !== undefined ? this.value : '';
13738
13739             // prevent input submission
13740             this.el.dom.removeAttribute('name');
13741             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13742              
13743              
13744         }
13745         
13746 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13747         
13748         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13749         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13750         if(this.triggerList){
13751             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13752         }
13753          
13754         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13755         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13756         
13757         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13758         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13759         
13760         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13761         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13762         
13763         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13764         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13765         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13766         
13767         this.okBtn.hide();
13768         this.cancelBtn.hide();
13769         
13770         var _this = this;
13771         
13772         (function(){
13773             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13774             _this.list.setWidth(lw);
13775         }).defer(100);
13776         
13777         this.list.on('mouseover', this.onViewOver, this);
13778         this.list.on('mousemove', this.onViewMove, this);
13779         
13780         this.list.on('scroll', this.onViewScroll, this);
13781         
13782         if(!this.tpl){
13783             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13784                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13785         }
13786
13787         this.view = new Roo.View(this.list, this.tpl, {
13788             singleSelect:true,
13789             tickable:true,
13790             parent:this,
13791             store: this.store,
13792             selectedClass: this.selectedClass
13793         });
13794         
13795         //this.view.wrapEl.setDisplayed(false);
13796         this.view.on('click', this.onViewClick, this);
13797         
13798         
13799         
13800         this.store.on('beforeload', this.onBeforeLoad, this);
13801         this.store.on('load', this.onLoad, this);
13802         this.store.on('loadexception', this.onLoadException, this);
13803         
13804         if(this.editable){
13805             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13806                 "up" : function(e){
13807                     this.inKeyMode = true;
13808                     this.selectPrev();
13809                 },
13810
13811                 "down" : function(e){
13812                     this.inKeyMode = true;
13813                     this.selectNext();
13814                 },
13815
13816                 "enter" : function(e){
13817                     if(this.fireEvent("specialkey", this, e)){
13818                         this.onViewClick(false);
13819                     }
13820                     
13821                     return true;
13822                 },
13823
13824                 "esc" : function(e){
13825                     this.onTickableFooterButtonClick(e, false, false);
13826                 },
13827
13828                 "tab" : function(e){
13829                     this.fireEvent("specialkey", this, e);
13830                     
13831                     this.onTickableFooterButtonClick(e, false, false);
13832                     
13833                     return true;
13834                 },
13835
13836                 scope : this,
13837
13838                 doRelay : function(e, fn, key){
13839                     if(this.scope.isExpanded()){
13840                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13841                     }
13842                     return true;
13843                 },
13844
13845                 forceKeyDown: true
13846             });
13847         }
13848         
13849         this.queryDelay = Math.max(this.queryDelay || 10,
13850                 this.mode == 'local' ? 10 : 250);
13851         
13852         
13853         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13854         
13855         if(this.typeAhead){
13856             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13857         }
13858         
13859         if(this.editable !== false){
13860             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13861         }
13862         
13863         this.indicator = this.indicatorEl();
13864         
13865         if(this.indicator){
13866             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13867             this.indicator.hide();
13868         }
13869         
13870     },
13871
13872     onDestroy : function(){
13873         if(this.view){
13874             this.view.setStore(null);
13875             this.view.el.removeAllListeners();
13876             this.view.el.remove();
13877             this.view.purgeListeners();
13878         }
13879         if(this.list){
13880             this.list.dom.innerHTML  = '';
13881         }
13882         
13883         if(this.store){
13884             this.store.un('beforeload', this.onBeforeLoad, this);
13885             this.store.un('load', this.onLoad, this);
13886             this.store.un('loadexception', this.onLoadException, this);
13887         }
13888         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13889     },
13890
13891     // private
13892     fireKey : function(e){
13893         if(e.isNavKeyPress() && !this.list.isVisible()){
13894             this.fireEvent("specialkey", this, e);
13895         }
13896     },
13897
13898     // private
13899     onResize: function(w, h){
13900 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13901 //        
13902 //        if(typeof w != 'number'){
13903 //            // we do not handle it!?!?
13904 //            return;
13905 //        }
13906 //        var tw = this.trigger.getWidth();
13907 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13908 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13909 //        var x = w - tw;
13910 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13911 //            
13912 //        //this.trigger.setStyle('left', x+'px');
13913 //        
13914 //        if(this.list && this.listWidth === undefined){
13915 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13916 //            this.list.setWidth(lw);
13917 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13918 //        }
13919         
13920     
13921         
13922     },
13923
13924     /**
13925      * Allow or prevent the user from directly editing the field text.  If false is passed,
13926      * the user will only be able to select from the items defined in the dropdown list.  This method
13927      * is the runtime equivalent of setting the 'editable' config option at config time.
13928      * @param {Boolean} value True to allow the user to directly edit the field text
13929      */
13930     setEditable : function(value){
13931         if(value == this.editable){
13932             return;
13933         }
13934         this.editable = value;
13935         if(!value){
13936             this.inputEl().dom.setAttribute('readOnly', true);
13937             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13938             this.inputEl().addClass('x-combo-noedit');
13939         }else{
13940             this.inputEl().dom.setAttribute('readOnly', false);
13941             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13942             this.inputEl().removeClass('x-combo-noedit');
13943         }
13944     },
13945
13946     // private
13947     
13948     onBeforeLoad : function(combo,opts){
13949         if(!this.hasFocus){
13950             return;
13951         }
13952          if (!opts.add) {
13953             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13954          }
13955         this.restrictHeight();
13956         this.selectedIndex = -1;
13957     },
13958
13959     // private
13960     onLoad : function(){
13961         
13962         this.hasQuery = false;
13963         
13964         if(!this.hasFocus){
13965             return;
13966         }
13967         
13968         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13969             this.loading.hide();
13970         }
13971         
13972         if(this.store.getCount() > 0){
13973             
13974             this.expand();
13975             this.restrictHeight();
13976             if(this.lastQuery == this.allQuery){
13977                 if(this.editable && !this.tickable){
13978                     this.inputEl().dom.select();
13979                 }
13980                 
13981                 if(
13982                     !this.selectByValue(this.value, true) &&
13983                     this.autoFocus && 
13984                     (
13985                         !this.store.lastOptions ||
13986                         typeof(this.store.lastOptions.add) == 'undefined' || 
13987                         this.store.lastOptions.add != true
13988                     )
13989                 ){
13990                     this.select(0, true);
13991                 }
13992             }else{
13993                 if(this.autoFocus){
13994                     this.selectNext();
13995                 }
13996                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13997                     this.taTask.delay(this.typeAheadDelay);
13998                 }
13999             }
14000         }else{
14001             this.onEmptyResults();
14002         }
14003         
14004         //this.el.focus();
14005     },
14006     // private
14007     onLoadException : function()
14008     {
14009         this.hasQuery = false;
14010         
14011         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14012             this.loading.hide();
14013         }
14014         
14015         if(this.tickable && this.editable){
14016             return;
14017         }
14018         
14019         this.collapse();
14020         // only causes errors at present
14021         //Roo.log(this.store.reader.jsonData);
14022         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14023             // fixme
14024             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14025         //}
14026         
14027         
14028     },
14029     // private
14030     onTypeAhead : function(){
14031         if(this.store.getCount() > 0){
14032             var r = this.store.getAt(0);
14033             var newValue = r.data[this.displayField];
14034             var len = newValue.length;
14035             var selStart = this.getRawValue().length;
14036             
14037             if(selStart != len){
14038                 this.setRawValue(newValue);
14039                 this.selectText(selStart, newValue.length);
14040             }
14041         }
14042     },
14043
14044     // private
14045     onSelect : function(record, index){
14046         
14047         if(this.fireEvent('beforeselect', this, record, index) !== false){
14048         
14049             this.setFromData(index > -1 ? record.data : false);
14050             
14051             this.collapse();
14052             this.fireEvent('select', this, record, index);
14053         }
14054     },
14055
14056     /**
14057      * Returns the currently selected field value or empty string if no value is set.
14058      * @return {String} value The selected value
14059      */
14060     getValue : function()
14061     {
14062         if(Roo.isIOS && this.useNativeIOS){
14063             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14064         }
14065         
14066         if(this.multiple){
14067             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14068         }
14069         
14070         if(this.valueField){
14071             return typeof this.value != 'undefined' ? this.value : '';
14072         }else{
14073             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14074         }
14075     },
14076     
14077     getRawValue : function()
14078     {
14079         if(Roo.isIOS && this.useNativeIOS){
14080             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14081         }
14082         
14083         var v = this.inputEl().getValue();
14084         
14085         return v;
14086     },
14087
14088     /**
14089      * Clears any text/value currently set in the field
14090      */
14091     clearValue : function(){
14092         
14093         if(this.hiddenField){
14094             this.hiddenField.dom.value = '';
14095         }
14096         this.value = '';
14097         this.setRawValue('');
14098         this.lastSelectionText = '';
14099         this.lastData = false;
14100         
14101         var close = this.closeTriggerEl();
14102         
14103         if(close){
14104             close.hide();
14105         }
14106         
14107         this.validate();
14108         
14109     },
14110
14111     /**
14112      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14113      * will be displayed in the field.  If the value does not match the data value of an existing item,
14114      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14115      * Otherwise the field will be blank (although the value will still be set).
14116      * @param {String} value The value to match
14117      */
14118     setValue : function(v)
14119     {
14120         if(Roo.isIOS && this.useNativeIOS){
14121             this.setIOSValue(v);
14122             return;
14123         }
14124         
14125         if(this.multiple){
14126             this.syncValue();
14127             return;
14128         }
14129         
14130         var text = v;
14131         if(this.valueField){
14132             var r = this.findRecord(this.valueField, v);
14133             if(r){
14134                 text = r.data[this.displayField];
14135             }else if(this.valueNotFoundText !== undefined){
14136                 text = this.valueNotFoundText;
14137             }
14138         }
14139         this.lastSelectionText = text;
14140         if(this.hiddenField){
14141             this.hiddenField.dom.value = v;
14142         }
14143         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14144         this.value = v;
14145         
14146         var close = this.closeTriggerEl();
14147         
14148         if(close){
14149             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14150         }
14151         
14152         this.validate();
14153     },
14154     /**
14155      * @property {Object} the last set data for the element
14156      */
14157     
14158     lastData : false,
14159     /**
14160      * Sets the value of the field based on a object which is related to the record format for the store.
14161      * @param {Object} value the value to set as. or false on reset?
14162      */
14163     setFromData : function(o){
14164         
14165         if(this.multiple){
14166             this.addItem(o);
14167             return;
14168         }
14169             
14170         var dv = ''; // display value
14171         var vv = ''; // value value..
14172         this.lastData = o;
14173         if (this.displayField) {
14174             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14175         } else {
14176             // this is an error condition!!!
14177             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14178         }
14179         
14180         if(this.valueField){
14181             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14182         }
14183         
14184         var close = this.closeTriggerEl();
14185         
14186         if(close){
14187             if(dv.length || vv * 1 > 0){
14188                 close.show() ;
14189                 this.blockFocus=true;
14190             } else {
14191                 close.hide();
14192             }             
14193         }
14194         
14195         if(this.hiddenField){
14196             this.hiddenField.dom.value = vv;
14197             
14198             this.lastSelectionText = dv;
14199             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14200             this.value = vv;
14201             return;
14202         }
14203         // no hidden field.. - we store the value in 'value', but still display
14204         // display field!!!!
14205         this.lastSelectionText = dv;
14206         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14207         this.value = vv;
14208         
14209         
14210         
14211     },
14212     // private
14213     reset : function(){
14214         // overridden so that last data is reset..
14215         
14216         if(this.multiple){
14217             this.clearItem();
14218             return;
14219         }
14220         
14221         this.setValue(this.originalValue);
14222         //this.clearInvalid();
14223         this.lastData = false;
14224         if (this.view) {
14225             this.view.clearSelections();
14226         }
14227         
14228         this.validate();
14229     },
14230     // private
14231     findRecord : function(prop, value){
14232         var record;
14233         if(this.store.getCount() > 0){
14234             this.store.each(function(r){
14235                 if(r.data[prop] == value){
14236                     record = r;
14237                     return false;
14238                 }
14239                 return true;
14240             });
14241         }
14242         return record;
14243     },
14244     
14245     getName: function()
14246     {
14247         // returns hidden if it's set..
14248         if (!this.rendered) {return ''};
14249         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14250         
14251     },
14252     // private
14253     onViewMove : function(e, t){
14254         this.inKeyMode = false;
14255     },
14256
14257     // private
14258     onViewOver : function(e, t){
14259         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14260             return;
14261         }
14262         var item = this.view.findItemFromChild(t);
14263         
14264         if(item){
14265             var index = this.view.indexOf(item);
14266             this.select(index, false);
14267         }
14268     },
14269
14270     // private
14271     onViewClick : function(view, doFocus, el, e)
14272     {
14273         var index = this.view.getSelectedIndexes()[0];
14274         
14275         var r = this.store.getAt(index);
14276         
14277         if(this.tickable){
14278             
14279             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14280                 return;
14281             }
14282             
14283             var rm = false;
14284             var _this = this;
14285             
14286             Roo.each(this.tickItems, function(v,k){
14287                 
14288                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14289                     Roo.log(v);
14290                     _this.tickItems.splice(k, 1);
14291                     
14292                     if(typeof(e) == 'undefined' && view == false){
14293                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14294                     }
14295                     
14296                     rm = true;
14297                     return;
14298                 }
14299             });
14300             
14301             if(rm){
14302                 return;
14303             }
14304             
14305             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14306                 this.tickItems.push(r.data);
14307             }
14308             
14309             if(typeof(e) == 'undefined' && view == false){
14310                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14311             }
14312                     
14313             return;
14314         }
14315         
14316         if(r){
14317             this.onSelect(r, index);
14318         }
14319         if(doFocus !== false && !this.blockFocus){
14320             this.inputEl().focus();
14321         }
14322     },
14323
14324     // private
14325     restrictHeight : function(){
14326         //this.innerList.dom.style.height = '';
14327         //var inner = this.innerList.dom;
14328         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14329         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14330         //this.list.beginUpdate();
14331         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14332         this.list.alignTo(this.inputEl(), this.listAlign);
14333         this.list.alignTo(this.inputEl(), this.listAlign);
14334         //this.list.endUpdate();
14335     },
14336
14337     // private
14338     onEmptyResults : function(){
14339         
14340         if(this.tickable && this.editable){
14341             this.hasFocus = false;
14342             this.restrictHeight();
14343             return;
14344         }
14345         
14346         this.collapse();
14347     },
14348
14349     /**
14350      * Returns true if the dropdown list is expanded, else false.
14351      */
14352     isExpanded : function(){
14353         return this.list.isVisible();
14354     },
14355
14356     /**
14357      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14358      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14359      * @param {String} value The data value of the item to select
14360      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14361      * selected item if it is not currently in view (defaults to true)
14362      * @return {Boolean} True if the value matched an item in the list, else false
14363      */
14364     selectByValue : function(v, scrollIntoView){
14365         if(v !== undefined && v !== null){
14366             var r = this.findRecord(this.valueField || this.displayField, v);
14367             if(r){
14368                 this.select(this.store.indexOf(r), scrollIntoView);
14369                 return true;
14370             }
14371         }
14372         return false;
14373     },
14374
14375     /**
14376      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14377      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14378      * @param {Number} index The zero-based index of the list item to select
14379      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14380      * selected item if it is not currently in view (defaults to true)
14381      */
14382     select : function(index, scrollIntoView){
14383         this.selectedIndex = index;
14384         this.view.select(index);
14385         if(scrollIntoView !== false){
14386             var el = this.view.getNode(index);
14387             /*
14388              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14389              */
14390             if(el){
14391                 this.list.scrollChildIntoView(el, false);
14392             }
14393         }
14394     },
14395
14396     // private
14397     selectNext : function(){
14398         var ct = this.store.getCount();
14399         if(ct > 0){
14400             if(this.selectedIndex == -1){
14401                 this.select(0);
14402             }else if(this.selectedIndex < ct-1){
14403                 this.select(this.selectedIndex+1);
14404             }
14405         }
14406     },
14407
14408     // private
14409     selectPrev : function(){
14410         var ct = this.store.getCount();
14411         if(ct > 0){
14412             if(this.selectedIndex == -1){
14413                 this.select(0);
14414             }else if(this.selectedIndex != 0){
14415                 this.select(this.selectedIndex-1);
14416             }
14417         }
14418     },
14419
14420     // private
14421     onKeyUp : function(e){
14422         if(this.editable !== false && !e.isSpecialKey()){
14423             this.lastKey = e.getKey();
14424             this.dqTask.delay(this.queryDelay);
14425         }
14426     },
14427
14428     // private
14429     validateBlur : function(){
14430         return !this.list || !this.list.isVisible();   
14431     },
14432
14433     // private
14434     initQuery : function(){
14435         
14436         var v = this.getRawValue();
14437         
14438         if(this.tickable && this.editable){
14439             v = this.tickableInputEl().getValue();
14440         }
14441         
14442         this.doQuery(v);
14443     },
14444
14445     // private
14446     doForce : function(){
14447         if(this.inputEl().dom.value.length > 0){
14448             this.inputEl().dom.value =
14449                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14450              
14451         }
14452     },
14453
14454     /**
14455      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14456      * query allowing the query action to be canceled if needed.
14457      * @param {String} query The SQL query to execute
14458      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14459      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14460      * saved in the current store (defaults to false)
14461      */
14462     doQuery : function(q, forceAll){
14463         
14464         if(q === undefined || q === null){
14465             q = '';
14466         }
14467         var qe = {
14468             query: q,
14469             forceAll: forceAll,
14470             combo: this,
14471             cancel:false
14472         };
14473         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14474             return false;
14475         }
14476         q = qe.query;
14477         
14478         forceAll = qe.forceAll;
14479         if(forceAll === true || (q.length >= this.minChars)){
14480             
14481             this.hasQuery = true;
14482             
14483             if(this.lastQuery != q || this.alwaysQuery){
14484                 this.lastQuery = q;
14485                 if(this.mode == 'local'){
14486                     this.selectedIndex = -1;
14487                     if(forceAll){
14488                         this.store.clearFilter();
14489                     }else{
14490                         
14491                         if(this.specialFilter){
14492                             this.fireEvent('specialfilter', this);
14493                             this.onLoad();
14494                             return;
14495                         }
14496                         
14497                         this.store.filter(this.displayField, q);
14498                     }
14499                     
14500                     this.store.fireEvent("datachanged", this.store);
14501                     
14502                     this.onLoad();
14503                     
14504                     
14505                 }else{
14506                     
14507                     this.store.baseParams[this.queryParam] = q;
14508                     
14509                     var options = {params : this.getParams(q)};
14510                     
14511                     if(this.loadNext){
14512                         options.add = true;
14513                         options.params.start = this.page * this.pageSize;
14514                     }
14515                     
14516                     this.store.load(options);
14517                     
14518                     /*
14519                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14520                      *  we should expand the list on onLoad
14521                      *  so command out it
14522                      */
14523 //                    this.expand();
14524                 }
14525             }else{
14526                 this.selectedIndex = -1;
14527                 this.onLoad();   
14528             }
14529         }
14530         
14531         this.loadNext = false;
14532     },
14533     
14534     // private
14535     getParams : function(q){
14536         var p = {};
14537         //p[this.queryParam] = q;
14538         
14539         if(this.pageSize){
14540             p.start = 0;
14541             p.limit = this.pageSize;
14542         }
14543         return p;
14544     },
14545
14546     /**
14547      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14548      */
14549     collapse : function(){
14550         if(!this.isExpanded()){
14551             return;
14552         }
14553         
14554         this.list.hide();
14555         
14556         this.hasFocus = false;
14557         
14558         if(this.tickable){
14559             this.okBtn.hide();
14560             this.cancelBtn.hide();
14561             this.trigger.show();
14562             
14563             if(this.editable){
14564                 this.tickableInputEl().dom.value = '';
14565                 this.tickableInputEl().blur();
14566             }
14567             
14568         }
14569         
14570         Roo.get(document).un('mousedown', this.collapseIf, this);
14571         Roo.get(document).un('mousewheel', this.collapseIf, this);
14572         if (!this.editable) {
14573             Roo.get(document).un('keydown', this.listKeyPress, this);
14574         }
14575         this.fireEvent('collapse', this);
14576         
14577         this.validate();
14578     },
14579
14580     // private
14581     collapseIf : function(e){
14582         var in_combo  = e.within(this.el);
14583         var in_list =  e.within(this.list);
14584         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14585         
14586         if (in_combo || in_list || is_list) {
14587             //e.stopPropagation();
14588             return;
14589         }
14590         
14591         if(this.tickable){
14592             this.onTickableFooterButtonClick(e, false, false);
14593         }
14594
14595         this.collapse();
14596         
14597     },
14598
14599     /**
14600      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14601      */
14602     expand : function(){
14603        
14604         if(this.isExpanded() || !this.hasFocus){
14605             return;
14606         }
14607         
14608         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14609         this.list.setWidth(lw);
14610         
14611         Roo.log('expand');
14612         
14613         this.list.show();
14614         
14615         this.restrictHeight();
14616         
14617         if(this.tickable){
14618             
14619             this.tickItems = Roo.apply([], this.item);
14620             
14621             this.okBtn.show();
14622             this.cancelBtn.show();
14623             this.trigger.hide();
14624             
14625             if(this.editable){
14626                 this.tickableInputEl().focus();
14627             }
14628             
14629         }
14630         
14631         Roo.get(document).on('mousedown', this.collapseIf, this);
14632         Roo.get(document).on('mousewheel', this.collapseIf, this);
14633         if (!this.editable) {
14634             Roo.get(document).on('keydown', this.listKeyPress, this);
14635         }
14636         
14637         this.fireEvent('expand', this);
14638     },
14639
14640     // private
14641     // Implements the default empty TriggerField.onTriggerClick function
14642     onTriggerClick : function(e)
14643     {
14644         Roo.log('trigger click');
14645         
14646         if(this.disabled || !this.triggerList){
14647             return;
14648         }
14649         
14650         this.page = 0;
14651         this.loadNext = false;
14652         
14653         if(this.isExpanded()){
14654             this.collapse();
14655             if (!this.blockFocus) {
14656                 this.inputEl().focus();
14657             }
14658             
14659         }else {
14660             this.hasFocus = true;
14661             if(this.triggerAction == 'all') {
14662                 this.doQuery(this.allQuery, true);
14663             } else {
14664                 this.doQuery(this.getRawValue());
14665             }
14666             if (!this.blockFocus) {
14667                 this.inputEl().focus();
14668             }
14669         }
14670     },
14671     
14672     onTickableTriggerClick : function(e)
14673     {
14674         if(this.disabled){
14675             return;
14676         }
14677         
14678         this.page = 0;
14679         this.loadNext = false;
14680         this.hasFocus = true;
14681         
14682         if(this.triggerAction == 'all') {
14683             this.doQuery(this.allQuery, true);
14684         } else {
14685             this.doQuery(this.getRawValue());
14686         }
14687     },
14688     
14689     onSearchFieldClick : function(e)
14690     {
14691         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14692             this.onTickableFooterButtonClick(e, false, false);
14693             return;
14694         }
14695         
14696         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14697             return;
14698         }
14699         
14700         this.page = 0;
14701         this.loadNext = false;
14702         this.hasFocus = true;
14703         
14704         if(this.triggerAction == 'all') {
14705             this.doQuery(this.allQuery, true);
14706         } else {
14707             this.doQuery(this.getRawValue());
14708         }
14709     },
14710     
14711     listKeyPress : function(e)
14712     {
14713         //Roo.log('listkeypress');
14714         // scroll to first matching element based on key pres..
14715         if (e.isSpecialKey()) {
14716             return false;
14717         }
14718         var k = String.fromCharCode(e.getKey()).toUpperCase();
14719         //Roo.log(k);
14720         var match  = false;
14721         var csel = this.view.getSelectedNodes();
14722         var cselitem = false;
14723         if (csel.length) {
14724             var ix = this.view.indexOf(csel[0]);
14725             cselitem  = this.store.getAt(ix);
14726             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14727                 cselitem = false;
14728             }
14729             
14730         }
14731         
14732         this.store.each(function(v) { 
14733             if (cselitem) {
14734                 // start at existing selection.
14735                 if (cselitem.id == v.id) {
14736                     cselitem = false;
14737                 }
14738                 return true;
14739             }
14740                 
14741             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14742                 match = this.store.indexOf(v);
14743                 return false;
14744             }
14745             return true;
14746         }, this);
14747         
14748         if (match === false) {
14749             return true; // no more action?
14750         }
14751         // scroll to?
14752         this.view.select(match);
14753         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14754         sn.scrollIntoView(sn.dom.parentNode, false);
14755     },
14756     
14757     onViewScroll : function(e, t){
14758         
14759         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){
14760             return;
14761         }
14762         
14763         this.hasQuery = true;
14764         
14765         this.loading = this.list.select('.loading', true).first();
14766         
14767         if(this.loading === null){
14768             this.list.createChild({
14769                 tag: 'div',
14770                 cls: 'loading roo-select2-more-results roo-select2-active',
14771                 html: 'Loading more results...'
14772             });
14773             
14774             this.loading = this.list.select('.loading', true).first();
14775             
14776             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14777             
14778             this.loading.hide();
14779         }
14780         
14781         this.loading.show();
14782         
14783         var _combo = this;
14784         
14785         this.page++;
14786         this.loadNext = true;
14787         
14788         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14789         
14790         return;
14791     },
14792     
14793     addItem : function(o)
14794     {   
14795         var dv = ''; // display value
14796         
14797         if (this.displayField) {
14798             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14799         } else {
14800             // this is an error condition!!!
14801             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14802         }
14803         
14804         if(!dv.length){
14805             return;
14806         }
14807         
14808         var choice = this.choices.createChild({
14809             tag: 'li',
14810             cls: 'roo-select2-search-choice',
14811             cn: [
14812                 {
14813                     tag: 'div',
14814                     html: dv
14815                 },
14816                 {
14817                     tag: 'a',
14818                     href: '#',
14819                     cls: 'roo-select2-search-choice-close fa fa-times',
14820                     tabindex: '-1'
14821                 }
14822             ]
14823             
14824         }, this.searchField);
14825         
14826         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14827         
14828         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14829         
14830         this.item.push(o);
14831         
14832         this.lastData = o;
14833         
14834         this.syncValue();
14835         
14836         this.inputEl().dom.value = '';
14837         
14838         this.validate();
14839     },
14840     
14841     onRemoveItem : function(e, _self, o)
14842     {
14843         e.preventDefault();
14844         
14845         this.lastItem = Roo.apply([], this.item);
14846         
14847         var index = this.item.indexOf(o.data) * 1;
14848         
14849         if( index < 0){
14850             Roo.log('not this item?!');
14851             return;
14852         }
14853         
14854         this.item.splice(index, 1);
14855         o.item.remove();
14856         
14857         this.syncValue();
14858         
14859         this.fireEvent('remove', this, e);
14860         
14861         this.validate();
14862         
14863     },
14864     
14865     syncValue : function()
14866     {
14867         if(!this.item.length){
14868             this.clearValue();
14869             return;
14870         }
14871             
14872         var value = [];
14873         var _this = this;
14874         Roo.each(this.item, function(i){
14875             if(_this.valueField){
14876                 value.push(i[_this.valueField]);
14877                 return;
14878             }
14879
14880             value.push(i);
14881         });
14882
14883         this.value = value.join(',');
14884
14885         if(this.hiddenField){
14886             this.hiddenField.dom.value = this.value;
14887         }
14888         
14889         this.store.fireEvent("datachanged", this.store);
14890         
14891         this.validate();
14892     },
14893     
14894     clearItem : function()
14895     {
14896         if(!this.multiple){
14897             return;
14898         }
14899         
14900         this.item = [];
14901         
14902         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14903            c.remove();
14904         });
14905         
14906         this.syncValue();
14907         
14908         this.validate();
14909         
14910         if(this.tickable && !Roo.isTouch){
14911             this.view.refresh();
14912         }
14913     },
14914     
14915     inputEl: function ()
14916     {
14917         if(Roo.isIOS && this.useNativeIOS){
14918             return this.el.select('select.roo-ios-select', true).first();
14919         }
14920         
14921         if(Roo.isTouch && this.mobileTouchView){
14922             return this.el.select('input.form-control',true).first();
14923         }
14924         
14925         if(this.tickable){
14926             return this.searchField;
14927         }
14928         
14929         return this.el.select('input.form-control',true).first();
14930     },
14931     
14932     onTickableFooterButtonClick : function(e, btn, el)
14933     {
14934         e.preventDefault();
14935         
14936         this.lastItem = Roo.apply([], this.item);
14937         
14938         if(btn && btn.name == 'cancel'){
14939             this.tickItems = Roo.apply([], this.item);
14940             this.collapse();
14941             return;
14942         }
14943         
14944         this.clearItem();
14945         
14946         var _this = this;
14947         
14948         Roo.each(this.tickItems, function(o){
14949             _this.addItem(o);
14950         });
14951         
14952         this.collapse();
14953         
14954     },
14955     
14956     validate : function()
14957     {
14958         if(this.getVisibilityEl().hasClass('hidden')){
14959             return true;
14960         }
14961         
14962         var v = this.getRawValue();
14963         
14964         if(this.multiple){
14965             v = this.getValue();
14966         }
14967         
14968         if(this.disabled || this.allowBlank || v.length){
14969             this.markValid();
14970             return true;
14971         }
14972         
14973         this.markInvalid();
14974         return false;
14975     },
14976     
14977     tickableInputEl : function()
14978     {
14979         if(!this.tickable || !this.editable){
14980             return this.inputEl();
14981         }
14982         
14983         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14984     },
14985     
14986     
14987     getAutoCreateTouchView : function()
14988     {
14989         var id = Roo.id();
14990         
14991         var cfg = {
14992             cls: 'form-group' //input-group
14993         };
14994         
14995         var input =  {
14996             tag: 'input',
14997             id : id,
14998             type : this.inputType,
14999             cls : 'form-control x-combo-noedit',
15000             autocomplete: 'new-password',
15001             placeholder : this.placeholder || '',
15002             readonly : true
15003         };
15004         
15005         if (this.name) {
15006             input.name = this.name;
15007         }
15008         
15009         if (this.size) {
15010             input.cls += ' input-' + this.size;
15011         }
15012         
15013         if (this.disabled) {
15014             input.disabled = true;
15015         }
15016         
15017         var inputblock = {
15018             cls : '',
15019             cn : [
15020                 input
15021             ]
15022         };
15023         
15024         if(this.before){
15025             inputblock.cls += ' input-group';
15026             
15027             inputblock.cn.unshift({
15028                 tag :'span',
15029                 cls : 'input-group-addon',
15030                 html : this.before
15031             });
15032         }
15033         
15034         if(this.removable && !this.multiple){
15035             inputblock.cls += ' roo-removable';
15036             
15037             inputblock.cn.push({
15038                 tag: 'button',
15039                 html : 'x',
15040                 cls : 'roo-combo-removable-btn close'
15041             });
15042         }
15043
15044         if(this.hasFeedback && !this.allowBlank){
15045             
15046             inputblock.cls += ' has-feedback';
15047             
15048             inputblock.cn.push({
15049                 tag: 'span',
15050                 cls: 'glyphicon form-control-feedback'
15051             });
15052             
15053         }
15054         
15055         if (this.after) {
15056             
15057             inputblock.cls += (this.before) ? '' : ' input-group';
15058             
15059             inputblock.cn.push({
15060                 tag :'span',
15061                 cls : 'input-group-addon',
15062                 html : this.after
15063             });
15064         }
15065
15066         var box = {
15067             tag: 'div',
15068             cn: [
15069                 {
15070                     tag: 'input',
15071                     type : 'hidden',
15072                     cls: 'form-hidden-field'
15073                 },
15074                 inputblock
15075             ]
15076             
15077         };
15078         
15079         if(this.multiple){
15080             box = {
15081                 tag: 'div',
15082                 cn: [
15083                     {
15084                         tag: 'input',
15085                         type : 'hidden',
15086                         cls: 'form-hidden-field'
15087                     },
15088                     {
15089                         tag: 'ul',
15090                         cls: 'roo-select2-choices',
15091                         cn:[
15092                             {
15093                                 tag: 'li',
15094                                 cls: 'roo-select2-search-field',
15095                                 cn: [
15096
15097                                     inputblock
15098                                 ]
15099                             }
15100                         ]
15101                     }
15102                 ]
15103             }
15104         };
15105         
15106         var combobox = {
15107             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15108             cn: [
15109                 box
15110             ]
15111         };
15112         
15113         if(!this.multiple && this.showToggleBtn){
15114             
15115             var caret = {
15116                         tag: 'span',
15117                         cls: 'caret'
15118             };
15119             
15120             if (this.caret != false) {
15121                 caret = {
15122                      tag: 'i',
15123                      cls: 'fa fa-' + this.caret
15124                 };
15125                 
15126             }
15127             
15128             combobox.cn.push({
15129                 tag :'span',
15130                 cls : 'input-group-addon btn dropdown-toggle',
15131                 cn : [
15132                     caret,
15133                     {
15134                         tag: 'span',
15135                         cls: 'combobox-clear',
15136                         cn  : [
15137                             {
15138                                 tag : 'i',
15139                                 cls: 'icon-remove'
15140                             }
15141                         ]
15142                     }
15143                 ]
15144
15145             })
15146         }
15147         
15148         if(this.multiple){
15149             combobox.cls += ' roo-select2-container-multi';
15150         }
15151         
15152         var align = this.labelAlign || this.parentLabelAlign();
15153         
15154         if (align ==='left' && this.fieldLabel.length) {
15155
15156             cfg.cn = [
15157                 {
15158                    tag : 'i',
15159                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15160                    tooltip : 'This field is required'
15161                 },
15162                 {
15163                     tag: 'label',
15164                     cls : 'control-label',
15165                     html : this.fieldLabel
15166
15167                 },
15168                 {
15169                     cls : '', 
15170                     cn: [
15171                         combobox
15172                     ]
15173                 }
15174             ];
15175             
15176             var labelCfg = cfg.cn[1];
15177             var contentCfg = cfg.cn[2];
15178             
15179
15180             if(this.indicatorpos == 'right'){
15181                 cfg.cn = [
15182                     {
15183                         tag: 'label',
15184                         'for' :  id,
15185                         cls : 'control-label',
15186                         cn : [
15187                             {
15188                                 tag : 'span',
15189                                 html : this.fieldLabel
15190                             },
15191                             {
15192                                 tag : 'i',
15193                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15194                                 tooltip : 'This field is required'
15195                             }
15196                         ]
15197                     },
15198                     {
15199                         cls : "",
15200                         cn: [
15201                             combobox
15202                         ]
15203                     }
15204
15205                 ];
15206                 
15207                 labelCfg = cfg.cn[0];
15208                 contentCfg = cfg.cn[1];
15209             }
15210             
15211            
15212             
15213             if(this.labelWidth > 12){
15214                 labelCfg.style = "width: " + this.labelWidth + 'px';
15215             }
15216             
15217             if(this.labelWidth < 13 && this.labelmd == 0){
15218                 this.labelmd = this.labelWidth;
15219             }
15220             
15221             if(this.labellg > 0){
15222                 labelCfg.cls += ' col-lg-' + this.labellg;
15223                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15224             }
15225             
15226             if(this.labelmd > 0){
15227                 labelCfg.cls += ' col-md-' + this.labelmd;
15228                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15229             }
15230             
15231             if(this.labelsm > 0){
15232                 labelCfg.cls += ' col-sm-' + this.labelsm;
15233                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15234             }
15235             
15236             if(this.labelxs > 0){
15237                 labelCfg.cls += ' col-xs-' + this.labelxs;
15238                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15239             }
15240                 
15241                 
15242         } else if ( this.fieldLabel.length) {
15243             cfg.cn = [
15244                 {
15245                    tag : 'i',
15246                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15247                    tooltip : 'This field is required'
15248                 },
15249                 {
15250                     tag: 'label',
15251                     cls : 'control-label',
15252                     html : this.fieldLabel
15253
15254                 },
15255                 {
15256                     cls : '', 
15257                     cn: [
15258                         combobox
15259                     ]
15260                 }
15261             ];
15262             
15263             if(this.indicatorpos == 'right'){
15264                 cfg.cn = [
15265                     {
15266                         tag: 'label',
15267                         cls : 'control-label',
15268                         html : this.fieldLabel,
15269                         cn : [
15270                             {
15271                                tag : 'i',
15272                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15273                                tooltip : 'This field is required'
15274                             }
15275                         ]
15276                     },
15277                     {
15278                         cls : '', 
15279                         cn: [
15280                             combobox
15281                         ]
15282                     }
15283                 ];
15284             }
15285         } else {
15286             cfg.cn = combobox;    
15287         }
15288         
15289         
15290         var settings = this;
15291         
15292         ['xs','sm','md','lg'].map(function(size){
15293             if (settings[size]) {
15294                 cfg.cls += ' col-' + size + '-' + settings[size];
15295             }
15296         });
15297         
15298         return cfg;
15299     },
15300     
15301     initTouchView : function()
15302     {
15303         this.renderTouchView();
15304         
15305         this.touchViewEl.on('scroll', function(){
15306             this.el.dom.scrollTop = 0;
15307         }, this);
15308         
15309         this.originalValue = this.getValue();
15310         
15311         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15312         
15313         this.inputEl().on("click", this.showTouchView, this);
15314         if (this.triggerEl) {
15315             this.triggerEl.on("click", this.showTouchView, this);
15316         }
15317         
15318         
15319         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15320         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15321         
15322         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15323         
15324         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15325         this.store.on('load', this.onTouchViewLoad, this);
15326         this.store.on('loadexception', this.onTouchViewLoadException, this);
15327         
15328         if(this.hiddenName){
15329             
15330             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15331             
15332             this.hiddenField.dom.value =
15333                 this.hiddenValue !== undefined ? this.hiddenValue :
15334                 this.value !== undefined ? this.value : '';
15335         
15336             this.el.dom.removeAttribute('name');
15337             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15338         }
15339         
15340         if(this.multiple){
15341             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15342             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15343         }
15344         
15345         if(this.removable && !this.multiple){
15346             var close = this.closeTriggerEl();
15347             if(close){
15348                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15349                 close.on('click', this.removeBtnClick, this, close);
15350             }
15351         }
15352         /*
15353          * fix the bug in Safari iOS8
15354          */
15355         this.inputEl().on("focus", function(e){
15356             document.activeElement.blur();
15357         }, this);
15358         
15359         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15360         
15361         return;
15362         
15363         
15364     },
15365     
15366     renderTouchView : function()
15367     {
15368         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15369         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15370         
15371         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15372         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15373         
15374         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15375         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15376         this.touchViewBodyEl.setStyle('overflow', 'auto');
15377         
15378         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15379         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15380         
15381         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15382         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15383         
15384     },
15385     
15386     showTouchView : function()
15387     {
15388         if(this.disabled){
15389             return;
15390         }
15391         
15392         this.touchViewHeaderEl.hide();
15393
15394         if(this.modalTitle.length){
15395             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15396             this.touchViewHeaderEl.show();
15397         }
15398
15399         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15400         this.touchViewEl.show();
15401
15402         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15403         
15404         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15405         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15406
15407         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15408
15409         if(this.modalTitle.length){
15410             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15411         }
15412         
15413         this.touchViewBodyEl.setHeight(bodyHeight);
15414
15415         if(this.animate){
15416             var _this = this;
15417             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15418         }else{
15419             this.touchViewEl.addClass('in');
15420         }
15421         
15422         if(this._touchViewMask){
15423             Roo.get(document.body).addClass("x-body-masked");
15424             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15425             this._touchViewMask.setStyle('z-index', 10000);
15426             this._touchViewMask.addClass('show');
15427         }
15428         
15429         this.doTouchViewQuery();
15430         
15431     },
15432     
15433     hideTouchView : function()
15434     {
15435         this.touchViewEl.removeClass('in');
15436
15437         if(this.animate){
15438             var _this = this;
15439             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15440         }else{
15441             this.touchViewEl.setStyle('display', 'none');
15442         }
15443         
15444         if(this._touchViewMask){
15445             this._touchViewMask.removeClass('show');
15446             Roo.get(document.body).removeClass("x-body-masked");
15447         }
15448     },
15449     
15450     setTouchViewValue : function()
15451     {
15452         if(this.multiple){
15453             this.clearItem();
15454         
15455             var _this = this;
15456
15457             Roo.each(this.tickItems, function(o){
15458                 this.addItem(o);
15459             }, this);
15460         }
15461         
15462         this.hideTouchView();
15463     },
15464     
15465     doTouchViewQuery : function()
15466     {
15467         var qe = {
15468             query: '',
15469             forceAll: true,
15470             combo: this,
15471             cancel:false
15472         };
15473         
15474         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15475             return false;
15476         }
15477         
15478         if(!this.alwaysQuery || this.mode == 'local'){
15479             this.onTouchViewLoad();
15480             return;
15481         }
15482         
15483         this.store.load();
15484     },
15485     
15486     onTouchViewBeforeLoad : function(combo,opts)
15487     {
15488         return;
15489     },
15490
15491     // private
15492     onTouchViewLoad : function()
15493     {
15494         if(this.store.getCount() < 1){
15495             this.onTouchViewEmptyResults();
15496             return;
15497         }
15498         
15499         this.clearTouchView();
15500         
15501         var rawValue = this.getRawValue();
15502         
15503         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15504         
15505         this.tickItems = [];
15506         
15507         this.store.data.each(function(d, rowIndex){
15508             var row = this.touchViewListGroup.createChild(template);
15509             
15510             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15511                 row.addClass(d.data.cls);
15512             }
15513             
15514             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15515                 var cfg = {
15516                     data : d.data,
15517                     html : d.data[this.displayField]
15518                 };
15519                 
15520                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15521                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15522                 }
15523             }
15524             row.removeClass('selected');
15525             if(!this.multiple && this.valueField &&
15526                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15527             {
15528                 // radio buttons..
15529                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15530                 row.addClass('selected');
15531             }
15532             
15533             if(this.multiple && this.valueField &&
15534                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15535             {
15536                 
15537                 // checkboxes...
15538                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15539                 this.tickItems.push(d.data);
15540             }
15541             
15542             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15543             
15544         }, this);
15545         
15546         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15547         
15548         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15549
15550         if(this.modalTitle.length){
15551             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15552         }
15553
15554         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15555         
15556         if(this.mobile_restrict_height && listHeight < bodyHeight){
15557             this.touchViewBodyEl.setHeight(listHeight);
15558         }
15559         
15560         var _this = this;
15561         
15562         if(firstChecked && listHeight > bodyHeight){
15563             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15564         }
15565         
15566     },
15567     
15568     onTouchViewLoadException : function()
15569     {
15570         this.hideTouchView();
15571     },
15572     
15573     onTouchViewEmptyResults : function()
15574     {
15575         this.clearTouchView();
15576         
15577         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15578         
15579         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15580         
15581     },
15582     
15583     clearTouchView : function()
15584     {
15585         this.touchViewListGroup.dom.innerHTML = '';
15586     },
15587     
15588     onTouchViewClick : function(e, el, o)
15589     {
15590         e.preventDefault();
15591         
15592         var row = o.row;
15593         var rowIndex = o.rowIndex;
15594         
15595         var r = this.store.getAt(rowIndex);
15596         
15597         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15598             
15599             if(!this.multiple){
15600                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15601                     c.dom.removeAttribute('checked');
15602                 }, this);
15603
15604                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15605
15606                 this.setFromData(r.data);
15607
15608                 var close = this.closeTriggerEl();
15609
15610                 if(close){
15611                     close.show();
15612                 }
15613
15614                 this.hideTouchView();
15615
15616                 this.fireEvent('select', this, r, rowIndex);
15617
15618                 return;
15619             }
15620
15621             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15622                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15623                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15624                 return;
15625             }
15626
15627             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15628             this.addItem(r.data);
15629             this.tickItems.push(r.data);
15630         }
15631     },
15632     
15633     getAutoCreateNativeIOS : function()
15634     {
15635         var cfg = {
15636             cls: 'form-group' //input-group,
15637         };
15638         
15639         var combobox =  {
15640             tag: 'select',
15641             cls : 'roo-ios-select'
15642         };
15643         
15644         if (this.name) {
15645             combobox.name = this.name;
15646         }
15647         
15648         if (this.disabled) {
15649             combobox.disabled = true;
15650         }
15651         
15652         var settings = this;
15653         
15654         ['xs','sm','md','lg'].map(function(size){
15655             if (settings[size]) {
15656                 cfg.cls += ' col-' + size + '-' + settings[size];
15657             }
15658         });
15659         
15660         cfg.cn = combobox;
15661         
15662         return cfg;
15663         
15664     },
15665     
15666     initIOSView : function()
15667     {
15668         this.store.on('load', this.onIOSViewLoad, this);
15669         
15670         return;
15671     },
15672     
15673     onIOSViewLoad : function()
15674     {
15675         if(this.store.getCount() < 1){
15676             return;
15677         }
15678         
15679         this.clearIOSView();
15680         
15681         if(this.allowBlank) {
15682             
15683             var default_text = '-- SELECT --';
15684             
15685             if(this.placeholder.length){
15686                 default_text = this.placeholder;
15687             }
15688             
15689             if(this.emptyTitle.length){
15690                 default_text += ' - ' + this.emptyTitle + ' -';
15691             }
15692             
15693             var opt = this.inputEl().createChild({
15694                 tag: 'option',
15695                 value : 0,
15696                 html : default_text
15697             });
15698             
15699             var o = {};
15700             o[this.valueField] = 0;
15701             o[this.displayField] = default_text;
15702             
15703             this.ios_options.push({
15704                 data : o,
15705                 el : opt
15706             });
15707             
15708         }
15709         
15710         this.store.data.each(function(d, rowIndex){
15711             
15712             var html = '';
15713             
15714             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15715                 html = d.data[this.displayField];
15716             }
15717             
15718             var value = '';
15719             
15720             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15721                 value = d.data[this.valueField];
15722             }
15723             
15724             var option = {
15725                 tag: 'option',
15726                 value : value,
15727                 html : html
15728             };
15729             
15730             if(this.value == d.data[this.valueField]){
15731                 option['selected'] = true;
15732             }
15733             
15734             var opt = this.inputEl().createChild(option);
15735             
15736             this.ios_options.push({
15737                 data : d.data,
15738                 el : opt
15739             });
15740             
15741         }, this);
15742         
15743         this.inputEl().on('change', function(){
15744            this.fireEvent('select', this);
15745         }, this);
15746         
15747     },
15748     
15749     clearIOSView: function()
15750     {
15751         this.inputEl().dom.innerHTML = '';
15752         
15753         this.ios_options = [];
15754     },
15755     
15756     setIOSValue: function(v)
15757     {
15758         this.value = v;
15759         
15760         if(!this.ios_options){
15761             return;
15762         }
15763         
15764         Roo.each(this.ios_options, function(opts){
15765            
15766            opts.el.dom.removeAttribute('selected');
15767            
15768            if(opts.data[this.valueField] != v){
15769                return;
15770            }
15771            
15772            opts.el.dom.setAttribute('selected', true);
15773            
15774         }, this);
15775     }
15776
15777     /** 
15778     * @cfg {Boolean} grow 
15779     * @hide 
15780     */
15781     /** 
15782     * @cfg {Number} growMin 
15783     * @hide 
15784     */
15785     /** 
15786     * @cfg {Number} growMax 
15787     * @hide 
15788     */
15789     /**
15790      * @hide
15791      * @method autoSize
15792      */
15793 });
15794
15795 Roo.apply(Roo.bootstrap.ComboBox,  {
15796     
15797     header : {
15798         tag: 'div',
15799         cls: 'modal-header',
15800         cn: [
15801             {
15802                 tag: 'h4',
15803                 cls: 'modal-title'
15804             }
15805         ]
15806     },
15807     
15808     body : {
15809         tag: 'div',
15810         cls: 'modal-body',
15811         cn: [
15812             {
15813                 tag: 'ul',
15814                 cls: 'list-group'
15815             }
15816         ]
15817     },
15818     
15819     listItemRadio : {
15820         tag: 'li',
15821         cls: 'list-group-item',
15822         cn: [
15823             {
15824                 tag: 'span',
15825                 cls: 'roo-combobox-list-group-item-value'
15826             },
15827             {
15828                 tag: 'div',
15829                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15830                 cn: [
15831                     {
15832                         tag: 'input',
15833                         type: 'radio'
15834                     },
15835                     {
15836                         tag: 'label'
15837                     }
15838                 ]
15839             }
15840         ]
15841     },
15842     
15843     listItemCheckbox : {
15844         tag: 'li',
15845         cls: 'list-group-item',
15846         cn: [
15847             {
15848                 tag: 'span',
15849                 cls: 'roo-combobox-list-group-item-value'
15850             },
15851             {
15852                 tag: 'div',
15853                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15854                 cn: [
15855                     {
15856                         tag: 'input',
15857                         type: 'checkbox'
15858                     },
15859                     {
15860                         tag: 'label'
15861                     }
15862                 ]
15863             }
15864         ]
15865     },
15866     
15867     emptyResult : {
15868         tag: 'div',
15869         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15870     },
15871     
15872     footer : {
15873         tag: 'div',
15874         cls: 'modal-footer',
15875         cn: [
15876             {
15877                 tag: 'div',
15878                 cls: 'row',
15879                 cn: [
15880                     {
15881                         tag: 'div',
15882                         cls: 'col-xs-6 text-left',
15883                         cn: {
15884                             tag: 'button',
15885                             cls: 'btn btn-danger roo-touch-view-cancel',
15886                             html: 'Cancel'
15887                         }
15888                     },
15889                     {
15890                         tag: 'div',
15891                         cls: 'col-xs-6 text-right',
15892                         cn: {
15893                             tag: 'button',
15894                             cls: 'btn btn-success roo-touch-view-ok',
15895                             html: 'OK'
15896                         }
15897                     }
15898                 ]
15899             }
15900         ]
15901         
15902     }
15903 });
15904
15905 Roo.apply(Roo.bootstrap.ComboBox,  {
15906     
15907     touchViewTemplate : {
15908         tag: 'div',
15909         cls: 'modal fade roo-combobox-touch-view',
15910         cn: [
15911             {
15912                 tag: 'div',
15913                 cls: 'modal-dialog',
15914                 style : 'position:fixed', // we have to fix position....
15915                 cn: [
15916                     {
15917                         tag: 'div',
15918                         cls: 'modal-content',
15919                         cn: [
15920                             Roo.bootstrap.ComboBox.header,
15921                             Roo.bootstrap.ComboBox.body,
15922                             Roo.bootstrap.ComboBox.footer
15923                         ]
15924                     }
15925                 ]
15926             }
15927         ]
15928     }
15929 });/*
15930  * Based on:
15931  * Ext JS Library 1.1.1
15932  * Copyright(c) 2006-2007, Ext JS, LLC.
15933  *
15934  * Originally Released Under LGPL - original licence link has changed is not relivant.
15935  *
15936  * Fork - LGPL
15937  * <script type="text/javascript">
15938  */
15939
15940 /**
15941  * @class Roo.View
15942  * @extends Roo.util.Observable
15943  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15944  * This class also supports single and multi selection modes. <br>
15945  * Create a data model bound view:
15946  <pre><code>
15947  var store = new Roo.data.Store(...);
15948
15949  var view = new Roo.View({
15950     el : "my-element",
15951     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15952  
15953     singleSelect: true,
15954     selectedClass: "ydataview-selected",
15955     store: store
15956  });
15957
15958  // listen for node click?
15959  view.on("click", function(vw, index, node, e){
15960  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15961  });
15962
15963  // load XML data
15964  dataModel.load("foobar.xml");
15965  </code></pre>
15966  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15967  * <br><br>
15968  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15969  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15970  * 
15971  * Note: old style constructor is still suported (container, template, config)
15972  * 
15973  * @constructor
15974  * Create a new View
15975  * @param {Object} config The config object
15976  * 
15977  */
15978 Roo.View = function(config, depreciated_tpl, depreciated_config){
15979     
15980     this.parent = false;
15981     
15982     if (typeof(depreciated_tpl) == 'undefined') {
15983         // new way.. - universal constructor.
15984         Roo.apply(this, config);
15985         this.el  = Roo.get(this.el);
15986     } else {
15987         // old format..
15988         this.el  = Roo.get(config);
15989         this.tpl = depreciated_tpl;
15990         Roo.apply(this, depreciated_config);
15991     }
15992     this.wrapEl  = this.el.wrap().wrap();
15993     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15994     
15995     
15996     if(typeof(this.tpl) == "string"){
15997         this.tpl = new Roo.Template(this.tpl);
15998     } else {
15999         // support xtype ctors..
16000         this.tpl = new Roo.factory(this.tpl, Roo);
16001     }
16002     
16003     
16004     this.tpl.compile();
16005     
16006     /** @private */
16007     this.addEvents({
16008         /**
16009          * @event beforeclick
16010          * Fires before a click is processed. Returns false to cancel the default action.
16011          * @param {Roo.View} this
16012          * @param {Number} index The index of the target node
16013          * @param {HTMLElement} node The target node
16014          * @param {Roo.EventObject} e The raw event object
16015          */
16016             "beforeclick" : true,
16017         /**
16018          * @event click
16019          * Fires when a template node is clicked.
16020          * @param {Roo.View} this
16021          * @param {Number} index The index of the target node
16022          * @param {HTMLElement} node The target node
16023          * @param {Roo.EventObject} e The raw event object
16024          */
16025             "click" : true,
16026         /**
16027          * @event dblclick
16028          * Fires when a template node is double clicked.
16029          * @param {Roo.View} this
16030          * @param {Number} index The index of the target node
16031          * @param {HTMLElement} node The target node
16032          * @param {Roo.EventObject} e The raw event object
16033          */
16034             "dblclick" : true,
16035         /**
16036          * @event contextmenu
16037          * Fires when a template node is right clicked.
16038          * @param {Roo.View} this
16039          * @param {Number} index The index of the target node
16040          * @param {HTMLElement} node The target node
16041          * @param {Roo.EventObject} e The raw event object
16042          */
16043             "contextmenu" : true,
16044         /**
16045          * @event selectionchange
16046          * Fires when the selected nodes change.
16047          * @param {Roo.View} this
16048          * @param {Array} selections Array of the selected nodes
16049          */
16050             "selectionchange" : true,
16051     
16052         /**
16053          * @event beforeselect
16054          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16055          * @param {Roo.View} this
16056          * @param {HTMLElement} node The node to be selected
16057          * @param {Array} selections Array of currently selected nodes
16058          */
16059             "beforeselect" : true,
16060         /**
16061          * @event preparedata
16062          * Fires on every row to render, to allow you to change the data.
16063          * @param {Roo.View} this
16064          * @param {Object} data to be rendered (change this)
16065          */
16066           "preparedata" : true
16067           
16068           
16069         });
16070
16071
16072
16073     this.el.on({
16074         "click": this.onClick,
16075         "dblclick": this.onDblClick,
16076         "contextmenu": this.onContextMenu,
16077         scope:this
16078     });
16079
16080     this.selections = [];
16081     this.nodes = [];
16082     this.cmp = new Roo.CompositeElementLite([]);
16083     if(this.store){
16084         this.store = Roo.factory(this.store, Roo.data);
16085         this.setStore(this.store, true);
16086     }
16087     
16088     if ( this.footer && this.footer.xtype) {
16089            
16090          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16091         
16092         this.footer.dataSource = this.store;
16093         this.footer.container = fctr;
16094         this.footer = Roo.factory(this.footer, Roo);
16095         fctr.insertFirst(this.el);
16096         
16097         // this is a bit insane - as the paging toolbar seems to detach the el..
16098 //        dom.parentNode.parentNode.parentNode
16099          // they get detached?
16100     }
16101     
16102     
16103     Roo.View.superclass.constructor.call(this);
16104     
16105     
16106 };
16107
16108 Roo.extend(Roo.View, Roo.util.Observable, {
16109     
16110      /**
16111      * @cfg {Roo.data.Store} store Data store to load data from.
16112      */
16113     store : false,
16114     
16115     /**
16116      * @cfg {String|Roo.Element} el The container element.
16117      */
16118     el : '',
16119     
16120     /**
16121      * @cfg {String|Roo.Template} tpl The template used by this View 
16122      */
16123     tpl : false,
16124     /**
16125      * @cfg {String} dataName the named area of the template to use as the data area
16126      *                          Works with domtemplates roo-name="name"
16127      */
16128     dataName: false,
16129     /**
16130      * @cfg {String} selectedClass The css class to add to selected nodes
16131      */
16132     selectedClass : "x-view-selected",
16133      /**
16134      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16135      */
16136     emptyText : "",
16137     
16138     /**
16139      * @cfg {String} text to display on mask (default Loading)
16140      */
16141     mask : false,
16142     /**
16143      * @cfg {Boolean} multiSelect Allow multiple selection
16144      */
16145     multiSelect : false,
16146     /**
16147      * @cfg {Boolean} singleSelect Allow single selection
16148      */
16149     singleSelect:  false,
16150     
16151     /**
16152      * @cfg {Boolean} toggleSelect - selecting 
16153      */
16154     toggleSelect : false,
16155     
16156     /**
16157      * @cfg {Boolean} tickable - selecting 
16158      */
16159     tickable : false,
16160     
16161     /**
16162      * Returns the element this view is bound to.
16163      * @return {Roo.Element}
16164      */
16165     getEl : function(){
16166         return this.wrapEl;
16167     },
16168     
16169     
16170
16171     /**
16172      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16173      */
16174     refresh : function(){
16175         //Roo.log('refresh');
16176         var t = this.tpl;
16177         
16178         // if we are using something like 'domtemplate', then
16179         // the what gets used is:
16180         // t.applySubtemplate(NAME, data, wrapping data..)
16181         // the outer template then get' applied with
16182         //     the store 'extra data'
16183         // and the body get's added to the
16184         //      roo-name="data" node?
16185         //      <span class='roo-tpl-{name}'></span> ?????
16186         
16187         
16188         
16189         this.clearSelections();
16190         this.el.update("");
16191         var html = [];
16192         var records = this.store.getRange();
16193         if(records.length < 1) {
16194             
16195             // is this valid??  = should it render a template??
16196             
16197             this.el.update(this.emptyText);
16198             return;
16199         }
16200         var el = this.el;
16201         if (this.dataName) {
16202             this.el.update(t.apply(this.store.meta)); //????
16203             el = this.el.child('.roo-tpl-' + this.dataName);
16204         }
16205         
16206         for(var i = 0, len = records.length; i < len; i++){
16207             var data = this.prepareData(records[i].data, i, records[i]);
16208             this.fireEvent("preparedata", this, data, i, records[i]);
16209             
16210             var d = Roo.apply({}, data);
16211             
16212             if(this.tickable){
16213                 Roo.apply(d, {'roo-id' : Roo.id()});
16214                 
16215                 var _this = this;
16216             
16217                 Roo.each(this.parent.item, function(item){
16218                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16219                         return;
16220                     }
16221                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16222                 });
16223             }
16224             
16225             html[html.length] = Roo.util.Format.trim(
16226                 this.dataName ?
16227                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16228                     t.apply(d)
16229             );
16230         }
16231         
16232         
16233         
16234         el.update(html.join(""));
16235         this.nodes = el.dom.childNodes;
16236         this.updateIndexes(0);
16237     },
16238     
16239
16240     /**
16241      * Function to override to reformat the data that is sent to
16242      * the template for each node.
16243      * DEPRICATED - use the preparedata event handler.
16244      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16245      * a JSON object for an UpdateManager bound view).
16246      */
16247     prepareData : function(data, index, record)
16248     {
16249         this.fireEvent("preparedata", this, data, index, record);
16250         return data;
16251     },
16252
16253     onUpdate : function(ds, record){
16254         // Roo.log('on update');   
16255         this.clearSelections();
16256         var index = this.store.indexOf(record);
16257         var n = this.nodes[index];
16258         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16259         n.parentNode.removeChild(n);
16260         this.updateIndexes(index, index);
16261     },
16262
16263     
16264     
16265 // --------- FIXME     
16266     onAdd : function(ds, records, index)
16267     {
16268         //Roo.log(['on Add', ds, records, index] );        
16269         this.clearSelections();
16270         if(this.nodes.length == 0){
16271             this.refresh();
16272             return;
16273         }
16274         var n = this.nodes[index];
16275         for(var i = 0, len = records.length; i < len; i++){
16276             var d = this.prepareData(records[i].data, i, records[i]);
16277             if(n){
16278                 this.tpl.insertBefore(n, d);
16279             }else{
16280                 
16281                 this.tpl.append(this.el, d);
16282             }
16283         }
16284         this.updateIndexes(index);
16285     },
16286
16287     onRemove : function(ds, record, index){
16288        // Roo.log('onRemove');
16289         this.clearSelections();
16290         var el = this.dataName  ?
16291             this.el.child('.roo-tpl-' + this.dataName) :
16292             this.el; 
16293         
16294         el.dom.removeChild(this.nodes[index]);
16295         this.updateIndexes(index);
16296     },
16297
16298     /**
16299      * Refresh an individual node.
16300      * @param {Number} index
16301      */
16302     refreshNode : function(index){
16303         this.onUpdate(this.store, this.store.getAt(index));
16304     },
16305
16306     updateIndexes : function(startIndex, endIndex){
16307         var ns = this.nodes;
16308         startIndex = startIndex || 0;
16309         endIndex = endIndex || ns.length - 1;
16310         for(var i = startIndex; i <= endIndex; i++){
16311             ns[i].nodeIndex = i;
16312         }
16313     },
16314
16315     /**
16316      * Changes the data store this view uses and refresh the view.
16317      * @param {Store} store
16318      */
16319     setStore : function(store, initial){
16320         if(!initial && this.store){
16321             this.store.un("datachanged", this.refresh);
16322             this.store.un("add", this.onAdd);
16323             this.store.un("remove", this.onRemove);
16324             this.store.un("update", this.onUpdate);
16325             this.store.un("clear", this.refresh);
16326             this.store.un("beforeload", this.onBeforeLoad);
16327             this.store.un("load", this.onLoad);
16328             this.store.un("loadexception", this.onLoad);
16329         }
16330         if(store){
16331           
16332             store.on("datachanged", this.refresh, this);
16333             store.on("add", this.onAdd, this);
16334             store.on("remove", this.onRemove, this);
16335             store.on("update", this.onUpdate, this);
16336             store.on("clear", this.refresh, this);
16337             store.on("beforeload", this.onBeforeLoad, this);
16338             store.on("load", this.onLoad, this);
16339             store.on("loadexception", this.onLoad, this);
16340         }
16341         
16342         if(store){
16343             this.refresh();
16344         }
16345     },
16346     /**
16347      * onbeforeLoad - masks the loading area.
16348      *
16349      */
16350     onBeforeLoad : function(store,opts)
16351     {
16352          //Roo.log('onBeforeLoad');   
16353         if (!opts.add) {
16354             this.el.update("");
16355         }
16356         this.el.mask(this.mask ? this.mask : "Loading" ); 
16357     },
16358     onLoad : function ()
16359     {
16360         this.el.unmask();
16361     },
16362     
16363
16364     /**
16365      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16366      * @param {HTMLElement} node
16367      * @return {HTMLElement} The template node
16368      */
16369     findItemFromChild : function(node){
16370         var el = this.dataName  ?
16371             this.el.child('.roo-tpl-' + this.dataName,true) :
16372             this.el.dom; 
16373         
16374         if(!node || node.parentNode == el){
16375                     return node;
16376             }
16377             var p = node.parentNode;
16378             while(p && p != el){
16379             if(p.parentNode == el){
16380                 return p;
16381             }
16382             p = p.parentNode;
16383         }
16384             return null;
16385     },
16386
16387     /** @ignore */
16388     onClick : function(e){
16389         var item = this.findItemFromChild(e.getTarget());
16390         if(item){
16391             var index = this.indexOf(item);
16392             if(this.onItemClick(item, index, e) !== false){
16393                 this.fireEvent("click", this, index, item, e);
16394             }
16395         }else{
16396             this.clearSelections();
16397         }
16398     },
16399
16400     /** @ignore */
16401     onContextMenu : function(e){
16402         var item = this.findItemFromChild(e.getTarget());
16403         if(item){
16404             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16405         }
16406     },
16407
16408     /** @ignore */
16409     onDblClick : function(e){
16410         var item = this.findItemFromChild(e.getTarget());
16411         if(item){
16412             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16413         }
16414     },
16415
16416     onItemClick : function(item, index, e)
16417     {
16418         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16419             return false;
16420         }
16421         if (this.toggleSelect) {
16422             var m = this.isSelected(item) ? 'unselect' : 'select';
16423             //Roo.log(m);
16424             var _t = this;
16425             _t[m](item, true, false);
16426             return true;
16427         }
16428         if(this.multiSelect || this.singleSelect){
16429             if(this.multiSelect && e.shiftKey && this.lastSelection){
16430                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16431             }else{
16432                 this.select(item, this.multiSelect && e.ctrlKey);
16433                 this.lastSelection = item;
16434             }
16435             
16436             if(!this.tickable){
16437                 e.preventDefault();
16438             }
16439             
16440         }
16441         return true;
16442     },
16443
16444     /**
16445      * Get the number of selected nodes.
16446      * @return {Number}
16447      */
16448     getSelectionCount : function(){
16449         return this.selections.length;
16450     },
16451
16452     /**
16453      * Get the currently selected nodes.
16454      * @return {Array} An array of HTMLElements
16455      */
16456     getSelectedNodes : function(){
16457         return this.selections;
16458     },
16459
16460     /**
16461      * Get the indexes of the selected nodes.
16462      * @return {Array}
16463      */
16464     getSelectedIndexes : function(){
16465         var indexes = [], s = this.selections;
16466         for(var i = 0, len = s.length; i < len; i++){
16467             indexes.push(s[i].nodeIndex);
16468         }
16469         return indexes;
16470     },
16471
16472     /**
16473      * Clear all selections
16474      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16475      */
16476     clearSelections : function(suppressEvent){
16477         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16478             this.cmp.elements = this.selections;
16479             this.cmp.removeClass(this.selectedClass);
16480             this.selections = [];
16481             if(!suppressEvent){
16482                 this.fireEvent("selectionchange", this, this.selections);
16483             }
16484         }
16485     },
16486
16487     /**
16488      * Returns true if the passed node is selected
16489      * @param {HTMLElement/Number} node The node or node index
16490      * @return {Boolean}
16491      */
16492     isSelected : function(node){
16493         var s = this.selections;
16494         if(s.length < 1){
16495             return false;
16496         }
16497         node = this.getNode(node);
16498         return s.indexOf(node) !== -1;
16499     },
16500
16501     /**
16502      * Selects nodes.
16503      * @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
16504      * @param {Boolean} keepExisting (optional) true to keep existing selections
16505      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16506      */
16507     select : function(nodeInfo, keepExisting, suppressEvent){
16508         if(nodeInfo instanceof Array){
16509             if(!keepExisting){
16510                 this.clearSelections(true);
16511             }
16512             for(var i = 0, len = nodeInfo.length; i < len; i++){
16513                 this.select(nodeInfo[i], true, true);
16514             }
16515             return;
16516         } 
16517         var node = this.getNode(nodeInfo);
16518         if(!node || this.isSelected(node)){
16519             return; // already selected.
16520         }
16521         if(!keepExisting){
16522             this.clearSelections(true);
16523         }
16524         
16525         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16526             Roo.fly(node).addClass(this.selectedClass);
16527             this.selections.push(node);
16528             if(!suppressEvent){
16529                 this.fireEvent("selectionchange", this, this.selections);
16530             }
16531         }
16532         
16533         
16534     },
16535       /**
16536      * Unselects nodes.
16537      * @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
16538      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16539      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16540      */
16541     unselect : function(nodeInfo, keepExisting, suppressEvent)
16542     {
16543         if(nodeInfo instanceof Array){
16544             Roo.each(this.selections, function(s) {
16545                 this.unselect(s, nodeInfo);
16546             }, this);
16547             return;
16548         }
16549         var node = this.getNode(nodeInfo);
16550         if(!node || !this.isSelected(node)){
16551             //Roo.log("not selected");
16552             return; // not selected.
16553         }
16554         // fireevent???
16555         var ns = [];
16556         Roo.each(this.selections, function(s) {
16557             if (s == node ) {
16558                 Roo.fly(node).removeClass(this.selectedClass);
16559
16560                 return;
16561             }
16562             ns.push(s);
16563         },this);
16564         
16565         this.selections= ns;
16566         this.fireEvent("selectionchange", this, this.selections);
16567     },
16568
16569     /**
16570      * Gets a template node.
16571      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16572      * @return {HTMLElement} The node or null if it wasn't found
16573      */
16574     getNode : function(nodeInfo){
16575         if(typeof nodeInfo == "string"){
16576             return document.getElementById(nodeInfo);
16577         }else if(typeof nodeInfo == "number"){
16578             return this.nodes[nodeInfo];
16579         }
16580         return nodeInfo;
16581     },
16582
16583     /**
16584      * Gets a range template nodes.
16585      * @param {Number} startIndex
16586      * @param {Number} endIndex
16587      * @return {Array} An array of nodes
16588      */
16589     getNodes : function(start, end){
16590         var ns = this.nodes;
16591         start = start || 0;
16592         end = typeof end == "undefined" ? ns.length - 1 : end;
16593         var nodes = [];
16594         if(start <= end){
16595             for(var i = start; i <= end; i++){
16596                 nodes.push(ns[i]);
16597             }
16598         } else{
16599             for(var i = start; i >= end; i--){
16600                 nodes.push(ns[i]);
16601             }
16602         }
16603         return nodes;
16604     },
16605
16606     /**
16607      * Finds the index of the passed node
16608      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16609      * @return {Number} The index of the node or -1
16610      */
16611     indexOf : function(node){
16612         node = this.getNode(node);
16613         if(typeof node.nodeIndex == "number"){
16614             return node.nodeIndex;
16615         }
16616         var ns = this.nodes;
16617         for(var i = 0, len = ns.length; i < len; i++){
16618             if(ns[i] == node){
16619                 return i;
16620             }
16621         }
16622         return -1;
16623     }
16624 });
16625 /*
16626  * - LGPL
16627  *
16628  * based on jquery fullcalendar
16629  * 
16630  */
16631
16632 Roo.bootstrap = Roo.bootstrap || {};
16633 /**
16634  * @class Roo.bootstrap.Calendar
16635  * @extends Roo.bootstrap.Component
16636  * Bootstrap Calendar class
16637  * @cfg {Boolean} loadMask (true|false) default false
16638  * @cfg {Object} header generate the user specific header of the calendar, default false
16639
16640  * @constructor
16641  * Create a new Container
16642  * @param {Object} config The config object
16643  */
16644
16645
16646
16647 Roo.bootstrap.Calendar = function(config){
16648     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16649      this.addEvents({
16650         /**
16651              * @event select
16652              * Fires when a date is selected
16653              * @param {DatePicker} this
16654              * @param {Date} date The selected date
16655              */
16656         'select': true,
16657         /**
16658              * @event monthchange
16659              * Fires when the displayed month changes 
16660              * @param {DatePicker} this
16661              * @param {Date} date The selected month
16662              */
16663         'monthchange': true,
16664         /**
16665              * @event evententer
16666              * Fires when mouse over an event
16667              * @param {Calendar} this
16668              * @param {event} Event
16669              */
16670         'evententer': true,
16671         /**
16672              * @event eventleave
16673              * Fires when the mouse leaves an
16674              * @param {Calendar} this
16675              * @param {event}
16676              */
16677         'eventleave': true,
16678         /**
16679              * @event eventclick
16680              * Fires when the mouse click an
16681              * @param {Calendar} this
16682              * @param {event}
16683              */
16684         'eventclick': true
16685         
16686     });
16687
16688 };
16689
16690 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16691     
16692      /**
16693      * @cfg {Number} startDay
16694      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16695      */
16696     startDay : 0,
16697     
16698     loadMask : false,
16699     
16700     header : false,
16701       
16702     getAutoCreate : function(){
16703         
16704         
16705         var fc_button = function(name, corner, style, content ) {
16706             return Roo.apply({},{
16707                 tag : 'span',
16708                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16709                          (corner.length ?
16710                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16711                             ''
16712                         ),
16713                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16714                 unselectable: 'on'
16715             });
16716         };
16717         
16718         var header = {};
16719         
16720         if(!this.header){
16721             header = {
16722                 tag : 'table',
16723                 cls : 'fc-header',
16724                 style : 'width:100%',
16725                 cn : [
16726                     {
16727                         tag: 'tr',
16728                         cn : [
16729                             {
16730                                 tag : 'td',
16731                                 cls : 'fc-header-left',
16732                                 cn : [
16733                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16734                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16735                                     { tag: 'span', cls: 'fc-header-space' },
16736                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16737
16738
16739                                 ]
16740                             },
16741
16742                             {
16743                                 tag : 'td',
16744                                 cls : 'fc-header-center',
16745                                 cn : [
16746                                     {
16747                                         tag: 'span',
16748                                         cls: 'fc-header-title',
16749                                         cn : {
16750                                             tag: 'H2',
16751                                             html : 'month / year'
16752                                         }
16753                                     }
16754
16755                                 ]
16756                             },
16757                             {
16758                                 tag : 'td',
16759                                 cls : 'fc-header-right',
16760                                 cn : [
16761                               /*      fc_button('month', 'left', '', 'month' ),
16762                                     fc_button('week', '', '', 'week' ),
16763                                     fc_button('day', 'right', '', 'day' )
16764                                 */    
16765
16766                                 ]
16767                             }
16768
16769                         ]
16770                     }
16771                 ]
16772             };
16773         }
16774         
16775         header = this.header;
16776         
16777        
16778         var cal_heads = function() {
16779             var ret = [];
16780             // fixme - handle this.
16781             
16782             for (var i =0; i < Date.dayNames.length; i++) {
16783                 var d = Date.dayNames[i];
16784                 ret.push({
16785                     tag: 'th',
16786                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16787                     html : d.substring(0,3)
16788                 });
16789                 
16790             }
16791             ret[0].cls += ' fc-first';
16792             ret[6].cls += ' fc-last';
16793             return ret;
16794         };
16795         var cal_cell = function(n) {
16796             return  {
16797                 tag: 'td',
16798                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16799                 cn : [
16800                     {
16801                         cn : [
16802                             {
16803                                 cls: 'fc-day-number',
16804                                 html: 'D'
16805                             },
16806                             {
16807                                 cls: 'fc-day-content',
16808                              
16809                                 cn : [
16810                                      {
16811                                         style: 'position: relative;' // height: 17px;
16812                                     }
16813                                 ]
16814                             }
16815                             
16816                             
16817                         ]
16818                     }
16819                 ]
16820                 
16821             }
16822         };
16823         var cal_rows = function() {
16824             
16825             var ret = [];
16826             for (var r = 0; r < 6; r++) {
16827                 var row= {
16828                     tag : 'tr',
16829                     cls : 'fc-week',
16830                     cn : []
16831                 };
16832                 
16833                 for (var i =0; i < Date.dayNames.length; i++) {
16834                     var d = Date.dayNames[i];
16835                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16836
16837                 }
16838                 row.cn[0].cls+=' fc-first';
16839                 row.cn[0].cn[0].style = 'min-height:90px';
16840                 row.cn[6].cls+=' fc-last';
16841                 ret.push(row);
16842                 
16843             }
16844             ret[0].cls += ' fc-first';
16845             ret[4].cls += ' fc-prev-last';
16846             ret[5].cls += ' fc-last';
16847             return ret;
16848             
16849         };
16850         
16851         var cal_table = {
16852             tag: 'table',
16853             cls: 'fc-border-separate',
16854             style : 'width:100%',
16855             cellspacing  : 0,
16856             cn : [
16857                 { 
16858                     tag: 'thead',
16859                     cn : [
16860                         { 
16861                             tag: 'tr',
16862                             cls : 'fc-first fc-last',
16863                             cn : cal_heads()
16864                         }
16865                     ]
16866                 },
16867                 { 
16868                     tag: 'tbody',
16869                     cn : cal_rows()
16870                 }
16871                   
16872             ]
16873         };
16874          
16875          var cfg = {
16876             cls : 'fc fc-ltr',
16877             cn : [
16878                 header,
16879                 {
16880                     cls : 'fc-content',
16881                     style : "position: relative;",
16882                     cn : [
16883                         {
16884                             cls : 'fc-view fc-view-month fc-grid',
16885                             style : 'position: relative',
16886                             unselectable : 'on',
16887                             cn : [
16888                                 {
16889                                     cls : 'fc-event-container',
16890                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16891                                 },
16892                                 cal_table
16893                             ]
16894                         }
16895                     ]
16896     
16897                 }
16898            ] 
16899             
16900         };
16901         
16902          
16903         
16904         return cfg;
16905     },
16906     
16907     
16908     initEvents : function()
16909     {
16910         if(!this.store){
16911             throw "can not find store for calendar";
16912         }
16913         
16914         var mark = {
16915             tag: "div",
16916             cls:"x-dlg-mask",
16917             style: "text-align:center",
16918             cn: [
16919                 {
16920                     tag: "div",
16921                     style: "background-color:white;width:50%;margin:250 auto",
16922                     cn: [
16923                         {
16924                             tag: "img",
16925                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16926                         },
16927                         {
16928                             tag: "span",
16929                             html: "Loading"
16930                         }
16931                         
16932                     ]
16933                 }
16934             ]
16935         };
16936         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16937         
16938         var size = this.el.select('.fc-content', true).first().getSize();
16939         this.maskEl.setSize(size.width, size.height);
16940         this.maskEl.enableDisplayMode("block");
16941         if(!this.loadMask){
16942             this.maskEl.hide();
16943         }
16944         
16945         this.store = Roo.factory(this.store, Roo.data);
16946         this.store.on('load', this.onLoad, this);
16947         this.store.on('beforeload', this.onBeforeLoad, this);
16948         
16949         this.resize();
16950         
16951         this.cells = this.el.select('.fc-day',true);
16952         //Roo.log(this.cells);
16953         this.textNodes = this.el.query('.fc-day-number');
16954         this.cells.addClassOnOver('fc-state-hover');
16955         
16956         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16957         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16958         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16959         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16960         
16961         this.on('monthchange', this.onMonthChange, this);
16962         
16963         this.update(new Date().clearTime());
16964     },
16965     
16966     resize : function() {
16967         var sz  = this.el.getSize();
16968         
16969         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16970         this.el.select('.fc-day-content div',true).setHeight(34);
16971     },
16972     
16973     
16974     // private
16975     showPrevMonth : function(e){
16976         this.update(this.activeDate.add("mo", -1));
16977     },
16978     showToday : function(e){
16979         this.update(new Date().clearTime());
16980     },
16981     // private
16982     showNextMonth : function(e){
16983         this.update(this.activeDate.add("mo", 1));
16984     },
16985
16986     // private
16987     showPrevYear : function(){
16988         this.update(this.activeDate.add("y", -1));
16989     },
16990
16991     // private
16992     showNextYear : function(){
16993         this.update(this.activeDate.add("y", 1));
16994     },
16995
16996     
16997    // private
16998     update : function(date)
16999     {
17000         var vd = this.activeDate;
17001         this.activeDate = date;
17002 //        if(vd && this.el){
17003 //            var t = date.getTime();
17004 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17005 //                Roo.log('using add remove');
17006 //                
17007 //                this.fireEvent('monthchange', this, date);
17008 //                
17009 //                this.cells.removeClass("fc-state-highlight");
17010 //                this.cells.each(function(c){
17011 //                   if(c.dateValue == t){
17012 //                       c.addClass("fc-state-highlight");
17013 //                       setTimeout(function(){
17014 //                            try{c.dom.firstChild.focus();}catch(e){}
17015 //                       }, 50);
17016 //                       return false;
17017 //                   }
17018 //                   return true;
17019 //                });
17020 //                return;
17021 //            }
17022 //        }
17023         
17024         var days = date.getDaysInMonth();
17025         
17026         var firstOfMonth = date.getFirstDateOfMonth();
17027         var startingPos = firstOfMonth.getDay()-this.startDay;
17028         
17029         if(startingPos < this.startDay){
17030             startingPos += 7;
17031         }
17032         
17033         var pm = date.add(Date.MONTH, -1);
17034         var prevStart = pm.getDaysInMonth()-startingPos;
17035 //        
17036         this.cells = this.el.select('.fc-day',true);
17037         this.textNodes = this.el.query('.fc-day-number');
17038         this.cells.addClassOnOver('fc-state-hover');
17039         
17040         var cells = this.cells.elements;
17041         var textEls = this.textNodes;
17042         
17043         Roo.each(cells, function(cell){
17044             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17045         });
17046         
17047         days += startingPos;
17048
17049         // convert everything to numbers so it's fast
17050         var day = 86400000;
17051         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17052         //Roo.log(d);
17053         //Roo.log(pm);
17054         //Roo.log(prevStart);
17055         
17056         var today = new Date().clearTime().getTime();
17057         var sel = date.clearTime().getTime();
17058         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17059         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17060         var ddMatch = this.disabledDatesRE;
17061         var ddText = this.disabledDatesText;
17062         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17063         var ddaysText = this.disabledDaysText;
17064         var format = this.format;
17065         
17066         var setCellClass = function(cal, cell){
17067             cell.row = 0;
17068             cell.events = [];
17069             cell.more = [];
17070             //Roo.log('set Cell Class');
17071             cell.title = "";
17072             var t = d.getTime();
17073             
17074             //Roo.log(d);
17075             
17076             cell.dateValue = t;
17077             if(t == today){
17078                 cell.className += " fc-today";
17079                 cell.className += " fc-state-highlight";
17080                 cell.title = cal.todayText;
17081             }
17082             if(t == sel){
17083                 // disable highlight in other month..
17084                 //cell.className += " fc-state-highlight";
17085                 
17086             }
17087             // disabling
17088             if(t < min) {
17089                 cell.className = " fc-state-disabled";
17090                 cell.title = cal.minText;
17091                 return;
17092             }
17093             if(t > max) {
17094                 cell.className = " fc-state-disabled";
17095                 cell.title = cal.maxText;
17096                 return;
17097             }
17098             if(ddays){
17099                 if(ddays.indexOf(d.getDay()) != -1){
17100                     cell.title = ddaysText;
17101                     cell.className = " fc-state-disabled";
17102                 }
17103             }
17104             if(ddMatch && format){
17105                 var fvalue = d.dateFormat(format);
17106                 if(ddMatch.test(fvalue)){
17107                     cell.title = ddText.replace("%0", fvalue);
17108                     cell.className = " fc-state-disabled";
17109                 }
17110             }
17111             
17112             if (!cell.initialClassName) {
17113                 cell.initialClassName = cell.dom.className;
17114             }
17115             
17116             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17117         };
17118
17119         var i = 0;
17120         
17121         for(; i < startingPos; i++) {
17122             textEls[i].innerHTML = (++prevStart);
17123             d.setDate(d.getDate()+1);
17124             
17125             cells[i].className = "fc-past fc-other-month";
17126             setCellClass(this, cells[i]);
17127         }
17128         
17129         var intDay = 0;
17130         
17131         for(; i < days; i++){
17132             intDay = i - startingPos + 1;
17133             textEls[i].innerHTML = (intDay);
17134             d.setDate(d.getDate()+1);
17135             
17136             cells[i].className = ''; // "x-date-active";
17137             setCellClass(this, cells[i]);
17138         }
17139         var extraDays = 0;
17140         
17141         for(; i < 42; i++) {
17142             textEls[i].innerHTML = (++extraDays);
17143             d.setDate(d.getDate()+1);
17144             
17145             cells[i].className = "fc-future fc-other-month";
17146             setCellClass(this, cells[i]);
17147         }
17148         
17149         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17150         
17151         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17152         
17153         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17154         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17155         
17156         if(totalRows != 6){
17157             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17158             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17159         }
17160         
17161         this.fireEvent('monthchange', this, date);
17162         
17163         
17164         /*
17165         if(!this.internalRender){
17166             var main = this.el.dom.firstChild;
17167             var w = main.offsetWidth;
17168             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17169             Roo.fly(main).setWidth(w);
17170             this.internalRender = true;
17171             // opera does not respect the auto grow header center column
17172             // then, after it gets a width opera refuses to recalculate
17173             // without a second pass
17174             if(Roo.isOpera && !this.secondPass){
17175                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17176                 this.secondPass = true;
17177                 this.update.defer(10, this, [date]);
17178             }
17179         }
17180         */
17181         
17182     },
17183     
17184     findCell : function(dt) {
17185         dt = dt.clearTime().getTime();
17186         var ret = false;
17187         this.cells.each(function(c){
17188             //Roo.log("check " +c.dateValue + '?=' + dt);
17189             if(c.dateValue == dt){
17190                 ret = c;
17191                 return false;
17192             }
17193             return true;
17194         });
17195         
17196         return ret;
17197     },
17198     
17199     findCells : function(ev) {
17200         var s = ev.start.clone().clearTime().getTime();
17201        // Roo.log(s);
17202         var e= ev.end.clone().clearTime().getTime();
17203        // Roo.log(e);
17204         var ret = [];
17205         this.cells.each(function(c){
17206              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17207             
17208             if(c.dateValue > e){
17209                 return ;
17210             }
17211             if(c.dateValue < s){
17212                 return ;
17213             }
17214             ret.push(c);
17215         });
17216         
17217         return ret;    
17218     },
17219     
17220 //    findBestRow: function(cells)
17221 //    {
17222 //        var ret = 0;
17223 //        
17224 //        for (var i =0 ; i < cells.length;i++) {
17225 //            ret  = Math.max(cells[i].rows || 0,ret);
17226 //        }
17227 //        return ret;
17228 //        
17229 //    },
17230     
17231     
17232     addItem : function(ev)
17233     {
17234         // look for vertical location slot in
17235         var cells = this.findCells(ev);
17236         
17237 //        ev.row = this.findBestRow(cells);
17238         
17239         // work out the location.
17240         
17241         var crow = false;
17242         var rows = [];
17243         for(var i =0; i < cells.length; i++) {
17244             
17245             cells[i].row = cells[0].row;
17246             
17247             if(i == 0){
17248                 cells[i].row = cells[i].row + 1;
17249             }
17250             
17251             if (!crow) {
17252                 crow = {
17253                     start : cells[i],
17254                     end :  cells[i]
17255                 };
17256                 continue;
17257             }
17258             if (crow.start.getY() == cells[i].getY()) {
17259                 // on same row.
17260                 crow.end = cells[i];
17261                 continue;
17262             }
17263             // different row.
17264             rows.push(crow);
17265             crow = {
17266                 start: cells[i],
17267                 end : cells[i]
17268             };
17269             
17270         }
17271         
17272         rows.push(crow);
17273         ev.els = [];
17274         ev.rows = rows;
17275         ev.cells = cells;
17276         
17277         cells[0].events.push(ev);
17278         
17279         this.calevents.push(ev);
17280     },
17281     
17282     clearEvents: function() {
17283         
17284         if(!this.calevents){
17285             return;
17286         }
17287         
17288         Roo.each(this.cells.elements, function(c){
17289             c.row = 0;
17290             c.events = [];
17291             c.more = [];
17292         });
17293         
17294         Roo.each(this.calevents, function(e) {
17295             Roo.each(e.els, function(el) {
17296                 el.un('mouseenter' ,this.onEventEnter, this);
17297                 el.un('mouseleave' ,this.onEventLeave, this);
17298                 el.remove();
17299             },this);
17300         },this);
17301         
17302         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17303             e.remove();
17304         });
17305         
17306     },
17307     
17308     renderEvents: function()
17309     {   
17310         var _this = this;
17311         
17312         this.cells.each(function(c) {
17313             
17314             if(c.row < 5){
17315                 return;
17316             }
17317             
17318             var ev = c.events;
17319             
17320             var r = 4;
17321             if(c.row != c.events.length){
17322                 r = 4 - (4 - (c.row - c.events.length));
17323             }
17324             
17325             c.events = ev.slice(0, r);
17326             c.more = ev.slice(r);
17327             
17328             if(c.more.length && c.more.length == 1){
17329                 c.events.push(c.more.pop());
17330             }
17331             
17332             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17333             
17334         });
17335             
17336         this.cells.each(function(c) {
17337             
17338             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17339             
17340             
17341             for (var e = 0; e < c.events.length; e++){
17342                 var ev = c.events[e];
17343                 var rows = ev.rows;
17344                 
17345                 for(var i = 0; i < rows.length; i++) {
17346                 
17347                     // how many rows should it span..
17348
17349                     var  cfg = {
17350                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17351                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17352
17353                         unselectable : "on",
17354                         cn : [
17355                             {
17356                                 cls: 'fc-event-inner',
17357                                 cn : [
17358     //                                {
17359     //                                  tag:'span',
17360     //                                  cls: 'fc-event-time',
17361     //                                  html : cells.length > 1 ? '' : ev.time
17362     //                                },
17363                                     {
17364                                       tag:'span',
17365                                       cls: 'fc-event-title',
17366                                       html : String.format('{0}', ev.title)
17367                                     }
17368
17369
17370                                 ]
17371                             },
17372                             {
17373                                 cls: 'ui-resizable-handle ui-resizable-e',
17374                                 html : '&nbsp;&nbsp;&nbsp'
17375                             }
17376
17377                         ]
17378                     };
17379
17380                     if (i == 0) {
17381                         cfg.cls += ' fc-event-start';
17382                     }
17383                     if ((i+1) == rows.length) {
17384                         cfg.cls += ' fc-event-end';
17385                     }
17386
17387                     var ctr = _this.el.select('.fc-event-container',true).first();
17388                     var cg = ctr.createChild(cfg);
17389
17390                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17391                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17392
17393                     var r = (c.more.length) ? 1 : 0;
17394                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17395                     cg.setWidth(ebox.right - sbox.x -2);
17396
17397                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17398                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17399                     cg.on('click', _this.onEventClick, _this, ev);
17400
17401                     ev.els.push(cg);
17402                     
17403                 }
17404                 
17405             }
17406             
17407             
17408             if(c.more.length){
17409                 var  cfg = {
17410                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17411                     style : 'position: absolute',
17412                     unselectable : "on",
17413                     cn : [
17414                         {
17415                             cls: 'fc-event-inner',
17416                             cn : [
17417                                 {
17418                                   tag:'span',
17419                                   cls: 'fc-event-title',
17420                                   html : 'More'
17421                                 }
17422
17423
17424                             ]
17425                         },
17426                         {
17427                             cls: 'ui-resizable-handle ui-resizable-e',
17428                             html : '&nbsp;&nbsp;&nbsp'
17429                         }
17430
17431                     ]
17432                 };
17433
17434                 var ctr = _this.el.select('.fc-event-container',true).first();
17435                 var cg = ctr.createChild(cfg);
17436
17437                 var sbox = c.select('.fc-day-content',true).first().getBox();
17438                 var ebox = c.select('.fc-day-content',true).first().getBox();
17439                 //Roo.log(cg);
17440                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17441                 cg.setWidth(ebox.right - sbox.x -2);
17442
17443                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17444                 
17445             }
17446             
17447         });
17448         
17449         
17450         
17451     },
17452     
17453     onEventEnter: function (e, el,event,d) {
17454         this.fireEvent('evententer', this, el, event);
17455     },
17456     
17457     onEventLeave: function (e, el,event,d) {
17458         this.fireEvent('eventleave', this, el, event);
17459     },
17460     
17461     onEventClick: function (e, el,event,d) {
17462         this.fireEvent('eventclick', this, el, event);
17463     },
17464     
17465     onMonthChange: function () {
17466         this.store.load();
17467     },
17468     
17469     onMoreEventClick: function(e, el, more)
17470     {
17471         var _this = this;
17472         
17473         this.calpopover.placement = 'right';
17474         this.calpopover.setTitle('More');
17475         
17476         this.calpopover.setContent('');
17477         
17478         var ctr = this.calpopover.el.select('.popover-content', true).first();
17479         
17480         Roo.each(more, function(m){
17481             var cfg = {
17482                 cls : 'fc-event-hori fc-event-draggable',
17483                 html : m.title
17484             };
17485             var cg = ctr.createChild(cfg);
17486             
17487             cg.on('click', _this.onEventClick, _this, m);
17488         });
17489         
17490         this.calpopover.show(el);
17491         
17492         
17493     },
17494     
17495     onLoad: function () 
17496     {   
17497         this.calevents = [];
17498         var cal = this;
17499         
17500         if(this.store.getCount() > 0){
17501             this.store.data.each(function(d){
17502                cal.addItem({
17503                     id : d.data.id,
17504                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17505                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17506                     time : d.data.start_time,
17507                     title : d.data.title,
17508                     description : d.data.description,
17509                     venue : d.data.venue
17510                 });
17511             });
17512         }
17513         
17514         this.renderEvents();
17515         
17516         if(this.calevents.length && this.loadMask){
17517             this.maskEl.hide();
17518         }
17519     },
17520     
17521     onBeforeLoad: function()
17522     {
17523         this.clearEvents();
17524         if(this.loadMask){
17525             this.maskEl.show();
17526         }
17527     }
17528 });
17529
17530  
17531  /*
17532  * - LGPL
17533  *
17534  * element
17535  * 
17536  */
17537
17538 /**
17539  * @class Roo.bootstrap.Popover
17540  * @extends Roo.bootstrap.Component
17541  * Bootstrap Popover class
17542  * @cfg {String} html contents of the popover   (or false to use children..)
17543  * @cfg {String} title of popover (or false to hide)
17544  * @cfg {String} placement how it is placed
17545  * @cfg {String} trigger click || hover (or false to trigger manually)
17546  * @cfg {String} over what (parent or false to trigger manually.)
17547  * @cfg {Number} delay - delay before showing
17548  
17549  * @constructor
17550  * Create a new Popover
17551  * @param {Object} config The config object
17552  */
17553
17554 Roo.bootstrap.Popover = function(config){
17555     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17556     
17557     this.addEvents({
17558         // raw events
17559          /**
17560          * @event show
17561          * After the popover show
17562          * 
17563          * @param {Roo.bootstrap.Popover} this
17564          */
17565         "show" : true,
17566         /**
17567          * @event hide
17568          * After the popover hide
17569          * 
17570          * @param {Roo.bootstrap.Popover} this
17571          */
17572         "hide" : true
17573     });
17574 };
17575
17576 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17577     
17578     title: 'Fill in a title',
17579     html: false,
17580     
17581     placement : 'right',
17582     trigger : 'hover', // hover
17583     
17584     delay : 0,
17585     
17586     over: 'parent',
17587     
17588     can_build_overlaid : false,
17589     
17590     getChildContainer : function()
17591     {
17592         return this.el.select('.popover-content',true).first();
17593     },
17594     
17595     getAutoCreate : function(){
17596          
17597         var cfg = {
17598            cls : 'popover roo-dynamic',
17599            style: 'display:block',
17600            cn : [
17601                 {
17602                     cls : 'arrow'
17603                 },
17604                 {
17605                     cls : 'popover-inner',
17606                     cn : [
17607                         {
17608                             tag: 'h3',
17609                             cls: 'popover-title',
17610                             html : this.title
17611                         },
17612                         {
17613                             cls : 'popover-content',
17614                             html : this.html
17615                         }
17616                     ]
17617                     
17618                 }
17619            ]
17620         };
17621         
17622         return cfg;
17623     },
17624     setTitle: function(str)
17625     {
17626         this.title = str;
17627         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17628     },
17629     setContent: function(str)
17630     {
17631         this.html = str;
17632         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17633     },
17634     // as it get's added to the bottom of the page.
17635     onRender : function(ct, position)
17636     {
17637         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17638         if(!this.el){
17639             var cfg = Roo.apply({},  this.getAutoCreate());
17640             cfg.id = Roo.id();
17641             
17642             if (this.cls) {
17643                 cfg.cls += ' ' + this.cls;
17644             }
17645             if (this.style) {
17646                 cfg.style = this.style;
17647             }
17648             //Roo.log("adding to ");
17649             this.el = Roo.get(document.body).createChild(cfg, position);
17650 //            Roo.log(this.el);
17651         }
17652         this.initEvents();
17653     },
17654     
17655     initEvents : function()
17656     {
17657         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17658         this.el.enableDisplayMode('block');
17659         this.el.hide();
17660         if (this.over === false) {
17661             return; 
17662         }
17663         if (this.triggers === false) {
17664             return;
17665         }
17666         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17667         var triggers = this.trigger ? this.trigger.split(' ') : [];
17668         Roo.each(triggers, function(trigger) {
17669         
17670             if (trigger == 'click') {
17671                 on_el.on('click', this.toggle, this);
17672             } else if (trigger != 'manual') {
17673                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17674                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17675       
17676                 on_el.on(eventIn  ,this.enter, this);
17677                 on_el.on(eventOut, this.leave, this);
17678             }
17679         }, this);
17680         
17681     },
17682     
17683     
17684     // private
17685     timeout : null,
17686     hoverState : null,
17687     
17688     toggle : function () {
17689         this.hoverState == 'in' ? this.leave() : this.enter();
17690     },
17691     
17692     enter : function () {
17693         
17694         clearTimeout(this.timeout);
17695     
17696         this.hoverState = 'in';
17697     
17698         if (!this.delay || !this.delay.show) {
17699             this.show();
17700             return;
17701         }
17702         var _t = this;
17703         this.timeout = setTimeout(function () {
17704             if (_t.hoverState == 'in') {
17705                 _t.show();
17706             }
17707         }, this.delay.show)
17708     },
17709     
17710     leave : function() {
17711         clearTimeout(this.timeout);
17712     
17713         this.hoverState = 'out';
17714     
17715         if (!this.delay || !this.delay.hide) {
17716             this.hide();
17717             return;
17718         }
17719         var _t = this;
17720         this.timeout = setTimeout(function () {
17721             if (_t.hoverState == 'out') {
17722                 _t.hide();
17723             }
17724         }, this.delay.hide)
17725     },
17726     
17727     show : function (on_el)
17728     {
17729         if (!on_el) {
17730             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17731         }
17732         
17733         // set content.
17734         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17735         if (this.html !== false) {
17736             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17737         }
17738         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17739         if (!this.title.length) {
17740             this.el.select('.popover-title',true).hide();
17741         }
17742         
17743         var placement = typeof this.placement == 'function' ?
17744             this.placement.call(this, this.el, on_el) :
17745             this.placement;
17746             
17747         var autoToken = /\s?auto?\s?/i;
17748         var autoPlace = autoToken.test(placement);
17749         if (autoPlace) {
17750             placement = placement.replace(autoToken, '') || 'top';
17751         }
17752         
17753         //this.el.detach()
17754         //this.el.setXY([0,0]);
17755         this.el.show();
17756         this.el.dom.style.display='block';
17757         this.el.addClass(placement);
17758         
17759         //this.el.appendTo(on_el);
17760         
17761         var p = this.getPosition();
17762         var box = this.el.getBox();
17763         
17764         if (autoPlace) {
17765             // fixme..
17766         }
17767         var align = Roo.bootstrap.Popover.alignment[placement];
17768         
17769 //        Roo.log(align);
17770         this.el.alignTo(on_el, align[0],align[1]);
17771         //var arrow = this.el.select('.arrow',true).first();
17772         //arrow.set(align[2], 
17773         
17774         this.el.addClass('in');
17775         
17776         
17777         if (this.el.hasClass('fade')) {
17778             // fade it?
17779         }
17780         
17781         this.hoverState = 'in';
17782         
17783         this.fireEvent('show', this);
17784         
17785     },
17786     hide : function()
17787     {
17788         this.el.setXY([0,0]);
17789         this.el.removeClass('in');
17790         this.el.hide();
17791         this.hoverState = null;
17792         
17793         this.fireEvent('hide', this);
17794     }
17795     
17796 });
17797
17798 Roo.bootstrap.Popover.alignment = {
17799     'left' : ['r-l', [-10,0], 'right'],
17800     'right' : ['l-r', [10,0], 'left'],
17801     'bottom' : ['t-b', [0,10], 'top'],
17802     'top' : [ 'b-t', [0,-10], 'bottom']
17803 };
17804
17805  /*
17806  * - LGPL
17807  *
17808  * Progress
17809  * 
17810  */
17811
17812 /**
17813  * @class Roo.bootstrap.Progress
17814  * @extends Roo.bootstrap.Component
17815  * Bootstrap Progress class
17816  * @cfg {Boolean} striped striped of the progress bar
17817  * @cfg {Boolean} active animated of the progress bar
17818  * 
17819  * 
17820  * @constructor
17821  * Create a new Progress
17822  * @param {Object} config The config object
17823  */
17824
17825 Roo.bootstrap.Progress = function(config){
17826     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17827 };
17828
17829 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17830     
17831     striped : false,
17832     active: false,
17833     
17834     getAutoCreate : function(){
17835         var cfg = {
17836             tag: 'div',
17837             cls: 'progress'
17838         };
17839         
17840         
17841         if(this.striped){
17842             cfg.cls += ' progress-striped';
17843         }
17844       
17845         if(this.active){
17846             cfg.cls += ' active';
17847         }
17848         
17849         
17850         return cfg;
17851     }
17852    
17853 });
17854
17855  
17856
17857  /*
17858  * - LGPL
17859  *
17860  * ProgressBar
17861  * 
17862  */
17863
17864 /**
17865  * @class Roo.bootstrap.ProgressBar
17866  * @extends Roo.bootstrap.Component
17867  * Bootstrap ProgressBar class
17868  * @cfg {Number} aria_valuenow aria-value now
17869  * @cfg {Number} aria_valuemin aria-value min
17870  * @cfg {Number} aria_valuemax aria-value max
17871  * @cfg {String} label label for the progress bar
17872  * @cfg {String} panel (success | info | warning | danger )
17873  * @cfg {String} role role of the progress bar
17874  * @cfg {String} sr_only text
17875  * 
17876  * 
17877  * @constructor
17878  * Create a new ProgressBar
17879  * @param {Object} config The config object
17880  */
17881
17882 Roo.bootstrap.ProgressBar = function(config){
17883     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17884 };
17885
17886 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17887     
17888     aria_valuenow : 0,
17889     aria_valuemin : 0,
17890     aria_valuemax : 100,
17891     label : false,
17892     panel : false,
17893     role : false,
17894     sr_only: false,
17895     
17896     getAutoCreate : function()
17897     {
17898         
17899         var cfg = {
17900             tag: 'div',
17901             cls: 'progress-bar',
17902             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17903         };
17904         
17905         if(this.sr_only){
17906             cfg.cn = {
17907                 tag: 'span',
17908                 cls: 'sr-only',
17909                 html: this.sr_only
17910             }
17911         }
17912         
17913         if(this.role){
17914             cfg.role = this.role;
17915         }
17916         
17917         if(this.aria_valuenow){
17918             cfg['aria-valuenow'] = this.aria_valuenow;
17919         }
17920         
17921         if(this.aria_valuemin){
17922             cfg['aria-valuemin'] = this.aria_valuemin;
17923         }
17924         
17925         if(this.aria_valuemax){
17926             cfg['aria-valuemax'] = this.aria_valuemax;
17927         }
17928         
17929         if(this.label && !this.sr_only){
17930             cfg.html = this.label;
17931         }
17932         
17933         if(this.panel){
17934             cfg.cls += ' progress-bar-' + this.panel;
17935         }
17936         
17937         return cfg;
17938     },
17939     
17940     update : function(aria_valuenow)
17941     {
17942         this.aria_valuenow = aria_valuenow;
17943         
17944         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17945     }
17946    
17947 });
17948
17949  
17950
17951  /*
17952  * - LGPL
17953  *
17954  * column
17955  * 
17956  */
17957
17958 /**
17959  * @class Roo.bootstrap.TabGroup
17960  * @extends Roo.bootstrap.Column
17961  * Bootstrap Column class
17962  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17963  * @cfg {Boolean} carousel true to make the group behave like a carousel
17964  * @cfg {Boolean} bullets show bullets for the panels
17965  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17966  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17967  * @cfg {Boolean} showarrow (true|false) show arrow default true
17968  * 
17969  * @constructor
17970  * Create a new TabGroup
17971  * @param {Object} config The config object
17972  */
17973
17974 Roo.bootstrap.TabGroup = function(config){
17975     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17976     if (!this.navId) {
17977         this.navId = Roo.id();
17978     }
17979     this.tabs = [];
17980     Roo.bootstrap.TabGroup.register(this);
17981     
17982 };
17983
17984 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17985     
17986     carousel : false,
17987     transition : false,
17988     bullets : 0,
17989     timer : 0,
17990     autoslide : false,
17991     slideFn : false,
17992     slideOnTouch : false,
17993     showarrow : true,
17994     
17995     getAutoCreate : function()
17996     {
17997         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17998         
17999         cfg.cls += ' tab-content';
18000         
18001         if (this.carousel) {
18002             cfg.cls += ' carousel slide';
18003             
18004             cfg.cn = [{
18005                cls : 'carousel-inner',
18006                cn : []
18007             }];
18008         
18009             if(this.bullets  && !Roo.isTouch){
18010                 
18011                 var bullets = {
18012                     cls : 'carousel-bullets',
18013                     cn : []
18014                 };
18015                
18016                 if(this.bullets_cls){
18017                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18018                 }
18019                 
18020                 bullets.cn.push({
18021                     cls : 'clear'
18022                 });
18023                 
18024                 cfg.cn[0].cn.push(bullets);
18025             }
18026             
18027             if(this.showarrow){
18028                 cfg.cn[0].cn.push({
18029                     tag : 'div',
18030                     class : 'carousel-arrow',
18031                     cn : [
18032                         {
18033                             tag : 'div',
18034                             class : 'carousel-prev',
18035                             cn : [
18036                                 {
18037                                     tag : 'i',
18038                                     class : 'fa fa-chevron-left'
18039                                 }
18040                             ]
18041                         },
18042                         {
18043                             tag : 'div',
18044                             class : 'carousel-next',
18045                             cn : [
18046                                 {
18047                                     tag : 'i',
18048                                     class : 'fa fa-chevron-right'
18049                                 }
18050                             ]
18051                         }
18052                     ]
18053                 });
18054             }
18055             
18056         }
18057         
18058         return cfg;
18059     },
18060     
18061     initEvents:  function()
18062     {
18063 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18064 //            this.el.on("touchstart", this.onTouchStart, this);
18065 //        }
18066         
18067         if(this.autoslide){
18068             var _this = this;
18069             
18070             this.slideFn = window.setInterval(function() {
18071                 _this.showPanelNext();
18072             }, this.timer);
18073         }
18074         
18075         if(this.showarrow){
18076             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18077             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18078         }
18079         
18080         
18081     },
18082     
18083 //    onTouchStart : function(e, el, o)
18084 //    {
18085 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18086 //            return;
18087 //        }
18088 //        
18089 //        this.showPanelNext();
18090 //    },
18091     
18092     
18093     getChildContainer : function()
18094     {
18095         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18096     },
18097     
18098     /**
18099     * register a Navigation item
18100     * @param {Roo.bootstrap.NavItem} the navitem to add
18101     */
18102     register : function(item)
18103     {
18104         this.tabs.push( item);
18105         item.navId = this.navId; // not really needed..
18106         this.addBullet();
18107     
18108     },
18109     
18110     getActivePanel : function()
18111     {
18112         var r = false;
18113         Roo.each(this.tabs, function(t) {
18114             if (t.active) {
18115                 r = t;
18116                 return false;
18117             }
18118             return null;
18119         });
18120         return r;
18121         
18122     },
18123     getPanelByName : function(n)
18124     {
18125         var r = false;
18126         Roo.each(this.tabs, function(t) {
18127             if (t.tabId == n) {
18128                 r = t;
18129                 return false;
18130             }
18131             return null;
18132         });
18133         return r;
18134     },
18135     indexOfPanel : function(p)
18136     {
18137         var r = false;
18138         Roo.each(this.tabs, function(t,i) {
18139             if (t.tabId == p.tabId) {
18140                 r = i;
18141                 return false;
18142             }
18143             return null;
18144         });
18145         return r;
18146     },
18147     /**
18148      * show a specific panel
18149      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18150      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18151      */
18152     showPanel : function (pan)
18153     {
18154         if(this.transition || typeof(pan) == 'undefined'){
18155             Roo.log("waiting for the transitionend");
18156             return;
18157         }
18158         
18159         if (typeof(pan) == 'number') {
18160             pan = this.tabs[pan];
18161         }
18162         
18163         if (typeof(pan) == 'string') {
18164             pan = this.getPanelByName(pan);
18165         }
18166         
18167         var cur = this.getActivePanel();
18168         
18169         if(!pan || !cur){
18170             Roo.log('pan or acitve pan is undefined');
18171             return false;
18172         }
18173         
18174         if (pan.tabId == this.getActivePanel().tabId) {
18175             return true;
18176         }
18177         
18178         if (false === cur.fireEvent('beforedeactivate')) {
18179             return false;
18180         }
18181         
18182         if(this.bullets > 0 && !Roo.isTouch){
18183             this.setActiveBullet(this.indexOfPanel(pan));
18184         }
18185         
18186         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18187             
18188             this.transition = true;
18189             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18190             var lr = dir == 'next' ? 'left' : 'right';
18191             pan.el.addClass(dir); // or prev
18192             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18193             cur.el.addClass(lr); // or right
18194             pan.el.addClass(lr);
18195             
18196             var _this = this;
18197             cur.el.on('transitionend', function() {
18198                 Roo.log("trans end?");
18199                 
18200                 pan.el.removeClass([lr,dir]);
18201                 pan.setActive(true);
18202                 
18203                 cur.el.removeClass([lr]);
18204                 cur.setActive(false);
18205                 
18206                 _this.transition = false;
18207                 
18208             }, this, { single:  true } );
18209             
18210             return true;
18211         }
18212         
18213         cur.setActive(false);
18214         pan.setActive(true);
18215         
18216         return true;
18217         
18218     },
18219     showPanelNext : function()
18220     {
18221         var i = this.indexOfPanel(this.getActivePanel());
18222         
18223         if (i >= this.tabs.length - 1 && !this.autoslide) {
18224             return;
18225         }
18226         
18227         if (i >= this.tabs.length - 1 && this.autoslide) {
18228             i = -1;
18229         }
18230         
18231         this.showPanel(this.tabs[i+1]);
18232     },
18233     
18234     showPanelPrev : function()
18235     {
18236         var i = this.indexOfPanel(this.getActivePanel());
18237         
18238         if (i  < 1 && !this.autoslide) {
18239             return;
18240         }
18241         
18242         if (i < 1 && this.autoslide) {
18243             i = this.tabs.length;
18244         }
18245         
18246         this.showPanel(this.tabs[i-1]);
18247     },
18248     
18249     
18250     addBullet: function()
18251     {
18252         if(!this.bullets || Roo.isTouch){
18253             return;
18254         }
18255         var ctr = this.el.select('.carousel-bullets',true).first();
18256         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18257         var bullet = ctr.createChild({
18258             cls : 'bullet bullet-' + i
18259         },ctr.dom.lastChild);
18260         
18261         
18262         var _this = this;
18263         
18264         bullet.on('click', (function(e, el, o, ii, t){
18265
18266             e.preventDefault();
18267
18268             this.showPanel(ii);
18269
18270             if(this.autoslide && this.slideFn){
18271                 clearInterval(this.slideFn);
18272                 this.slideFn = window.setInterval(function() {
18273                     _this.showPanelNext();
18274                 }, this.timer);
18275             }
18276
18277         }).createDelegate(this, [i, bullet], true));
18278                 
18279         
18280     },
18281      
18282     setActiveBullet : function(i)
18283     {
18284         if(Roo.isTouch){
18285             return;
18286         }
18287         
18288         Roo.each(this.el.select('.bullet', true).elements, function(el){
18289             el.removeClass('selected');
18290         });
18291
18292         var bullet = this.el.select('.bullet-' + i, true).first();
18293         
18294         if(!bullet){
18295             return;
18296         }
18297         
18298         bullet.addClass('selected');
18299     }
18300     
18301     
18302   
18303 });
18304
18305  
18306
18307  
18308  
18309 Roo.apply(Roo.bootstrap.TabGroup, {
18310     
18311     groups: {},
18312      /**
18313     * register a Navigation Group
18314     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18315     */
18316     register : function(navgrp)
18317     {
18318         this.groups[navgrp.navId] = navgrp;
18319         
18320     },
18321     /**
18322     * fetch a Navigation Group based on the navigation ID
18323     * if one does not exist , it will get created.
18324     * @param {string} the navgroup to add
18325     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18326     */
18327     get: function(navId) {
18328         if (typeof(this.groups[navId]) == 'undefined') {
18329             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18330         }
18331         return this.groups[navId] ;
18332     }
18333     
18334     
18335     
18336 });
18337
18338  /*
18339  * - LGPL
18340  *
18341  * TabPanel
18342  * 
18343  */
18344
18345 /**
18346  * @class Roo.bootstrap.TabPanel
18347  * @extends Roo.bootstrap.Component
18348  * Bootstrap TabPanel class
18349  * @cfg {Boolean} active panel active
18350  * @cfg {String} html panel content
18351  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18352  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18353  * @cfg {String} href click to link..
18354  * 
18355  * 
18356  * @constructor
18357  * Create a new TabPanel
18358  * @param {Object} config The config object
18359  */
18360
18361 Roo.bootstrap.TabPanel = function(config){
18362     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18363     this.addEvents({
18364         /**
18365              * @event changed
18366              * Fires when the active status changes
18367              * @param {Roo.bootstrap.TabPanel} this
18368              * @param {Boolean} state the new state
18369             
18370          */
18371         'changed': true,
18372         /**
18373              * @event beforedeactivate
18374              * Fires before a tab is de-activated - can be used to do validation on a form.
18375              * @param {Roo.bootstrap.TabPanel} this
18376              * @return {Boolean} false if there is an error
18377             
18378          */
18379         'beforedeactivate': true
18380      });
18381     
18382     this.tabId = this.tabId || Roo.id();
18383   
18384 };
18385
18386 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18387     
18388     active: false,
18389     html: false,
18390     tabId: false,
18391     navId : false,
18392     href : '',
18393     
18394     getAutoCreate : function(){
18395         var cfg = {
18396             tag: 'div',
18397             // item is needed for carousel - not sure if it has any effect otherwise
18398             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18399             html: this.html || ''
18400         };
18401         
18402         if(this.active){
18403             cfg.cls += ' active';
18404         }
18405         
18406         if(this.tabId){
18407             cfg.tabId = this.tabId;
18408         }
18409         
18410         
18411         return cfg;
18412     },
18413     
18414     initEvents:  function()
18415     {
18416         var p = this.parent();
18417         
18418         this.navId = this.navId || p.navId;
18419         
18420         if (typeof(this.navId) != 'undefined') {
18421             // not really needed.. but just in case.. parent should be a NavGroup.
18422             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18423             
18424             tg.register(this);
18425             
18426             var i = tg.tabs.length - 1;
18427             
18428             if(this.active && tg.bullets > 0 && i < tg.bullets){
18429                 tg.setActiveBullet(i);
18430             }
18431         }
18432         
18433         this.el.on('click', this.onClick, this);
18434         
18435         if(Roo.isTouch){
18436             this.el.on("touchstart", this.onTouchStart, this);
18437             this.el.on("touchmove", this.onTouchMove, this);
18438             this.el.on("touchend", this.onTouchEnd, this);
18439         }
18440         
18441     },
18442     
18443     onRender : function(ct, position)
18444     {
18445         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18446     },
18447     
18448     setActive : function(state)
18449     {
18450         Roo.log("panel - set active " + this.tabId + "=" + state);
18451         
18452         this.active = state;
18453         if (!state) {
18454             this.el.removeClass('active');
18455             
18456         } else  if (!this.el.hasClass('active')) {
18457             this.el.addClass('active');
18458         }
18459         
18460         this.fireEvent('changed', this, state);
18461     },
18462     
18463     onClick : function(e)
18464     {
18465         e.preventDefault();
18466         
18467         if(!this.href.length){
18468             return;
18469         }
18470         
18471         window.location.href = this.href;
18472     },
18473     
18474     startX : 0,
18475     startY : 0,
18476     endX : 0,
18477     endY : 0,
18478     swiping : false,
18479     
18480     onTouchStart : function(e)
18481     {
18482         this.swiping = false;
18483         
18484         this.startX = e.browserEvent.touches[0].clientX;
18485         this.startY = e.browserEvent.touches[0].clientY;
18486     },
18487     
18488     onTouchMove : function(e)
18489     {
18490         this.swiping = true;
18491         
18492         this.endX = e.browserEvent.touches[0].clientX;
18493         this.endY = e.browserEvent.touches[0].clientY;
18494     },
18495     
18496     onTouchEnd : function(e)
18497     {
18498         if(!this.swiping){
18499             this.onClick(e);
18500             return;
18501         }
18502         
18503         var tabGroup = this.parent();
18504         
18505         if(this.endX > this.startX){ // swiping right
18506             tabGroup.showPanelPrev();
18507             return;
18508         }
18509         
18510         if(this.startX > this.endX){ // swiping left
18511             tabGroup.showPanelNext();
18512             return;
18513         }
18514     }
18515     
18516     
18517 });
18518  
18519
18520  
18521
18522  /*
18523  * - LGPL
18524  *
18525  * DateField
18526  * 
18527  */
18528
18529 /**
18530  * @class Roo.bootstrap.DateField
18531  * @extends Roo.bootstrap.Input
18532  * Bootstrap DateField class
18533  * @cfg {Number} weekStart default 0
18534  * @cfg {String} viewMode default empty, (months|years)
18535  * @cfg {String} minViewMode default empty, (months|years)
18536  * @cfg {Number} startDate default -Infinity
18537  * @cfg {Number} endDate default Infinity
18538  * @cfg {Boolean} todayHighlight default false
18539  * @cfg {Boolean} todayBtn default false
18540  * @cfg {Boolean} calendarWeeks default false
18541  * @cfg {Object} daysOfWeekDisabled default empty
18542  * @cfg {Boolean} singleMode default false (true | false)
18543  * 
18544  * @cfg {Boolean} keyboardNavigation default true
18545  * @cfg {String} language default en
18546  * 
18547  * @constructor
18548  * Create a new DateField
18549  * @param {Object} config The config object
18550  */
18551
18552 Roo.bootstrap.DateField = function(config){
18553     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18554      this.addEvents({
18555             /**
18556              * @event show
18557              * Fires when this field show.
18558              * @param {Roo.bootstrap.DateField} this
18559              * @param {Mixed} date The date value
18560              */
18561             show : true,
18562             /**
18563              * @event show
18564              * Fires when this field hide.
18565              * @param {Roo.bootstrap.DateField} this
18566              * @param {Mixed} date The date value
18567              */
18568             hide : true,
18569             /**
18570              * @event select
18571              * Fires when select a date.
18572              * @param {Roo.bootstrap.DateField} this
18573              * @param {Mixed} date The date value
18574              */
18575             select : true,
18576             /**
18577              * @event beforeselect
18578              * Fires when before select a date.
18579              * @param {Roo.bootstrap.DateField} this
18580              * @param {Mixed} date The date value
18581              */
18582             beforeselect : true
18583         });
18584 };
18585
18586 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18587     
18588     /**
18589      * @cfg {String} format
18590      * The default date format string which can be overriden for localization support.  The format must be
18591      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18592      */
18593     format : "m/d/y",
18594     /**
18595      * @cfg {String} altFormats
18596      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18597      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18598      */
18599     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18600     
18601     weekStart : 0,
18602     
18603     viewMode : '',
18604     
18605     minViewMode : '',
18606     
18607     todayHighlight : false,
18608     
18609     todayBtn: false,
18610     
18611     language: 'en',
18612     
18613     keyboardNavigation: true,
18614     
18615     calendarWeeks: false,
18616     
18617     startDate: -Infinity,
18618     
18619     endDate: Infinity,
18620     
18621     daysOfWeekDisabled: [],
18622     
18623     _events: [],
18624     
18625     singleMode : false,
18626     
18627     UTCDate: function()
18628     {
18629         return new Date(Date.UTC.apply(Date, arguments));
18630     },
18631     
18632     UTCToday: function()
18633     {
18634         var today = new Date();
18635         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18636     },
18637     
18638     getDate: function() {
18639             var d = this.getUTCDate();
18640             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18641     },
18642     
18643     getUTCDate: function() {
18644             return this.date;
18645     },
18646     
18647     setDate: function(d) {
18648             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18649     },
18650     
18651     setUTCDate: function(d) {
18652             this.date = d;
18653             this.setValue(this.formatDate(this.date));
18654     },
18655         
18656     onRender: function(ct, position)
18657     {
18658         
18659         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18660         
18661         this.language = this.language || 'en';
18662         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18663         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18664         
18665         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18666         this.format = this.format || 'm/d/y';
18667         this.isInline = false;
18668         this.isInput = true;
18669         this.component = this.el.select('.add-on', true).first() || false;
18670         this.component = (this.component && this.component.length === 0) ? false : this.component;
18671         this.hasInput = this.component && this.inputEl().length;
18672         
18673         if (typeof(this.minViewMode === 'string')) {
18674             switch (this.minViewMode) {
18675                 case 'months':
18676                     this.minViewMode = 1;
18677                     break;
18678                 case 'years':
18679                     this.minViewMode = 2;
18680                     break;
18681                 default:
18682                     this.minViewMode = 0;
18683                     break;
18684             }
18685         }
18686         
18687         if (typeof(this.viewMode === 'string')) {
18688             switch (this.viewMode) {
18689                 case 'months':
18690                     this.viewMode = 1;
18691                     break;
18692                 case 'years':
18693                     this.viewMode = 2;
18694                     break;
18695                 default:
18696                     this.viewMode = 0;
18697                     break;
18698             }
18699         }
18700                 
18701         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18702         
18703 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18704         
18705         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18706         
18707         this.picker().on('mousedown', this.onMousedown, this);
18708         this.picker().on('click', this.onClick, this);
18709         
18710         this.picker().addClass('datepicker-dropdown');
18711         
18712         this.startViewMode = this.viewMode;
18713         
18714         if(this.singleMode){
18715             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18716                 v.setVisibilityMode(Roo.Element.DISPLAY);
18717                 v.hide();
18718             });
18719             
18720             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18721                 v.setStyle('width', '189px');
18722             });
18723         }
18724         
18725         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18726             if(!this.calendarWeeks){
18727                 v.remove();
18728                 return;
18729             }
18730             
18731             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18732             v.attr('colspan', function(i, val){
18733                 return parseInt(val) + 1;
18734             });
18735         });
18736                         
18737         
18738         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18739         
18740         this.setStartDate(this.startDate);
18741         this.setEndDate(this.endDate);
18742         
18743         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18744         
18745         this.fillDow();
18746         this.fillMonths();
18747         this.update();
18748         this.showMode();
18749         
18750         if(this.isInline) {
18751             this.showPopup();
18752         }
18753     },
18754     
18755     picker : function()
18756     {
18757         return this.pickerEl;
18758 //        return this.el.select('.datepicker', true).first();
18759     },
18760     
18761     fillDow: function()
18762     {
18763         var dowCnt = this.weekStart;
18764         
18765         var dow = {
18766             tag: 'tr',
18767             cn: [
18768                 
18769             ]
18770         };
18771         
18772         if(this.calendarWeeks){
18773             dow.cn.push({
18774                 tag: 'th',
18775                 cls: 'cw',
18776                 html: '&nbsp;'
18777             })
18778         }
18779         
18780         while (dowCnt < this.weekStart + 7) {
18781             dow.cn.push({
18782                 tag: 'th',
18783                 cls: 'dow',
18784                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18785             });
18786         }
18787         
18788         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18789     },
18790     
18791     fillMonths: function()
18792     {    
18793         var i = 0;
18794         var months = this.picker().select('>.datepicker-months td', true).first();
18795         
18796         months.dom.innerHTML = '';
18797         
18798         while (i < 12) {
18799             var month = {
18800                 tag: 'span',
18801                 cls: 'month',
18802                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18803             };
18804             
18805             months.createChild(month);
18806         }
18807         
18808     },
18809     
18810     update: function()
18811     {
18812         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;
18813         
18814         if (this.date < this.startDate) {
18815             this.viewDate = new Date(this.startDate);
18816         } else if (this.date > this.endDate) {
18817             this.viewDate = new Date(this.endDate);
18818         } else {
18819             this.viewDate = new Date(this.date);
18820         }
18821         
18822         this.fill();
18823     },
18824     
18825     fill: function() 
18826     {
18827         var d = new Date(this.viewDate),
18828                 year = d.getUTCFullYear(),
18829                 month = d.getUTCMonth(),
18830                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18831                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18832                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18833                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18834                 currentDate = this.date && this.date.valueOf(),
18835                 today = this.UTCToday();
18836         
18837         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18838         
18839 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18840         
18841 //        this.picker.select('>tfoot th.today').
18842 //                                              .text(dates[this.language].today)
18843 //                                              .toggle(this.todayBtn !== false);
18844     
18845         this.updateNavArrows();
18846         this.fillMonths();
18847                                                 
18848         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18849         
18850         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18851          
18852         prevMonth.setUTCDate(day);
18853         
18854         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18855         
18856         var nextMonth = new Date(prevMonth);
18857         
18858         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18859         
18860         nextMonth = nextMonth.valueOf();
18861         
18862         var fillMonths = false;
18863         
18864         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18865         
18866         while(prevMonth.valueOf() <= nextMonth) {
18867             var clsName = '';
18868             
18869             if (prevMonth.getUTCDay() === this.weekStart) {
18870                 if(fillMonths){
18871                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18872                 }
18873                     
18874                 fillMonths = {
18875                     tag: 'tr',
18876                     cn: []
18877                 };
18878                 
18879                 if(this.calendarWeeks){
18880                     // ISO 8601: First week contains first thursday.
18881                     // ISO also states week starts on Monday, but we can be more abstract here.
18882                     var
18883                     // Start of current week: based on weekstart/current date
18884                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18885                     // Thursday of this week
18886                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18887                     // First Thursday of year, year from thursday
18888                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18889                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18890                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18891                     
18892                     fillMonths.cn.push({
18893                         tag: 'td',
18894                         cls: 'cw',
18895                         html: calWeek
18896                     });
18897                 }
18898             }
18899             
18900             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18901                 clsName += ' old';
18902             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18903                 clsName += ' new';
18904             }
18905             if (this.todayHighlight &&
18906                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18907                 prevMonth.getUTCMonth() == today.getMonth() &&
18908                 prevMonth.getUTCDate() == today.getDate()) {
18909                 clsName += ' today';
18910             }
18911             
18912             if (currentDate && prevMonth.valueOf() === currentDate) {
18913                 clsName += ' active';
18914             }
18915             
18916             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18917                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18918                     clsName += ' disabled';
18919             }
18920             
18921             fillMonths.cn.push({
18922                 tag: 'td',
18923                 cls: 'day ' + clsName,
18924                 html: prevMonth.getDate()
18925             });
18926             
18927             prevMonth.setDate(prevMonth.getDate()+1);
18928         }
18929           
18930         var currentYear = this.date && this.date.getUTCFullYear();
18931         var currentMonth = this.date && this.date.getUTCMonth();
18932         
18933         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18934         
18935         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18936             v.removeClass('active');
18937             
18938             if(currentYear === year && k === currentMonth){
18939                 v.addClass('active');
18940             }
18941             
18942             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18943                 v.addClass('disabled');
18944             }
18945             
18946         });
18947         
18948         
18949         year = parseInt(year/10, 10) * 10;
18950         
18951         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18952         
18953         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18954         
18955         year -= 1;
18956         for (var i = -1; i < 11; i++) {
18957             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18958                 tag: 'span',
18959                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18960                 html: year
18961             });
18962             
18963             year += 1;
18964         }
18965     },
18966     
18967     showMode: function(dir) 
18968     {
18969         if (dir) {
18970             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18971         }
18972         
18973         Roo.each(this.picker().select('>div',true).elements, function(v){
18974             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18975             v.hide();
18976         });
18977         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18978     },
18979     
18980     place: function()
18981     {
18982         if(this.isInline) {
18983             return;
18984         }
18985         
18986         this.picker().removeClass(['bottom', 'top']);
18987         
18988         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18989             /*
18990              * place to the top of element!
18991              *
18992              */
18993             
18994             this.picker().addClass('top');
18995             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18996             
18997             return;
18998         }
18999         
19000         this.picker().addClass('bottom');
19001         
19002         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19003     },
19004     
19005     parseDate : function(value)
19006     {
19007         if(!value || value instanceof Date){
19008             return value;
19009         }
19010         var v = Date.parseDate(value, this.format);
19011         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19012             v = Date.parseDate(value, 'Y-m-d');
19013         }
19014         if(!v && this.altFormats){
19015             if(!this.altFormatsArray){
19016                 this.altFormatsArray = this.altFormats.split("|");
19017             }
19018             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19019                 v = Date.parseDate(value, this.altFormatsArray[i]);
19020             }
19021         }
19022         return v;
19023     },
19024     
19025     formatDate : function(date, fmt)
19026     {   
19027         return (!date || !(date instanceof Date)) ?
19028         date : date.dateFormat(fmt || this.format);
19029     },
19030     
19031     onFocus : function()
19032     {
19033         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19034         this.showPopup();
19035     },
19036     
19037     onBlur : function()
19038     {
19039         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19040         
19041         var d = this.inputEl().getValue();
19042         
19043         this.setValue(d);
19044                 
19045         this.hidePopup();
19046     },
19047     
19048     showPopup : function()
19049     {
19050         this.picker().show();
19051         this.update();
19052         this.place();
19053         
19054         this.fireEvent('showpopup', this, this.date);
19055     },
19056     
19057     hidePopup : function()
19058     {
19059         if(this.isInline) {
19060             return;
19061         }
19062         this.picker().hide();
19063         this.viewMode = this.startViewMode;
19064         this.showMode();
19065         
19066         this.fireEvent('hidepopup', this, this.date);
19067         
19068     },
19069     
19070     onMousedown: function(e)
19071     {
19072         e.stopPropagation();
19073         e.preventDefault();
19074     },
19075     
19076     keyup: function(e)
19077     {
19078         Roo.bootstrap.DateField.superclass.keyup.call(this);
19079         this.update();
19080     },
19081
19082     setValue: function(v)
19083     {
19084         if(this.fireEvent('beforeselect', this, v) !== false){
19085             var d = new Date(this.parseDate(v) ).clearTime();
19086         
19087             if(isNaN(d.getTime())){
19088                 this.date = this.viewDate = '';
19089                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19090                 return;
19091             }
19092
19093             v = this.formatDate(d);
19094
19095             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19096
19097             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19098
19099             this.update();
19100
19101             this.fireEvent('select', this, this.date);
19102         }
19103     },
19104     
19105     getValue: function()
19106     {
19107         return this.formatDate(this.date);
19108     },
19109     
19110     fireKey: function(e)
19111     {
19112         if (!this.picker().isVisible()){
19113             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19114                 this.showPopup();
19115             }
19116             return;
19117         }
19118         
19119         var dateChanged = false,
19120         dir, day, month,
19121         newDate, newViewDate;
19122         
19123         switch(e.keyCode){
19124             case 27: // escape
19125                 this.hidePopup();
19126                 e.preventDefault();
19127                 break;
19128             case 37: // left
19129             case 39: // right
19130                 if (!this.keyboardNavigation) {
19131                     break;
19132                 }
19133                 dir = e.keyCode == 37 ? -1 : 1;
19134                 
19135                 if (e.ctrlKey){
19136                     newDate = this.moveYear(this.date, dir);
19137                     newViewDate = this.moveYear(this.viewDate, dir);
19138                 } else if (e.shiftKey){
19139                     newDate = this.moveMonth(this.date, dir);
19140                     newViewDate = this.moveMonth(this.viewDate, dir);
19141                 } else {
19142                     newDate = new Date(this.date);
19143                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19144                     newViewDate = new Date(this.viewDate);
19145                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19146                 }
19147                 if (this.dateWithinRange(newDate)){
19148                     this.date = newDate;
19149                     this.viewDate = newViewDate;
19150                     this.setValue(this.formatDate(this.date));
19151 //                    this.update();
19152                     e.preventDefault();
19153                     dateChanged = true;
19154                 }
19155                 break;
19156             case 38: // up
19157             case 40: // down
19158                 if (!this.keyboardNavigation) {
19159                     break;
19160                 }
19161                 dir = e.keyCode == 38 ? -1 : 1;
19162                 if (e.ctrlKey){
19163                     newDate = this.moveYear(this.date, dir);
19164                     newViewDate = this.moveYear(this.viewDate, dir);
19165                 } else if (e.shiftKey){
19166                     newDate = this.moveMonth(this.date, dir);
19167                     newViewDate = this.moveMonth(this.viewDate, dir);
19168                 } else {
19169                     newDate = new Date(this.date);
19170                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19171                     newViewDate = new Date(this.viewDate);
19172                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19173                 }
19174                 if (this.dateWithinRange(newDate)){
19175                     this.date = newDate;
19176                     this.viewDate = newViewDate;
19177                     this.setValue(this.formatDate(this.date));
19178 //                    this.update();
19179                     e.preventDefault();
19180                     dateChanged = true;
19181                 }
19182                 break;
19183             case 13: // enter
19184                 this.setValue(this.formatDate(this.date));
19185                 this.hidePopup();
19186                 e.preventDefault();
19187                 break;
19188             case 9: // tab
19189                 this.setValue(this.formatDate(this.date));
19190                 this.hidePopup();
19191                 break;
19192             case 16: // shift
19193             case 17: // ctrl
19194             case 18: // alt
19195                 break;
19196             default :
19197                 this.hidePopup();
19198                 
19199         }
19200     },
19201     
19202     
19203     onClick: function(e) 
19204     {
19205         e.stopPropagation();
19206         e.preventDefault();
19207         
19208         var target = e.getTarget();
19209         
19210         if(target.nodeName.toLowerCase() === 'i'){
19211             target = Roo.get(target).dom.parentNode;
19212         }
19213         
19214         var nodeName = target.nodeName;
19215         var className = target.className;
19216         var html = target.innerHTML;
19217         //Roo.log(nodeName);
19218         
19219         switch(nodeName.toLowerCase()) {
19220             case 'th':
19221                 switch(className) {
19222                     case 'switch':
19223                         this.showMode(1);
19224                         break;
19225                     case 'prev':
19226                     case 'next':
19227                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19228                         switch(this.viewMode){
19229                                 case 0:
19230                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19231                                         break;
19232                                 case 1:
19233                                 case 2:
19234                                         this.viewDate = this.moveYear(this.viewDate, dir);
19235                                         break;
19236                         }
19237                         this.fill();
19238                         break;
19239                     case 'today':
19240                         var date = new Date();
19241                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19242 //                        this.fill()
19243                         this.setValue(this.formatDate(this.date));
19244                         
19245                         this.hidePopup();
19246                         break;
19247                 }
19248                 break;
19249             case 'span':
19250                 if (className.indexOf('disabled') < 0) {
19251                     this.viewDate.setUTCDate(1);
19252                     if (className.indexOf('month') > -1) {
19253                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19254                     } else {
19255                         var year = parseInt(html, 10) || 0;
19256                         this.viewDate.setUTCFullYear(year);
19257                         
19258                     }
19259                     
19260                     if(this.singleMode){
19261                         this.setValue(this.formatDate(this.viewDate));
19262                         this.hidePopup();
19263                         return;
19264                     }
19265                     
19266                     this.showMode(-1);
19267                     this.fill();
19268                 }
19269                 break;
19270                 
19271             case 'td':
19272                 //Roo.log(className);
19273                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19274                     var day = parseInt(html, 10) || 1;
19275                     var year = this.viewDate.getUTCFullYear(),
19276                         month = this.viewDate.getUTCMonth();
19277
19278                     if (className.indexOf('old') > -1) {
19279                         if(month === 0 ){
19280                             month = 11;
19281                             year -= 1;
19282                         }else{
19283                             month -= 1;
19284                         }
19285                     } else if (className.indexOf('new') > -1) {
19286                         if (month == 11) {
19287                             month = 0;
19288                             year += 1;
19289                         } else {
19290                             month += 1;
19291                         }
19292                     }
19293                     //Roo.log([year,month,day]);
19294                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19295                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19296 //                    this.fill();
19297                     //Roo.log(this.formatDate(this.date));
19298                     this.setValue(this.formatDate(this.date));
19299                     this.hidePopup();
19300                 }
19301                 break;
19302         }
19303     },
19304     
19305     setStartDate: function(startDate)
19306     {
19307         this.startDate = startDate || -Infinity;
19308         if (this.startDate !== -Infinity) {
19309             this.startDate = this.parseDate(this.startDate);
19310         }
19311         this.update();
19312         this.updateNavArrows();
19313     },
19314
19315     setEndDate: function(endDate)
19316     {
19317         this.endDate = endDate || Infinity;
19318         if (this.endDate !== Infinity) {
19319             this.endDate = this.parseDate(this.endDate);
19320         }
19321         this.update();
19322         this.updateNavArrows();
19323     },
19324     
19325     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19326     {
19327         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19328         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19329             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19330         }
19331         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19332             return parseInt(d, 10);
19333         });
19334         this.update();
19335         this.updateNavArrows();
19336     },
19337     
19338     updateNavArrows: function() 
19339     {
19340         if(this.singleMode){
19341             return;
19342         }
19343         
19344         var d = new Date(this.viewDate),
19345         year = d.getUTCFullYear(),
19346         month = d.getUTCMonth();
19347         
19348         Roo.each(this.picker().select('.prev', true).elements, function(v){
19349             v.show();
19350             switch (this.viewMode) {
19351                 case 0:
19352
19353                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19354                         v.hide();
19355                     }
19356                     break;
19357                 case 1:
19358                 case 2:
19359                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19360                         v.hide();
19361                     }
19362                     break;
19363             }
19364         });
19365         
19366         Roo.each(this.picker().select('.next', true).elements, function(v){
19367             v.show();
19368             switch (this.viewMode) {
19369                 case 0:
19370
19371                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19372                         v.hide();
19373                     }
19374                     break;
19375                 case 1:
19376                 case 2:
19377                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19378                         v.hide();
19379                     }
19380                     break;
19381             }
19382         })
19383     },
19384     
19385     moveMonth: function(date, dir)
19386     {
19387         if (!dir) {
19388             return date;
19389         }
19390         var new_date = new Date(date.valueOf()),
19391         day = new_date.getUTCDate(),
19392         month = new_date.getUTCMonth(),
19393         mag = Math.abs(dir),
19394         new_month, test;
19395         dir = dir > 0 ? 1 : -1;
19396         if (mag == 1){
19397             test = dir == -1
19398             // If going back one month, make sure month is not current month
19399             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19400             ? function(){
19401                 return new_date.getUTCMonth() == month;
19402             }
19403             // If going forward one month, make sure month is as expected
19404             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19405             : function(){
19406                 return new_date.getUTCMonth() != new_month;
19407             };
19408             new_month = month + dir;
19409             new_date.setUTCMonth(new_month);
19410             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19411             if (new_month < 0 || new_month > 11) {
19412                 new_month = (new_month + 12) % 12;
19413             }
19414         } else {
19415             // For magnitudes >1, move one month at a time...
19416             for (var i=0; i<mag; i++) {
19417                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19418                 new_date = this.moveMonth(new_date, dir);
19419             }
19420             // ...then reset the day, keeping it in the new month
19421             new_month = new_date.getUTCMonth();
19422             new_date.setUTCDate(day);
19423             test = function(){
19424                 return new_month != new_date.getUTCMonth();
19425             };
19426         }
19427         // Common date-resetting loop -- if date is beyond end of month, make it
19428         // end of month
19429         while (test()){
19430             new_date.setUTCDate(--day);
19431             new_date.setUTCMonth(new_month);
19432         }
19433         return new_date;
19434     },
19435
19436     moveYear: function(date, dir)
19437     {
19438         return this.moveMonth(date, dir*12);
19439     },
19440
19441     dateWithinRange: function(date)
19442     {
19443         return date >= this.startDate && date <= this.endDate;
19444     },
19445
19446     
19447     remove: function() 
19448     {
19449         this.picker().remove();
19450     },
19451     
19452     validateValue : function(value)
19453     {
19454         if(this.getVisibilityEl().hasClass('hidden')){
19455             return true;
19456         }
19457         
19458         if(value.length < 1)  {
19459             if(this.allowBlank){
19460                 return true;
19461             }
19462             return false;
19463         }
19464         
19465         if(value.length < this.minLength){
19466             return false;
19467         }
19468         if(value.length > this.maxLength){
19469             return false;
19470         }
19471         if(this.vtype){
19472             var vt = Roo.form.VTypes;
19473             if(!vt[this.vtype](value, this)){
19474                 return false;
19475             }
19476         }
19477         if(typeof this.validator == "function"){
19478             var msg = this.validator(value);
19479             if(msg !== true){
19480                 return false;
19481             }
19482         }
19483         
19484         if(this.regex && !this.regex.test(value)){
19485             return false;
19486         }
19487         
19488         if(typeof(this.parseDate(value)) == 'undefined'){
19489             return false;
19490         }
19491         
19492         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19493             return false;
19494         }      
19495         
19496         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19497             return false;
19498         } 
19499         
19500         
19501         return true;
19502     },
19503     
19504     reset : function()
19505     {
19506         this.date = this.viewDate = '';
19507         
19508         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19509     }
19510    
19511 });
19512
19513 Roo.apply(Roo.bootstrap.DateField,  {
19514     
19515     head : {
19516         tag: 'thead',
19517         cn: [
19518         {
19519             tag: 'tr',
19520             cn: [
19521             {
19522                 tag: 'th',
19523                 cls: 'prev',
19524                 html: '<i class="fa fa-arrow-left"/>'
19525             },
19526             {
19527                 tag: 'th',
19528                 cls: 'switch',
19529                 colspan: '5'
19530             },
19531             {
19532                 tag: 'th',
19533                 cls: 'next',
19534                 html: '<i class="fa fa-arrow-right"/>'
19535             }
19536
19537             ]
19538         }
19539         ]
19540     },
19541     
19542     content : {
19543         tag: 'tbody',
19544         cn: [
19545         {
19546             tag: 'tr',
19547             cn: [
19548             {
19549                 tag: 'td',
19550                 colspan: '7'
19551             }
19552             ]
19553         }
19554         ]
19555     },
19556     
19557     footer : {
19558         tag: 'tfoot',
19559         cn: [
19560         {
19561             tag: 'tr',
19562             cn: [
19563             {
19564                 tag: 'th',
19565                 colspan: '7',
19566                 cls: 'today'
19567             }
19568                     
19569             ]
19570         }
19571         ]
19572     },
19573     
19574     dates:{
19575         en: {
19576             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19577             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19578             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19579             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19580             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19581             today: "Today"
19582         }
19583     },
19584     
19585     modes: [
19586     {
19587         clsName: 'days',
19588         navFnc: 'Month',
19589         navStep: 1
19590     },
19591     {
19592         clsName: 'months',
19593         navFnc: 'FullYear',
19594         navStep: 1
19595     },
19596     {
19597         clsName: 'years',
19598         navFnc: 'FullYear',
19599         navStep: 10
19600     }]
19601 });
19602
19603 Roo.apply(Roo.bootstrap.DateField,  {
19604   
19605     template : {
19606         tag: 'div',
19607         cls: 'datepicker dropdown-menu roo-dynamic',
19608         cn: [
19609         {
19610             tag: 'div',
19611             cls: 'datepicker-days',
19612             cn: [
19613             {
19614                 tag: 'table',
19615                 cls: 'table-condensed',
19616                 cn:[
19617                 Roo.bootstrap.DateField.head,
19618                 {
19619                     tag: 'tbody'
19620                 },
19621                 Roo.bootstrap.DateField.footer
19622                 ]
19623             }
19624             ]
19625         },
19626         {
19627             tag: 'div',
19628             cls: 'datepicker-months',
19629             cn: [
19630             {
19631                 tag: 'table',
19632                 cls: 'table-condensed',
19633                 cn:[
19634                 Roo.bootstrap.DateField.head,
19635                 Roo.bootstrap.DateField.content,
19636                 Roo.bootstrap.DateField.footer
19637                 ]
19638             }
19639             ]
19640         },
19641         {
19642             tag: 'div',
19643             cls: 'datepicker-years',
19644             cn: [
19645             {
19646                 tag: 'table',
19647                 cls: 'table-condensed',
19648                 cn:[
19649                 Roo.bootstrap.DateField.head,
19650                 Roo.bootstrap.DateField.content,
19651                 Roo.bootstrap.DateField.footer
19652                 ]
19653             }
19654             ]
19655         }
19656         ]
19657     }
19658 });
19659
19660  
19661
19662  /*
19663  * - LGPL
19664  *
19665  * TimeField
19666  * 
19667  */
19668
19669 /**
19670  * @class Roo.bootstrap.TimeField
19671  * @extends Roo.bootstrap.Input
19672  * Bootstrap DateField class
19673  * 
19674  * 
19675  * @constructor
19676  * Create a new TimeField
19677  * @param {Object} config The config object
19678  */
19679
19680 Roo.bootstrap.TimeField = function(config){
19681     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19682     this.addEvents({
19683             /**
19684              * @event show
19685              * Fires when this field show.
19686              * @param {Roo.bootstrap.DateField} thisthis
19687              * @param {Mixed} date The date value
19688              */
19689             show : true,
19690             /**
19691              * @event show
19692              * Fires when this field hide.
19693              * @param {Roo.bootstrap.DateField} this
19694              * @param {Mixed} date The date value
19695              */
19696             hide : true,
19697             /**
19698              * @event select
19699              * Fires when select a date.
19700              * @param {Roo.bootstrap.DateField} this
19701              * @param {Mixed} date The date value
19702              */
19703             select : true
19704         });
19705 };
19706
19707 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19708     
19709     /**
19710      * @cfg {String} format
19711      * The default time format string which can be overriden for localization support.  The format must be
19712      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19713      */
19714     format : "H:i",
19715        
19716     onRender: function(ct, position)
19717     {
19718         
19719         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19720                 
19721         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19722         
19723         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19724         
19725         this.pop = this.picker().select('>.datepicker-time',true).first();
19726         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19727         
19728         this.picker().on('mousedown', this.onMousedown, this);
19729         this.picker().on('click', this.onClick, this);
19730         
19731         this.picker().addClass('datepicker-dropdown');
19732     
19733         this.fillTime();
19734         this.update();
19735             
19736         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19737         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19738         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19739         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19740         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19741         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19742
19743     },
19744     
19745     fireKey: function(e){
19746         if (!this.picker().isVisible()){
19747             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19748                 this.show();
19749             }
19750             return;
19751         }
19752
19753         e.preventDefault();
19754         
19755         switch(e.keyCode){
19756             case 27: // escape
19757                 this.hide();
19758                 break;
19759             case 37: // left
19760             case 39: // right
19761                 this.onTogglePeriod();
19762                 break;
19763             case 38: // up
19764                 this.onIncrementMinutes();
19765                 break;
19766             case 40: // down
19767                 this.onDecrementMinutes();
19768                 break;
19769             case 13: // enter
19770             case 9: // tab
19771                 this.setTime();
19772                 break;
19773         }
19774     },
19775     
19776     onClick: function(e) {
19777         e.stopPropagation();
19778         e.preventDefault();
19779     },
19780     
19781     picker : function()
19782     {
19783         return this.el.select('.datepicker', true).first();
19784     },
19785     
19786     fillTime: function()
19787     {    
19788         var time = this.pop.select('tbody', true).first();
19789         
19790         time.dom.innerHTML = '';
19791         
19792         time.createChild({
19793             tag: 'tr',
19794             cn: [
19795                 {
19796                     tag: 'td',
19797                     cn: [
19798                         {
19799                             tag: 'a',
19800                             href: '#',
19801                             cls: 'btn',
19802                             cn: [
19803                                 {
19804                                     tag: 'span',
19805                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19806                                 }
19807                             ]
19808                         } 
19809                     ]
19810                 },
19811                 {
19812                     tag: 'td',
19813                     cls: 'separator'
19814                 },
19815                 {
19816                     tag: 'td',
19817                     cn: [
19818                         {
19819                             tag: 'a',
19820                             href: '#',
19821                             cls: 'btn',
19822                             cn: [
19823                                 {
19824                                     tag: 'span',
19825                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19826                                 }
19827                             ]
19828                         }
19829                     ]
19830                 },
19831                 {
19832                     tag: 'td',
19833                     cls: 'separator'
19834                 }
19835             ]
19836         });
19837         
19838         time.createChild({
19839             tag: 'tr',
19840             cn: [
19841                 {
19842                     tag: 'td',
19843                     cn: [
19844                         {
19845                             tag: 'span',
19846                             cls: 'timepicker-hour',
19847                             html: '00'
19848                         }  
19849                     ]
19850                 },
19851                 {
19852                     tag: 'td',
19853                     cls: 'separator',
19854                     html: ':'
19855                 },
19856                 {
19857                     tag: 'td',
19858                     cn: [
19859                         {
19860                             tag: 'span',
19861                             cls: 'timepicker-minute',
19862                             html: '00'
19863                         }  
19864                     ]
19865                 },
19866                 {
19867                     tag: 'td',
19868                     cls: 'separator'
19869                 },
19870                 {
19871                     tag: 'td',
19872                     cn: [
19873                         {
19874                             tag: 'button',
19875                             type: 'button',
19876                             cls: 'btn btn-primary period',
19877                             html: 'AM'
19878                             
19879                         }
19880                     ]
19881                 }
19882             ]
19883         });
19884         
19885         time.createChild({
19886             tag: 'tr',
19887             cn: [
19888                 {
19889                     tag: 'td',
19890                     cn: [
19891                         {
19892                             tag: 'a',
19893                             href: '#',
19894                             cls: 'btn',
19895                             cn: [
19896                                 {
19897                                     tag: 'span',
19898                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19899                                 }
19900                             ]
19901                         }
19902                     ]
19903                 },
19904                 {
19905                     tag: 'td',
19906                     cls: 'separator'
19907                 },
19908                 {
19909                     tag: 'td',
19910                     cn: [
19911                         {
19912                             tag: 'a',
19913                             href: '#',
19914                             cls: 'btn',
19915                             cn: [
19916                                 {
19917                                     tag: 'span',
19918                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19919                                 }
19920                             ]
19921                         }
19922                     ]
19923                 },
19924                 {
19925                     tag: 'td',
19926                     cls: 'separator'
19927                 }
19928             ]
19929         });
19930         
19931     },
19932     
19933     update: function()
19934     {
19935         
19936         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19937         
19938         this.fill();
19939     },
19940     
19941     fill: function() 
19942     {
19943         var hours = this.time.getHours();
19944         var minutes = this.time.getMinutes();
19945         var period = 'AM';
19946         
19947         if(hours > 11){
19948             period = 'PM';
19949         }
19950         
19951         if(hours == 0){
19952             hours = 12;
19953         }
19954         
19955         
19956         if(hours > 12){
19957             hours = hours - 12;
19958         }
19959         
19960         if(hours < 10){
19961             hours = '0' + hours;
19962         }
19963         
19964         if(minutes < 10){
19965             minutes = '0' + minutes;
19966         }
19967         
19968         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19969         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19970         this.pop.select('button', true).first().dom.innerHTML = period;
19971         
19972     },
19973     
19974     place: function()
19975     {   
19976         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19977         
19978         var cls = ['bottom'];
19979         
19980         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19981             cls.pop();
19982             cls.push('top');
19983         }
19984         
19985         cls.push('right');
19986         
19987         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19988             cls.pop();
19989             cls.push('left');
19990         }
19991         
19992         this.picker().addClass(cls.join('-'));
19993         
19994         var _this = this;
19995         
19996         Roo.each(cls, function(c){
19997             if(c == 'bottom'){
19998                 _this.picker().setTop(_this.inputEl().getHeight());
19999                 return;
20000             }
20001             if(c == 'top'){
20002                 _this.picker().setTop(0 - _this.picker().getHeight());
20003                 return;
20004             }
20005             
20006             if(c == 'left'){
20007                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20008                 return;
20009             }
20010             if(c == 'right'){
20011                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20012                 return;
20013             }
20014         });
20015         
20016     },
20017   
20018     onFocus : function()
20019     {
20020         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20021         this.show();
20022     },
20023     
20024     onBlur : function()
20025     {
20026         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20027         this.hide();
20028     },
20029     
20030     show : function()
20031     {
20032         this.picker().show();
20033         this.pop.show();
20034         this.update();
20035         this.place();
20036         
20037         this.fireEvent('show', this, this.date);
20038     },
20039     
20040     hide : function()
20041     {
20042         this.picker().hide();
20043         this.pop.hide();
20044         
20045         this.fireEvent('hide', this, this.date);
20046     },
20047     
20048     setTime : function()
20049     {
20050         this.hide();
20051         this.setValue(this.time.format(this.format));
20052         
20053         this.fireEvent('select', this, this.date);
20054         
20055         
20056     },
20057     
20058     onMousedown: function(e){
20059         e.stopPropagation();
20060         e.preventDefault();
20061     },
20062     
20063     onIncrementHours: function()
20064     {
20065         Roo.log('onIncrementHours');
20066         this.time = this.time.add(Date.HOUR, 1);
20067         this.update();
20068         
20069     },
20070     
20071     onDecrementHours: function()
20072     {
20073         Roo.log('onDecrementHours');
20074         this.time = this.time.add(Date.HOUR, -1);
20075         this.update();
20076     },
20077     
20078     onIncrementMinutes: function()
20079     {
20080         Roo.log('onIncrementMinutes');
20081         this.time = this.time.add(Date.MINUTE, 1);
20082         this.update();
20083     },
20084     
20085     onDecrementMinutes: function()
20086     {
20087         Roo.log('onDecrementMinutes');
20088         this.time = this.time.add(Date.MINUTE, -1);
20089         this.update();
20090     },
20091     
20092     onTogglePeriod: function()
20093     {
20094         Roo.log('onTogglePeriod');
20095         this.time = this.time.add(Date.HOUR, 12);
20096         this.update();
20097     }
20098     
20099    
20100 });
20101
20102 Roo.apply(Roo.bootstrap.TimeField,  {
20103     
20104     content : {
20105         tag: 'tbody',
20106         cn: [
20107             {
20108                 tag: 'tr',
20109                 cn: [
20110                 {
20111                     tag: 'td',
20112                     colspan: '7'
20113                 }
20114                 ]
20115             }
20116         ]
20117     },
20118     
20119     footer : {
20120         tag: 'tfoot',
20121         cn: [
20122             {
20123                 tag: 'tr',
20124                 cn: [
20125                 {
20126                     tag: 'th',
20127                     colspan: '7',
20128                     cls: '',
20129                     cn: [
20130                         {
20131                             tag: 'button',
20132                             cls: 'btn btn-info ok',
20133                             html: 'OK'
20134                         }
20135                     ]
20136                 }
20137
20138                 ]
20139             }
20140         ]
20141     }
20142 });
20143
20144 Roo.apply(Roo.bootstrap.TimeField,  {
20145   
20146     template : {
20147         tag: 'div',
20148         cls: 'datepicker dropdown-menu',
20149         cn: [
20150             {
20151                 tag: 'div',
20152                 cls: 'datepicker-time',
20153                 cn: [
20154                 {
20155                     tag: 'table',
20156                     cls: 'table-condensed',
20157                     cn:[
20158                     Roo.bootstrap.TimeField.content,
20159                     Roo.bootstrap.TimeField.footer
20160                     ]
20161                 }
20162                 ]
20163             }
20164         ]
20165     }
20166 });
20167
20168  
20169
20170  /*
20171  * - LGPL
20172  *
20173  * MonthField
20174  * 
20175  */
20176
20177 /**
20178  * @class Roo.bootstrap.MonthField
20179  * @extends Roo.bootstrap.Input
20180  * Bootstrap MonthField class
20181  * 
20182  * @cfg {String} language default en
20183  * 
20184  * @constructor
20185  * Create a new MonthField
20186  * @param {Object} config The config object
20187  */
20188
20189 Roo.bootstrap.MonthField = function(config){
20190     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20191     
20192     this.addEvents({
20193         /**
20194          * @event show
20195          * Fires when this field show.
20196          * @param {Roo.bootstrap.MonthField} this
20197          * @param {Mixed} date The date value
20198          */
20199         show : true,
20200         /**
20201          * @event show
20202          * Fires when this field hide.
20203          * @param {Roo.bootstrap.MonthField} this
20204          * @param {Mixed} date The date value
20205          */
20206         hide : true,
20207         /**
20208          * @event select
20209          * Fires when select a date.
20210          * @param {Roo.bootstrap.MonthField} this
20211          * @param {String} oldvalue The old value
20212          * @param {String} newvalue The new value
20213          */
20214         select : true
20215     });
20216 };
20217
20218 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20219     
20220     onRender: function(ct, position)
20221     {
20222         
20223         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20224         
20225         this.language = this.language || 'en';
20226         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20227         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20228         
20229         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20230         this.isInline = false;
20231         this.isInput = true;
20232         this.component = this.el.select('.add-on', true).first() || false;
20233         this.component = (this.component && this.component.length === 0) ? false : this.component;
20234         this.hasInput = this.component && this.inputEL().length;
20235         
20236         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20237         
20238         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20239         
20240         this.picker().on('mousedown', this.onMousedown, this);
20241         this.picker().on('click', this.onClick, this);
20242         
20243         this.picker().addClass('datepicker-dropdown');
20244         
20245         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20246             v.setStyle('width', '189px');
20247         });
20248         
20249         this.fillMonths();
20250         
20251         this.update();
20252         
20253         if(this.isInline) {
20254             this.show();
20255         }
20256         
20257     },
20258     
20259     setValue: function(v, suppressEvent)
20260     {   
20261         var o = this.getValue();
20262         
20263         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20264         
20265         this.update();
20266
20267         if(suppressEvent !== true){
20268             this.fireEvent('select', this, o, v);
20269         }
20270         
20271     },
20272     
20273     getValue: function()
20274     {
20275         return this.value;
20276     },
20277     
20278     onClick: function(e) 
20279     {
20280         e.stopPropagation();
20281         e.preventDefault();
20282         
20283         var target = e.getTarget();
20284         
20285         if(target.nodeName.toLowerCase() === 'i'){
20286             target = Roo.get(target).dom.parentNode;
20287         }
20288         
20289         var nodeName = target.nodeName;
20290         var className = target.className;
20291         var html = target.innerHTML;
20292         
20293         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20294             return;
20295         }
20296         
20297         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20298         
20299         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20300         
20301         this.hide();
20302                         
20303     },
20304     
20305     picker : function()
20306     {
20307         return this.pickerEl;
20308     },
20309     
20310     fillMonths: function()
20311     {    
20312         var i = 0;
20313         var months = this.picker().select('>.datepicker-months td', true).first();
20314         
20315         months.dom.innerHTML = '';
20316         
20317         while (i < 12) {
20318             var month = {
20319                 tag: 'span',
20320                 cls: 'month',
20321                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20322             };
20323             
20324             months.createChild(month);
20325         }
20326         
20327     },
20328     
20329     update: function()
20330     {
20331         var _this = this;
20332         
20333         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20334             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20335         }
20336         
20337         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20338             e.removeClass('active');
20339             
20340             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20341                 e.addClass('active');
20342             }
20343         })
20344     },
20345     
20346     place: function()
20347     {
20348         if(this.isInline) {
20349             return;
20350         }
20351         
20352         this.picker().removeClass(['bottom', 'top']);
20353         
20354         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20355             /*
20356              * place to the top of element!
20357              *
20358              */
20359             
20360             this.picker().addClass('top');
20361             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20362             
20363             return;
20364         }
20365         
20366         this.picker().addClass('bottom');
20367         
20368         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20369     },
20370     
20371     onFocus : function()
20372     {
20373         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20374         this.show();
20375     },
20376     
20377     onBlur : function()
20378     {
20379         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20380         
20381         var d = this.inputEl().getValue();
20382         
20383         this.setValue(d);
20384                 
20385         this.hide();
20386     },
20387     
20388     show : function()
20389     {
20390         this.picker().show();
20391         this.picker().select('>.datepicker-months', true).first().show();
20392         this.update();
20393         this.place();
20394         
20395         this.fireEvent('show', this, this.date);
20396     },
20397     
20398     hide : function()
20399     {
20400         if(this.isInline) {
20401             return;
20402         }
20403         this.picker().hide();
20404         this.fireEvent('hide', this, this.date);
20405         
20406     },
20407     
20408     onMousedown: function(e)
20409     {
20410         e.stopPropagation();
20411         e.preventDefault();
20412     },
20413     
20414     keyup: function(e)
20415     {
20416         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20417         this.update();
20418     },
20419
20420     fireKey: function(e)
20421     {
20422         if (!this.picker().isVisible()){
20423             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20424                 this.show();
20425             }
20426             return;
20427         }
20428         
20429         var dir;
20430         
20431         switch(e.keyCode){
20432             case 27: // escape
20433                 this.hide();
20434                 e.preventDefault();
20435                 break;
20436             case 37: // left
20437             case 39: // right
20438                 dir = e.keyCode == 37 ? -1 : 1;
20439                 
20440                 this.vIndex = this.vIndex + dir;
20441                 
20442                 if(this.vIndex < 0){
20443                     this.vIndex = 0;
20444                 }
20445                 
20446                 if(this.vIndex > 11){
20447                     this.vIndex = 11;
20448                 }
20449                 
20450                 if(isNaN(this.vIndex)){
20451                     this.vIndex = 0;
20452                 }
20453                 
20454                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20455                 
20456                 break;
20457             case 38: // up
20458             case 40: // down
20459                 
20460                 dir = e.keyCode == 38 ? -1 : 1;
20461                 
20462                 this.vIndex = this.vIndex + dir * 4;
20463                 
20464                 if(this.vIndex < 0){
20465                     this.vIndex = 0;
20466                 }
20467                 
20468                 if(this.vIndex > 11){
20469                     this.vIndex = 11;
20470                 }
20471                 
20472                 if(isNaN(this.vIndex)){
20473                     this.vIndex = 0;
20474                 }
20475                 
20476                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20477                 break;
20478                 
20479             case 13: // enter
20480                 
20481                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20482                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20483                 }
20484                 
20485                 this.hide();
20486                 e.preventDefault();
20487                 break;
20488             case 9: // tab
20489                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20490                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20491                 }
20492                 this.hide();
20493                 break;
20494             case 16: // shift
20495             case 17: // ctrl
20496             case 18: // alt
20497                 break;
20498             default :
20499                 this.hide();
20500                 
20501         }
20502     },
20503     
20504     remove: function() 
20505     {
20506         this.picker().remove();
20507     }
20508    
20509 });
20510
20511 Roo.apply(Roo.bootstrap.MonthField,  {
20512     
20513     content : {
20514         tag: 'tbody',
20515         cn: [
20516         {
20517             tag: 'tr',
20518             cn: [
20519             {
20520                 tag: 'td',
20521                 colspan: '7'
20522             }
20523             ]
20524         }
20525         ]
20526     },
20527     
20528     dates:{
20529         en: {
20530             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20531             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20532         }
20533     }
20534 });
20535
20536 Roo.apply(Roo.bootstrap.MonthField,  {
20537   
20538     template : {
20539         tag: 'div',
20540         cls: 'datepicker dropdown-menu roo-dynamic',
20541         cn: [
20542             {
20543                 tag: 'div',
20544                 cls: 'datepicker-months',
20545                 cn: [
20546                 {
20547                     tag: 'table',
20548                     cls: 'table-condensed',
20549                     cn:[
20550                         Roo.bootstrap.DateField.content
20551                     ]
20552                 }
20553                 ]
20554             }
20555         ]
20556     }
20557 });
20558
20559  
20560
20561  
20562  /*
20563  * - LGPL
20564  *
20565  * CheckBox
20566  * 
20567  */
20568
20569 /**
20570  * @class Roo.bootstrap.CheckBox
20571  * @extends Roo.bootstrap.Input
20572  * Bootstrap CheckBox class
20573  * 
20574  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20575  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20576  * @cfg {String} boxLabel The text that appears beside the checkbox
20577  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20578  * @cfg {Boolean} checked initnal the element
20579  * @cfg {Boolean} inline inline the element (default false)
20580  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20581  * @cfg {String} tooltip label tooltip
20582  * 
20583  * @constructor
20584  * Create a new CheckBox
20585  * @param {Object} config The config object
20586  */
20587
20588 Roo.bootstrap.CheckBox = function(config){
20589     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20590    
20591     this.addEvents({
20592         /**
20593         * @event check
20594         * Fires when the element is checked or unchecked.
20595         * @param {Roo.bootstrap.CheckBox} this This input
20596         * @param {Boolean} checked The new checked value
20597         */
20598        check : true,
20599        /**
20600         * @event click
20601         * Fires when the element is click.
20602         * @param {Roo.bootstrap.CheckBox} this This input
20603         */
20604        click : true
20605     });
20606     
20607 };
20608
20609 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20610   
20611     inputType: 'checkbox',
20612     inputValue: 1,
20613     valueOff: 0,
20614     boxLabel: false,
20615     checked: false,
20616     weight : false,
20617     inline: false,
20618     tooltip : '',
20619     
20620     getAutoCreate : function()
20621     {
20622         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20623         
20624         var id = Roo.id();
20625         
20626         var cfg = {};
20627         
20628         cfg.cls = 'form-group ' + this.inputType; //input-group
20629         
20630         if(this.inline){
20631             cfg.cls += ' ' + this.inputType + '-inline';
20632         }
20633         
20634         var input =  {
20635             tag: 'input',
20636             id : id,
20637             type : this.inputType,
20638             value : this.inputValue,
20639             cls : 'roo-' + this.inputType, //'form-box',
20640             placeholder : this.placeholder || ''
20641             
20642         };
20643         
20644         if(this.inputType != 'radio'){
20645             var hidden =  {
20646                 tag: 'input',
20647                 type : 'hidden',
20648                 cls : 'roo-hidden-value',
20649                 value : this.checked ? this.inputValue : this.valueOff
20650             };
20651         }
20652         
20653             
20654         if (this.weight) { // Validity check?
20655             cfg.cls += " " + this.inputType + "-" + this.weight;
20656         }
20657         
20658         if (this.disabled) {
20659             input.disabled=true;
20660         }
20661         
20662         if(this.checked){
20663             input.checked = this.checked;
20664         }
20665         
20666         if (this.name) {
20667             
20668             input.name = this.name;
20669             
20670             if(this.inputType != 'radio'){
20671                 hidden.name = this.name;
20672                 input.name = '_hidden_' + this.name;
20673             }
20674         }
20675         
20676         if (this.size) {
20677             input.cls += ' input-' + this.size;
20678         }
20679         
20680         var settings=this;
20681         
20682         ['xs','sm','md','lg'].map(function(size){
20683             if (settings[size]) {
20684                 cfg.cls += ' col-' + size + '-' + settings[size];
20685             }
20686         });
20687         
20688         var inputblock = input;
20689          
20690         if (this.before || this.after) {
20691             
20692             inputblock = {
20693                 cls : 'input-group',
20694                 cn :  [] 
20695             };
20696             
20697             if (this.before) {
20698                 inputblock.cn.push({
20699                     tag :'span',
20700                     cls : 'input-group-addon',
20701                     html : this.before
20702                 });
20703             }
20704             
20705             inputblock.cn.push(input);
20706             
20707             if(this.inputType != 'radio'){
20708                 inputblock.cn.push(hidden);
20709             }
20710             
20711             if (this.after) {
20712                 inputblock.cn.push({
20713                     tag :'span',
20714                     cls : 'input-group-addon',
20715                     html : this.after
20716                 });
20717             }
20718             
20719         }
20720         
20721         if (align ==='left' && this.fieldLabel.length) {
20722 //                Roo.log("left and has label");
20723             cfg.cn = [
20724                 {
20725                     tag: 'label',
20726                     'for' :  id,
20727                     cls : 'control-label',
20728                     html : this.fieldLabel
20729                 },
20730                 {
20731                     cls : "", 
20732                     cn: [
20733                         inputblock
20734                     ]
20735                 }
20736             ];
20737             
20738             if(this.labelWidth > 12){
20739                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20740             }
20741             
20742             if(this.labelWidth < 13 && this.labelmd == 0){
20743                 this.labelmd = this.labelWidth;
20744             }
20745             
20746             if(this.labellg > 0){
20747                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20748                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20749             }
20750             
20751             if(this.labelmd > 0){
20752                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20753                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20754             }
20755             
20756             if(this.labelsm > 0){
20757                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20758                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20759             }
20760             
20761             if(this.labelxs > 0){
20762                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20763                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20764             }
20765             
20766         } else if ( this.fieldLabel.length) {
20767 //                Roo.log(" label");
20768                 cfg.cn = [
20769                    
20770                     {
20771                         tag: this.boxLabel ? 'span' : 'label',
20772                         'for': id,
20773                         cls: 'control-label box-input-label',
20774                         //cls : 'input-group-addon',
20775                         html : this.fieldLabel
20776                     },
20777                     
20778                     inputblock
20779                     
20780                 ];
20781
20782         } else {
20783             
20784 //                Roo.log(" no label && no align");
20785                 cfg.cn = [  inputblock ] ;
20786                 
20787                 
20788         }
20789         
20790         if(this.boxLabel){
20791              var boxLabelCfg = {
20792                 tag: 'label',
20793                 //'for': id, // box label is handled by onclick - so no for...
20794                 cls: 'box-label',
20795                 html: this.boxLabel
20796             };
20797             
20798             if(this.tooltip){
20799                 boxLabelCfg.tooltip = this.tooltip;
20800             }
20801              
20802             cfg.cn.push(boxLabelCfg);
20803         }
20804         
20805         if(this.inputType != 'radio'){
20806             cfg.cn.push(hidden);
20807         }
20808         
20809         return cfg;
20810         
20811     },
20812     
20813     /**
20814      * return the real input element.
20815      */
20816     inputEl: function ()
20817     {
20818         return this.el.select('input.roo-' + this.inputType,true).first();
20819     },
20820     hiddenEl: function ()
20821     {
20822         return this.el.select('input.roo-hidden-value',true).first();
20823     },
20824     
20825     labelEl: function()
20826     {
20827         return this.el.select('label.control-label',true).first();
20828     },
20829     /* depricated... */
20830     
20831     label: function()
20832     {
20833         return this.labelEl();
20834     },
20835     
20836     boxLabelEl: function()
20837     {
20838         return this.el.select('label.box-label',true).first();
20839     },
20840     
20841     initEvents : function()
20842     {
20843 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20844         
20845         this.inputEl().on('click', this.onClick,  this);
20846         
20847         if (this.boxLabel) { 
20848             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20849         }
20850         
20851         this.startValue = this.getValue();
20852         
20853         if(this.groupId){
20854             Roo.bootstrap.CheckBox.register(this);
20855         }
20856     },
20857     
20858     onClick : function(e)
20859     {   
20860         if(this.fireEvent('click', this, e) !== false){
20861             this.setChecked(!this.checked);
20862         }
20863         
20864     },
20865     
20866     setChecked : function(state,suppressEvent)
20867     {
20868         this.startValue = this.getValue();
20869
20870         if(this.inputType == 'radio'){
20871             
20872             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20873                 e.dom.checked = false;
20874             });
20875             
20876             this.inputEl().dom.checked = true;
20877             
20878             this.inputEl().dom.value = this.inputValue;
20879             
20880             if(suppressEvent !== true){
20881                 this.fireEvent('check', this, true);
20882             }
20883             
20884             this.validate();
20885             
20886             return;
20887         }
20888         
20889         this.checked = state;
20890         
20891         this.inputEl().dom.checked = state;
20892         
20893         
20894         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20895         
20896         if(suppressEvent !== true){
20897             this.fireEvent('check', this, state);
20898         }
20899         
20900         this.validate();
20901     },
20902     
20903     getValue : function()
20904     {
20905         if(this.inputType == 'radio'){
20906             return this.getGroupValue();
20907         }
20908         
20909         return this.hiddenEl().dom.value;
20910         
20911     },
20912     
20913     getGroupValue : function()
20914     {
20915         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20916             return '';
20917         }
20918         
20919         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20920     },
20921     
20922     setValue : function(v,suppressEvent)
20923     {
20924         if(this.inputType == 'radio'){
20925             this.setGroupValue(v, suppressEvent);
20926             return;
20927         }
20928         
20929         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20930         
20931         this.validate();
20932     },
20933     
20934     setGroupValue : function(v, suppressEvent)
20935     {
20936         this.startValue = this.getValue();
20937         
20938         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20939             e.dom.checked = false;
20940             
20941             if(e.dom.value == v){
20942                 e.dom.checked = true;
20943             }
20944         });
20945         
20946         if(suppressEvent !== true){
20947             this.fireEvent('check', this, true);
20948         }
20949
20950         this.validate();
20951         
20952         return;
20953     },
20954     
20955     validate : function()
20956     {
20957         if(this.getVisibilityEl().hasClass('hidden')){
20958             return true;
20959         }
20960         
20961         if(
20962                 this.disabled || 
20963                 (this.inputType == 'radio' && this.validateRadio()) ||
20964                 (this.inputType == 'checkbox' && this.validateCheckbox())
20965         ){
20966             this.markValid();
20967             return true;
20968         }
20969         
20970         this.markInvalid();
20971         return false;
20972     },
20973     
20974     validateRadio : function()
20975     {
20976         if(this.getVisibilityEl().hasClass('hidden')){
20977             return true;
20978         }
20979         
20980         if(this.allowBlank){
20981             return true;
20982         }
20983         
20984         var valid = false;
20985         
20986         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20987             if(!e.dom.checked){
20988                 return;
20989             }
20990             
20991             valid = true;
20992             
20993             return false;
20994         });
20995         
20996         return valid;
20997     },
20998     
20999     validateCheckbox : function()
21000     {
21001         if(!this.groupId){
21002             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21003             //return (this.getValue() == this.inputValue) ? true : false;
21004         }
21005         
21006         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21007         
21008         if(!group){
21009             return false;
21010         }
21011         
21012         var r = false;
21013         
21014         for(var i in group){
21015             if(group[i].el.isVisible(true)){
21016                 r = false;
21017                 break;
21018             }
21019             
21020             r = true;
21021         }
21022         
21023         for(var i in group){
21024             if(r){
21025                 break;
21026             }
21027             
21028             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21029         }
21030         
21031         return r;
21032     },
21033     
21034     /**
21035      * Mark this field as valid
21036      */
21037     markValid : function()
21038     {
21039         var _this = this;
21040         
21041         this.fireEvent('valid', this);
21042         
21043         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21044         
21045         if(this.groupId){
21046             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21047         }
21048         
21049         if(label){
21050             label.markValid();
21051         }
21052
21053         if(this.inputType == 'radio'){
21054             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21055                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21056                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21057             });
21058             
21059             return;
21060         }
21061
21062         if(!this.groupId){
21063             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21064             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21065             return;
21066         }
21067         
21068         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21069         
21070         if(!group){
21071             return;
21072         }
21073         
21074         for(var i in group){
21075             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21076             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21077         }
21078     },
21079     
21080      /**
21081      * Mark this field as invalid
21082      * @param {String} msg The validation message
21083      */
21084     markInvalid : function(msg)
21085     {
21086         if(this.allowBlank){
21087             return;
21088         }
21089         
21090         var _this = this;
21091         
21092         this.fireEvent('invalid', this, msg);
21093         
21094         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21095         
21096         if(this.groupId){
21097             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21098         }
21099         
21100         if(label){
21101             label.markInvalid();
21102         }
21103             
21104         if(this.inputType == 'radio'){
21105             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21106                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21107                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21108             });
21109             
21110             return;
21111         }
21112         
21113         if(!this.groupId){
21114             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21115             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21116             return;
21117         }
21118         
21119         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21120         
21121         if(!group){
21122             return;
21123         }
21124         
21125         for(var i in group){
21126             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21127             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21128         }
21129         
21130     },
21131     
21132     clearInvalid : function()
21133     {
21134         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21135         
21136         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21137         
21138         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21139         
21140         if (label && label.iconEl) {
21141             label.iconEl.removeClass(label.validClass);
21142             label.iconEl.removeClass(label.invalidClass);
21143         }
21144     },
21145     
21146     disable : function()
21147     {
21148         if(this.inputType != 'radio'){
21149             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21150             return;
21151         }
21152         
21153         var _this = this;
21154         
21155         if(this.rendered){
21156             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21157                 _this.getActionEl().addClass(this.disabledClass);
21158                 e.dom.disabled = true;
21159             });
21160         }
21161         
21162         this.disabled = true;
21163         this.fireEvent("disable", this);
21164         return this;
21165     },
21166
21167     enable : function()
21168     {
21169         if(this.inputType != 'radio'){
21170             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21171             return;
21172         }
21173         
21174         var _this = this;
21175         
21176         if(this.rendered){
21177             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21178                 _this.getActionEl().removeClass(this.disabledClass);
21179                 e.dom.disabled = false;
21180             });
21181         }
21182         
21183         this.disabled = false;
21184         this.fireEvent("enable", this);
21185         return this;
21186     },
21187     
21188     setBoxLabel : function(v)
21189     {
21190         this.boxLabel = v;
21191         
21192         if(this.rendered){
21193             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21194         }
21195     }
21196
21197 });
21198
21199 Roo.apply(Roo.bootstrap.CheckBox, {
21200     
21201     groups: {},
21202     
21203      /**
21204     * register a CheckBox Group
21205     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21206     */
21207     register : function(checkbox)
21208     {
21209         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21210             this.groups[checkbox.groupId] = {};
21211         }
21212         
21213         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21214             return;
21215         }
21216         
21217         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21218         
21219     },
21220     /**
21221     * fetch a CheckBox Group based on the group ID
21222     * @param {string} the group ID
21223     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21224     */
21225     get: function(groupId) {
21226         if (typeof(this.groups[groupId]) == 'undefined') {
21227             return false;
21228         }
21229         
21230         return this.groups[groupId] ;
21231     }
21232     
21233     
21234 });
21235 /*
21236  * - LGPL
21237  *
21238  * RadioItem
21239  * 
21240  */
21241
21242 /**
21243  * @class Roo.bootstrap.Radio
21244  * @extends Roo.bootstrap.Component
21245  * Bootstrap Radio class
21246  * @cfg {String} boxLabel - the label associated
21247  * @cfg {String} value - the value of radio
21248  * 
21249  * @constructor
21250  * Create a new Radio
21251  * @param {Object} config The config object
21252  */
21253 Roo.bootstrap.Radio = function(config){
21254     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21255     
21256 };
21257
21258 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21259     
21260     boxLabel : '',
21261     
21262     value : '',
21263     
21264     getAutoCreate : function()
21265     {
21266         var cfg = {
21267             tag : 'div',
21268             cls : 'form-group radio',
21269             cn : [
21270                 {
21271                     tag : 'label',
21272                     cls : 'box-label',
21273                     html : this.boxLabel
21274                 }
21275             ]
21276         };
21277         
21278         return cfg;
21279     },
21280     
21281     initEvents : function() 
21282     {
21283         this.parent().register(this);
21284         
21285         this.el.on('click', this.onClick, this);
21286         
21287     },
21288     
21289     onClick : function(e)
21290     {
21291         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21292             this.setChecked(true);
21293         }
21294     },
21295     
21296     setChecked : function(state, suppressEvent)
21297     {
21298         this.parent().setValue(this.value, suppressEvent);
21299         
21300     },
21301     
21302     setBoxLabel : function(v)
21303     {
21304         this.boxLabel = v;
21305         
21306         if(this.rendered){
21307             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21308         }
21309     }
21310     
21311 });
21312  
21313
21314  /*
21315  * - LGPL
21316  *
21317  * Input
21318  * 
21319  */
21320
21321 /**
21322  * @class Roo.bootstrap.SecurePass
21323  * @extends Roo.bootstrap.Input
21324  * Bootstrap SecurePass class
21325  *
21326  * 
21327  * @constructor
21328  * Create a new SecurePass
21329  * @param {Object} config The config object
21330  */
21331  
21332 Roo.bootstrap.SecurePass = function (config) {
21333     // these go here, so the translation tool can replace them..
21334     this.errors = {
21335         PwdEmpty: "Please type a password, and then retype it to confirm.",
21336         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21337         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21338         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21339         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21340         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21341         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21342         TooWeak: "Your password is Too Weak."
21343     },
21344     this.meterLabel = "Password strength:";
21345     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21346     this.meterClass = [
21347         "roo-password-meter-tooweak", 
21348         "roo-password-meter-weak", 
21349         "roo-password-meter-medium", 
21350         "roo-password-meter-strong", 
21351         "roo-password-meter-grey"
21352     ];
21353     
21354     this.errors = {};
21355     
21356     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21357 }
21358
21359 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21360     /**
21361      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21362      * {
21363      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21364      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21365      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21366      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21367      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21368      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21369      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21370      * })
21371      */
21372     // private
21373     
21374     meterWidth: 300,
21375     errorMsg :'',    
21376     errors: false,
21377     imageRoot: '/',
21378     /**
21379      * @cfg {String/Object} Label for the strength meter (defaults to
21380      * 'Password strength:')
21381      */
21382     // private
21383     meterLabel: '',
21384     /**
21385      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21386      * ['Weak', 'Medium', 'Strong'])
21387      */
21388     // private    
21389     pwdStrengths: false,    
21390     // private
21391     strength: 0,
21392     // private
21393     _lastPwd: null,
21394     // private
21395     kCapitalLetter: 0,
21396     kSmallLetter: 1,
21397     kDigit: 2,
21398     kPunctuation: 3,
21399     
21400     insecure: false,
21401     // private
21402     initEvents: function ()
21403     {
21404         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21405
21406         if (this.el.is('input[type=password]') && Roo.isSafari) {
21407             this.el.on('keydown', this.SafariOnKeyDown, this);
21408         }
21409
21410         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21411     },
21412     // private
21413     onRender: function (ct, position)
21414     {
21415         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21416         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21417         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21418
21419         this.trigger.createChild({
21420                    cn: [
21421                     {
21422                     //id: 'PwdMeter',
21423                     tag: 'div',
21424                     cls: 'roo-password-meter-grey col-xs-12',
21425                     style: {
21426                         //width: 0,
21427                         //width: this.meterWidth + 'px'                                                
21428                         }
21429                     },
21430                     {                            
21431                          cls: 'roo-password-meter-text'                          
21432                     }
21433                 ]            
21434         });
21435
21436          
21437         if (this.hideTrigger) {
21438             this.trigger.setDisplayed(false);
21439         }
21440         this.setSize(this.width || '', this.height || '');
21441     },
21442     // private
21443     onDestroy: function ()
21444     {
21445         if (this.trigger) {
21446             this.trigger.removeAllListeners();
21447             this.trigger.remove();
21448         }
21449         if (this.wrap) {
21450             this.wrap.remove();
21451         }
21452         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21453     },
21454     // private
21455     checkStrength: function ()
21456     {
21457         var pwd = this.inputEl().getValue();
21458         if (pwd == this._lastPwd) {
21459             return;
21460         }
21461
21462         var strength;
21463         if (this.ClientSideStrongPassword(pwd)) {
21464             strength = 3;
21465         } else if (this.ClientSideMediumPassword(pwd)) {
21466             strength = 2;
21467         } else if (this.ClientSideWeakPassword(pwd)) {
21468             strength = 1;
21469         } else {
21470             strength = 0;
21471         }
21472         
21473         Roo.log('strength1: ' + strength);
21474         
21475         //var pm = this.trigger.child('div/div/div').dom;
21476         var pm = this.trigger.child('div/div');
21477         pm.removeClass(this.meterClass);
21478         pm.addClass(this.meterClass[strength]);
21479                 
21480         
21481         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21482                 
21483         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21484         
21485         this._lastPwd = pwd;
21486     },
21487     reset: function ()
21488     {
21489         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21490         
21491         this._lastPwd = '';
21492         
21493         var pm = this.trigger.child('div/div');
21494         pm.removeClass(this.meterClass);
21495         pm.addClass('roo-password-meter-grey');        
21496         
21497         
21498         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21499         
21500         pt.innerHTML = '';
21501         this.inputEl().dom.type='password';
21502     },
21503     // private
21504     validateValue: function (value)
21505     {
21506         
21507         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21508             return false;
21509         }
21510         if (value.length == 0) {
21511             if (this.allowBlank) {
21512                 this.clearInvalid();
21513                 return true;
21514             }
21515
21516             this.markInvalid(this.errors.PwdEmpty);
21517             this.errorMsg = this.errors.PwdEmpty;
21518             return false;
21519         }
21520         
21521         if(this.insecure){
21522             return true;
21523         }
21524         
21525         if ('[\x21-\x7e]*'.match(value)) {
21526             this.markInvalid(this.errors.PwdBadChar);
21527             this.errorMsg = this.errors.PwdBadChar;
21528             return false;
21529         }
21530         if (value.length < 6) {
21531             this.markInvalid(this.errors.PwdShort);
21532             this.errorMsg = this.errors.PwdShort;
21533             return false;
21534         }
21535         if (value.length > 16) {
21536             this.markInvalid(this.errors.PwdLong);
21537             this.errorMsg = this.errors.PwdLong;
21538             return false;
21539         }
21540         var strength;
21541         if (this.ClientSideStrongPassword(value)) {
21542             strength = 3;
21543         } else if (this.ClientSideMediumPassword(value)) {
21544             strength = 2;
21545         } else if (this.ClientSideWeakPassword(value)) {
21546             strength = 1;
21547         } else {
21548             strength = 0;
21549         }
21550
21551         
21552         if (strength < 2) {
21553             //this.markInvalid(this.errors.TooWeak);
21554             this.errorMsg = this.errors.TooWeak;
21555             //return false;
21556         }
21557         
21558         
21559         console.log('strength2: ' + strength);
21560         
21561         //var pm = this.trigger.child('div/div/div').dom;
21562         
21563         var pm = this.trigger.child('div/div');
21564         pm.removeClass(this.meterClass);
21565         pm.addClass(this.meterClass[strength]);
21566                 
21567         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21568                 
21569         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21570         
21571         this.errorMsg = ''; 
21572         return true;
21573     },
21574     // private
21575     CharacterSetChecks: function (type)
21576     {
21577         this.type = type;
21578         this.fResult = false;
21579     },
21580     // private
21581     isctype: function (character, type)
21582     {
21583         switch (type) {  
21584             case this.kCapitalLetter:
21585                 if (character >= 'A' && character <= 'Z') {
21586                     return true;
21587                 }
21588                 break;
21589             
21590             case this.kSmallLetter:
21591                 if (character >= 'a' && character <= 'z') {
21592                     return true;
21593                 }
21594                 break;
21595             
21596             case this.kDigit:
21597                 if (character >= '0' && character <= '9') {
21598                     return true;
21599                 }
21600                 break;
21601             
21602             case this.kPunctuation:
21603                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21604                     return true;
21605                 }
21606                 break;
21607             
21608             default:
21609                 return false;
21610         }
21611
21612     },
21613     // private
21614     IsLongEnough: function (pwd, size)
21615     {
21616         return !(pwd == null || isNaN(size) || pwd.length < size);
21617     },
21618     // private
21619     SpansEnoughCharacterSets: function (word, nb)
21620     {
21621         if (!this.IsLongEnough(word, nb))
21622         {
21623             return false;
21624         }
21625
21626         var characterSetChecks = new Array(
21627             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21628             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21629         );
21630         
21631         for (var index = 0; index < word.length; ++index) {
21632             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21633                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21634                     characterSetChecks[nCharSet].fResult = true;
21635                     break;
21636                 }
21637             }
21638         }
21639
21640         var nCharSets = 0;
21641         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21642             if (characterSetChecks[nCharSet].fResult) {
21643                 ++nCharSets;
21644             }
21645         }
21646
21647         if (nCharSets < nb) {
21648             return false;
21649         }
21650         return true;
21651     },
21652     // private
21653     ClientSideStrongPassword: function (pwd)
21654     {
21655         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21656     },
21657     // private
21658     ClientSideMediumPassword: function (pwd)
21659     {
21660         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21661     },
21662     // private
21663     ClientSideWeakPassword: function (pwd)
21664     {
21665         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21666     }
21667           
21668 })//<script type="text/javascript">
21669
21670 /*
21671  * Based  Ext JS Library 1.1.1
21672  * Copyright(c) 2006-2007, Ext JS, LLC.
21673  * LGPL
21674  *
21675  */
21676  
21677 /**
21678  * @class Roo.HtmlEditorCore
21679  * @extends Roo.Component
21680  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21681  *
21682  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21683  */
21684
21685 Roo.HtmlEditorCore = function(config){
21686     
21687     
21688     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21689     
21690     
21691     this.addEvents({
21692         /**
21693          * @event initialize
21694          * Fires when the editor is fully initialized (including the iframe)
21695          * @param {Roo.HtmlEditorCore} this
21696          */
21697         initialize: true,
21698         /**
21699          * @event activate
21700          * Fires when the editor is first receives the focus. Any insertion must wait
21701          * until after this event.
21702          * @param {Roo.HtmlEditorCore} this
21703          */
21704         activate: true,
21705          /**
21706          * @event beforesync
21707          * Fires before the textarea is updated with content from the editor iframe. Return false
21708          * to cancel the sync.
21709          * @param {Roo.HtmlEditorCore} this
21710          * @param {String} html
21711          */
21712         beforesync: true,
21713          /**
21714          * @event beforepush
21715          * Fires before the iframe editor is updated with content from the textarea. Return false
21716          * to cancel the push.
21717          * @param {Roo.HtmlEditorCore} this
21718          * @param {String} html
21719          */
21720         beforepush: true,
21721          /**
21722          * @event sync
21723          * Fires when the textarea is updated with content from the editor iframe.
21724          * @param {Roo.HtmlEditorCore} this
21725          * @param {String} html
21726          */
21727         sync: true,
21728          /**
21729          * @event push
21730          * Fires when the iframe editor is updated with content from the textarea.
21731          * @param {Roo.HtmlEditorCore} this
21732          * @param {String} html
21733          */
21734         push: true,
21735         
21736         /**
21737          * @event editorevent
21738          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21739          * @param {Roo.HtmlEditorCore} this
21740          */
21741         editorevent: true
21742         
21743     });
21744     
21745     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21746     
21747     // defaults : white / black...
21748     this.applyBlacklists();
21749     
21750     
21751     
21752 };
21753
21754
21755 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21756
21757
21758      /**
21759      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21760      */
21761     
21762     owner : false,
21763     
21764      /**
21765      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21766      *                        Roo.resizable.
21767      */
21768     resizable : false,
21769      /**
21770      * @cfg {Number} height (in pixels)
21771      */   
21772     height: 300,
21773    /**
21774      * @cfg {Number} width (in pixels)
21775      */   
21776     width: 500,
21777     
21778     /**
21779      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21780      * 
21781      */
21782     stylesheets: false,
21783     
21784     // id of frame..
21785     frameId: false,
21786     
21787     // private properties
21788     validationEvent : false,
21789     deferHeight: true,
21790     initialized : false,
21791     activated : false,
21792     sourceEditMode : false,
21793     onFocus : Roo.emptyFn,
21794     iframePad:3,
21795     hideMode:'offsets',
21796     
21797     clearUp: true,
21798     
21799     // blacklist + whitelisted elements..
21800     black: false,
21801     white: false,
21802      
21803     bodyCls : '',
21804
21805     /**
21806      * Protected method that will not generally be called directly. It
21807      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21808      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21809      */
21810     getDocMarkup : function(){
21811         // body styles..
21812         var st = '';
21813         
21814         // inherit styels from page...?? 
21815         if (this.stylesheets === false) {
21816             
21817             Roo.get(document.head).select('style').each(function(node) {
21818                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21819             });
21820             
21821             Roo.get(document.head).select('link').each(function(node) { 
21822                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21823             });
21824             
21825         } else if (!this.stylesheets.length) {
21826                 // simple..
21827                 st = '<style type="text/css">' +
21828                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21829                    '</style>';
21830         } else { 
21831             st = '<style type="text/css">' +
21832                     this.stylesheets +
21833                 '</style>';
21834         }
21835         
21836         st +=  '<style type="text/css">' +
21837             'IMG { cursor: pointer } ' +
21838         '</style>';
21839
21840         var cls = 'roo-htmleditor-body';
21841         
21842         if(this.bodyCls.length){
21843             cls += ' ' + this.bodyCls;
21844         }
21845         
21846         return '<html><head>' + st  +
21847             //<style type="text/css">' +
21848             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21849             //'</style>' +
21850             ' </head><body class="' +  cls + '"></body></html>';
21851     },
21852
21853     // private
21854     onRender : function(ct, position)
21855     {
21856         var _t = this;
21857         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21858         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21859         
21860         
21861         this.el.dom.style.border = '0 none';
21862         this.el.dom.setAttribute('tabIndex', -1);
21863         this.el.addClass('x-hidden hide');
21864         
21865         
21866         
21867         if(Roo.isIE){ // fix IE 1px bogus margin
21868             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21869         }
21870        
21871         
21872         this.frameId = Roo.id();
21873         
21874          
21875         
21876         var iframe = this.owner.wrap.createChild({
21877             tag: 'iframe',
21878             cls: 'form-control', // bootstrap..
21879             id: this.frameId,
21880             name: this.frameId,
21881             frameBorder : 'no',
21882             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21883         }, this.el
21884         );
21885         
21886         
21887         this.iframe = iframe.dom;
21888
21889          this.assignDocWin();
21890         
21891         this.doc.designMode = 'on';
21892        
21893         this.doc.open();
21894         this.doc.write(this.getDocMarkup());
21895         this.doc.close();
21896
21897         
21898         var task = { // must defer to wait for browser to be ready
21899             run : function(){
21900                 //console.log("run task?" + this.doc.readyState);
21901                 this.assignDocWin();
21902                 if(this.doc.body || this.doc.readyState == 'complete'){
21903                     try {
21904                         this.doc.designMode="on";
21905                     } catch (e) {
21906                         return;
21907                     }
21908                     Roo.TaskMgr.stop(task);
21909                     this.initEditor.defer(10, this);
21910                 }
21911             },
21912             interval : 10,
21913             duration: 10000,
21914             scope: this
21915         };
21916         Roo.TaskMgr.start(task);
21917
21918     },
21919
21920     // private
21921     onResize : function(w, h)
21922     {
21923          Roo.log('resize: ' +w + ',' + h );
21924         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21925         if(!this.iframe){
21926             return;
21927         }
21928         if(typeof w == 'number'){
21929             
21930             this.iframe.style.width = w + 'px';
21931         }
21932         if(typeof h == 'number'){
21933             
21934             this.iframe.style.height = h + 'px';
21935             if(this.doc){
21936                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21937             }
21938         }
21939         
21940     },
21941
21942     /**
21943      * Toggles the editor between standard and source edit mode.
21944      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21945      */
21946     toggleSourceEdit : function(sourceEditMode){
21947         
21948         this.sourceEditMode = sourceEditMode === true;
21949         
21950         if(this.sourceEditMode){
21951  
21952             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21953             
21954         }else{
21955             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21956             //this.iframe.className = '';
21957             this.deferFocus();
21958         }
21959         //this.setSize(this.owner.wrap.getSize());
21960         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21961     },
21962
21963     
21964   
21965
21966     /**
21967      * Protected method that will not generally be called directly. If you need/want
21968      * custom HTML cleanup, this is the method you should override.
21969      * @param {String} html The HTML to be cleaned
21970      * return {String} The cleaned HTML
21971      */
21972     cleanHtml : function(html){
21973         html = String(html);
21974         if(html.length > 5){
21975             if(Roo.isSafari){ // strip safari nonsense
21976                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21977             }
21978         }
21979         if(html == '&nbsp;'){
21980             html = '';
21981         }
21982         return html;
21983     },
21984
21985     /**
21986      * HTML Editor -> Textarea
21987      * Protected method that will not generally be called directly. Syncs the contents
21988      * of the editor iframe with the textarea.
21989      */
21990     syncValue : function(){
21991         if(this.initialized){
21992             var bd = (this.doc.body || this.doc.documentElement);
21993             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21994             var html = bd.innerHTML;
21995             if(Roo.isSafari){
21996                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21997                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21998                 if(m && m[1]){
21999                     html = '<div style="'+m[0]+'">' + html + '</div>';
22000                 }
22001             }
22002             html = this.cleanHtml(html);
22003             // fix up the special chars.. normaly like back quotes in word...
22004             // however we do not want to do this with chinese..
22005             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22006                 var cc = b.charCodeAt();
22007                 if (
22008                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22009                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22010                     (cc >= 0xf900 && cc < 0xfb00 )
22011                 ) {
22012                         return b;
22013                 }
22014                 return "&#"+cc+";" 
22015             });
22016             if(this.owner.fireEvent('beforesync', this, html) !== false){
22017                 this.el.dom.value = html;
22018                 this.owner.fireEvent('sync', this, html);
22019             }
22020         }
22021     },
22022
22023     /**
22024      * Protected method that will not generally be called directly. Pushes the value of the textarea
22025      * into the iframe editor.
22026      */
22027     pushValue : function(){
22028         if(this.initialized){
22029             var v = this.el.dom.value.trim();
22030             
22031 //            if(v.length < 1){
22032 //                v = '&#160;';
22033 //            }
22034             
22035             if(this.owner.fireEvent('beforepush', this, v) !== false){
22036                 var d = (this.doc.body || this.doc.documentElement);
22037                 d.innerHTML = v;
22038                 this.cleanUpPaste();
22039                 this.el.dom.value = d.innerHTML;
22040                 this.owner.fireEvent('push', this, v);
22041             }
22042         }
22043     },
22044
22045     // private
22046     deferFocus : function(){
22047         this.focus.defer(10, this);
22048     },
22049
22050     // doc'ed in Field
22051     focus : function(){
22052         if(this.win && !this.sourceEditMode){
22053             this.win.focus();
22054         }else{
22055             this.el.focus();
22056         }
22057     },
22058     
22059     assignDocWin: function()
22060     {
22061         var iframe = this.iframe;
22062         
22063          if(Roo.isIE){
22064             this.doc = iframe.contentWindow.document;
22065             this.win = iframe.contentWindow;
22066         } else {
22067 //            if (!Roo.get(this.frameId)) {
22068 //                return;
22069 //            }
22070 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22071 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22072             
22073             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22074                 return;
22075             }
22076             
22077             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22078             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22079         }
22080     },
22081     
22082     // private
22083     initEditor : function(){
22084         //console.log("INIT EDITOR");
22085         this.assignDocWin();
22086         
22087         
22088         
22089         this.doc.designMode="on";
22090         this.doc.open();
22091         this.doc.write(this.getDocMarkup());
22092         this.doc.close();
22093         
22094         var dbody = (this.doc.body || this.doc.documentElement);
22095         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22096         // this copies styles from the containing element into thsi one..
22097         // not sure why we need all of this..
22098         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22099         
22100         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22101         //ss['background-attachment'] = 'fixed'; // w3c
22102         dbody.bgProperties = 'fixed'; // ie
22103         //Roo.DomHelper.applyStyles(dbody, ss);
22104         Roo.EventManager.on(this.doc, {
22105             //'mousedown': this.onEditorEvent,
22106             'mouseup': this.onEditorEvent,
22107             'dblclick': this.onEditorEvent,
22108             'click': this.onEditorEvent,
22109             'keyup': this.onEditorEvent,
22110             buffer:100,
22111             scope: this
22112         });
22113         if(Roo.isGecko){
22114             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22115         }
22116         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22117             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22118         }
22119         this.initialized = true;
22120
22121         this.owner.fireEvent('initialize', this);
22122         this.pushValue();
22123     },
22124
22125     // private
22126     onDestroy : function(){
22127         
22128         
22129         
22130         if(this.rendered){
22131             
22132             //for (var i =0; i < this.toolbars.length;i++) {
22133             //    // fixme - ask toolbars for heights?
22134             //    this.toolbars[i].onDestroy();
22135            // }
22136             
22137             //this.wrap.dom.innerHTML = '';
22138             //this.wrap.remove();
22139         }
22140     },
22141
22142     // private
22143     onFirstFocus : function(){
22144         
22145         this.assignDocWin();
22146         
22147         
22148         this.activated = true;
22149          
22150     
22151         if(Roo.isGecko){ // prevent silly gecko errors
22152             this.win.focus();
22153             var s = this.win.getSelection();
22154             if(!s.focusNode || s.focusNode.nodeType != 3){
22155                 var r = s.getRangeAt(0);
22156                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22157                 r.collapse(true);
22158                 this.deferFocus();
22159             }
22160             try{
22161                 this.execCmd('useCSS', true);
22162                 this.execCmd('styleWithCSS', false);
22163             }catch(e){}
22164         }
22165         this.owner.fireEvent('activate', this);
22166     },
22167
22168     // private
22169     adjustFont: function(btn){
22170         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22171         //if(Roo.isSafari){ // safari
22172         //    adjust *= 2;
22173        // }
22174         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22175         if(Roo.isSafari){ // safari
22176             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22177             v =  (v < 10) ? 10 : v;
22178             v =  (v > 48) ? 48 : v;
22179             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22180             
22181         }
22182         
22183         
22184         v = Math.max(1, v+adjust);
22185         
22186         this.execCmd('FontSize', v  );
22187     },
22188
22189     onEditorEvent : function(e)
22190     {
22191         this.owner.fireEvent('editorevent', this, e);
22192       //  this.updateToolbar();
22193         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22194     },
22195
22196     insertTag : function(tg)
22197     {
22198         // could be a bit smarter... -> wrap the current selected tRoo..
22199         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22200             
22201             range = this.createRange(this.getSelection());
22202             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22203             wrappingNode.appendChild(range.extractContents());
22204             range.insertNode(wrappingNode);
22205
22206             return;
22207             
22208             
22209             
22210         }
22211         this.execCmd("formatblock",   tg);
22212         
22213     },
22214     
22215     insertText : function(txt)
22216     {
22217         
22218         
22219         var range = this.createRange();
22220         range.deleteContents();
22221                //alert(Sender.getAttribute('label'));
22222                
22223         range.insertNode(this.doc.createTextNode(txt));
22224     } ,
22225     
22226      
22227
22228     /**
22229      * Executes a Midas editor command on the editor document and performs necessary focus and
22230      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22231      * @param {String} cmd The Midas command
22232      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22233      */
22234     relayCmd : function(cmd, value){
22235         this.win.focus();
22236         this.execCmd(cmd, value);
22237         this.owner.fireEvent('editorevent', this);
22238         //this.updateToolbar();
22239         this.owner.deferFocus();
22240     },
22241
22242     /**
22243      * Executes a Midas editor command directly on the editor document.
22244      * For visual commands, you should use {@link #relayCmd} instead.
22245      * <b>This should only be called after the editor is initialized.</b>
22246      * @param {String} cmd The Midas command
22247      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22248      */
22249     execCmd : function(cmd, value){
22250         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22251         this.syncValue();
22252     },
22253  
22254  
22255    
22256     /**
22257      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22258      * to insert tRoo.
22259      * @param {String} text | dom node.. 
22260      */
22261     insertAtCursor : function(text)
22262     {
22263         
22264         if(!this.activated){
22265             return;
22266         }
22267         /*
22268         if(Roo.isIE){
22269             this.win.focus();
22270             var r = this.doc.selection.createRange();
22271             if(r){
22272                 r.collapse(true);
22273                 r.pasteHTML(text);
22274                 this.syncValue();
22275                 this.deferFocus();
22276             
22277             }
22278             return;
22279         }
22280         */
22281         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22282             this.win.focus();
22283             
22284             
22285             // from jquery ui (MIT licenced)
22286             var range, node;
22287             var win = this.win;
22288             
22289             if (win.getSelection && win.getSelection().getRangeAt) {
22290                 range = win.getSelection().getRangeAt(0);
22291                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22292                 range.insertNode(node);
22293             } else if (win.document.selection && win.document.selection.createRange) {
22294                 // no firefox support
22295                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22296                 win.document.selection.createRange().pasteHTML(txt);
22297             } else {
22298                 // no firefox support
22299                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22300                 this.execCmd('InsertHTML', txt);
22301             } 
22302             
22303             this.syncValue();
22304             
22305             this.deferFocus();
22306         }
22307     },
22308  // private
22309     mozKeyPress : function(e){
22310         if(e.ctrlKey){
22311             var c = e.getCharCode(), cmd;
22312           
22313             if(c > 0){
22314                 c = String.fromCharCode(c).toLowerCase();
22315                 switch(c){
22316                     case 'b':
22317                         cmd = 'bold';
22318                         break;
22319                     case 'i':
22320                         cmd = 'italic';
22321                         break;
22322                     
22323                     case 'u':
22324                         cmd = 'underline';
22325                         break;
22326                     
22327                     case 'v':
22328                         this.cleanUpPaste.defer(100, this);
22329                         return;
22330                         
22331                 }
22332                 if(cmd){
22333                     this.win.focus();
22334                     this.execCmd(cmd);
22335                     this.deferFocus();
22336                     e.preventDefault();
22337                 }
22338                 
22339             }
22340         }
22341     },
22342
22343     // private
22344     fixKeys : function(){ // load time branching for fastest keydown performance
22345         if(Roo.isIE){
22346             return function(e){
22347                 var k = e.getKey(), r;
22348                 if(k == e.TAB){
22349                     e.stopEvent();
22350                     r = this.doc.selection.createRange();
22351                     if(r){
22352                         r.collapse(true);
22353                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22354                         this.deferFocus();
22355                     }
22356                     return;
22357                 }
22358                 
22359                 if(k == e.ENTER){
22360                     r = this.doc.selection.createRange();
22361                     if(r){
22362                         var target = r.parentElement();
22363                         if(!target || target.tagName.toLowerCase() != 'li'){
22364                             e.stopEvent();
22365                             r.pasteHTML('<br />');
22366                             r.collapse(false);
22367                             r.select();
22368                         }
22369                     }
22370                 }
22371                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22372                     this.cleanUpPaste.defer(100, this);
22373                     return;
22374                 }
22375                 
22376                 
22377             };
22378         }else if(Roo.isOpera){
22379             return function(e){
22380                 var k = e.getKey();
22381                 if(k == e.TAB){
22382                     e.stopEvent();
22383                     this.win.focus();
22384                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22385                     this.deferFocus();
22386                 }
22387                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22388                     this.cleanUpPaste.defer(100, this);
22389                     return;
22390                 }
22391                 
22392             };
22393         }else if(Roo.isSafari){
22394             return function(e){
22395                 var k = e.getKey();
22396                 
22397                 if(k == e.TAB){
22398                     e.stopEvent();
22399                     this.execCmd('InsertText','\t');
22400                     this.deferFocus();
22401                     return;
22402                 }
22403                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22404                     this.cleanUpPaste.defer(100, this);
22405                     return;
22406                 }
22407                 
22408              };
22409         }
22410     }(),
22411     
22412     getAllAncestors: function()
22413     {
22414         var p = this.getSelectedNode();
22415         var a = [];
22416         if (!p) {
22417             a.push(p); // push blank onto stack..
22418             p = this.getParentElement();
22419         }
22420         
22421         
22422         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22423             a.push(p);
22424             p = p.parentNode;
22425         }
22426         a.push(this.doc.body);
22427         return a;
22428     },
22429     lastSel : false,
22430     lastSelNode : false,
22431     
22432     
22433     getSelection : function() 
22434     {
22435         this.assignDocWin();
22436         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22437     },
22438     
22439     getSelectedNode: function() 
22440     {
22441         // this may only work on Gecko!!!
22442         
22443         // should we cache this!!!!
22444         
22445         
22446         
22447          
22448         var range = this.createRange(this.getSelection()).cloneRange();
22449         
22450         if (Roo.isIE) {
22451             var parent = range.parentElement();
22452             while (true) {
22453                 var testRange = range.duplicate();
22454                 testRange.moveToElementText(parent);
22455                 if (testRange.inRange(range)) {
22456                     break;
22457                 }
22458                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22459                     break;
22460                 }
22461                 parent = parent.parentElement;
22462             }
22463             return parent;
22464         }
22465         
22466         // is ancestor a text element.
22467         var ac =  range.commonAncestorContainer;
22468         if (ac.nodeType == 3) {
22469             ac = ac.parentNode;
22470         }
22471         
22472         var ar = ac.childNodes;
22473          
22474         var nodes = [];
22475         var other_nodes = [];
22476         var has_other_nodes = false;
22477         for (var i=0;i<ar.length;i++) {
22478             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22479                 continue;
22480             }
22481             // fullly contained node.
22482             
22483             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22484                 nodes.push(ar[i]);
22485                 continue;
22486             }
22487             
22488             // probably selected..
22489             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22490                 other_nodes.push(ar[i]);
22491                 continue;
22492             }
22493             // outer..
22494             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22495                 continue;
22496             }
22497             
22498             
22499             has_other_nodes = true;
22500         }
22501         if (!nodes.length && other_nodes.length) {
22502             nodes= other_nodes;
22503         }
22504         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22505             return false;
22506         }
22507         
22508         return nodes[0];
22509     },
22510     createRange: function(sel)
22511     {
22512         // this has strange effects when using with 
22513         // top toolbar - not sure if it's a great idea.
22514         //this.editor.contentWindow.focus();
22515         if (typeof sel != "undefined") {
22516             try {
22517                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22518             } catch(e) {
22519                 return this.doc.createRange();
22520             }
22521         } else {
22522             return this.doc.createRange();
22523         }
22524     },
22525     getParentElement: function()
22526     {
22527         
22528         this.assignDocWin();
22529         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22530         
22531         var range = this.createRange(sel);
22532          
22533         try {
22534             var p = range.commonAncestorContainer;
22535             while (p.nodeType == 3) { // text node
22536                 p = p.parentNode;
22537             }
22538             return p;
22539         } catch (e) {
22540             return null;
22541         }
22542     
22543     },
22544     /***
22545      *
22546      * Range intersection.. the hard stuff...
22547      *  '-1' = before
22548      *  '0' = hits..
22549      *  '1' = after.
22550      *         [ -- selected range --- ]
22551      *   [fail]                        [fail]
22552      *
22553      *    basically..
22554      *      if end is before start or  hits it. fail.
22555      *      if start is after end or hits it fail.
22556      *
22557      *   if either hits (but other is outside. - then it's not 
22558      *   
22559      *    
22560      **/
22561     
22562     
22563     // @see http://www.thismuchiknow.co.uk/?p=64.
22564     rangeIntersectsNode : function(range, node)
22565     {
22566         var nodeRange = node.ownerDocument.createRange();
22567         try {
22568             nodeRange.selectNode(node);
22569         } catch (e) {
22570             nodeRange.selectNodeContents(node);
22571         }
22572     
22573         var rangeStartRange = range.cloneRange();
22574         rangeStartRange.collapse(true);
22575     
22576         var rangeEndRange = range.cloneRange();
22577         rangeEndRange.collapse(false);
22578     
22579         var nodeStartRange = nodeRange.cloneRange();
22580         nodeStartRange.collapse(true);
22581     
22582         var nodeEndRange = nodeRange.cloneRange();
22583         nodeEndRange.collapse(false);
22584     
22585         return rangeStartRange.compareBoundaryPoints(
22586                  Range.START_TO_START, nodeEndRange) == -1 &&
22587                rangeEndRange.compareBoundaryPoints(
22588                  Range.START_TO_START, nodeStartRange) == 1;
22589         
22590          
22591     },
22592     rangeCompareNode : function(range, node)
22593     {
22594         var nodeRange = node.ownerDocument.createRange();
22595         try {
22596             nodeRange.selectNode(node);
22597         } catch (e) {
22598             nodeRange.selectNodeContents(node);
22599         }
22600         
22601         
22602         range.collapse(true);
22603     
22604         nodeRange.collapse(true);
22605      
22606         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22607         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22608          
22609         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22610         
22611         var nodeIsBefore   =  ss == 1;
22612         var nodeIsAfter    = ee == -1;
22613         
22614         if (nodeIsBefore && nodeIsAfter) {
22615             return 0; // outer
22616         }
22617         if (!nodeIsBefore && nodeIsAfter) {
22618             return 1; //right trailed.
22619         }
22620         
22621         if (nodeIsBefore && !nodeIsAfter) {
22622             return 2;  // left trailed.
22623         }
22624         // fully contined.
22625         return 3;
22626     },
22627
22628     // private? - in a new class?
22629     cleanUpPaste :  function()
22630     {
22631         // cleans up the whole document..
22632         Roo.log('cleanuppaste');
22633         
22634         this.cleanUpChildren(this.doc.body);
22635         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22636         if (clean != this.doc.body.innerHTML) {
22637             this.doc.body.innerHTML = clean;
22638         }
22639         
22640     },
22641     
22642     cleanWordChars : function(input) {// change the chars to hex code
22643         var he = Roo.HtmlEditorCore;
22644         
22645         var output = input;
22646         Roo.each(he.swapCodes, function(sw) { 
22647             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22648             
22649             output = output.replace(swapper, sw[1]);
22650         });
22651         
22652         return output;
22653     },
22654     
22655     
22656     cleanUpChildren : function (n)
22657     {
22658         if (!n.childNodes.length) {
22659             return;
22660         }
22661         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22662            this.cleanUpChild(n.childNodes[i]);
22663         }
22664     },
22665     
22666     
22667         
22668     
22669     cleanUpChild : function (node)
22670     {
22671         var ed = this;
22672         //console.log(node);
22673         if (node.nodeName == "#text") {
22674             // clean up silly Windows -- stuff?
22675             return; 
22676         }
22677         if (node.nodeName == "#comment") {
22678             node.parentNode.removeChild(node);
22679             // clean up silly Windows -- stuff?
22680             return; 
22681         }
22682         var lcname = node.tagName.toLowerCase();
22683         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22684         // whitelist of tags..
22685         
22686         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22687             // remove node.
22688             node.parentNode.removeChild(node);
22689             return;
22690             
22691         }
22692         
22693         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22694         
22695         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22696         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22697         
22698         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22699         //    remove_keep_children = true;
22700         //}
22701         
22702         if (remove_keep_children) {
22703             this.cleanUpChildren(node);
22704             // inserts everything just before this node...
22705             while (node.childNodes.length) {
22706                 var cn = node.childNodes[0];
22707                 node.removeChild(cn);
22708                 node.parentNode.insertBefore(cn, node);
22709             }
22710             node.parentNode.removeChild(node);
22711             return;
22712         }
22713         
22714         if (!node.attributes || !node.attributes.length) {
22715             this.cleanUpChildren(node);
22716             return;
22717         }
22718         
22719         function cleanAttr(n,v)
22720         {
22721             
22722             if (v.match(/^\./) || v.match(/^\//)) {
22723                 return;
22724             }
22725             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22726                 return;
22727             }
22728             if (v.match(/^#/)) {
22729                 return;
22730             }
22731 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22732             node.removeAttribute(n);
22733             
22734         }
22735         
22736         var cwhite = this.cwhite;
22737         var cblack = this.cblack;
22738             
22739         function cleanStyle(n,v)
22740         {
22741             if (v.match(/expression/)) { //XSS?? should we even bother..
22742                 node.removeAttribute(n);
22743                 return;
22744             }
22745             
22746             var parts = v.split(/;/);
22747             var clean = [];
22748             
22749             Roo.each(parts, function(p) {
22750                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22751                 if (!p.length) {
22752                     return true;
22753                 }
22754                 var l = p.split(':').shift().replace(/\s+/g,'');
22755                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22756                 
22757                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22758 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22759                     //node.removeAttribute(n);
22760                     return true;
22761                 }
22762                 //Roo.log()
22763                 // only allow 'c whitelisted system attributes'
22764                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22765 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22766                     //node.removeAttribute(n);
22767                     return true;
22768                 }
22769                 
22770                 
22771                  
22772                 
22773                 clean.push(p);
22774                 return true;
22775             });
22776             if (clean.length) { 
22777                 node.setAttribute(n, clean.join(';'));
22778             } else {
22779                 node.removeAttribute(n);
22780             }
22781             
22782         }
22783         
22784         
22785         for (var i = node.attributes.length-1; i > -1 ; i--) {
22786             var a = node.attributes[i];
22787             //console.log(a);
22788             
22789             if (a.name.toLowerCase().substr(0,2)=='on')  {
22790                 node.removeAttribute(a.name);
22791                 continue;
22792             }
22793             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22794                 node.removeAttribute(a.name);
22795                 continue;
22796             }
22797             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22798                 cleanAttr(a.name,a.value); // fixme..
22799                 continue;
22800             }
22801             if (a.name == 'style') {
22802                 cleanStyle(a.name,a.value);
22803                 continue;
22804             }
22805             /// clean up MS crap..
22806             // tecnically this should be a list of valid class'es..
22807             
22808             
22809             if (a.name == 'class') {
22810                 if (a.value.match(/^Mso/)) {
22811                     node.className = '';
22812                 }
22813                 
22814                 if (a.value.match(/^body$/)) {
22815                     node.className = '';
22816                 }
22817                 continue;
22818             }
22819             
22820             // style cleanup!?
22821             // class cleanup?
22822             
22823         }
22824         
22825         
22826         this.cleanUpChildren(node);
22827         
22828         
22829     },
22830     
22831     /**
22832      * Clean up MS wordisms...
22833      */
22834     cleanWord : function(node)
22835     {
22836         
22837         
22838         if (!node) {
22839             this.cleanWord(this.doc.body);
22840             return;
22841         }
22842         if (node.nodeName == "#text") {
22843             // clean up silly Windows -- stuff?
22844             return; 
22845         }
22846         if (node.nodeName == "#comment") {
22847             node.parentNode.removeChild(node);
22848             // clean up silly Windows -- stuff?
22849             return; 
22850         }
22851         
22852         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22853             node.parentNode.removeChild(node);
22854             return;
22855         }
22856         
22857         // remove - but keep children..
22858         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22859             while (node.childNodes.length) {
22860                 var cn = node.childNodes[0];
22861                 node.removeChild(cn);
22862                 node.parentNode.insertBefore(cn, node);
22863             }
22864             node.parentNode.removeChild(node);
22865             this.iterateChildren(node, this.cleanWord);
22866             return;
22867         }
22868         // clean styles
22869         if (node.className.length) {
22870             
22871             var cn = node.className.split(/\W+/);
22872             var cna = [];
22873             Roo.each(cn, function(cls) {
22874                 if (cls.match(/Mso[a-zA-Z]+/)) {
22875                     return;
22876                 }
22877                 cna.push(cls);
22878             });
22879             node.className = cna.length ? cna.join(' ') : '';
22880             if (!cna.length) {
22881                 node.removeAttribute("class");
22882             }
22883         }
22884         
22885         if (node.hasAttribute("lang")) {
22886             node.removeAttribute("lang");
22887         }
22888         
22889         if (node.hasAttribute("style")) {
22890             
22891             var styles = node.getAttribute("style").split(";");
22892             var nstyle = [];
22893             Roo.each(styles, function(s) {
22894                 if (!s.match(/:/)) {
22895                     return;
22896                 }
22897                 var kv = s.split(":");
22898                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22899                     return;
22900                 }
22901                 // what ever is left... we allow.
22902                 nstyle.push(s);
22903             });
22904             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22905             if (!nstyle.length) {
22906                 node.removeAttribute('style');
22907             }
22908         }
22909         this.iterateChildren(node, this.cleanWord);
22910         
22911         
22912         
22913     },
22914     /**
22915      * iterateChildren of a Node, calling fn each time, using this as the scole..
22916      * @param {DomNode} node node to iterate children of.
22917      * @param {Function} fn method of this class to call on each item.
22918      */
22919     iterateChildren : function(node, fn)
22920     {
22921         if (!node.childNodes.length) {
22922                 return;
22923         }
22924         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22925            fn.call(this, node.childNodes[i])
22926         }
22927     },
22928     
22929     
22930     /**
22931      * cleanTableWidths.
22932      *
22933      * Quite often pasting from word etc.. results in tables with column and widths.
22934      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22935      *
22936      */
22937     cleanTableWidths : function(node)
22938     {
22939          
22940          
22941         if (!node) {
22942             this.cleanTableWidths(this.doc.body);
22943             return;
22944         }
22945         
22946         // ignore list...
22947         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22948             return; 
22949         }
22950         Roo.log(node.tagName);
22951         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22952             this.iterateChildren(node, this.cleanTableWidths);
22953             return;
22954         }
22955         if (node.hasAttribute('width')) {
22956             node.removeAttribute('width');
22957         }
22958         
22959          
22960         if (node.hasAttribute("style")) {
22961             // pretty basic...
22962             
22963             var styles = node.getAttribute("style").split(";");
22964             var nstyle = [];
22965             Roo.each(styles, function(s) {
22966                 if (!s.match(/:/)) {
22967                     return;
22968                 }
22969                 var kv = s.split(":");
22970                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22971                     return;
22972                 }
22973                 // what ever is left... we allow.
22974                 nstyle.push(s);
22975             });
22976             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22977             if (!nstyle.length) {
22978                 node.removeAttribute('style');
22979             }
22980         }
22981         
22982         this.iterateChildren(node, this.cleanTableWidths);
22983         
22984         
22985     },
22986     
22987     
22988     
22989     
22990     domToHTML : function(currentElement, depth, nopadtext) {
22991         
22992         depth = depth || 0;
22993         nopadtext = nopadtext || false;
22994     
22995         if (!currentElement) {
22996             return this.domToHTML(this.doc.body);
22997         }
22998         
22999         //Roo.log(currentElement);
23000         var j;
23001         var allText = false;
23002         var nodeName = currentElement.nodeName;
23003         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23004         
23005         if  (nodeName == '#text') {
23006             
23007             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23008         }
23009         
23010         
23011         var ret = '';
23012         if (nodeName != 'BODY') {
23013              
23014             var i = 0;
23015             // Prints the node tagName, such as <A>, <IMG>, etc
23016             if (tagName) {
23017                 var attr = [];
23018                 for(i = 0; i < currentElement.attributes.length;i++) {
23019                     // quoting?
23020                     var aname = currentElement.attributes.item(i).name;
23021                     if (!currentElement.attributes.item(i).value.length) {
23022                         continue;
23023                     }
23024                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23025                 }
23026                 
23027                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23028             } 
23029             else {
23030                 
23031                 // eack
23032             }
23033         } else {
23034             tagName = false;
23035         }
23036         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23037             return ret;
23038         }
23039         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23040             nopadtext = true;
23041         }
23042         
23043         
23044         // Traverse the tree
23045         i = 0;
23046         var currentElementChild = currentElement.childNodes.item(i);
23047         var allText = true;
23048         var innerHTML  = '';
23049         lastnode = '';
23050         while (currentElementChild) {
23051             // Formatting code (indent the tree so it looks nice on the screen)
23052             var nopad = nopadtext;
23053             if (lastnode == 'SPAN') {
23054                 nopad  = true;
23055             }
23056             // text
23057             if  (currentElementChild.nodeName == '#text') {
23058                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23059                 toadd = nopadtext ? toadd : toadd.trim();
23060                 if (!nopad && toadd.length > 80) {
23061                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23062                 }
23063                 innerHTML  += toadd;
23064                 
23065                 i++;
23066                 currentElementChild = currentElement.childNodes.item(i);
23067                 lastNode = '';
23068                 continue;
23069             }
23070             allText = false;
23071             
23072             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23073                 
23074             // Recursively traverse the tree structure of the child node
23075             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23076             lastnode = currentElementChild.nodeName;
23077             i++;
23078             currentElementChild=currentElement.childNodes.item(i);
23079         }
23080         
23081         ret += innerHTML;
23082         
23083         if (!allText) {
23084                 // The remaining code is mostly for formatting the tree
23085             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23086         }
23087         
23088         
23089         if (tagName) {
23090             ret+= "</"+tagName+">";
23091         }
23092         return ret;
23093         
23094     },
23095         
23096     applyBlacklists : function()
23097     {
23098         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23099         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23100         
23101         this.white = [];
23102         this.black = [];
23103         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23104             if (b.indexOf(tag) > -1) {
23105                 return;
23106             }
23107             this.white.push(tag);
23108             
23109         }, this);
23110         
23111         Roo.each(w, function(tag) {
23112             if (b.indexOf(tag) > -1) {
23113                 return;
23114             }
23115             if (this.white.indexOf(tag) > -1) {
23116                 return;
23117             }
23118             this.white.push(tag);
23119             
23120         }, this);
23121         
23122         
23123         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23124             if (w.indexOf(tag) > -1) {
23125                 return;
23126             }
23127             this.black.push(tag);
23128             
23129         }, this);
23130         
23131         Roo.each(b, function(tag) {
23132             if (w.indexOf(tag) > -1) {
23133                 return;
23134             }
23135             if (this.black.indexOf(tag) > -1) {
23136                 return;
23137             }
23138             this.black.push(tag);
23139             
23140         }, this);
23141         
23142         
23143         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23144         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23145         
23146         this.cwhite = [];
23147         this.cblack = [];
23148         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23149             if (b.indexOf(tag) > -1) {
23150                 return;
23151             }
23152             this.cwhite.push(tag);
23153             
23154         }, this);
23155         
23156         Roo.each(w, function(tag) {
23157             if (b.indexOf(tag) > -1) {
23158                 return;
23159             }
23160             if (this.cwhite.indexOf(tag) > -1) {
23161                 return;
23162             }
23163             this.cwhite.push(tag);
23164             
23165         }, this);
23166         
23167         
23168         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23169             if (w.indexOf(tag) > -1) {
23170                 return;
23171             }
23172             this.cblack.push(tag);
23173             
23174         }, this);
23175         
23176         Roo.each(b, function(tag) {
23177             if (w.indexOf(tag) > -1) {
23178                 return;
23179             }
23180             if (this.cblack.indexOf(tag) > -1) {
23181                 return;
23182             }
23183             this.cblack.push(tag);
23184             
23185         }, this);
23186     },
23187     
23188     setStylesheets : function(stylesheets)
23189     {
23190         if(typeof(stylesheets) == 'string'){
23191             Roo.get(this.iframe.contentDocument.head).createChild({
23192                 tag : 'link',
23193                 rel : 'stylesheet',
23194                 type : 'text/css',
23195                 href : stylesheets
23196             });
23197             
23198             return;
23199         }
23200         var _this = this;
23201      
23202         Roo.each(stylesheets, function(s) {
23203             if(!s.length){
23204                 return;
23205             }
23206             
23207             Roo.get(_this.iframe.contentDocument.head).createChild({
23208                 tag : 'link',
23209                 rel : 'stylesheet',
23210                 type : 'text/css',
23211                 href : s
23212             });
23213         });
23214
23215         
23216     },
23217     
23218     removeStylesheets : function()
23219     {
23220         var _this = this;
23221         
23222         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23223             s.remove();
23224         });
23225     },
23226     
23227     setStyle : function(style)
23228     {
23229         Roo.get(this.iframe.contentDocument.head).createChild({
23230             tag : 'style',
23231             type : 'text/css',
23232             html : style
23233         });
23234
23235         return;
23236     }
23237     
23238     // hide stuff that is not compatible
23239     /**
23240      * @event blur
23241      * @hide
23242      */
23243     /**
23244      * @event change
23245      * @hide
23246      */
23247     /**
23248      * @event focus
23249      * @hide
23250      */
23251     /**
23252      * @event specialkey
23253      * @hide
23254      */
23255     /**
23256      * @cfg {String} fieldClass @hide
23257      */
23258     /**
23259      * @cfg {String} focusClass @hide
23260      */
23261     /**
23262      * @cfg {String} autoCreate @hide
23263      */
23264     /**
23265      * @cfg {String} inputType @hide
23266      */
23267     /**
23268      * @cfg {String} invalidClass @hide
23269      */
23270     /**
23271      * @cfg {String} invalidText @hide
23272      */
23273     /**
23274      * @cfg {String} msgFx @hide
23275      */
23276     /**
23277      * @cfg {String} validateOnBlur @hide
23278      */
23279 });
23280
23281 Roo.HtmlEditorCore.white = [
23282         'area', 'br', 'img', 'input', 'hr', 'wbr',
23283         
23284        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23285        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23286        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23287        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23288        'table',   'ul',         'xmp', 
23289        
23290        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23291       'thead',   'tr', 
23292      
23293       'dir', 'menu', 'ol', 'ul', 'dl',
23294        
23295       'embed',  'object'
23296 ];
23297
23298
23299 Roo.HtmlEditorCore.black = [
23300     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23301         'applet', // 
23302         'base',   'basefont', 'bgsound', 'blink',  'body', 
23303         'frame',  'frameset', 'head',    'html',   'ilayer', 
23304         'iframe', 'layer',  'link',     'meta',    'object',   
23305         'script', 'style' ,'title',  'xml' // clean later..
23306 ];
23307 Roo.HtmlEditorCore.clean = [
23308     'script', 'style', 'title', 'xml'
23309 ];
23310 Roo.HtmlEditorCore.remove = [
23311     'font'
23312 ];
23313 // attributes..
23314
23315 Roo.HtmlEditorCore.ablack = [
23316     'on'
23317 ];
23318     
23319 Roo.HtmlEditorCore.aclean = [ 
23320     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23321 ];
23322
23323 // protocols..
23324 Roo.HtmlEditorCore.pwhite= [
23325         'http',  'https',  'mailto'
23326 ];
23327
23328 // white listed style attributes.
23329 Roo.HtmlEditorCore.cwhite= [
23330       //  'text-align', /// default is to allow most things..
23331       
23332          
23333 //        'font-size'//??
23334 ];
23335
23336 // black listed style attributes.
23337 Roo.HtmlEditorCore.cblack= [
23338       //  'font-size' -- this can be set by the project 
23339 ];
23340
23341
23342 Roo.HtmlEditorCore.swapCodes   =[ 
23343     [    8211, "--" ], 
23344     [    8212, "--" ], 
23345     [    8216,  "'" ],  
23346     [    8217, "'" ],  
23347     [    8220, '"' ],  
23348     [    8221, '"' ],  
23349     [    8226, "*" ],  
23350     [    8230, "..." ]
23351 ]; 
23352
23353     /*
23354  * - LGPL
23355  *
23356  * HtmlEditor
23357  * 
23358  */
23359
23360 /**
23361  * @class Roo.bootstrap.HtmlEditor
23362  * @extends Roo.bootstrap.TextArea
23363  * Bootstrap HtmlEditor class
23364
23365  * @constructor
23366  * Create a new HtmlEditor
23367  * @param {Object} config The config object
23368  */
23369
23370 Roo.bootstrap.HtmlEditor = function(config){
23371     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23372     if (!this.toolbars) {
23373         this.toolbars = [];
23374     }
23375     
23376     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23377     this.addEvents({
23378             /**
23379              * @event initialize
23380              * Fires when the editor is fully initialized (including the iframe)
23381              * @param {HtmlEditor} this
23382              */
23383             initialize: true,
23384             /**
23385              * @event activate
23386              * Fires when the editor is first receives the focus. Any insertion must wait
23387              * until after this event.
23388              * @param {HtmlEditor} this
23389              */
23390             activate: true,
23391              /**
23392              * @event beforesync
23393              * Fires before the textarea is updated with content from the editor iframe. Return false
23394              * to cancel the sync.
23395              * @param {HtmlEditor} this
23396              * @param {String} html
23397              */
23398             beforesync: true,
23399              /**
23400              * @event beforepush
23401              * Fires before the iframe editor is updated with content from the textarea. Return false
23402              * to cancel the push.
23403              * @param {HtmlEditor} this
23404              * @param {String} html
23405              */
23406             beforepush: true,
23407              /**
23408              * @event sync
23409              * Fires when the textarea is updated with content from the editor iframe.
23410              * @param {HtmlEditor} this
23411              * @param {String} html
23412              */
23413             sync: true,
23414              /**
23415              * @event push
23416              * Fires when the iframe editor is updated with content from the textarea.
23417              * @param {HtmlEditor} this
23418              * @param {String} html
23419              */
23420             push: true,
23421              /**
23422              * @event editmodechange
23423              * Fires when the editor switches edit modes
23424              * @param {HtmlEditor} this
23425              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23426              */
23427             editmodechange: true,
23428             /**
23429              * @event editorevent
23430              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23431              * @param {HtmlEditor} this
23432              */
23433             editorevent: true,
23434             /**
23435              * @event firstfocus
23436              * Fires when on first focus - needed by toolbars..
23437              * @param {HtmlEditor} this
23438              */
23439             firstfocus: true,
23440             /**
23441              * @event autosave
23442              * Auto save the htmlEditor value as a file into Events
23443              * @param {HtmlEditor} this
23444              */
23445             autosave: true,
23446             /**
23447              * @event savedpreview
23448              * preview the saved version of htmlEditor
23449              * @param {HtmlEditor} this
23450              */
23451             savedpreview: true
23452         });
23453 };
23454
23455
23456 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23457     
23458     
23459       /**
23460      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23461      */
23462     toolbars : false,
23463     
23464      /**
23465     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23466     */
23467     btns : [],
23468    
23469      /**
23470      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23471      *                        Roo.resizable.
23472      */
23473     resizable : false,
23474      /**
23475      * @cfg {Number} height (in pixels)
23476      */   
23477     height: 300,
23478    /**
23479      * @cfg {Number} width (in pixels)
23480      */   
23481     width: false,
23482     
23483     /**
23484      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23485      * 
23486      */
23487     stylesheets: false,
23488     
23489     // id of frame..
23490     frameId: false,
23491     
23492     // private properties
23493     validationEvent : false,
23494     deferHeight: true,
23495     initialized : false,
23496     activated : false,
23497     
23498     onFocus : Roo.emptyFn,
23499     iframePad:3,
23500     hideMode:'offsets',
23501     
23502     tbContainer : false,
23503     
23504     bodyCls : '',
23505     
23506     toolbarContainer :function() {
23507         return this.wrap.select('.x-html-editor-tb',true).first();
23508     },
23509
23510     /**
23511      * Protected method that will not generally be called directly. It
23512      * is called when the editor creates its toolbar. Override this method if you need to
23513      * add custom toolbar buttons.
23514      * @param {HtmlEditor} editor
23515      */
23516     createToolbar : function(){
23517         Roo.log('renewing');
23518         Roo.log("create toolbars");
23519         
23520         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23521         this.toolbars[0].render(this.toolbarContainer());
23522         
23523         return;
23524         
23525 //        if (!editor.toolbars || !editor.toolbars.length) {
23526 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23527 //        }
23528 //        
23529 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23530 //            editor.toolbars[i] = Roo.factory(
23531 //                    typeof(editor.toolbars[i]) == 'string' ?
23532 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23533 //                Roo.bootstrap.HtmlEditor);
23534 //            editor.toolbars[i].init(editor);
23535 //        }
23536     },
23537
23538      
23539     // private
23540     onRender : function(ct, position)
23541     {
23542        // Roo.log("Call onRender: " + this.xtype);
23543         var _t = this;
23544         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23545       
23546         this.wrap = this.inputEl().wrap({
23547             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23548         });
23549         
23550         this.editorcore.onRender(ct, position);
23551          
23552         if (this.resizable) {
23553             this.resizeEl = new Roo.Resizable(this.wrap, {
23554                 pinned : true,
23555                 wrap: true,
23556                 dynamic : true,
23557                 minHeight : this.height,
23558                 height: this.height,
23559                 handles : this.resizable,
23560                 width: this.width,
23561                 listeners : {
23562                     resize : function(r, w, h) {
23563                         _t.onResize(w,h); // -something
23564                     }
23565                 }
23566             });
23567             
23568         }
23569         this.createToolbar(this);
23570        
23571         
23572         if(!this.width && this.resizable){
23573             this.setSize(this.wrap.getSize());
23574         }
23575         if (this.resizeEl) {
23576             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23577             // should trigger onReize..
23578         }
23579         
23580     },
23581
23582     // private
23583     onResize : function(w, h)
23584     {
23585         Roo.log('resize: ' +w + ',' + h );
23586         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23587         var ew = false;
23588         var eh = false;
23589         
23590         if(this.inputEl() ){
23591             if(typeof w == 'number'){
23592                 var aw = w - this.wrap.getFrameWidth('lr');
23593                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23594                 ew = aw;
23595             }
23596             if(typeof h == 'number'){
23597                  var tbh = -11;  // fixme it needs to tool bar size!
23598                 for (var i =0; i < this.toolbars.length;i++) {
23599                     // fixme - ask toolbars for heights?
23600                     tbh += this.toolbars[i].el.getHeight();
23601                     //if (this.toolbars[i].footer) {
23602                     //    tbh += this.toolbars[i].footer.el.getHeight();
23603                     //}
23604                 }
23605               
23606                 
23607                 
23608                 
23609                 
23610                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23611                 ah -= 5; // knock a few pixes off for look..
23612                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23613                 var eh = ah;
23614             }
23615         }
23616         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23617         this.editorcore.onResize(ew,eh);
23618         
23619     },
23620
23621     /**
23622      * Toggles the editor between standard and source edit mode.
23623      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23624      */
23625     toggleSourceEdit : function(sourceEditMode)
23626     {
23627         this.editorcore.toggleSourceEdit(sourceEditMode);
23628         
23629         if(this.editorcore.sourceEditMode){
23630             Roo.log('editor - showing textarea');
23631             
23632 //            Roo.log('in');
23633 //            Roo.log(this.syncValue());
23634             this.syncValue();
23635             this.inputEl().removeClass(['hide', 'x-hidden']);
23636             this.inputEl().dom.removeAttribute('tabIndex');
23637             this.inputEl().focus();
23638         }else{
23639             Roo.log('editor - hiding textarea');
23640 //            Roo.log('out')
23641 //            Roo.log(this.pushValue()); 
23642             this.pushValue();
23643             
23644             this.inputEl().addClass(['hide', 'x-hidden']);
23645             this.inputEl().dom.setAttribute('tabIndex', -1);
23646             //this.deferFocus();
23647         }
23648          
23649         if(this.resizable){
23650             this.setSize(this.wrap.getSize());
23651         }
23652         
23653         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23654     },
23655  
23656     // private (for BoxComponent)
23657     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23658
23659     // private (for BoxComponent)
23660     getResizeEl : function(){
23661         return this.wrap;
23662     },
23663
23664     // private (for BoxComponent)
23665     getPositionEl : function(){
23666         return this.wrap;
23667     },
23668
23669     // private
23670     initEvents : function(){
23671         this.originalValue = this.getValue();
23672     },
23673
23674 //    /**
23675 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23676 //     * @method
23677 //     */
23678 //    markInvalid : Roo.emptyFn,
23679 //    /**
23680 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23681 //     * @method
23682 //     */
23683 //    clearInvalid : Roo.emptyFn,
23684
23685     setValue : function(v){
23686         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23687         this.editorcore.pushValue();
23688     },
23689
23690      
23691     // private
23692     deferFocus : function(){
23693         this.focus.defer(10, this);
23694     },
23695
23696     // doc'ed in Field
23697     focus : function(){
23698         this.editorcore.focus();
23699         
23700     },
23701       
23702
23703     // private
23704     onDestroy : function(){
23705         
23706         
23707         
23708         if(this.rendered){
23709             
23710             for (var i =0; i < this.toolbars.length;i++) {
23711                 // fixme - ask toolbars for heights?
23712                 this.toolbars[i].onDestroy();
23713             }
23714             
23715             this.wrap.dom.innerHTML = '';
23716             this.wrap.remove();
23717         }
23718     },
23719
23720     // private
23721     onFirstFocus : function(){
23722         //Roo.log("onFirstFocus");
23723         this.editorcore.onFirstFocus();
23724          for (var i =0; i < this.toolbars.length;i++) {
23725             this.toolbars[i].onFirstFocus();
23726         }
23727         
23728     },
23729     
23730     // private
23731     syncValue : function()
23732     {   
23733         this.editorcore.syncValue();
23734     },
23735     
23736     pushValue : function()
23737     {   
23738         this.editorcore.pushValue();
23739     }
23740      
23741     
23742     // hide stuff that is not compatible
23743     /**
23744      * @event blur
23745      * @hide
23746      */
23747     /**
23748      * @event change
23749      * @hide
23750      */
23751     /**
23752      * @event focus
23753      * @hide
23754      */
23755     /**
23756      * @event specialkey
23757      * @hide
23758      */
23759     /**
23760      * @cfg {String} fieldClass @hide
23761      */
23762     /**
23763      * @cfg {String} focusClass @hide
23764      */
23765     /**
23766      * @cfg {String} autoCreate @hide
23767      */
23768     /**
23769      * @cfg {String} inputType @hide
23770      */
23771     /**
23772      * @cfg {String} invalidClass @hide
23773      */
23774     /**
23775      * @cfg {String} invalidText @hide
23776      */
23777     /**
23778      * @cfg {String} msgFx @hide
23779      */
23780     /**
23781      * @cfg {String} validateOnBlur @hide
23782      */
23783 });
23784  
23785     
23786    
23787    
23788    
23789       
23790 Roo.namespace('Roo.bootstrap.htmleditor');
23791 /**
23792  * @class Roo.bootstrap.HtmlEditorToolbar1
23793  * Basic Toolbar
23794  * 
23795  * Usage:
23796  *
23797  new Roo.bootstrap.HtmlEditor({
23798     ....
23799     toolbars : [
23800         new Roo.bootstrap.HtmlEditorToolbar1({
23801             disable : { fonts: 1 , format: 1, ..., ... , ...],
23802             btns : [ .... ]
23803         })
23804     }
23805      
23806  * 
23807  * @cfg {Object} disable List of elements to disable..
23808  * @cfg {Array} btns List of additional buttons.
23809  * 
23810  * 
23811  * NEEDS Extra CSS? 
23812  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23813  */
23814  
23815 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23816 {
23817     
23818     Roo.apply(this, config);
23819     
23820     // default disabled, based on 'good practice'..
23821     this.disable = this.disable || {};
23822     Roo.applyIf(this.disable, {
23823         fontSize : true,
23824         colors : true,
23825         specialElements : true
23826     });
23827     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23828     
23829     this.editor = config.editor;
23830     this.editorcore = config.editor.editorcore;
23831     
23832     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23833     
23834     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23835     // dont call parent... till later.
23836 }
23837 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23838      
23839     bar : true,
23840     
23841     editor : false,
23842     editorcore : false,
23843     
23844     
23845     formats : [
23846         "p" ,  
23847         "h1","h2","h3","h4","h5","h6", 
23848         "pre", "code", 
23849         "abbr", "acronym", "address", "cite", "samp", "var",
23850         'div','span'
23851     ],
23852     
23853     onRender : function(ct, position)
23854     {
23855        // Roo.log("Call onRender: " + this.xtype);
23856         
23857        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23858        Roo.log(this.el);
23859        this.el.dom.style.marginBottom = '0';
23860        var _this = this;
23861        var editorcore = this.editorcore;
23862        var editor= this.editor;
23863        
23864        var children = [];
23865        var btn = function(id,cmd , toggle, handler, html){
23866        
23867             var  event = toggle ? 'toggle' : 'click';
23868        
23869             var a = {
23870                 size : 'sm',
23871                 xtype: 'Button',
23872                 xns: Roo.bootstrap,
23873                 glyphicon : id,
23874                 cmd : id || cmd,
23875                 enableToggle:toggle !== false,
23876                 html : html || '',
23877                 pressed : toggle ? false : null,
23878                 listeners : {}
23879             };
23880             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23881                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23882             };
23883             children.push(a);
23884             return a;
23885        }
23886        
23887     //    var cb_box = function...
23888         
23889         var style = {
23890                 xtype: 'Button',
23891                 size : 'sm',
23892                 xns: Roo.bootstrap,
23893                 glyphicon : 'font',
23894                 //html : 'submit'
23895                 menu : {
23896                     xtype: 'Menu',
23897                     xns: Roo.bootstrap,
23898                     items:  []
23899                 }
23900         };
23901         Roo.each(this.formats, function(f) {
23902             style.menu.items.push({
23903                 xtype :'MenuItem',
23904                 xns: Roo.bootstrap,
23905                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23906                 tagname : f,
23907                 listeners : {
23908                     click : function()
23909                     {
23910                         editorcore.insertTag(this.tagname);
23911                         editor.focus();
23912                     }
23913                 }
23914                 
23915             });
23916         });
23917         children.push(style);   
23918         
23919         btn('bold',false,true);
23920         btn('italic',false,true);
23921         btn('align-left', 'justifyleft',true);
23922         btn('align-center', 'justifycenter',true);
23923         btn('align-right' , 'justifyright',true);
23924         btn('link', false, false, function(btn) {
23925             //Roo.log("create link?");
23926             var url = prompt(this.createLinkText, this.defaultLinkValue);
23927             if(url && url != 'http:/'+'/'){
23928                 this.editorcore.relayCmd('createlink', url);
23929             }
23930         }),
23931         btn('list','insertunorderedlist',true);
23932         btn('pencil', false,true, function(btn){
23933                 Roo.log(this);
23934                 this.toggleSourceEdit(btn.pressed);
23935         });
23936         
23937         if (this.editor.btns.length > 0) {
23938             for (var i = 0; i<this.editor.btns.length; i++) {
23939                 children.push(this.editor.btns[i]);
23940             }
23941         }
23942         
23943         /*
23944         var cog = {
23945                 xtype: 'Button',
23946                 size : 'sm',
23947                 xns: Roo.bootstrap,
23948                 glyphicon : 'cog',
23949                 //html : 'submit'
23950                 menu : {
23951                     xtype: 'Menu',
23952                     xns: Roo.bootstrap,
23953                     items:  []
23954                 }
23955         };
23956         
23957         cog.menu.items.push({
23958             xtype :'MenuItem',
23959             xns: Roo.bootstrap,
23960             html : Clean styles,
23961             tagname : f,
23962             listeners : {
23963                 click : function()
23964                 {
23965                     editorcore.insertTag(this.tagname);
23966                     editor.focus();
23967                 }
23968             }
23969             
23970         });
23971        */
23972         
23973          
23974        this.xtype = 'NavSimplebar';
23975         
23976         for(var i=0;i< children.length;i++) {
23977             
23978             this.buttons.add(this.addxtypeChild(children[i]));
23979             
23980         }
23981         
23982         editor.on('editorevent', this.updateToolbar, this);
23983     },
23984     onBtnClick : function(id)
23985     {
23986        this.editorcore.relayCmd(id);
23987        this.editorcore.focus();
23988     },
23989     
23990     /**
23991      * Protected method that will not generally be called directly. It triggers
23992      * a toolbar update by reading the markup state of the current selection in the editor.
23993      */
23994     updateToolbar: function(){
23995
23996         if(!this.editorcore.activated){
23997             this.editor.onFirstFocus(); // is this neeed?
23998             return;
23999         }
24000
24001         var btns = this.buttons; 
24002         var doc = this.editorcore.doc;
24003         btns.get('bold').setActive(doc.queryCommandState('bold'));
24004         btns.get('italic').setActive(doc.queryCommandState('italic'));
24005         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24006         
24007         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24008         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24009         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24010         
24011         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24012         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24013          /*
24014         
24015         var ans = this.editorcore.getAllAncestors();
24016         if (this.formatCombo) {
24017             
24018             
24019             var store = this.formatCombo.store;
24020             this.formatCombo.setValue("");
24021             for (var i =0; i < ans.length;i++) {
24022                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24023                     // select it..
24024                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24025                     break;
24026                 }
24027             }
24028         }
24029         
24030         
24031         
24032         // hides menus... - so this cant be on a menu...
24033         Roo.bootstrap.MenuMgr.hideAll();
24034         */
24035         Roo.bootstrap.MenuMgr.hideAll();
24036         //this.editorsyncValue();
24037     },
24038     onFirstFocus: function() {
24039         this.buttons.each(function(item){
24040            item.enable();
24041         });
24042     },
24043     toggleSourceEdit : function(sourceEditMode){
24044         
24045           
24046         if(sourceEditMode){
24047             Roo.log("disabling buttons");
24048            this.buttons.each( function(item){
24049                 if(item.cmd != 'pencil'){
24050                     item.disable();
24051                 }
24052             });
24053           
24054         }else{
24055             Roo.log("enabling buttons");
24056             if(this.editorcore.initialized){
24057                 this.buttons.each( function(item){
24058                     item.enable();
24059                 });
24060             }
24061             
24062         }
24063         Roo.log("calling toggole on editor");
24064         // tell the editor that it's been pressed..
24065         this.editor.toggleSourceEdit(sourceEditMode);
24066        
24067     }
24068 });
24069
24070
24071
24072
24073
24074 /**
24075  * @class Roo.bootstrap.Table.AbstractSelectionModel
24076  * @extends Roo.util.Observable
24077  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24078  * implemented by descendant classes.  This class should not be directly instantiated.
24079  * @constructor
24080  */
24081 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24082     this.locked = false;
24083     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24084 };
24085
24086
24087 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24088     /** @ignore Called by the grid automatically. Do not call directly. */
24089     init : function(grid){
24090         this.grid = grid;
24091         this.initEvents();
24092     },
24093
24094     /**
24095      * Locks the selections.
24096      */
24097     lock : function(){
24098         this.locked = true;
24099     },
24100
24101     /**
24102      * Unlocks the selections.
24103      */
24104     unlock : function(){
24105         this.locked = false;
24106     },
24107
24108     /**
24109      * Returns true if the selections are locked.
24110      * @return {Boolean}
24111      */
24112     isLocked : function(){
24113         return this.locked;
24114     }
24115 });
24116 /**
24117  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24118  * @class Roo.bootstrap.Table.RowSelectionModel
24119  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24120  * It supports multiple selections and keyboard selection/navigation. 
24121  * @constructor
24122  * @param {Object} config
24123  */
24124
24125 Roo.bootstrap.Table.RowSelectionModel = function(config){
24126     Roo.apply(this, config);
24127     this.selections = new Roo.util.MixedCollection(false, function(o){
24128         return o.id;
24129     });
24130
24131     this.last = false;
24132     this.lastActive = false;
24133
24134     this.addEvents({
24135         /**
24136              * @event selectionchange
24137              * Fires when the selection changes
24138              * @param {SelectionModel} this
24139              */
24140             "selectionchange" : true,
24141         /**
24142              * @event afterselectionchange
24143              * Fires after the selection changes (eg. by key press or clicking)
24144              * @param {SelectionModel} this
24145              */
24146             "afterselectionchange" : true,
24147         /**
24148              * @event beforerowselect
24149              * Fires when a row is selected being selected, return false to cancel.
24150              * @param {SelectionModel} this
24151              * @param {Number} rowIndex The selected index
24152              * @param {Boolean} keepExisting False if other selections will be cleared
24153              */
24154             "beforerowselect" : true,
24155         /**
24156              * @event rowselect
24157              * Fires when a row is selected.
24158              * @param {SelectionModel} this
24159              * @param {Number} rowIndex The selected index
24160              * @param {Roo.data.Record} r The record
24161              */
24162             "rowselect" : true,
24163         /**
24164              * @event rowdeselect
24165              * Fires when a row is deselected.
24166              * @param {SelectionModel} this
24167              * @param {Number} rowIndex The selected index
24168              */
24169         "rowdeselect" : true
24170     });
24171     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24172     this.locked = false;
24173  };
24174
24175 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24176     /**
24177      * @cfg {Boolean} singleSelect
24178      * True to allow selection of only one row at a time (defaults to false)
24179      */
24180     singleSelect : false,
24181
24182     // private
24183     initEvents : function()
24184     {
24185
24186         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24187         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24188         //}else{ // allow click to work like normal
24189          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24190         //}
24191         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24192         this.grid.on("rowclick", this.handleMouseDown, this);
24193         
24194         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24195             "up" : function(e){
24196                 if(!e.shiftKey){
24197                     this.selectPrevious(e.shiftKey);
24198                 }else if(this.last !== false && this.lastActive !== false){
24199                     var last = this.last;
24200                     this.selectRange(this.last,  this.lastActive-1);
24201                     this.grid.getView().focusRow(this.lastActive);
24202                     if(last !== false){
24203                         this.last = last;
24204                     }
24205                 }else{
24206                     this.selectFirstRow();
24207                 }
24208                 this.fireEvent("afterselectionchange", this);
24209             },
24210             "down" : function(e){
24211                 if(!e.shiftKey){
24212                     this.selectNext(e.shiftKey);
24213                 }else if(this.last !== false && this.lastActive !== false){
24214                     var last = this.last;
24215                     this.selectRange(this.last,  this.lastActive+1);
24216                     this.grid.getView().focusRow(this.lastActive);
24217                     if(last !== false){
24218                         this.last = last;
24219                     }
24220                 }else{
24221                     this.selectFirstRow();
24222                 }
24223                 this.fireEvent("afterselectionchange", this);
24224             },
24225             scope: this
24226         });
24227         this.grid.store.on('load', function(){
24228             this.selections.clear();
24229         },this);
24230         /*
24231         var view = this.grid.view;
24232         view.on("refresh", this.onRefresh, this);
24233         view.on("rowupdated", this.onRowUpdated, this);
24234         view.on("rowremoved", this.onRemove, this);
24235         */
24236     },
24237
24238     // private
24239     onRefresh : function()
24240     {
24241         var ds = this.grid.store, i, v = this.grid.view;
24242         var s = this.selections;
24243         s.each(function(r){
24244             if((i = ds.indexOfId(r.id)) != -1){
24245                 v.onRowSelect(i);
24246             }else{
24247                 s.remove(r);
24248             }
24249         });
24250     },
24251
24252     // private
24253     onRemove : function(v, index, r){
24254         this.selections.remove(r);
24255     },
24256
24257     // private
24258     onRowUpdated : function(v, index, r){
24259         if(this.isSelected(r)){
24260             v.onRowSelect(index);
24261         }
24262     },
24263
24264     /**
24265      * Select records.
24266      * @param {Array} records The records to select
24267      * @param {Boolean} keepExisting (optional) True to keep existing selections
24268      */
24269     selectRecords : function(records, keepExisting)
24270     {
24271         if(!keepExisting){
24272             this.clearSelections();
24273         }
24274             var ds = this.grid.store;
24275         for(var i = 0, len = records.length; i < len; i++){
24276             this.selectRow(ds.indexOf(records[i]), true);
24277         }
24278     },
24279
24280     /**
24281      * Gets the number of selected rows.
24282      * @return {Number}
24283      */
24284     getCount : function(){
24285         return this.selections.length;
24286     },
24287
24288     /**
24289      * Selects the first row in the grid.
24290      */
24291     selectFirstRow : function(){
24292         this.selectRow(0);
24293     },
24294
24295     /**
24296      * Select the last row.
24297      * @param {Boolean} keepExisting (optional) True to keep existing selections
24298      */
24299     selectLastRow : function(keepExisting){
24300         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24301         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24302     },
24303
24304     /**
24305      * Selects the row immediately following the last selected row.
24306      * @param {Boolean} keepExisting (optional) True to keep existing selections
24307      */
24308     selectNext : function(keepExisting)
24309     {
24310             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24311             this.selectRow(this.last+1, keepExisting);
24312             this.grid.getView().focusRow(this.last);
24313         }
24314     },
24315
24316     /**
24317      * Selects the row that precedes the last selected row.
24318      * @param {Boolean} keepExisting (optional) True to keep existing selections
24319      */
24320     selectPrevious : function(keepExisting){
24321         if(this.last){
24322             this.selectRow(this.last-1, keepExisting);
24323             this.grid.getView().focusRow(this.last);
24324         }
24325     },
24326
24327     /**
24328      * Returns the selected records
24329      * @return {Array} Array of selected records
24330      */
24331     getSelections : function(){
24332         return [].concat(this.selections.items);
24333     },
24334
24335     /**
24336      * Returns the first selected record.
24337      * @return {Record}
24338      */
24339     getSelected : function(){
24340         return this.selections.itemAt(0);
24341     },
24342
24343
24344     /**
24345      * Clears all selections.
24346      */
24347     clearSelections : function(fast)
24348     {
24349         if(this.locked) {
24350             return;
24351         }
24352         if(fast !== true){
24353                 var ds = this.grid.store;
24354             var s = this.selections;
24355             s.each(function(r){
24356                 this.deselectRow(ds.indexOfId(r.id));
24357             }, this);
24358             s.clear();
24359         }else{
24360             this.selections.clear();
24361         }
24362         this.last = false;
24363     },
24364
24365
24366     /**
24367      * Selects all rows.
24368      */
24369     selectAll : function(){
24370         if(this.locked) {
24371             return;
24372         }
24373         this.selections.clear();
24374         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24375             this.selectRow(i, true);
24376         }
24377     },
24378
24379     /**
24380      * Returns True if there is a selection.
24381      * @return {Boolean}
24382      */
24383     hasSelection : function(){
24384         return this.selections.length > 0;
24385     },
24386
24387     /**
24388      * Returns True if the specified row is selected.
24389      * @param {Number/Record} record The record or index of the record to check
24390      * @return {Boolean}
24391      */
24392     isSelected : function(index){
24393             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24394         return (r && this.selections.key(r.id) ? true : false);
24395     },
24396
24397     /**
24398      * Returns True if the specified record id is selected.
24399      * @param {String} id The id of record to check
24400      * @return {Boolean}
24401      */
24402     isIdSelected : function(id){
24403         return (this.selections.key(id) ? true : false);
24404     },
24405
24406
24407     // private
24408     handleMouseDBClick : function(e, t){
24409         
24410     },
24411     // private
24412     handleMouseDown : function(e, t)
24413     {
24414             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24415         if(this.isLocked() || rowIndex < 0 ){
24416             return;
24417         };
24418         if(e.shiftKey && this.last !== false){
24419             var last = this.last;
24420             this.selectRange(last, rowIndex, e.ctrlKey);
24421             this.last = last; // reset the last
24422             t.focus();
24423     
24424         }else{
24425             var isSelected = this.isSelected(rowIndex);
24426             //Roo.log("select row:" + rowIndex);
24427             if(isSelected){
24428                 this.deselectRow(rowIndex);
24429             } else {
24430                         this.selectRow(rowIndex, true);
24431             }
24432     
24433             /*
24434                 if(e.button !== 0 && isSelected){
24435                 alert('rowIndex 2: ' + rowIndex);
24436                     view.focusRow(rowIndex);
24437                 }else if(e.ctrlKey && isSelected){
24438                     this.deselectRow(rowIndex);
24439                 }else if(!isSelected){
24440                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24441                     view.focusRow(rowIndex);
24442                 }
24443             */
24444         }
24445         this.fireEvent("afterselectionchange", this);
24446     },
24447     // private
24448     handleDragableRowClick :  function(grid, rowIndex, e) 
24449     {
24450         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24451             this.selectRow(rowIndex, false);
24452             grid.view.focusRow(rowIndex);
24453              this.fireEvent("afterselectionchange", this);
24454         }
24455     },
24456     
24457     /**
24458      * Selects multiple rows.
24459      * @param {Array} rows Array of the indexes of the row to select
24460      * @param {Boolean} keepExisting (optional) True to keep existing selections
24461      */
24462     selectRows : function(rows, keepExisting){
24463         if(!keepExisting){
24464             this.clearSelections();
24465         }
24466         for(var i = 0, len = rows.length; i < len; i++){
24467             this.selectRow(rows[i], true);
24468         }
24469     },
24470
24471     /**
24472      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24473      * @param {Number} startRow The index of the first row in the range
24474      * @param {Number} endRow The index of the last row in the range
24475      * @param {Boolean} keepExisting (optional) True to retain existing selections
24476      */
24477     selectRange : function(startRow, endRow, keepExisting){
24478         if(this.locked) {
24479             return;
24480         }
24481         if(!keepExisting){
24482             this.clearSelections();
24483         }
24484         if(startRow <= endRow){
24485             for(var i = startRow; i <= endRow; i++){
24486                 this.selectRow(i, true);
24487             }
24488         }else{
24489             for(var i = startRow; i >= endRow; i--){
24490                 this.selectRow(i, true);
24491             }
24492         }
24493     },
24494
24495     /**
24496      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24497      * @param {Number} startRow The index of the first row in the range
24498      * @param {Number} endRow The index of the last row in the range
24499      */
24500     deselectRange : function(startRow, endRow, preventViewNotify){
24501         if(this.locked) {
24502             return;
24503         }
24504         for(var i = startRow; i <= endRow; i++){
24505             this.deselectRow(i, preventViewNotify);
24506         }
24507     },
24508
24509     /**
24510      * Selects a row.
24511      * @param {Number} row The index of the row to select
24512      * @param {Boolean} keepExisting (optional) True to keep existing selections
24513      */
24514     selectRow : function(index, keepExisting, preventViewNotify)
24515     {
24516             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24517             return;
24518         }
24519         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24520             if(!keepExisting || this.singleSelect){
24521                 this.clearSelections();
24522             }
24523             
24524             var r = this.grid.store.getAt(index);
24525             //console.log('selectRow - record id :' + r.id);
24526             
24527             this.selections.add(r);
24528             this.last = this.lastActive = index;
24529             if(!preventViewNotify){
24530                 var proxy = new Roo.Element(
24531                                 this.grid.getRowDom(index)
24532                 );
24533                 proxy.addClass('bg-info info');
24534             }
24535             this.fireEvent("rowselect", this, index, r);
24536             this.fireEvent("selectionchange", this);
24537         }
24538     },
24539
24540     /**
24541      * Deselects a row.
24542      * @param {Number} row The index of the row to deselect
24543      */
24544     deselectRow : function(index, preventViewNotify)
24545     {
24546         if(this.locked) {
24547             return;
24548         }
24549         if(this.last == index){
24550             this.last = false;
24551         }
24552         if(this.lastActive == index){
24553             this.lastActive = false;
24554         }
24555         
24556         var r = this.grid.store.getAt(index);
24557         if (!r) {
24558             return;
24559         }
24560         
24561         this.selections.remove(r);
24562         //.console.log('deselectRow - record id :' + r.id);
24563         if(!preventViewNotify){
24564         
24565             var proxy = new Roo.Element(
24566                 this.grid.getRowDom(index)
24567             );
24568             proxy.removeClass('bg-info info');
24569         }
24570         this.fireEvent("rowdeselect", this, index);
24571         this.fireEvent("selectionchange", this);
24572     },
24573
24574     // private
24575     restoreLast : function(){
24576         if(this._last){
24577             this.last = this._last;
24578         }
24579     },
24580
24581     // private
24582     acceptsNav : function(row, col, cm){
24583         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24584     },
24585
24586     // private
24587     onEditorKey : function(field, e){
24588         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24589         if(k == e.TAB){
24590             e.stopEvent();
24591             ed.completeEdit();
24592             if(e.shiftKey){
24593                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24594             }else{
24595                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24596             }
24597         }else if(k == e.ENTER && !e.ctrlKey){
24598             e.stopEvent();
24599             ed.completeEdit();
24600             if(e.shiftKey){
24601                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24602             }else{
24603                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24604             }
24605         }else if(k == e.ESC){
24606             ed.cancelEdit();
24607         }
24608         if(newCell){
24609             g.startEditing(newCell[0], newCell[1]);
24610         }
24611     }
24612 });
24613 /*
24614  * Based on:
24615  * Ext JS Library 1.1.1
24616  * Copyright(c) 2006-2007, Ext JS, LLC.
24617  *
24618  * Originally Released Under LGPL - original licence link has changed is not relivant.
24619  *
24620  * Fork - LGPL
24621  * <script type="text/javascript">
24622  */
24623  
24624 /**
24625  * @class Roo.bootstrap.PagingToolbar
24626  * @extends Roo.bootstrap.NavSimplebar
24627  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24628  * @constructor
24629  * Create a new PagingToolbar
24630  * @param {Object} config The config object
24631  * @param {Roo.data.Store} store
24632  */
24633 Roo.bootstrap.PagingToolbar = function(config)
24634 {
24635     // old args format still supported... - xtype is prefered..
24636         // created from xtype...
24637     
24638     this.ds = config.dataSource;
24639     
24640     if (config.store && !this.ds) {
24641         this.store= Roo.factory(config.store, Roo.data);
24642         this.ds = this.store;
24643         this.ds.xmodule = this.xmodule || false;
24644     }
24645     
24646     this.toolbarItems = [];
24647     if (config.items) {
24648         this.toolbarItems = config.items;
24649     }
24650     
24651     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24652     
24653     this.cursor = 0;
24654     
24655     if (this.ds) { 
24656         this.bind(this.ds);
24657     }
24658     
24659     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24660     
24661 };
24662
24663 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24664     /**
24665      * @cfg {Roo.data.Store} dataSource
24666      * The underlying data store providing the paged data
24667      */
24668     /**
24669      * @cfg {String/HTMLElement/Element} container
24670      * container The id or element that will contain the toolbar
24671      */
24672     /**
24673      * @cfg {Boolean} displayInfo
24674      * True to display the displayMsg (defaults to false)
24675      */
24676     /**
24677      * @cfg {Number} pageSize
24678      * The number of records to display per page (defaults to 20)
24679      */
24680     pageSize: 20,
24681     /**
24682      * @cfg {String} displayMsg
24683      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24684      */
24685     displayMsg : 'Displaying {0} - {1} of {2}',
24686     /**
24687      * @cfg {String} emptyMsg
24688      * The message to display when no records are found (defaults to "No data to display")
24689      */
24690     emptyMsg : 'No data to display',
24691     /**
24692      * Customizable piece of the default paging text (defaults to "Page")
24693      * @type String
24694      */
24695     beforePageText : "Page",
24696     /**
24697      * Customizable piece of the default paging text (defaults to "of %0")
24698      * @type String
24699      */
24700     afterPageText : "of {0}",
24701     /**
24702      * Customizable piece of the default paging text (defaults to "First Page")
24703      * @type String
24704      */
24705     firstText : "First Page",
24706     /**
24707      * Customizable piece of the default paging text (defaults to "Previous Page")
24708      * @type String
24709      */
24710     prevText : "Previous Page",
24711     /**
24712      * Customizable piece of the default paging text (defaults to "Next Page")
24713      * @type String
24714      */
24715     nextText : "Next Page",
24716     /**
24717      * Customizable piece of the default paging text (defaults to "Last Page")
24718      * @type String
24719      */
24720     lastText : "Last Page",
24721     /**
24722      * Customizable piece of the default paging text (defaults to "Refresh")
24723      * @type String
24724      */
24725     refreshText : "Refresh",
24726
24727     buttons : false,
24728     // private
24729     onRender : function(ct, position) 
24730     {
24731         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24732         this.navgroup.parentId = this.id;
24733         this.navgroup.onRender(this.el, null);
24734         // add the buttons to the navgroup
24735         
24736         if(this.displayInfo){
24737             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24738             this.displayEl = this.el.select('.x-paging-info', true).first();
24739 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24740 //            this.displayEl = navel.el.select('span',true).first();
24741         }
24742         
24743         var _this = this;
24744         
24745         if(this.buttons){
24746             Roo.each(_this.buttons, function(e){ // this might need to use render????
24747                Roo.factory(e).render(_this.el);
24748             });
24749         }
24750             
24751         Roo.each(_this.toolbarItems, function(e) {
24752             _this.navgroup.addItem(e);
24753         });
24754         
24755         
24756         this.first = this.navgroup.addItem({
24757             tooltip: this.firstText,
24758             cls: "prev",
24759             icon : 'fa fa-step-backward',
24760             disabled: true,
24761             preventDefault: true,
24762             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24763         });
24764         
24765         this.prev =  this.navgroup.addItem({
24766             tooltip: this.prevText,
24767             cls: "prev",
24768             icon : 'fa fa-backward',
24769             disabled: true,
24770             preventDefault: true,
24771             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24772         });
24773     //this.addSeparator();
24774         
24775         
24776         var field = this.navgroup.addItem( {
24777             tagtype : 'span',
24778             cls : 'x-paging-position',
24779             
24780             html : this.beforePageText  +
24781                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24782                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24783          } ); //?? escaped?
24784         
24785         this.field = field.el.select('input', true).first();
24786         this.field.on("keydown", this.onPagingKeydown, this);
24787         this.field.on("focus", function(){this.dom.select();});
24788     
24789     
24790         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24791         //this.field.setHeight(18);
24792         //this.addSeparator();
24793         this.next = this.navgroup.addItem({
24794             tooltip: this.nextText,
24795             cls: "next",
24796             html : ' <i class="fa fa-forward">',
24797             disabled: true,
24798             preventDefault: true,
24799             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24800         });
24801         this.last = this.navgroup.addItem({
24802             tooltip: this.lastText,
24803             icon : 'fa fa-step-forward',
24804             cls: "next",
24805             disabled: true,
24806             preventDefault: true,
24807             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24808         });
24809     //this.addSeparator();
24810         this.loading = this.navgroup.addItem({
24811             tooltip: this.refreshText,
24812             icon: 'fa fa-refresh',
24813             preventDefault: true,
24814             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24815         });
24816         
24817     },
24818
24819     // private
24820     updateInfo : function(){
24821         if(this.displayEl){
24822             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24823             var msg = count == 0 ?
24824                 this.emptyMsg :
24825                 String.format(
24826                     this.displayMsg,
24827                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24828                 );
24829             this.displayEl.update(msg);
24830         }
24831     },
24832
24833     // private
24834     onLoad : function(ds, r, o)
24835     {
24836         this.cursor = o.params.start ? o.params.start : 0;
24837         
24838         var d = this.getPageData(),
24839             ap = d.activePage,
24840             ps = d.pages;
24841         
24842         
24843         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24844         this.field.dom.value = ap;
24845         this.first.setDisabled(ap == 1);
24846         this.prev.setDisabled(ap == 1);
24847         this.next.setDisabled(ap == ps);
24848         this.last.setDisabled(ap == ps);
24849         this.loading.enable();
24850         this.updateInfo();
24851     },
24852
24853     // private
24854     getPageData : function(){
24855         var total = this.ds.getTotalCount();
24856         return {
24857             total : total,
24858             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24859             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24860         };
24861     },
24862
24863     // private
24864     onLoadError : function(){
24865         this.loading.enable();
24866     },
24867
24868     // private
24869     onPagingKeydown : function(e){
24870         var k = e.getKey();
24871         var d = this.getPageData();
24872         if(k == e.RETURN){
24873             var v = this.field.dom.value, pageNum;
24874             if(!v || isNaN(pageNum = parseInt(v, 10))){
24875                 this.field.dom.value = d.activePage;
24876                 return;
24877             }
24878             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24879             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24880             e.stopEvent();
24881         }
24882         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))
24883         {
24884           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24885           this.field.dom.value = pageNum;
24886           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24887           e.stopEvent();
24888         }
24889         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24890         {
24891           var v = this.field.dom.value, pageNum; 
24892           var increment = (e.shiftKey) ? 10 : 1;
24893           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24894                 increment *= -1;
24895           }
24896           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24897             this.field.dom.value = d.activePage;
24898             return;
24899           }
24900           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24901           {
24902             this.field.dom.value = parseInt(v, 10) + increment;
24903             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24904             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24905           }
24906           e.stopEvent();
24907         }
24908     },
24909
24910     // private
24911     beforeLoad : function(){
24912         if(this.loading){
24913             this.loading.disable();
24914         }
24915     },
24916
24917     // private
24918     onClick : function(which){
24919         
24920         var ds = this.ds;
24921         if (!ds) {
24922             return;
24923         }
24924         
24925         switch(which){
24926             case "first":
24927                 ds.load({params:{start: 0, limit: this.pageSize}});
24928             break;
24929             case "prev":
24930                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24931             break;
24932             case "next":
24933                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24934             break;
24935             case "last":
24936                 var total = ds.getTotalCount();
24937                 var extra = total % this.pageSize;
24938                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24939                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24940             break;
24941             case "refresh":
24942                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24943             break;
24944         }
24945     },
24946
24947     /**
24948      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24949      * @param {Roo.data.Store} store The data store to unbind
24950      */
24951     unbind : function(ds){
24952         ds.un("beforeload", this.beforeLoad, this);
24953         ds.un("load", this.onLoad, this);
24954         ds.un("loadexception", this.onLoadError, this);
24955         ds.un("remove", this.updateInfo, this);
24956         ds.un("add", this.updateInfo, this);
24957         this.ds = undefined;
24958     },
24959
24960     /**
24961      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24962      * @param {Roo.data.Store} store The data store to bind
24963      */
24964     bind : function(ds){
24965         ds.on("beforeload", this.beforeLoad, this);
24966         ds.on("load", this.onLoad, this);
24967         ds.on("loadexception", this.onLoadError, this);
24968         ds.on("remove", this.updateInfo, this);
24969         ds.on("add", this.updateInfo, this);
24970         this.ds = ds;
24971     }
24972 });/*
24973  * - LGPL
24974  *
24975  * element
24976  * 
24977  */
24978
24979 /**
24980  * @class Roo.bootstrap.MessageBar
24981  * @extends Roo.bootstrap.Component
24982  * Bootstrap MessageBar class
24983  * @cfg {String} html contents of the MessageBar
24984  * @cfg {String} weight (info | success | warning | danger) default info
24985  * @cfg {String} beforeClass insert the bar before the given class
24986  * @cfg {Boolean} closable (true | false) default false
24987  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24988  * 
24989  * @constructor
24990  * Create a new Element
24991  * @param {Object} config The config object
24992  */
24993
24994 Roo.bootstrap.MessageBar = function(config){
24995     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24996 };
24997
24998 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24999     
25000     html: '',
25001     weight: 'info',
25002     closable: false,
25003     fixed: false,
25004     beforeClass: 'bootstrap-sticky-wrap',
25005     
25006     getAutoCreate : function(){
25007         
25008         var cfg = {
25009             tag: 'div',
25010             cls: 'alert alert-dismissable alert-' + this.weight,
25011             cn: [
25012                 {
25013                     tag: 'span',
25014                     cls: 'message',
25015                     html: this.html || ''
25016                 }
25017             ]
25018         };
25019         
25020         if(this.fixed){
25021             cfg.cls += ' alert-messages-fixed';
25022         }
25023         
25024         if(this.closable){
25025             cfg.cn.push({
25026                 tag: 'button',
25027                 cls: 'close',
25028                 html: 'x'
25029             });
25030         }
25031         
25032         return cfg;
25033     },
25034     
25035     onRender : function(ct, position)
25036     {
25037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25038         
25039         if(!this.el){
25040             var cfg = Roo.apply({},  this.getAutoCreate());
25041             cfg.id = Roo.id();
25042             
25043             if (this.cls) {
25044                 cfg.cls += ' ' + this.cls;
25045             }
25046             if (this.style) {
25047                 cfg.style = this.style;
25048             }
25049             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25050             
25051             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25052         }
25053         
25054         this.el.select('>button.close').on('click', this.hide, this);
25055         
25056     },
25057     
25058     show : function()
25059     {
25060         if (!this.rendered) {
25061             this.render();
25062         }
25063         
25064         this.el.show();
25065         
25066         this.fireEvent('show', this);
25067         
25068     },
25069     
25070     hide : function()
25071     {
25072         if (!this.rendered) {
25073             this.render();
25074         }
25075         
25076         this.el.hide();
25077         
25078         this.fireEvent('hide', this);
25079     },
25080     
25081     update : function()
25082     {
25083 //        var e = this.el.dom.firstChild;
25084 //        
25085 //        if(this.closable){
25086 //            e = e.nextSibling;
25087 //        }
25088 //        
25089 //        e.data = this.html || '';
25090
25091         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25092     }
25093    
25094 });
25095
25096  
25097
25098      /*
25099  * - LGPL
25100  *
25101  * Graph
25102  * 
25103  */
25104
25105
25106 /**
25107  * @class Roo.bootstrap.Graph
25108  * @extends Roo.bootstrap.Component
25109  * Bootstrap Graph class
25110 > Prameters
25111  -sm {number} sm 4
25112  -md {number} md 5
25113  @cfg {String} graphtype  bar | vbar | pie
25114  @cfg {number} g_x coodinator | centre x (pie)
25115  @cfg {number} g_y coodinator | centre y (pie)
25116  @cfg {number} g_r radius (pie)
25117  @cfg {number} g_height height of the chart (respected by all elements in the set)
25118  @cfg {number} g_width width of the chart (respected by all elements in the set)
25119  @cfg {Object} title The title of the chart
25120     
25121  -{Array}  values
25122  -opts (object) options for the chart 
25123      o {
25124      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25125      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25126      o vgutter (number)
25127      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.
25128      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25129      o to
25130      o stretch (boolean)
25131      o }
25132  -opts (object) options for the pie
25133      o{
25134      o cut
25135      o startAngle (number)
25136      o endAngle (number)
25137      } 
25138  *
25139  * @constructor
25140  * Create a new Input
25141  * @param {Object} config The config object
25142  */
25143
25144 Roo.bootstrap.Graph = function(config){
25145     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25146     
25147     this.addEvents({
25148         // img events
25149         /**
25150          * @event click
25151          * The img click event for the img.
25152          * @param {Roo.EventObject} e
25153          */
25154         "click" : true
25155     });
25156 };
25157
25158 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25159     
25160     sm: 4,
25161     md: 5,
25162     graphtype: 'bar',
25163     g_height: 250,
25164     g_width: 400,
25165     g_x: 50,
25166     g_y: 50,
25167     g_r: 30,
25168     opts:{
25169         //g_colors: this.colors,
25170         g_type: 'soft',
25171         g_gutter: '20%'
25172
25173     },
25174     title : false,
25175
25176     getAutoCreate : function(){
25177         
25178         var cfg = {
25179             tag: 'div',
25180             html : null
25181         };
25182         
25183         
25184         return  cfg;
25185     },
25186
25187     onRender : function(ct,position){
25188         
25189         
25190         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25191         
25192         if (typeof(Raphael) == 'undefined') {
25193             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25194             return;
25195         }
25196         
25197         this.raphael = Raphael(this.el.dom);
25198         
25199                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25200                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25201                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25202                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25203                 /*
25204                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25205                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25206                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25207                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25208                 
25209                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25210                 r.barchart(330, 10, 300, 220, data1);
25211                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25212                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25213                 */
25214                 
25215                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25216                 // r.barchart(30, 30, 560, 250,  xdata, {
25217                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25218                 //     axis : "0 0 1 1",
25219                 //     axisxlabels :  xdata
25220                 //     //yvalues : cols,
25221                    
25222                 // });
25223 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25224 //        
25225 //        this.load(null,xdata,{
25226 //                axis : "0 0 1 1",
25227 //                axisxlabels :  xdata
25228 //                });
25229
25230     },
25231
25232     load : function(graphtype,xdata,opts)
25233     {
25234         this.raphael.clear();
25235         if(!graphtype) {
25236             graphtype = this.graphtype;
25237         }
25238         if(!opts){
25239             opts = this.opts;
25240         }
25241         var r = this.raphael,
25242             fin = function () {
25243                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25244             },
25245             fout = function () {
25246                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25247             },
25248             pfin = function() {
25249                 this.sector.stop();
25250                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25251
25252                 if (this.label) {
25253                     this.label[0].stop();
25254                     this.label[0].attr({ r: 7.5 });
25255                     this.label[1].attr({ "font-weight": 800 });
25256                 }
25257             },
25258             pfout = function() {
25259                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25260
25261                 if (this.label) {
25262                     this.label[0].animate({ r: 5 }, 500, "bounce");
25263                     this.label[1].attr({ "font-weight": 400 });
25264                 }
25265             };
25266
25267         switch(graphtype){
25268             case 'bar':
25269                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25270                 break;
25271             case 'hbar':
25272                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25273                 break;
25274             case 'pie':
25275 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25276 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25277 //            
25278                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25279                 
25280                 break;
25281
25282         }
25283         
25284         if(this.title){
25285             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25286         }
25287         
25288     },
25289     
25290     setTitle: function(o)
25291     {
25292         this.title = o;
25293     },
25294     
25295     initEvents: function() {
25296         
25297         if(!this.href){
25298             this.el.on('click', this.onClick, this);
25299         }
25300     },
25301     
25302     onClick : function(e)
25303     {
25304         Roo.log('img onclick');
25305         this.fireEvent('click', this, e);
25306     }
25307    
25308 });
25309
25310  
25311 /*
25312  * - LGPL
25313  *
25314  * numberBox
25315  * 
25316  */
25317 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25318
25319 /**
25320  * @class Roo.bootstrap.dash.NumberBox
25321  * @extends Roo.bootstrap.Component
25322  * Bootstrap NumberBox class
25323  * @cfg {String} headline Box headline
25324  * @cfg {String} content Box content
25325  * @cfg {String} icon Box icon
25326  * @cfg {String} footer Footer text
25327  * @cfg {String} fhref Footer href
25328  * 
25329  * @constructor
25330  * Create a new NumberBox
25331  * @param {Object} config The config object
25332  */
25333
25334
25335 Roo.bootstrap.dash.NumberBox = function(config){
25336     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25337     
25338 };
25339
25340 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25341     
25342     headline : '',
25343     content : '',
25344     icon : '',
25345     footer : '',
25346     fhref : '',
25347     ficon : '',
25348     
25349     getAutoCreate : function(){
25350         
25351         var cfg = {
25352             tag : 'div',
25353             cls : 'small-box ',
25354             cn : [
25355                 {
25356                     tag : 'div',
25357                     cls : 'inner',
25358                     cn :[
25359                         {
25360                             tag : 'h3',
25361                             cls : 'roo-headline',
25362                             html : this.headline
25363                         },
25364                         {
25365                             tag : 'p',
25366                             cls : 'roo-content',
25367                             html : this.content
25368                         }
25369                     ]
25370                 }
25371             ]
25372         };
25373         
25374         if(this.icon){
25375             cfg.cn.push({
25376                 tag : 'div',
25377                 cls : 'icon',
25378                 cn :[
25379                     {
25380                         tag : 'i',
25381                         cls : 'ion ' + this.icon
25382                     }
25383                 ]
25384             });
25385         }
25386         
25387         if(this.footer){
25388             var footer = {
25389                 tag : 'a',
25390                 cls : 'small-box-footer',
25391                 href : this.fhref || '#',
25392                 html : this.footer
25393             };
25394             
25395             cfg.cn.push(footer);
25396             
25397         }
25398         
25399         return  cfg;
25400     },
25401
25402     onRender : function(ct,position){
25403         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25404
25405
25406        
25407                 
25408     },
25409
25410     setHeadline: function (value)
25411     {
25412         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25413     },
25414     
25415     setFooter: function (value, href)
25416     {
25417         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25418         
25419         if(href){
25420             this.el.select('a.small-box-footer',true).first().attr('href', href);
25421         }
25422         
25423     },
25424
25425     setContent: function (value)
25426     {
25427         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25428     },
25429
25430     initEvents: function() 
25431     {   
25432         
25433     }
25434     
25435 });
25436
25437  
25438 /*
25439  * - LGPL
25440  *
25441  * TabBox
25442  * 
25443  */
25444 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25445
25446 /**
25447  * @class Roo.bootstrap.dash.TabBox
25448  * @extends Roo.bootstrap.Component
25449  * Bootstrap TabBox class
25450  * @cfg {String} title Title of the TabBox
25451  * @cfg {String} icon Icon of the TabBox
25452  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25453  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25454  * 
25455  * @constructor
25456  * Create a new TabBox
25457  * @param {Object} config The config object
25458  */
25459
25460
25461 Roo.bootstrap.dash.TabBox = function(config){
25462     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25463     this.addEvents({
25464         // raw events
25465         /**
25466          * @event addpane
25467          * When a pane is added
25468          * @param {Roo.bootstrap.dash.TabPane} pane
25469          */
25470         "addpane" : true,
25471         /**
25472          * @event activatepane
25473          * When a pane is activated
25474          * @param {Roo.bootstrap.dash.TabPane} pane
25475          */
25476         "activatepane" : true
25477         
25478          
25479     });
25480     
25481     this.panes = [];
25482 };
25483
25484 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25485
25486     title : '',
25487     icon : false,
25488     showtabs : true,
25489     tabScrollable : false,
25490     
25491     getChildContainer : function()
25492     {
25493         return this.el.select('.tab-content', true).first();
25494     },
25495     
25496     getAutoCreate : function(){
25497         
25498         var header = {
25499             tag: 'li',
25500             cls: 'pull-left header',
25501             html: this.title,
25502             cn : []
25503         };
25504         
25505         if(this.icon){
25506             header.cn.push({
25507                 tag: 'i',
25508                 cls: 'fa ' + this.icon
25509             });
25510         }
25511         
25512         var h = {
25513             tag: 'ul',
25514             cls: 'nav nav-tabs pull-right',
25515             cn: [
25516                 header
25517             ]
25518         };
25519         
25520         if(this.tabScrollable){
25521             h = {
25522                 tag: 'div',
25523                 cls: 'tab-header',
25524                 cn: [
25525                     {
25526                         tag: 'ul',
25527                         cls: 'nav nav-tabs pull-right',
25528                         cn: [
25529                             header
25530                         ]
25531                     }
25532                 ]
25533             };
25534         }
25535         
25536         var cfg = {
25537             tag: 'div',
25538             cls: 'nav-tabs-custom',
25539             cn: [
25540                 h,
25541                 {
25542                     tag: 'div',
25543                     cls: 'tab-content no-padding',
25544                     cn: []
25545                 }
25546             ]
25547         };
25548
25549         return  cfg;
25550     },
25551     initEvents : function()
25552     {
25553         //Roo.log('add add pane handler');
25554         this.on('addpane', this.onAddPane, this);
25555     },
25556      /**
25557      * Updates the box title
25558      * @param {String} html to set the title to.
25559      */
25560     setTitle : function(value)
25561     {
25562         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25563     },
25564     onAddPane : function(pane)
25565     {
25566         this.panes.push(pane);
25567         //Roo.log('addpane');
25568         //Roo.log(pane);
25569         // tabs are rendere left to right..
25570         if(!this.showtabs){
25571             return;
25572         }
25573         
25574         var ctr = this.el.select('.nav-tabs', true).first();
25575          
25576          
25577         var existing = ctr.select('.nav-tab',true);
25578         var qty = existing.getCount();;
25579         
25580         
25581         var tab = ctr.createChild({
25582             tag : 'li',
25583             cls : 'nav-tab' + (qty ? '' : ' active'),
25584             cn : [
25585                 {
25586                     tag : 'a',
25587                     href:'#',
25588                     html : pane.title
25589                 }
25590             ]
25591         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25592         pane.tab = tab;
25593         
25594         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25595         if (!qty) {
25596             pane.el.addClass('active');
25597         }
25598         
25599                 
25600     },
25601     onTabClick : function(ev,un,ob,pane)
25602     {
25603         //Roo.log('tab - prev default');
25604         ev.preventDefault();
25605         
25606         
25607         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25608         pane.tab.addClass('active');
25609         //Roo.log(pane.title);
25610         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25611         // technically we should have a deactivate event.. but maybe add later.
25612         // and it should not de-activate the selected tab...
25613         this.fireEvent('activatepane', pane);
25614         pane.el.addClass('active');
25615         pane.fireEvent('activate');
25616         
25617         
25618     },
25619     
25620     getActivePane : function()
25621     {
25622         var r = false;
25623         Roo.each(this.panes, function(p) {
25624             if(p.el.hasClass('active')){
25625                 r = p;
25626                 return false;
25627             }
25628             
25629             return;
25630         });
25631         
25632         return r;
25633     }
25634     
25635     
25636 });
25637
25638  
25639 /*
25640  * - LGPL
25641  *
25642  * Tab pane
25643  * 
25644  */
25645 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25646 /**
25647  * @class Roo.bootstrap.TabPane
25648  * @extends Roo.bootstrap.Component
25649  * Bootstrap TabPane class
25650  * @cfg {Boolean} active (false | true) Default false
25651  * @cfg {String} title title of panel
25652
25653  * 
25654  * @constructor
25655  * Create a new TabPane
25656  * @param {Object} config The config object
25657  */
25658
25659 Roo.bootstrap.dash.TabPane = function(config){
25660     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25661     
25662     this.addEvents({
25663         // raw events
25664         /**
25665          * @event activate
25666          * When a pane is activated
25667          * @param {Roo.bootstrap.dash.TabPane} pane
25668          */
25669         "activate" : true
25670          
25671     });
25672 };
25673
25674 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25675     
25676     active : false,
25677     title : '',
25678     
25679     // the tabBox that this is attached to.
25680     tab : false,
25681      
25682     getAutoCreate : function() 
25683     {
25684         var cfg = {
25685             tag: 'div',
25686             cls: 'tab-pane'
25687         };
25688         
25689         if(this.active){
25690             cfg.cls += ' active';
25691         }
25692         
25693         return cfg;
25694     },
25695     initEvents  : function()
25696     {
25697         //Roo.log('trigger add pane handler');
25698         this.parent().fireEvent('addpane', this)
25699     },
25700     
25701      /**
25702      * Updates the tab title 
25703      * @param {String} html to set the title to.
25704      */
25705     setTitle: function(str)
25706     {
25707         if (!this.tab) {
25708             return;
25709         }
25710         this.title = str;
25711         this.tab.select('a', true).first().dom.innerHTML = str;
25712         
25713     }
25714     
25715     
25716     
25717 });
25718
25719  
25720
25721
25722  /*
25723  * - LGPL
25724  *
25725  * menu
25726  * 
25727  */
25728 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25729
25730 /**
25731  * @class Roo.bootstrap.menu.Menu
25732  * @extends Roo.bootstrap.Component
25733  * Bootstrap Menu class - container for Menu
25734  * @cfg {String} html Text of the menu
25735  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25736  * @cfg {String} icon Font awesome icon
25737  * @cfg {String} pos Menu align to (top | bottom) default bottom
25738  * 
25739  * 
25740  * @constructor
25741  * Create a new Menu
25742  * @param {Object} config The config object
25743  */
25744
25745
25746 Roo.bootstrap.menu.Menu = function(config){
25747     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25748     
25749     this.addEvents({
25750         /**
25751          * @event beforeshow
25752          * Fires before this menu is displayed
25753          * @param {Roo.bootstrap.menu.Menu} this
25754          */
25755         beforeshow : true,
25756         /**
25757          * @event beforehide
25758          * Fires before this menu is hidden
25759          * @param {Roo.bootstrap.menu.Menu} this
25760          */
25761         beforehide : true,
25762         /**
25763          * @event show
25764          * Fires after this menu is displayed
25765          * @param {Roo.bootstrap.menu.Menu} this
25766          */
25767         show : true,
25768         /**
25769          * @event hide
25770          * Fires after this menu is hidden
25771          * @param {Roo.bootstrap.menu.Menu} this
25772          */
25773         hide : true,
25774         /**
25775          * @event click
25776          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25777          * @param {Roo.bootstrap.menu.Menu} this
25778          * @param {Roo.EventObject} e
25779          */
25780         click : true
25781     });
25782     
25783 };
25784
25785 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25786     
25787     submenu : false,
25788     html : '',
25789     weight : 'default',
25790     icon : false,
25791     pos : 'bottom',
25792     
25793     
25794     getChildContainer : function() {
25795         if(this.isSubMenu){
25796             return this.el;
25797         }
25798         
25799         return this.el.select('ul.dropdown-menu', true).first();  
25800     },
25801     
25802     getAutoCreate : function()
25803     {
25804         var text = [
25805             {
25806                 tag : 'span',
25807                 cls : 'roo-menu-text',
25808                 html : this.html
25809             }
25810         ];
25811         
25812         if(this.icon){
25813             text.unshift({
25814                 tag : 'i',
25815                 cls : 'fa ' + this.icon
25816             })
25817         }
25818         
25819         
25820         var cfg = {
25821             tag : 'div',
25822             cls : 'btn-group',
25823             cn : [
25824                 {
25825                     tag : 'button',
25826                     cls : 'dropdown-button btn btn-' + this.weight,
25827                     cn : text
25828                 },
25829                 {
25830                     tag : 'button',
25831                     cls : 'dropdown-toggle btn btn-' + this.weight,
25832                     cn : [
25833                         {
25834                             tag : 'span',
25835                             cls : 'caret'
25836                         }
25837                     ]
25838                 },
25839                 {
25840                     tag : 'ul',
25841                     cls : 'dropdown-menu'
25842                 }
25843             ]
25844             
25845         };
25846         
25847         if(this.pos == 'top'){
25848             cfg.cls += ' dropup';
25849         }
25850         
25851         if(this.isSubMenu){
25852             cfg = {
25853                 tag : 'ul',
25854                 cls : 'dropdown-menu'
25855             }
25856         }
25857         
25858         return cfg;
25859     },
25860     
25861     onRender : function(ct, position)
25862     {
25863         this.isSubMenu = ct.hasClass('dropdown-submenu');
25864         
25865         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25866     },
25867     
25868     initEvents : function() 
25869     {
25870         if(this.isSubMenu){
25871             return;
25872         }
25873         
25874         this.hidden = true;
25875         
25876         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25877         this.triggerEl.on('click', this.onTriggerPress, this);
25878         
25879         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25880         this.buttonEl.on('click', this.onClick, this);
25881         
25882     },
25883     
25884     list : function()
25885     {
25886         if(this.isSubMenu){
25887             return this.el;
25888         }
25889         
25890         return this.el.select('ul.dropdown-menu', true).first();
25891     },
25892     
25893     onClick : function(e)
25894     {
25895         this.fireEvent("click", this, e);
25896     },
25897     
25898     onTriggerPress  : function(e)
25899     {   
25900         if (this.isVisible()) {
25901             this.hide();
25902         } else {
25903             this.show();
25904         }
25905     },
25906     
25907     isVisible : function(){
25908         return !this.hidden;
25909     },
25910     
25911     show : function()
25912     {
25913         this.fireEvent("beforeshow", this);
25914         
25915         this.hidden = false;
25916         this.el.addClass('open');
25917         
25918         Roo.get(document).on("mouseup", this.onMouseUp, this);
25919         
25920         this.fireEvent("show", this);
25921         
25922         
25923     },
25924     
25925     hide : function()
25926     {
25927         this.fireEvent("beforehide", this);
25928         
25929         this.hidden = true;
25930         this.el.removeClass('open');
25931         
25932         Roo.get(document).un("mouseup", this.onMouseUp);
25933         
25934         this.fireEvent("hide", this);
25935     },
25936     
25937     onMouseUp : function()
25938     {
25939         this.hide();
25940     }
25941     
25942 });
25943
25944  
25945  /*
25946  * - LGPL
25947  *
25948  * menu item
25949  * 
25950  */
25951 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25952
25953 /**
25954  * @class Roo.bootstrap.menu.Item
25955  * @extends Roo.bootstrap.Component
25956  * Bootstrap MenuItem class
25957  * @cfg {Boolean} submenu (true | false) default false
25958  * @cfg {String} html text of the item
25959  * @cfg {String} href the link
25960  * @cfg {Boolean} disable (true | false) default false
25961  * @cfg {Boolean} preventDefault (true | false) default true
25962  * @cfg {String} icon Font awesome icon
25963  * @cfg {String} pos Submenu align to (left | right) default right 
25964  * 
25965  * 
25966  * @constructor
25967  * Create a new Item
25968  * @param {Object} config The config object
25969  */
25970
25971
25972 Roo.bootstrap.menu.Item = function(config){
25973     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25974     this.addEvents({
25975         /**
25976          * @event mouseover
25977          * Fires when the mouse is hovering over this menu
25978          * @param {Roo.bootstrap.menu.Item} this
25979          * @param {Roo.EventObject} e
25980          */
25981         mouseover : true,
25982         /**
25983          * @event mouseout
25984          * Fires when the mouse exits this menu
25985          * @param {Roo.bootstrap.menu.Item} this
25986          * @param {Roo.EventObject} e
25987          */
25988         mouseout : true,
25989         // raw events
25990         /**
25991          * @event click
25992          * The raw click event for the entire grid.
25993          * @param {Roo.EventObject} e
25994          */
25995         click : true
25996     });
25997 };
25998
25999 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26000     
26001     submenu : false,
26002     href : '',
26003     html : '',
26004     preventDefault: true,
26005     disable : false,
26006     icon : false,
26007     pos : 'right',
26008     
26009     getAutoCreate : function()
26010     {
26011         var text = [
26012             {
26013                 tag : 'span',
26014                 cls : 'roo-menu-item-text',
26015                 html : this.html
26016             }
26017         ];
26018         
26019         if(this.icon){
26020             text.unshift({
26021                 tag : 'i',
26022                 cls : 'fa ' + this.icon
26023             })
26024         }
26025         
26026         var cfg = {
26027             tag : 'li',
26028             cn : [
26029                 {
26030                     tag : 'a',
26031                     href : this.href || '#',
26032                     cn : text
26033                 }
26034             ]
26035         };
26036         
26037         if(this.disable){
26038             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26039         }
26040         
26041         if(this.submenu){
26042             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26043             
26044             if(this.pos == 'left'){
26045                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26046             }
26047         }
26048         
26049         return cfg;
26050     },
26051     
26052     initEvents : function() 
26053     {
26054         this.el.on('mouseover', this.onMouseOver, this);
26055         this.el.on('mouseout', this.onMouseOut, this);
26056         
26057         this.el.select('a', true).first().on('click', this.onClick, this);
26058         
26059     },
26060     
26061     onClick : function(e)
26062     {
26063         if(this.preventDefault){
26064             e.preventDefault();
26065         }
26066         
26067         this.fireEvent("click", this, e);
26068     },
26069     
26070     onMouseOver : function(e)
26071     {
26072         if(this.submenu && this.pos == 'left'){
26073             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26074         }
26075         
26076         this.fireEvent("mouseover", this, e);
26077     },
26078     
26079     onMouseOut : function(e)
26080     {
26081         this.fireEvent("mouseout", this, e);
26082     }
26083 });
26084
26085  
26086
26087  /*
26088  * - LGPL
26089  *
26090  * menu separator
26091  * 
26092  */
26093 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26094
26095 /**
26096  * @class Roo.bootstrap.menu.Separator
26097  * @extends Roo.bootstrap.Component
26098  * Bootstrap Separator class
26099  * 
26100  * @constructor
26101  * Create a new Separator
26102  * @param {Object} config The config object
26103  */
26104
26105
26106 Roo.bootstrap.menu.Separator = function(config){
26107     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26108 };
26109
26110 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26111     
26112     getAutoCreate : function(){
26113         var cfg = {
26114             tag : 'li',
26115             cls: 'divider'
26116         };
26117         
26118         return cfg;
26119     }
26120    
26121 });
26122
26123  
26124
26125  /*
26126  * - LGPL
26127  *
26128  * Tooltip
26129  * 
26130  */
26131
26132 /**
26133  * @class Roo.bootstrap.Tooltip
26134  * Bootstrap Tooltip class
26135  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26136  * to determine which dom element triggers the tooltip.
26137  * 
26138  * It needs to add support for additional attributes like tooltip-position
26139  * 
26140  * @constructor
26141  * Create a new Toolti
26142  * @param {Object} config The config object
26143  */
26144
26145 Roo.bootstrap.Tooltip = function(config){
26146     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26147     
26148     this.alignment = Roo.bootstrap.Tooltip.alignment;
26149     
26150     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26151         this.alignment = config.alignment;
26152     }
26153     
26154 };
26155
26156 Roo.apply(Roo.bootstrap.Tooltip, {
26157     /**
26158      * @function init initialize tooltip monitoring.
26159      * @static
26160      */
26161     currentEl : false,
26162     currentTip : false,
26163     currentRegion : false,
26164     
26165     //  init : delay?
26166     
26167     init : function()
26168     {
26169         Roo.get(document).on('mouseover', this.enter ,this);
26170         Roo.get(document).on('mouseout', this.leave, this);
26171          
26172         
26173         this.currentTip = new Roo.bootstrap.Tooltip();
26174     },
26175     
26176     enter : function(ev)
26177     {
26178         var dom = ev.getTarget();
26179         
26180         //Roo.log(['enter',dom]);
26181         var el = Roo.fly(dom);
26182         if (this.currentEl) {
26183             //Roo.log(dom);
26184             //Roo.log(this.currentEl);
26185             //Roo.log(this.currentEl.contains(dom));
26186             if (this.currentEl == el) {
26187                 return;
26188             }
26189             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26190                 return;
26191             }
26192
26193         }
26194         
26195         if (this.currentTip.el) {
26196             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26197         }    
26198         //Roo.log(ev);
26199         
26200         if(!el || el.dom == document){
26201             return;
26202         }
26203         
26204         var bindEl = el;
26205         
26206         // you can not look for children, as if el is the body.. then everythign is the child..
26207         if (!el.attr('tooltip')) { //
26208             if (!el.select("[tooltip]").elements.length) {
26209                 return;
26210             }
26211             // is the mouse over this child...?
26212             bindEl = el.select("[tooltip]").first();
26213             var xy = ev.getXY();
26214             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26215                 //Roo.log("not in region.");
26216                 return;
26217             }
26218             //Roo.log("child element over..");
26219             
26220         }
26221         this.currentEl = bindEl;
26222         this.currentTip.bind(bindEl);
26223         this.currentRegion = Roo.lib.Region.getRegion(dom);
26224         this.currentTip.enter();
26225         
26226     },
26227     leave : function(ev)
26228     {
26229         var dom = ev.getTarget();
26230         //Roo.log(['leave',dom]);
26231         if (!this.currentEl) {
26232             return;
26233         }
26234         
26235         
26236         if (dom != this.currentEl.dom) {
26237             return;
26238         }
26239         var xy = ev.getXY();
26240         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26241             return;
26242         }
26243         // only activate leave if mouse cursor is outside... bounding box..
26244         
26245         
26246         
26247         
26248         if (this.currentTip) {
26249             this.currentTip.leave();
26250         }
26251         //Roo.log('clear currentEl');
26252         this.currentEl = false;
26253         
26254         
26255     },
26256     alignment : {
26257         'left' : ['r-l', [-2,0], 'right'],
26258         'right' : ['l-r', [2,0], 'left'],
26259         'bottom' : ['t-b', [0,2], 'top'],
26260         'top' : [ 'b-t', [0,-2], 'bottom']
26261     }
26262     
26263 });
26264
26265
26266 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26267     
26268     
26269     bindEl : false,
26270     
26271     delay : null, // can be { show : 300 , hide: 500}
26272     
26273     timeout : null,
26274     
26275     hoverState : null, //???
26276     
26277     placement : 'bottom', 
26278     
26279     alignment : false,
26280     
26281     getAutoCreate : function(){
26282     
26283         var cfg = {
26284            cls : 'tooltip',
26285            role : 'tooltip',
26286            cn : [
26287                 {
26288                     cls : 'tooltip-arrow'
26289                 },
26290                 {
26291                     cls : 'tooltip-inner'
26292                 }
26293            ]
26294         };
26295         
26296         return cfg;
26297     },
26298     bind : function(el)
26299     {
26300         this.bindEl = el;
26301     },
26302       
26303     
26304     enter : function () {
26305        
26306         if (this.timeout != null) {
26307             clearTimeout(this.timeout);
26308         }
26309         
26310         this.hoverState = 'in';
26311          //Roo.log("enter - show");
26312         if (!this.delay || !this.delay.show) {
26313             this.show();
26314             return;
26315         }
26316         var _t = this;
26317         this.timeout = setTimeout(function () {
26318             if (_t.hoverState == 'in') {
26319                 _t.show();
26320             }
26321         }, this.delay.show);
26322     },
26323     leave : function()
26324     {
26325         clearTimeout(this.timeout);
26326     
26327         this.hoverState = 'out';
26328          if (!this.delay || !this.delay.hide) {
26329             this.hide();
26330             return;
26331         }
26332        
26333         var _t = this;
26334         this.timeout = setTimeout(function () {
26335             //Roo.log("leave - timeout");
26336             
26337             if (_t.hoverState == 'out') {
26338                 _t.hide();
26339                 Roo.bootstrap.Tooltip.currentEl = false;
26340             }
26341         }, delay);
26342     },
26343     
26344     show : function (msg)
26345     {
26346         if (!this.el) {
26347             this.render(document.body);
26348         }
26349         // set content.
26350         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26351         
26352         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26353         
26354         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26355         
26356         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26357         
26358         var placement = typeof this.placement == 'function' ?
26359             this.placement.call(this, this.el, on_el) :
26360             this.placement;
26361             
26362         var autoToken = /\s?auto?\s?/i;
26363         var autoPlace = autoToken.test(placement);
26364         if (autoPlace) {
26365             placement = placement.replace(autoToken, '') || 'top';
26366         }
26367         
26368         //this.el.detach()
26369         //this.el.setXY([0,0]);
26370         this.el.show();
26371         //this.el.dom.style.display='block';
26372         
26373         //this.el.appendTo(on_el);
26374         
26375         var p = this.getPosition();
26376         var box = this.el.getBox();
26377         
26378         if (autoPlace) {
26379             // fixme..
26380         }
26381         
26382         var align = this.alignment[placement];
26383         
26384         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26385         
26386         if(placement == 'top' || placement == 'bottom'){
26387             if(xy[0] < 0){
26388                 placement = 'right';
26389             }
26390             
26391             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26392                 placement = 'left';
26393             }
26394             
26395             var scroll = Roo.select('body', true).first().getScroll();
26396             
26397             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26398                 placement = 'top';
26399             }
26400             
26401             align = this.alignment[placement];
26402         }
26403         
26404         this.el.alignTo(this.bindEl, align[0],align[1]);
26405         //var arrow = this.el.select('.arrow',true).first();
26406         //arrow.set(align[2], 
26407         
26408         this.el.addClass(placement);
26409         
26410         this.el.addClass('in fade');
26411         
26412         this.hoverState = null;
26413         
26414         if (this.el.hasClass('fade')) {
26415             // fade it?
26416         }
26417         
26418     },
26419     hide : function()
26420     {
26421          
26422         if (!this.el) {
26423             return;
26424         }
26425         //this.el.setXY([0,0]);
26426         this.el.removeClass('in');
26427         //this.el.hide();
26428         
26429     }
26430     
26431 });
26432  
26433
26434  /*
26435  * - LGPL
26436  *
26437  * Location Picker
26438  * 
26439  */
26440
26441 /**
26442  * @class Roo.bootstrap.LocationPicker
26443  * @extends Roo.bootstrap.Component
26444  * Bootstrap LocationPicker class
26445  * @cfg {Number} latitude Position when init default 0
26446  * @cfg {Number} longitude Position when init default 0
26447  * @cfg {Number} zoom default 15
26448  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26449  * @cfg {Boolean} mapTypeControl default false
26450  * @cfg {Boolean} disableDoubleClickZoom default false
26451  * @cfg {Boolean} scrollwheel default true
26452  * @cfg {Boolean} streetViewControl default false
26453  * @cfg {Number} radius default 0
26454  * @cfg {String} locationName
26455  * @cfg {Boolean} draggable default true
26456  * @cfg {Boolean} enableAutocomplete default false
26457  * @cfg {Boolean} enableReverseGeocode default true
26458  * @cfg {String} markerTitle
26459  * 
26460  * @constructor
26461  * Create a new LocationPicker
26462  * @param {Object} config The config object
26463  */
26464
26465
26466 Roo.bootstrap.LocationPicker = function(config){
26467     
26468     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26469     
26470     this.addEvents({
26471         /**
26472          * @event initial
26473          * Fires when the picker initialized.
26474          * @param {Roo.bootstrap.LocationPicker} this
26475          * @param {Google Location} location
26476          */
26477         initial : true,
26478         /**
26479          * @event positionchanged
26480          * Fires when the picker position changed.
26481          * @param {Roo.bootstrap.LocationPicker} this
26482          * @param {Google Location} location
26483          */
26484         positionchanged : true,
26485         /**
26486          * @event resize
26487          * Fires when the map resize.
26488          * @param {Roo.bootstrap.LocationPicker} this
26489          */
26490         resize : true,
26491         /**
26492          * @event show
26493          * Fires when the map show.
26494          * @param {Roo.bootstrap.LocationPicker} this
26495          */
26496         show : true,
26497         /**
26498          * @event hide
26499          * Fires when the map hide.
26500          * @param {Roo.bootstrap.LocationPicker} this
26501          */
26502         hide : true,
26503         /**
26504          * @event mapClick
26505          * Fires when click the map.
26506          * @param {Roo.bootstrap.LocationPicker} this
26507          * @param {Map event} e
26508          */
26509         mapClick : true,
26510         /**
26511          * @event mapRightClick
26512          * Fires when right click the map.
26513          * @param {Roo.bootstrap.LocationPicker} this
26514          * @param {Map event} e
26515          */
26516         mapRightClick : true,
26517         /**
26518          * @event markerClick
26519          * Fires when click the marker.
26520          * @param {Roo.bootstrap.LocationPicker} this
26521          * @param {Map event} e
26522          */
26523         markerClick : true,
26524         /**
26525          * @event markerRightClick
26526          * Fires when right click the marker.
26527          * @param {Roo.bootstrap.LocationPicker} this
26528          * @param {Map event} e
26529          */
26530         markerRightClick : true,
26531         /**
26532          * @event OverlayViewDraw
26533          * Fires when OverlayView Draw
26534          * @param {Roo.bootstrap.LocationPicker} this
26535          */
26536         OverlayViewDraw : true,
26537         /**
26538          * @event OverlayViewOnAdd
26539          * Fires when OverlayView Draw
26540          * @param {Roo.bootstrap.LocationPicker} this
26541          */
26542         OverlayViewOnAdd : true,
26543         /**
26544          * @event OverlayViewOnRemove
26545          * Fires when OverlayView Draw
26546          * @param {Roo.bootstrap.LocationPicker} this
26547          */
26548         OverlayViewOnRemove : true,
26549         /**
26550          * @event OverlayViewShow
26551          * Fires when OverlayView Draw
26552          * @param {Roo.bootstrap.LocationPicker} this
26553          * @param {Pixel} cpx
26554          */
26555         OverlayViewShow : true,
26556         /**
26557          * @event OverlayViewHide
26558          * Fires when OverlayView Draw
26559          * @param {Roo.bootstrap.LocationPicker} this
26560          */
26561         OverlayViewHide : true,
26562         /**
26563          * @event loadexception
26564          * Fires when load google lib failed.
26565          * @param {Roo.bootstrap.LocationPicker} this
26566          */
26567         loadexception : true
26568     });
26569         
26570 };
26571
26572 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26573     
26574     gMapContext: false,
26575     
26576     latitude: 0,
26577     longitude: 0,
26578     zoom: 15,
26579     mapTypeId: false,
26580     mapTypeControl: false,
26581     disableDoubleClickZoom: false,
26582     scrollwheel: true,
26583     streetViewControl: false,
26584     radius: 0,
26585     locationName: '',
26586     draggable: true,
26587     enableAutocomplete: false,
26588     enableReverseGeocode: true,
26589     markerTitle: '',
26590     
26591     getAutoCreate: function()
26592     {
26593
26594         var cfg = {
26595             tag: 'div',
26596             cls: 'roo-location-picker'
26597         };
26598         
26599         return cfg
26600     },
26601     
26602     initEvents: function(ct, position)
26603     {       
26604         if(!this.el.getWidth() || this.isApplied()){
26605             return;
26606         }
26607         
26608         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26609         
26610         this.initial();
26611     },
26612     
26613     initial: function()
26614     {
26615         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26616             this.fireEvent('loadexception', this);
26617             return;
26618         }
26619         
26620         if(!this.mapTypeId){
26621             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26622         }
26623         
26624         this.gMapContext = this.GMapContext();
26625         
26626         this.initOverlayView();
26627         
26628         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26629         
26630         var _this = this;
26631                 
26632         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26633             _this.setPosition(_this.gMapContext.marker.position);
26634         });
26635         
26636         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26637             _this.fireEvent('mapClick', this, event);
26638             
26639         });
26640
26641         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26642             _this.fireEvent('mapRightClick', this, event);
26643             
26644         });
26645         
26646         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26647             _this.fireEvent('markerClick', this, event);
26648             
26649         });
26650
26651         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26652             _this.fireEvent('markerRightClick', this, event);
26653             
26654         });
26655         
26656         this.setPosition(this.gMapContext.location);
26657         
26658         this.fireEvent('initial', this, this.gMapContext.location);
26659     },
26660     
26661     initOverlayView: function()
26662     {
26663         var _this = this;
26664         
26665         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26666             
26667             draw: function()
26668             {
26669                 _this.fireEvent('OverlayViewDraw', _this);
26670             },
26671             
26672             onAdd: function()
26673             {
26674                 _this.fireEvent('OverlayViewOnAdd', _this);
26675             },
26676             
26677             onRemove: function()
26678             {
26679                 _this.fireEvent('OverlayViewOnRemove', _this);
26680             },
26681             
26682             show: function(cpx)
26683             {
26684                 _this.fireEvent('OverlayViewShow', _this, cpx);
26685             },
26686             
26687             hide: function()
26688             {
26689                 _this.fireEvent('OverlayViewHide', _this);
26690             }
26691             
26692         });
26693     },
26694     
26695     fromLatLngToContainerPixel: function(event)
26696     {
26697         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26698     },
26699     
26700     isApplied: function() 
26701     {
26702         return this.getGmapContext() == false ? false : true;
26703     },
26704     
26705     getGmapContext: function() 
26706     {
26707         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26708     },
26709     
26710     GMapContext: function() 
26711     {
26712         var position = new google.maps.LatLng(this.latitude, this.longitude);
26713         
26714         var _map = new google.maps.Map(this.el.dom, {
26715             center: position,
26716             zoom: this.zoom,
26717             mapTypeId: this.mapTypeId,
26718             mapTypeControl: this.mapTypeControl,
26719             disableDoubleClickZoom: this.disableDoubleClickZoom,
26720             scrollwheel: this.scrollwheel,
26721             streetViewControl: this.streetViewControl,
26722             locationName: this.locationName,
26723             draggable: this.draggable,
26724             enableAutocomplete: this.enableAutocomplete,
26725             enableReverseGeocode: this.enableReverseGeocode
26726         });
26727         
26728         var _marker = new google.maps.Marker({
26729             position: position,
26730             map: _map,
26731             title: this.markerTitle,
26732             draggable: this.draggable
26733         });
26734         
26735         return {
26736             map: _map,
26737             marker: _marker,
26738             circle: null,
26739             location: position,
26740             radius: this.radius,
26741             locationName: this.locationName,
26742             addressComponents: {
26743                 formatted_address: null,
26744                 addressLine1: null,
26745                 addressLine2: null,
26746                 streetName: null,
26747                 streetNumber: null,
26748                 city: null,
26749                 district: null,
26750                 state: null,
26751                 stateOrProvince: null
26752             },
26753             settings: this,
26754             domContainer: this.el.dom,
26755             geodecoder: new google.maps.Geocoder()
26756         };
26757     },
26758     
26759     drawCircle: function(center, radius, options) 
26760     {
26761         if (this.gMapContext.circle != null) {
26762             this.gMapContext.circle.setMap(null);
26763         }
26764         if (radius > 0) {
26765             radius *= 1;
26766             options = Roo.apply({}, options, {
26767                 strokeColor: "#0000FF",
26768                 strokeOpacity: .35,
26769                 strokeWeight: 2,
26770                 fillColor: "#0000FF",
26771                 fillOpacity: .2
26772             });
26773             
26774             options.map = this.gMapContext.map;
26775             options.radius = radius;
26776             options.center = center;
26777             this.gMapContext.circle = new google.maps.Circle(options);
26778             return this.gMapContext.circle;
26779         }
26780         
26781         return null;
26782     },
26783     
26784     setPosition: function(location) 
26785     {
26786         this.gMapContext.location = location;
26787         this.gMapContext.marker.setPosition(location);
26788         this.gMapContext.map.panTo(location);
26789         this.drawCircle(location, this.gMapContext.radius, {});
26790         
26791         var _this = this;
26792         
26793         if (this.gMapContext.settings.enableReverseGeocode) {
26794             this.gMapContext.geodecoder.geocode({
26795                 latLng: this.gMapContext.location
26796             }, function(results, status) {
26797                 
26798                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26799                     _this.gMapContext.locationName = results[0].formatted_address;
26800                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26801                     
26802                     _this.fireEvent('positionchanged', this, location);
26803                 }
26804             });
26805             
26806             return;
26807         }
26808         
26809         this.fireEvent('positionchanged', this, location);
26810     },
26811     
26812     resize: function()
26813     {
26814         google.maps.event.trigger(this.gMapContext.map, "resize");
26815         
26816         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26817         
26818         this.fireEvent('resize', this);
26819     },
26820     
26821     setPositionByLatLng: function(latitude, longitude)
26822     {
26823         this.setPosition(new google.maps.LatLng(latitude, longitude));
26824     },
26825     
26826     getCurrentPosition: function() 
26827     {
26828         return {
26829             latitude: this.gMapContext.location.lat(),
26830             longitude: this.gMapContext.location.lng()
26831         };
26832     },
26833     
26834     getAddressName: function() 
26835     {
26836         return this.gMapContext.locationName;
26837     },
26838     
26839     getAddressComponents: function() 
26840     {
26841         return this.gMapContext.addressComponents;
26842     },
26843     
26844     address_component_from_google_geocode: function(address_components) 
26845     {
26846         var result = {};
26847         
26848         for (var i = 0; i < address_components.length; i++) {
26849             var component = address_components[i];
26850             if (component.types.indexOf("postal_code") >= 0) {
26851                 result.postalCode = component.short_name;
26852             } else if (component.types.indexOf("street_number") >= 0) {
26853                 result.streetNumber = component.short_name;
26854             } else if (component.types.indexOf("route") >= 0) {
26855                 result.streetName = component.short_name;
26856             } else if (component.types.indexOf("neighborhood") >= 0) {
26857                 result.city = component.short_name;
26858             } else if (component.types.indexOf("locality") >= 0) {
26859                 result.city = component.short_name;
26860             } else if (component.types.indexOf("sublocality") >= 0) {
26861                 result.district = component.short_name;
26862             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26863                 result.stateOrProvince = component.short_name;
26864             } else if (component.types.indexOf("country") >= 0) {
26865                 result.country = component.short_name;
26866             }
26867         }
26868         
26869         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26870         result.addressLine2 = "";
26871         return result;
26872     },
26873     
26874     setZoomLevel: function(zoom)
26875     {
26876         this.gMapContext.map.setZoom(zoom);
26877     },
26878     
26879     show: function()
26880     {
26881         if(!this.el){
26882             return;
26883         }
26884         
26885         this.el.show();
26886         
26887         this.resize();
26888         
26889         this.fireEvent('show', this);
26890     },
26891     
26892     hide: function()
26893     {
26894         if(!this.el){
26895             return;
26896         }
26897         
26898         this.el.hide();
26899         
26900         this.fireEvent('hide', this);
26901     }
26902     
26903 });
26904
26905 Roo.apply(Roo.bootstrap.LocationPicker, {
26906     
26907     OverlayView : function(map, options)
26908     {
26909         options = options || {};
26910         
26911         this.setMap(map);
26912     }
26913     
26914     
26915 });/*
26916  * - LGPL
26917  *
26918  * Alert
26919  * 
26920  */
26921
26922 /**
26923  * @class Roo.bootstrap.Alert
26924  * @extends Roo.bootstrap.Component
26925  * Bootstrap Alert class
26926  * @cfg {String} title The title of alert
26927  * @cfg {String} html The content of alert
26928  * @cfg {String} weight (  success | info | warning | danger )
26929  * @cfg {String} faicon font-awesomeicon
26930  * 
26931  * @constructor
26932  * Create a new alert
26933  * @param {Object} config The config object
26934  */
26935
26936
26937 Roo.bootstrap.Alert = function(config){
26938     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26939     
26940 };
26941
26942 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26943     
26944     title: '',
26945     html: '',
26946     weight: false,
26947     faicon: false,
26948     
26949     getAutoCreate : function()
26950     {
26951         
26952         var cfg = {
26953             tag : 'div',
26954             cls : 'alert',
26955             cn : [
26956                 {
26957                     tag : 'i',
26958                     cls : 'roo-alert-icon'
26959                     
26960                 },
26961                 {
26962                     tag : 'b',
26963                     cls : 'roo-alert-title',
26964                     html : this.title
26965                 },
26966                 {
26967                     tag : 'span',
26968                     cls : 'roo-alert-text',
26969                     html : this.html
26970                 }
26971             ]
26972         };
26973         
26974         if(this.faicon){
26975             cfg.cn[0].cls += ' fa ' + this.faicon;
26976         }
26977         
26978         if(this.weight){
26979             cfg.cls += ' alert-' + this.weight;
26980         }
26981         
26982         return cfg;
26983     },
26984     
26985     initEvents: function() 
26986     {
26987         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26988     },
26989     
26990     setTitle : function(str)
26991     {
26992         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26993     },
26994     
26995     setText : function(str)
26996     {
26997         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26998     },
26999     
27000     setWeight : function(weight)
27001     {
27002         if(this.weight){
27003             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27004         }
27005         
27006         this.weight = weight;
27007         
27008         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27009     },
27010     
27011     setIcon : function(icon)
27012     {
27013         if(this.faicon){
27014             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27015         }
27016         
27017         this.faicon = icon;
27018         
27019         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27020     },
27021     
27022     hide: function() 
27023     {
27024         this.el.hide();   
27025     },
27026     
27027     show: function() 
27028     {  
27029         this.el.show();   
27030     }
27031     
27032 });
27033
27034  
27035 /*
27036 * Licence: LGPL
27037 */
27038
27039 /**
27040  * @class Roo.bootstrap.UploadCropbox
27041  * @extends Roo.bootstrap.Component
27042  * Bootstrap UploadCropbox class
27043  * @cfg {String} emptyText show when image has been loaded
27044  * @cfg {String} rotateNotify show when image too small to rotate
27045  * @cfg {Number} errorTimeout default 3000
27046  * @cfg {Number} minWidth default 300
27047  * @cfg {Number} minHeight default 300
27048  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27049  * @cfg {Boolean} isDocument (true|false) default false
27050  * @cfg {String} url action url
27051  * @cfg {String} paramName default 'imageUpload'
27052  * @cfg {String} method default POST
27053  * @cfg {Boolean} loadMask (true|false) default true
27054  * @cfg {Boolean} loadingText default 'Loading...'
27055  * 
27056  * @constructor
27057  * Create a new UploadCropbox
27058  * @param {Object} config The config object
27059  */
27060
27061 Roo.bootstrap.UploadCropbox = function(config){
27062     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27063     
27064     this.addEvents({
27065         /**
27066          * @event beforeselectfile
27067          * Fire before select file
27068          * @param {Roo.bootstrap.UploadCropbox} this
27069          */
27070         "beforeselectfile" : true,
27071         /**
27072          * @event initial
27073          * Fire after initEvent
27074          * @param {Roo.bootstrap.UploadCropbox} this
27075          */
27076         "initial" : true,
27077         /**
27078          * @event crop
27079          * Fire after initEvent
27080          * @param {Roo.bootstrap.UploadCropbox} this
27081          * @param {String} data
27082          */
27083         "crop" : true,
27084         /**
27085          * @event prepare
27086          * Fire when preparing the file data
27087          * @param {Roo.bootstrap.UploadCropbox} this
27088          * @param {Object} file
27089          */
27090         "prepare" : true,
27091         /**
27092          * @event exception
27093          * Fire when get exception
27094          * @param {Roo.bootstrap.UploadCropbox} this
27095          * @param {XMLHttpRequest} xhr
27096          */
27097         "exception" : true,
27098         /**
27099          * @event beforeloadcanvas
27100          * Fire before load the canvas
27101          * @param {Roo.bootstrap.UploadCropbox} this
27102          * @param {String} src
27103          */
27104         "beforeloadcanvas" : true,
27105         /**
27106          * @event trash
27107          * Fire when trash image
27108          * @param {Roo.bootstrap.UploadCropbox} this
27109          */
27110         "trash" : true,
27111         /**
27112          * @event download
27113          * Fire when download the image
27114          * @param {Roo.bootstrap.UploadCropbox} this
27115          */
27116         "download" : true,
27117         /**
27118          * @event footerbuttonclick
27119          * Fire when footerbuttonclick
27120          * @param {Roo.bootstrap.UploadCropbox} this
27121          * @param {String} type
27122          */
27123         "footerbuttonclick" : true,
27124         /**
27125          * @event resize
27126          * Fire when resize
27127          * @param {Roo.bootstrap.UploadCropbox} this
27128          */
27129         "resize" : true,
27130         /**
27131          * @event rotate
27132          * Fire when rotate the image
27133          * @param {Roo.bootstrap.UploadCropbox} this
27134          * @param {String} pos
27135          */
27136         "rotate" : true,
27137         /**
27138          * @event inspect
27139          * Fire when inspect the file
27140          * @param {Roo.bootstrap.UploadCropbox} this
27141          * @param {Object} file
27142          */
27143         "inspect" : true,
27144         /**
27145          * @event upload
27146          * Fire when xhr upload the file
27147          * @param {Roo.bootstrap.UploadCropbox} this
27148          * @param {Object} data
27149          */
27150         "upload" : true,
27151         /**
27152          * @event arrange
27153          * Fire when arrange the file data
27154          * @param {Roo.bootstrap.UploadCropbox} this
27155          * @param {Object} formData
27156          */
27157         "arrange" : true
27158     });
27159     
27160     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27161 };
27162
27163 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27164     
27165     emptyText : 'Click to upload image',
27166     rotateNotify : 'Image is too small to rotate',
27167     errorTimeout : 3000,
27168     scale : 0,
27169     baseScale : 1,
27170     rotate : 0,
27171     dragable : false,
27172     pinching : false,
27173     mouseX : 0,
27174     mouseY : 0,
27175     cropData : false,
27176     minWidth : 300,
27177     minHeight : 300,
27178     file : false,
27179     exif : {},
27180     baseRotate : 1,
27181     cropType : 'image/jpeg',
27182     buttons : false,
27183     canvasLoaded : false,
27184     isDocument : false,
27185     method : 'POST',
27186     paramName : 'imageUpload',
27187     loadMask : true,
27188     loadingText : 'Loading...',
27189     maskEl : false,
27190     
27191     getAutoCreate : function()
27192     {
27193         var cfg = {
27194             tag : 'div',
27195             cls : 'roo-upload-cropbox',
27196             cn : [
27197                 {
27198                     tag : 'input',
27199                     cls : 'roo-upload-cropbox-selector',
27200                     type : 'file'
27201                 },
27202                 {
27203                     tag : 'div',
27204                     cls : 'roo-upload-cropbox-body',
27205                     style : 'cursor:pointer',
27206                     cn : [
27207                         {
27208                             tag : 'div',
27209                             cls : 'roo-upload-cropbox-preview'
27210                         },
27211                         {
27212                             tag : 'div',
27213                             cls : 'roo-upload-cropbox-thumb'
27214                         },
27215                         {
27216                             tag : 'div',
27217                             cls : 'roo-upload-cropbox-empty-notify',
27218                             html : this.emptyText
27219                         },
27220                         {
27221                             tag : 'div',
27222                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27223                             html : this.rotateNotify
27224                         }
27225                     ]
27226                 },
27227                 {
27228                     tag : 'div',
27229                     cls : 'roo-upload-cropbox-footer',
27230                     cn : {
27231                         tag : 'div',
27232                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27233                         cn : []
27234                     }
27235                 }
27236             ]
27237         };
27238         
27239         return cfg;
27240     },
27241     
27242     onRender : function(ct, position)
27243     {
27244         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27245         
27246         if (this.buttons.length) {
27247             
27248             Roo.each(this.buttons, function(bb) {
27249                 
27250                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27251                 
27252                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27253                 
27254             }, this);
27255         }
27256         
27257         if(this.loadMask){
27258             this.maskEl = this.el;
27259         }
27260     },
27261     
27262     initEvents : function()
27263     {
27264         this.urlAPI = (window.createObjectURL && window) || 
27265                                 (window.URL && URL.revokeObjectURL && URL) || 
27266                                 (window.webkitURL && webkitURL);
27267                         
27268         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27269         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27270         
27271         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27272         this.selectorEl.hide();
27273         
27274         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27275         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27276         
27277         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27278         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27279         this.thumbEl.hide();
27280         
27281         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27282         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27283         
27284         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27285         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27286         this.errorEl.hide();
27287         
27288         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27289         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27290         this.footerEl.hide();
27291         
27292         this.setThumbBoxSize();
27293         
27294         this.bind();
27295         
27296         this.resize();
27297         
27298         this.fireEvent('initial', this);
27299     },
27300
27301     bind : function()
27302     {
27303         var _this = this;
27304         
27305         window.addEventListener("resize", function() { _this.resize(); } );
27306         
27307         this.bodyEl.on('click', this.beforeSelectFile, this);
27308         
27309         if(Roo.isTouch){
27310             this.bodyEl.on('touchstart', this.onTouchStart, this);
27311             this.bodyEl.on('touchmove', this.onTouchMove, this);
27312             this.bodyEl.on('touchend', this.onTouchEnd, this);
27313         }
27314         
27315         if(!Roo.isTouch){
27316             this.bodyEl.on('mousedown', this.onMouseDown, this);
27317             this.bodyEl.on('mousemove', this.onMouseMove, this);
27318             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27319             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27320             Roo.get(document).on('mouseup', this.onMouseUp, this);
27321         }
27322         
27323         this.selectorEl.on('change', this.onFileSelected, this);
27324     },
27325     
27326     reset : function()
27327     {    
27328         this.scale = 0;
27329         this.baseScale = 1;
27330         this.rotate = 0;
27331         this.baseRotate = 1;
27332         this.dragable = false;
27333         this.pinching = false;
27334         this.mouseX = 0;
27335         this.mouseY = 0;
27336         this.cropData = false;
27337         this.notifyEl.dom.innerHTML = this.emptyText;
27338         
27339         this.selectorEl.dom.value = '';
27340         
27341     },
27342     
27343     resize : function()
27344     {
27345         if(this.fireEvent('resize', this) != false){
27346             this.setThumbBoxPosition();
27347             this.setCanvasPosition();
27348         }
27349     },
27350     
27351     onFooterButtonClick : function(e, el, o, type)
27352     {
27353         switch (type) {
27354             case 'rotate-left' :
27355                 this.onRotateLeft(e);
27356                 break;
27357             case 'rotate-right' :
27358                 this.onRotateRight(e);
27359                 break;
27360             case 'picture' :
27361                 this.beforeSelectFile(e);
27362                 break;
27363             case 'trash' :
27364                 this.trash(e);
27365                 break;
27366             case 'crop' :
27367                 this.crop(e);
27368                 break;
27369             case 'download' :
27370                 this.download(e);
27371                 break;
27372             default :
27373                 break;
27374         }
27375         
27376         this.fireEvent('footerbuttonclick', this, type);
27377     },
27378     
27379     beforeSelectFile : function(e)
27380     {
27381         e.preventDefault();
27382         
27383         if(this.fireEvent('beforeselectfile', this) != false){
27384             this.selectorEl.dom.click();
27385         }
27386     },
27387     
27388     onFileSelected : function(e)
27389     {
27390         e.preventDefault();
27391         
27392         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27393             return;
27394         }
27395         
27396         var file = this.selectorEl.dom.files[0];
27397         
27398         if(this.fireEvent('inspect', this, file) != false){
27399             this.prepare(file);
27400         }
27401         
27402     },
27403     
27404     trash : function(e)
27405     {
27406         this.fireEvent('trash', this);
27407     },
27408     
27409     download : function(e)
27410     {
27411         this.fireEvent('download', this);
27412     },
27413     
27414     loadCanvas : function(src)
27415     {   
27416         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27417             
27418             this.reset();
27419             
27420             this.imageEl = document.createElement('img');
27421             
27422             var _this = this;
27423             
27424             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27425             
27426             this.imageEl.src = src;
27427         }
27428     },
27429     
27430     onLoadCanvas : function()
27431     {   
27432         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27433         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27434         
27435         this.bodyEl.un('click', this.beforeSelectFile, this);
27436         
27437         this.notifyEl.hide();
27438         this.thumbEl.show();
27439         this.footerEl.show();
27440         
27441         this.baseRotateLevel();
27442         
27443         if(this.isDocument){
27444             this.setThumbBoxSize();
27445         }
27446         
27447         this.setThumbBoxPosition();
27448         
27449         this.baseScaleLevel();
27450         
27451         this.draw();
27452         
27453         this.resize();
27454         
27455         this.canvasLoaded = true;
27456         
27457         if(this.loadMask){
27458             this.maskEl.unmask();
27459         }
27460         
27461     },
27462     
27463     setCanvasPosition : function()
27464     {   
27465         if(!this.canvasEl){
27466             return;
27467         }
27468         
27469         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27470         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27471         
27472         this.previewEl.setLeft(pw);
27473         this.previewEl.setTop(ph);
27474         
27475     },
27476     
27477     onMouseDown : function(e)
27478     {   
27479         e.stopEvent();
27480         
27481         this.dragable = true;
27482         this.pinching = false;
27483         
27484         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27485             this.dragable = false;
27486             return;
27487         }
27488         
27489         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27490         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27491         
27492     },
27493     
27494     onMouseMove : function(e)
27495     {   
27496         e.stopEvent();
27497         
27498         if(!this.canvasLoaded){
27499             return;
27500         }
27501         
27502         if (!this.dragable){
27503             return;
27504         }
27505         
27506         var minX = Math.ceil(this.thumbEl.getLeft(true));
27507         var minY = Math.ceil(this.thumbEl.getTop(true));
27508         
27509         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27510         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27511         
27512         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27513         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27514         
27515         x = x - this.mouseX;
27516         y = y - this.mouseY;
27517         
27518         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27519         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27520         
27521         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27522         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27523         
27524         this.previewEl.setLeft(bgX);
27525         this.previewEl.setTop(bgY);
27526         
27527         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27528         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27529     },
27530     
27531     onMouseUp : function(e)
27532     {   
27533         e.stopEvent();
27534         
27535         this.dragable = false;
27536     },
27537     
27538     onMouseWheel : function(e)
27539     {   
27540         e.stopEvent();
27541         
27542         this.startScale = this.scale;
27543         
27544         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27545         
27546         if(!this.zoomable()){
27547             this.scale = this.startScale;
27548             return;
27549         }
27550         
27551         this.draw();
27552         
27553         return;
27554     },
27555     
27556     zoomable : function()
27557     {
27558         var minScale = this.thumbEl.getWidth() / this.minWidth;
27559         
27560         if(this.minWidth < this.minHeight){
27561             minScale = this.thumbEl.getHeight() / this.minHeight;
27562         }
27563         
27564         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27565         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27566         
27567         if(
27568                 this.isDocument &&
27569                 (this.rotate == 0 || this.rotate == 180) && 
27570                 (
27571                     width > this.imageEl.OriginWidth || 
27572                     height > this.imageEl.OriginHeight ||
27573                     (width < this.minWidth && height < this.minHeight)
27574                 )
27575         ){
27576             return false;
27577         }
27578         
27579         if(
27580                 this.isDocument &&
27581                 (this.rotate == 90 || this.rotate == 270) && 
27582                 (
27583                     width > this.imageEl.OriginWidth || 
27584                     height > this.imageEl.OriginHeight ||
27585                     (width < this.minHeight && height < this.minWidth)
27586                 )
27587         ){
27588             return false;
27589         }
27590         
27591         if(
27592                 !this.isDocument &&
27593                 (this.rotate == 0 || this.rotate == 180) && 
27594                 (
27595                     width < this.minWidth || 
27596                     width > this.imageEl.OriginWidth || 
27597                     height < this.minHeight || 
27598                     height > this.imageEl.OriginHeight
27599                 )
27600         ){
27601             return false;
27602         }
27603         
27604         if(
27605                 !this.isDocument &&
27606                 (this.rotate == 90 || this.rotate == 270) && 
27607                 (
27608                     width < this.minHeight || 
27609                     width > this.imageEl.OriginWidth || 
27610                     height < this.minWidth || 
27611                     height > this.imageEl.OriginHeight
27612                 )
27613         ){
27614             return false;
27615         }
27616         
27617         return true;
27618         
27619     },
27620     
27621     onRotateLeft : function(e)
27622     {   
27623         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27624             
27625             var minScale = this.thumbEl.getWidth() / this.minWidth;
27626             
27627             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27628             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27629             
27630             this.startScale = this.scale;
27631             
27632             while (this.getScaleLevel() < minScale){
27633             
27634                 this.scale = this.scale + 1;
27635                 
27636                 if(!this.zoomable()){
27637                     break;
27638                 }
27639                 
27640                 if(
27641                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27642                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27643                 ){
27644                     continue;
27645                 }
27646                 
27647                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27648
27649                 this.draw();
27650                 
27651                 return;
27652             }
27653             
27654             this.scale = this.startScale;
27655             
27656             this.onRotateFail();
27657             
27658             return false;
27659         }
27660         
27661         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27662
27663         if(this.isDocument){
27664             this.setThumbBoxSize();
27665             this.setThumbBoxPosition();
27666             this.setCanvasPosition();
27667         }
27668         
27669         this.draw();
27670         
27671         this.fireEvent('rotate', this, 'left');
27672         
27673     },
27674     
27675     onRotateRight : function(e)
27676     {
27677         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27678             
27679             var minScale = this.thumbEl.getWidth() / this.minWidth;
27680         
27681             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27682             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27683             
27684             this.startScale = this.scale;
27685             
27686             while (this.getScaleLevel() < minScale){
27687             
27688                 this.scale = this.scale + 1;
27689                 
27690                 if(!this.zoomable()){
27691                     break;
27692                 }
27693                 
27694                 if(
27695                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27696                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27697                 ){
27698                     continue;
27699                 }
27700                 
27701                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27702
27703                 this.draw();
27704                 
27705                 return;
27706             }
27707             
27708             this.scale = this.startScale;
27709             
27710             this.onRotateFail();
27711             
27712             return false;
27713         }
27714         
27715         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27716
27717         if(this.isDocument){
27718             this.setThumbBoxSize();
27719             this.setThumbBoxPosition();
27720             this.setCanvasPosition();
27721         }
27722         
27723         this.draw();
27724         
27725         this.fireEvent('rotate', this, 'right');
27726     },
27727     
27728     onRotateFail : function()
27729     {
27730         this.errorEl.show(true);
27731         
27732         var _this = this;
27733         
27734         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27735     },
27736     
27737     draw : function()
27738     {
27739         this.previewEl.dom.innerHTML = '';
27740         
27741         var canvasEl = document.createElement("canvas");
27742         
27743         var contextEl = canvasEl.getContext("2d");
27744         
27745         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27746         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27747         var center = this.imageEl.OriginWidth / 2;
27748         
27749         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27750             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27751             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27752             center = this.imageEl.OriginHeight / 2;
27753         }
27754         
27755         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27756         
27757         contextEl.translate(center, center);
27758         contextEl.rotate(this.rotate * Math.PI / 180);
27759
27760         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27761         
27762         this.canvasEl = document.createElement("canvas");
27763         
27764         this.contextEl = this.canvasEl.getContext("2d");
27765         
27766         switch (this.rotate) {
27767             case 0 :
27768                 
27769                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27770                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27771                 
27772                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27773                 
27774                 break;
27775             case 90 : 
27776                 
27777                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27778                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27779                 
27780                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27781                     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);
27782                     break;
27783                 }
27784                 
27785                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27786                 
27787                 break;
27788             case 180 :
27789                 
27790                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27791                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27792                 
27793                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27794                     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);
27795                     break;
27796                 }
27797                 
27798                 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);
27799                 
27800                 break;
27801             case 270 :
27802                 
27803                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27804                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27805         
27806                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27807                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27808                     break;
27809                 }
27810                 
27811                 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);
27812                 
27813                 break;
27814             default : 
27815                 break;
27816         }
27817         
27818         this.previewEl.appendChild(this.canvasEl);
27819         
27820         this.setCanvasPosition();
27821     },
27822     
27823     crop : function()
27824     {
27825         if(!this.canvasLoaded){
27826             return;
27827         }
27828         
27829         var imageCanvas = document.createElement("canvas");
27830         
27831         var imageContext = imageCanvas.getContext("2d");
27832         
27833         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27834         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27835         
27836         var center = imageCanvas.width / 2;
27837         
27838         imageContext.translate(center, center);
27839         
27840         imageContext.rotate(this.rotate * Math.PI / 180);
27841         
27842         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27843         
27844         var canvas = document.createElement("canvas");
27845         
27846         var context = canvas.getContext("2d");
27847                 
27848         canvas.width = this.minWidth;
27849         canvas.height = this.minHeight;
27850
27851         switch (this.rotate) {
27852             case 0 :
27853                 
27854                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27855                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27856                 
27857                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27858                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27859                 
27860                 var targetWidth = this.minWidth - 2 * x;
27861                 var targetHeight = this.minHeight - 2 * y;
27862                 
27863                 var scale = 1;
27864                 
27865                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27866                     scale = targetWidth / width;
27867                 }
27868                 
27869                 if(x > 0 && y == 0){
27870                     scale = targetHeight / height;
27871                 }
27872                 
27873                 if(x > 0 && y > 0){
27874                     scale = targetWidth / width;
27875                     
27876                     if(width < height){
27877                         scale = targetHeight / height;
27878                     }
27879                 }
27880                 
27881                 context.scale(scale, scale);
27882                 
27883                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27884                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27885
27886                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27887                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27888
27889                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27890                 
27891                 break;
27892             case 90 : 
27893                 
27894                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27895                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27896                 
27897                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27898                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27899                 
27900                 var targetWidth = this.minWidth - 2 * x;
27901                 var targetHeight = this.minHeight - 2 * y;
27902                 
27903                 var scale = 1;
27904                 
27905                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27906                     scale = targetWidth / width;
27907                 }
27908                 
27909                 if(x > 0 && y == 0){
27910                     scale = targetHeight / height;
27911                 }
27912                 
27913                 if(x > 0 && y > 0){
27914                     scale = targetWidth / width;
27915                     
27916                     if(width < height){
27917                         scale = targetHeight / height;
27918                     }
27919                 }
27920                 
27921                 context.scale(scale, scale);
27922                 
27923                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27924                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27925
27926                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27927                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27928                 
27929                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27930                 
27931                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27932                 
27933                 break;
27934             case 180 :
27935                 
27936                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27937                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27938                 
27939                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27940                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27941                 
27942                 var targetWidth = this.minWidth - 2 * x;
27943                 var targetHeight = this.minHeight - 2 * y;
27944                 
27945                 var scale = 1;
27946                 
27947                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27948                     scale = targetWidth / width;
27949                 }
27950                 
27951                 if(x > 0 && y == 0){
27952                     scale = targetHeight / height;
27953                 }
27954                 
27955                 if(x > 0 && y > 0){
27956                     scale = targetWidth / width;
27957                     
27958                     if(width < height){
27959                         scale = targetHeight / height;
27960                     }
27961                 }
27962                 
27963                 context.scale(scale, scale);
27964                 
27965                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27966                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27967
27968                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27969                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27970
27971                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27972                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27973                 
27974                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27975                 
27976                 break;
27977             case 270 :
27978                 
27979                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27980                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27981                 
27982                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27983                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27984                 
27985                 var targetWidth = this.minWidth - 2 * x;
27986                 var targetHeight = this.minHeight - 2 * y;
27987                 
27988                 var scale = 1;
27989                 
27990                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27991                     scale = targetWidth / width;
27992                 }
27993                 
27994                 if(x > 0 && y == 0){
27995                     scale = targetHeight / height;
27996                 }
27997                 
27998                 if(x > 0 && y > 0){
27999                     scale = targetWidth / width;
28000                     
28001                     if(width < height){
28002                         scale = targetHeight / height;
28003                     }
28004                 }
28005                 
28006                 context.scale(scale, scale);
28007                 
28008                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28009                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28010
28011                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28012                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28013                 
28014                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28015                 
28016                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28017                 
28018                 break;
28019             default : 
28020                 break;
28021         }
28022         
28023         this.cropData = canvas.toDataURL(this.cropType);
28024         
28025         if(this.fireEvent('crop', this, this.cropData) !== false){
28026             this.process(this.file, this.cropData);
28027         }
28028         
28029         return;
28030         
28031     },
28032     
28033     setThumbBoxSize : function()
28034     {
28035         var width, height;
28036         
28037         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28038             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28039             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28040             
28041             this.minWidth = width;
28042             this.minHeight = height;
28043             
28044             if(this.rotate == 90 || this.rotate == 270){
28045                 this.minWidth = height;
28046                 this.minHeight = width;
28047             }
28048         }
28049         
28050         height = 300;
28051         width = Math.ceil(this.minWidth * height / this.minHeight);
28052         
28053         if(this.minWidth > this.minHeight){
28054             width = 300;
28055             height = Math.ceil(this.minHeight * width / this.minWidth);
28056         }
28057         
28058         this.thumbEl.setStyle({
28059             width : width + 'px',
28060             height : height + 'px'
28061         });
28062
28063         return;
28064             
28065     },
28066     
28067     setThumbBoxPosition : function()
28068     {
28069         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28070         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28071         
28072         this.thumbEl.setLeft(x);
28073         this.thumbEl.setTop(y);
28074         
28075     },
28076     
28077     baseRotateLevel : function()
28078     {
28079         this.baseRotate = 1;
28080         
28081         if(
28082                 typeof(this.exif) != 'undefined' &&
28083                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28084                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28085         ){
28086             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28087         }
28088         
28089         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28090         
28091     },
28092     
28093     baseScaleLevel : function()
28094     {
28095         var width, height;
28096         
28097         if(this.isDocument){
28098             
28099             if(this.baseRotate == 6 || this.baseRotate == 8){
28100             
28101                 height = this.thumbEl.getHeight();
28102                 this.baseScale = height / this.imageEl.OriginWidth;
28103
28104                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28105                     width = this.thumbEl.getWidth();
28106                     this.baseScale = width / this.imageEl.OriginHeight;
28107                 }
28108
28109                 return;
28110             }
28111
28112             height = this.thumbEl.getHeight();
28113             this.baseScale = height / this.imageEl.OriginHeight;
28114
28115             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28116                 width = this.thumbEl.getWidth();
28117                 this.baseScale = width / this.imageEl.OriginWidth;
28118             }
28119
28120             return;
28121         }
28122         
28123         if(this.baseRotate == 6 || this.baseRotate == 8){
28124             
28125             width = this.thumbEl.getHeight();
28126             this.baseScale = width / this.imageEl.OriginHeight;
28127             
28128             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28129                 height = this.thumbEl.getWidth();
28130                 this.baseScale = height / this.imageEl.OriginHeight;
28131             }
28132             
28133             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28134                 height = this.thumbEl.getWidth();
28135                 this.baseScale = height / this.imageEl.OriginHeight;
28136                 
28137                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28138                     width = this.thumbEl.getHeight();
28139                     this.baseScale = width / this.imageEl.OriginWidth;
28140                 }
28141             }
28142             
28143             return;
28144         }
28145         
28146         width = this.thumbEl.getWidth();
28147         this.baseScale = width / this.imageEl.OriginWidth;
28148         
28149         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28150             height = this.thumbEl.getHeight();
28151             this.baseScale = height / this.imageEl.OriginHeight;
28152         }
28153         
28154         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28155             
28156             height = this.thumbEl.getHeight();
28157             this.baseScale = height / this.imageEl.OriginHeight;
28158             
28159             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28160                 width = this.thumbEl.getWidth();
28161                 this.baseScale = width / this.imageEl.OriginWidth;
28162             }
28163             
28164         }
28165         
28166         return;
28167     },
28168     
28169     getScaleLevel : function()
28170     {
28171         return this.baseScale * Math.pow(1.1, this.scale);
28172     },
28173     
28174     onTouchStart : function(e)
28175     {
28176         if(!this.canvasLoaded){
28177             this.beforeSelectFile(e);
28178             return;
28179         }
28180         
28181         var touches = e.browserEvent.touches;
28182         
28183         if(!touches){
28184             return;
28185         }
28186         
28187         if(touches.length == 1){
28188             this.onMouseDown(e);
28189             return;
28190         }
28191         
28192         if(touches.length != 2){
28193             return;
28194         }
28195         
28196         var coords = [];
28197         
28198         for(var i = 0, finger; finger = touches[i]; i++){
28199             coords.push(finger.pageX, finger.pageY);
28200         }
28201         
28202         var x = Math.pow(coords[0] - coords[2], 2);
28203         var y = Math.pow(coords[1] - coords[3], 2);
28204         
28205         this.startDistance = Math.sqrt(x + y);
28206         
28207         this.startScale = this.scale;
28208         
28209         this.pinching = true;
28210         this.dragable = false;
28211         
28212     },
28213     
28214     onTouchMove : function(e)
28215     {
28216         if(!this.pinching && !this.dragable){
28217             return;
28218         }
28219         
28220         var touches = e.browserEvent.touches;
28221         
28222         if(!touches){
28223             return;
28224         }
28225         
28226         if(this.dragable){
28227             this.onMouseMove(e);
28228             return;
28229         }
28230         
28231         var coords = [];
28232         
28233         for(var i = 0, finger; finger = touches[i]; i++){
28234             coords.push(finger.pageX, finger.pageY);
28235         }
28236         
28237         var x = Math.pow(coords[0] - coords[2], 2);
28238         var y = Math.pow(coords[1] - coords[3], 2);
28239         
28240         this.endDistance = Math.sqrt(x + y);
28241         
28242         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28243         
28244         if(!this.zoomable()){
28245             this.scale = this.startScale;
28246             return;
28247         }
28248         
28249         this.draw();
28250         
28251     },
28252     
28253     onTouchEnd : function(e)
28254     {
28255         this.pinching = false;
28256         this.dragable = false;
28257         
28258     },
28259     
28260     process : function(file, crop)
28261     {
28262         if(this.loadMask){
28263             this.maskEl.mask(this.loadingText);
28264         }
28265         
28266         this.xhr = new XMLHttpRequest();
28267         
28268         file.xhr = this.xhr;
28269
28270         this.xhr.open(this.method, this.url, true);
28271         
28272         var headers = {
28273             "Accept": "application/json",
28274             "Cache-Control": "no-cache",
28275             "X-Requested-With": "XMLHttpRequest"
28276         };
28277         
28278         for (var headerName in headers) {
28279             var headerValue = headers[headerName];
28280             if (headerValue) {
28281                 this.xhr.setRequestHeader(headerName, headerValue);
28282             }
28283         }
28284         
28285         var _this = this;
28286         
28287         this.xhr.onload = function()
28288         {
28289             _this.xhrOnLoad(_this.xhr);
28290         }
28291         
28292         this.xhr.onerror = function()
28293         {
28294             _this.xhrOnError(_this.xhr);
28295         }
28296         
28297         var formData = new FormData();
28298
28299         formData.append('returnHTML', 'NO');
28300         
28301         if(crop){
28302             formData.append('crop', crop);
28303         }
28304         
28305         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28306             formData.append(this.paramName, file, file.name);
28307         }
28308         
28309         if(typeof(file.filename) != 'undefined'){
28310             formData.append('filename', file.filename);
28311         }
28312         
28313         if(typeof(file.mimetype) != 'undefined'){
28314             formData.append('mimetype', file.mimetype);
28315         }
28316         
28317         if(this.fireEvent('arrange', this, formData) != false){
28318             this.xhr.send(formData);
28319         };
28320     },
28321     
28322     xhrOnLoad : function(xhr)
28323     {
28324         if(this.loadMask){
28325             this.maskEl.unmask();
28326         }
28327         
28328         if (xhr.readyState !== 4) {
28329             this.fireEvent('exception', this, xhr);
28330             return;
28331         }
28332
28333         var response = Roo.decode(xhr.responseText);
28334         
28335         if(!response.success){
28336             this.fireEvent('exception', this, xhr);
28337             return;
28338         }
28339         
28340         var response = Roo.decode(xhr.responseText);
28341         
28342         this.fireEvent('upload', this, response);
28343         
28344     },
28345     
28346     xhrOnError : function()
28347     {
28348         if(this.loadMask){
28349             this.maskEl.unmask();
28350         }
28351         
28352         Roo.log('xhr on error');
28353         
28354         var response = Roo.decode(xhr.responseText);
28355           
28356         Roo.log(response);
28357         
28358     },
28359     
28360     prepare : function(file)
28361     {   
28362         if(this.loadMask){
28363             this.maskEl.mask(this.loadingText);
28364         }
28365         
28366         this.file = false;
28367         this.exif = {};
28368         
28369         if(typeof(file) === 'string'){
28370             this.loadCanvas(file);
28371             return;
28372         }
28373         
28374         if(!file || !this.urlAPI){
28375             return;
28376         }
28377         
28378         this.file = file;
28379         this.cropType = file.type;
28380         
28381         var _this = this;
28382         
28383         if(this.fireEvent('prepare', this, this.file) != false){
28384             
28385             var reader = new FileReader();
28386             
28387             reader.onload = function (e) {
28388                 if (e.target.error) {
28389                     Roo.log(e.target.error);
28390                     return;
28391                 }
28392                 
28393                 var buffer = e.target.result,
28394                     dataView = new DataView(buffer),
28395                     offset = 2,
28396                     maxOffset = dataView.byteLength - 4,
28397                     markerBytes,
28398                     markerLength;
28399                 
28400                 if (dataView.getUint16(0) === 0xffd8) {
28401                     while (offset < maxOffset) {
28402                         markerBytes = dataView.getUint16(offset);
28403                         
28404                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28405                             markerLength = dataView.getUint16(offset + 2) + 2;
28406                             if (offset + markerLength > dataView.byteLength) {
28407                                 Roo.log('Invalid meta data: Invalid segment size.');
28408                                 break;
28409                             }
28410                             
28411                             if(markerBytes == 0xffe1){
28412                                 _this.parseExifData(
28413                                     dataView,
28414                                     offset,
28415                                     markerLength
28416                                 );
28417                             }
28418                             
28419                             offset += markerLength;
28420                             
28421                             continue;
28422                         }
28423                         
28424                         break;
28425                     }
28426                     
28427                 }
28428                 
28429                 var url = _this.urlAPI.createObjectURL(_this.file);
28430                 
28431                 _this.loadCanvas(url);
28432                 
28433                 return;
28434             }
28435             
28436             reader.readAsArrayBuffer(this.file);
28437             
28438         }
28439         
28440     },
28441     
28442     parseExifData : function(dataView, offset, length)
28443     {
28444         var tiffOffset = offset + 10,
28445             littleEndian,
28446             dirOffset;
28447     
28448         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28449             // No Exif data, might be XMP data instead
28450             return;
28451         }
28452         
28453         // Check for the ASCII code for "Exif" (0x45786966):
28454         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28455             // No Exif data, might be XMP data instead
28456             return;
28457         }
28458         if (tiffOffset + 8 > dataView.byteLength) {
28459             Roo.log('Invalid Exif data: Invalid segment size.');
28460             return;
28461         }
28462         // Check for the two null bytes:
28463         if (dataView.getUint16(offset + 8) !== 0x0000) {
28464             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28465             return;
28466         }
28467         // Check the byte alignment:
28468         switch (dataView.getUint16(tiffOffset)) {
28469         case 0x4949:
28470             littleEndian = true;
28471             break;
28472         case 0x4D4D:
28473             littleEndian = false;
28474             break;
28475         default:
28476             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28477             return;
28478         }
28479         // Check for the TIFF tag marker (0x002A):
28480         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28481             Roo.log('Invalid Exif data: Missing TIFF marker.');
28482             return;
28483         }
28484         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28485         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28486         
28487         this.parseExifTags(
28488             dataView,
28489             tiffOffset,
28490             tiffOffset + dirOffset,
28491             littleEndian
28492         );
28493     },
28494     
28495     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28496     {
28497         var tagsNumber,
28498             dirEndOffset,
28499             i;
28500         if (dirOffset + 6 > dataView.byteLength) {
28501             Roo.log('Invalid Exif data: Invalid directory offset.');
28502             return;
28503         }
28504         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28505         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28506         if (dirEndOffset + 4 > dataView.byteLength) {
28507             Roo.log('Invalid Exif data: Invalid directory size.');
28508             return;
28509         }
28510         for (i = 0; i < tagsNumber; i += 1) {
28511             this.parseExifTag(
28512                 dataView,
28513                 tiffOffset,
28514                 dirOffset + 2 + 12 * i, // tag offset
28515                 littleEndian
28516             );
28517         }
28518         // Return the offset to the next directory:
28519         return dataView.getUint32(dirEndOffset, littleEndian);
28520     },
28521     
28522     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28523     {
28524         var tag = dataView.getUint16(offset, littleEndian);
28525         
28526         this.exif[tag] = this.getExifValue(
28527             dataView,
28528             tiffOffset,
28529             offset,
28530             dataView.getUint16(offset + 2, littleEndian), // tag type
28531             dataView.getUint32(offset + 4, littleEndian), // tag length
28532             littleEndian
28533         );
28534     },
28535     
28536     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28537     {
28538         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28539             tagSize,
28540             dataOffset,
28541             values,
28542             i,
28543             str,
28544             c;
28545     
28546         if (!tagType) {
28547             Roo.log('Invalid Exif data: Invalid tag type.');
28548             return;
28549         }
28550         
28551         tagSize = tagType.size * length;
28552         // Determine if the value is contained in the dataOffset bytes,
28553         // or if the value at the dataOffset is a pointer to the actual data:
28554         dataOffset = tagSize > 4 ?
28555                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28556         if (dataOffset + tagSize > dataView.byteLength) {
28557             Roo.log('Invalid Exif data: Invalid data offset.');
28558             return;
28559         }
28560         if (length === 1) {
28561             return tagType.getValue(dataView, dataOffset, littleEndian);
28562         }
28563         values = [];
28564         for (i = 0; i < length; i += 1) {
28565             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28566         }
28567         
28568         if (tagType.ascii) {
28569             str = '';
28570             // Concatenate the chars:
28571             for (i = 0; i < values.length; i += 1) {
28572                 c = values[i];
28573                 // Ignore the terminating NULL byte(s):
28574                 if (c === '\u0000') {
28575                     break;
28576                 }
28577                 str += c;
28578             }
28579             return str;
28580         }
28581         return values;
28582     }
28583     
28584 });
28585
28586 Roo.apply(Roo.bootstrap.UploadCropbox, {
28587     tags : {
28588         'Orientation': 0x0112
28589     },
28590     
28591     Orientation: {
28592             1: 0, //'top-left',
28593 //            2: 'top-right',
28594             3: 180, //'bottom-right',
28595 //            4: 'bottom-left',
28596 //            5: 'left-top',
28597             6: 90, //'right-top',
28598 //            7: 'right-bottom',
28599             8: 270 //'left-bottom'
28600     },
28601     
28602     exifTagTypes : {
28603         // byte, 8-bit unsigned int:
28604         1: {
28605             getValue: function (dataView, dataOffset) {
28606                 return dataView.getUint8(dataOffset);
28607             },
28608             size: 1
28609         },
28610         // ascii, 8-bit byte:
28611         2: {
28612             getValue: function (dataView, dataOffset) {
28613                 return String.fromCharCode(dataView.getUint8(dataOffset));
28614             },
28615             size: 1,
28616             ascii: true
28617         },
28618         // short, 16 bit int:
28619         3: {
28620             getValue: function (dataView, dataOffset, littleEndian) {
28621                 return dataView.getUint16(dataOffset, littleEndian);
28622             },
28623             size: 2
28624         },
28625         // long, 32 bit int:
28626         4: {
28627             getValue: function (dataView, dataOffset, littleEndian) {
28628                 return dataView.getUint32(dataOffset, littleEndian);
28629             },
28630             size: 4
28631         },
28632         // rational = two long values, first is numerator, second is denominator:
28633         5: {
28634             getValue: function (dataView, dataOffset, littleEndian) {
28635                 return dataView.getUint32(dataOffset, littleEndian) /
28636                     dataView.getUint32(dataOffset + 4, littleEndian);
28637             },
28638             size: 8
28639         },
28640         // slong, 32 bit signed int:
28641         9: {
28642             getValue: function (dataView, dataOffset, littleEndian) {
28643                 return dataView.getInt32(dataOffset, littleEndian);
28644             },
28645             size: 4
28646         },
28647         // srational, two slongs, first is numerator, second is denominator:
28648         10: {
28649             getValue: function (dataView, dataOffset, littleEndian) {
28650                 return dataView.getInt32(dataOffset, littleEndian) /
28651                     dataView.getInt32(dataOffset + 4, littleEndian);
28652             },
28653             size: 8
28654         }
28655     },
28656     
28657     footer : {
28658         STANDARD : [
28659             {
28660                 tag : 'div',
28661                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28662                 action : 'rotate-left',
28663                 cn : [
28664                     {
28665                         tag : 'button',
28666                         cls : 'btn btn-default',
28667                         html : '<i class="fa fa-undo"></i>'
28668                     }
28669                 ]
28670             },
28671             {
28672                 tag : 'div',
28673                 cls : 'btn-group roo-upload-cropbox-picture',
28674                 action : 'picture',
28675                 cn : [
28676                     {
28677                         tag : 'button',
28678                         cls : 'btn btn-default',
28679                         html : '<i class="fa fa-picture-o"></i>'
28680                     }
28681                 ]
28682             },
28683             {
28684                 tag : 'div',
28685                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28686                 action : 'rotate-right',
28687                 cn : [
28688                     {
28689                         tag : 'button',
28690                         cls : 'btn btn-default',
28691                         html : '<i class="fa fa-repeat"></i>'
28692                     }
28693                 ]
28694             }
28695         ],
28696         DOCUMENT : [
28697             {
28698                 tag : 'div',
28699                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28700                 action : 'rotate-left',
28701                 cn : [
28702                     {
28703                         tag : 'button',
28704                         cls : 'btn btn-default',
28705                         html : '<i class="fa fa-undo"></i>'
28706                     }
28707                 ]
28708             },
28709             {
28710                 tag : 'div',
28711                 cls : 'btn-group roo-upload-cropbox-download',
28712                 action : 'download',
28713                 cn : [
28714                     {
28715                         tag : 'button',
28716                         cls : 'btn btn-default',
28717                         html : '<i class="fa fa-download"></i>'
28718                     }
28719                 ]
28720             },
28721             {
28722                 tag : 'div',
28723                 cls : 'btn-group roo-upload-cropbox-crop',
28724                 action : 'crop',
28725                 cn : [
28726                     {
28727                         tag : 'button',
28728                         cls : 'btn btn-default',
28729                         html : '<i class="fa fa-crop"></i>'
28730                     }
28731                 ]
28732             },
28733             {
28734                 tag : 'div',
28735                 cls : 'btn-group roo-upload-cropbox-trash',
28736                 action : 'trash',
28737                 cn : [
28738                     {
28739                         tag : 'button',
28740                         cls : 'btn btn-default',
28741                         html : '<i class="fa fa-trash"></i>'
28742                     }
28743                 ]
28744             },
28745             {
28746                 tag : 'div',
28747                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28748                 action : 'rotate-right',
28749                 cn : [
28750                     {
28751                         tag : 'button',
28752                         cls : 'btn btn-default',
28753                         html : '<i class="fa fa-repeat"></i>'
28754                     }
28755                 ]
28756             }
28757         ],
28758         ROTATOR : [
28759             {
28760                 tag : 'div',
28761                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28762                 action : 'rotate-left',
28763                 cn : [
28764                     {
28765                         tag : 'button',
28766                         cls : 'btn btn-default',
28767                         html : '<i class="fa fa-undo"></i>'
28768                     }
28769                 ]
28770             },
28771             {
28772                 tag : 'div',
28773                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28774                 action : 'rotate-right',
28775                 cn : [
28776                     {
28777                         tag : 'button',
28778                         cls : 'btn btn-default',
28779                         html : '<i class="fa fa-repeat"></i>'
28780                     }
28781                 ]
28782             }
28783         ]
28784     }
28785 });
28786
28787 /*
28788 * Licence: LGPL
28789 */
28790
28791 /**
28792  * @class Roo.bootstrap.DocumentManager
28793  * @extends Roo.bootstrap.Component
28794  * Bootstrap DocumentManager class
28795  * @cfg {String} paramName default 'imageUpload'
28796  * @cfg {String} toolTipName default 'filename'
28797  * @cfg {String} method default POST
28798  * @cfg {String} url action url
28799  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28800  * @cfg {Boolean} multiple multiple upload default true
28801  * @cfg {Number} thumbSize default 300
28802  * @cfg {String} fieldLabel
28803  * @cfg {Number} labelWidth default 4
28804  * @cfg {String} labelAlign (left|top) default left
28805  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28806 * @cfg {Number} labellg set the width of label (1-12)
28807  * @cfg {Number} labelmd set the width of label (1-12)
28808  * @cfg {Number} labelsm set the width of label (1-12)
28809  * @cfg {Number} labelxs set the width of label (1-12)
28810  * 
28811  * @constructor
28812  * Create a new DocumentManager
28813  * @param {Object} config The config object
28814  */
28815
28816 Roo.bootstrap.DocumentManager = function(config){
28817     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28818     
28819     this.files = [];
28820     this.delegates = [];
28821     
28822     this.addEvents({
28823         /**
28824          * @event initial
28825          * Fire when initial the DocumentManager
28826          * @param {Roo.bootstrap.DocumentManager} this
28827          */
28828         "initial" : true,
28829         /**
28830          * @event inspect
28831          * inspect selected file
28832          * @param {Roo.bootstrap.DocumentManager} this
28833          * @param {File} file
28834          */
28835         "inspect" : true,
28836         /**
28837          * @event exception
28838          * Fire when xhr load exception
28839          * @param {Roo.bootstrap.DocumentManager} this
28840          * @param {XMLHttpRequest} xhr
28841          */
28842         "exception" : true,
28843         /**
28844          * @event afterupload
28845          * Fire when xhr load exception
28846          * @param {Roo.bootstrap.DocumentManager} this
28847          * @param {XMLHttpRequest} xhr
28848          */
28849         "afterupload" : true,
28850         /**
28851          * @event prepare
28852          * prepare the form data
28853          * @param {Roo.bootstrap.DocumentManager} this
28854          * @param {Object} formData
28855          */
28856         "prepare" : true,
28857         /**
28858          * @event remove
28859          * Fire when remove the file
28860          * @param {Roo.bootstrap.DocumentManager} this
28861          * @param {Object} file
28862          */
28863         "remove" : true,
28864         /**
28865          * @event refresh
28866          * Fire after refresh the file
28867          * @param {Roo.bootstrap.DocumentManager} this
28868          */
28869         "refresh" : true,
28870         /**
28871          * @event click
28872          * Fire after click the image
28873          * @param {Roo.bootstrap.DocumentManager} this
28874          * @param {Object} file
28875          */
28876         "click" : true,
28877         /**
28878          * @event edit
28879          * Fire when upload a image and editable set to true
28880          * @param {Roo.bootstrap.DocumentManager} this
28881          * @param {Object} file
28882          */
28883         "edit" : true,
28884         /**
28885          * @event beforeselectfile
28886          * Fire before select file
28887          * @param {Roo.bootstrap.DocumentManager} this
28888          */
28889         "beforeselectfile" : true,
28890         /**
28891          * @event process
28892          * Fire before process file
28893          * @param {Roo.bootstrap.DocumentManager} this
28894          * @param {Object} file
28895          */
28896         "process" : true,
28897         /**
28898          * @event previewrendered
28899          * Fire when preview rendered
28900          * @param {Roo.bootstrap.DocumentManager} this
28901          * @param {Object} file
28902          */
28903         "previewrendered" : true,
28904         /**
28905          */
28906         "previewResize" : true
28907         
28908     });
28909 };
28910
28911 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28912     
28913     boxes : 0,
28914     inputName : '',
28915     thumbSize : 300,
28916     multiple : true,
28917     files : false,
28918     method : 'POST',
28919     url : '',
28920     paramName : 'imageUpload',
28921     toolTipName : 'filename',
28922     fieldLabel : '',
28923     labelWidth : 4,
28924     labelAlign : 'left',
28925     editable : true,
28926     delegates : false,
28927     xhr : false, 
28928     
28929     labellg : 0,
28930     labelmd : 0,
28931     labelsm : 0,
28932     labelxs : 0,
28933     
28934     getAutoCreate : function()
28935     {   
28936         var managerWidget = {
28937             tag : 'div',
28938             cls : 'roo-document-manager',
28939             cn : [
28940                 {
28941                     tag : 'input',
28942                     cls : 'roo-document-manager-selector',
28943                     type : 'file'
28944                 },
28945                 {
28946                     tag : 'div',
28947                     cls : 'roo-document-manager-uploader',
28948                     cn : [
28949                         {
28950                             tag : 'div',
28951                             cls : 'roo-document-manager-upload-btn',
28952                             html : '<i class="fa fa-plus"></i>'
28953                         }
28954                     ]
28955                     
28956                 }
28957             ]
28958         };
28959         
28960         var content = [
28961             {
28962                 tag : 'div',
28963                 cls : 'column col-md-12',
28964                 cn : managerWidget
28965             }
28966         ];
28967         
28968         if(this.fieldLabel.length){
28969             
28970             content = [
28971                 {
28972                     tag : 'div',
28973                     cls : 'column col-md-12',
28974                     html : this.fieldLabel
28975                 },
28976                 {
28977                     tag : 'div',
28978                     cls : 'column col-md-12',
28979                     cn : managerWidget
28980                 }
28981             ];
28982
28983             if(this.labelAlign == 'left'){
28984                 content = [
28985                     {
28986                         tag : 'div',
28987                         cls : 'column',
28988                         html : this.fieldLabel
28989                     },
28990                     {
28991                         tag : 'div',
28992                         cls : 'column',
28993                         cn : managerWidget
28994                     }
28995                 ];
28996                 
28997                 if(this.labelWidth > 12){
28998                     content[0].style = "width: " + this.labelWidth + 'px';
28999                 }
29000
29001                 if(this.labelWidth < 13 && this.labelmd == 0){
29002                     this.labelmd = this.labelWidth;
29003                 }
29004
29005                 if(this.labellg > 0){
29006                     content[0].cls += ' col-lg-' + this.labellg;
29007                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29008                 }
29009
29010                 if(this.labelmd > 0){
29011                     content[0].cls += ' col-md-' + this.labelmd;
29012                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29013                 }
29014
29015                 if(this.labelsm > 0){
29016                     content[0].cls += ' col-sm-' + this.labelsm;
29017                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29018                 }
29019
29020                 if(this.labelxs > 0){
29021                     content[0].cls += ' col-xs-' + this.labelxs;
29022                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29023                 }
29024                 
29025             }
29026         }
29027         
29028         var cfg = {
29029             tag : 'div',
29030             cls : 'row clearfix',
29031             cn : content
29032         };
29033         
29034         return cfg;
29035         
29036     },
29037     
29038     initEvents : function()
29039     {
29040         this.managerEl = this.el.select('.roo-document-manager', true).first();
29041         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29042         
29043         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29044         this.selectorEl.hide();
29045         
29046         if(this.multiple){
29047             this.selectorEl.attr('multiple', 'multiple');
29048         }
29049         
29050         this.selectorEl.on('change', this.onFileSelected, this);
29051         
29052         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29053         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29054         
29055         this.uploader.on('click', this.onUploaderClick, this);
29056         
29057         this.renderProgressDialog();
29058         
29059         var _this = this;
29060         
29061         window.addEventListener("resize", function() { _this.refresh(); } );
29062         
29063         this.fireEvent('initial', this);
29064     },
29065     
29066     renderProgressDialog : function()
29067     {
29068         var _this = this;
29069         
29070         this.progressDialog = new Roo.bootstrap.Modal({
29071             cls : 'roo-document-manager-progress-dialog',
29072             allow_close : false,
29073             title : '',
29074             buttons : [
29075                 {
29076                     name  :'cancel',
29077                     weight : 'danger',
29078                     html : 'Cancel'
29079                 }
29080             ], 
29081             listeners : { 
29082                 btnclick : function() {
29083                     _this.uploadCancel();
29084                     this.hide();
29085                 }
29086             }
29087         });
29088          
29089         this.progressDialog.render(Roo.get(document.body));
29090          
29091         this.progress = new Roo.bootstrap.Progress({
29092             cls : 'roo-document-manager-progress',
29093             active : true,
29094             striped : true
29095         });
29096         
29097         this.progress.render(this.progressDialog.getChildContainer());
29098         
29099         this.progressBar = new Roo.bootstrap.ProgressBar({
29100             cls : 'roo-document-manager-progress-bar',
29101             aria_valuenow : 0,
29102             aria_valuemin : 0,
29103             aria_valuemax : 12,
29104             panel : 'success'
29105         });
29106         
29107         this.progressBar.render(this.progress.getChildContainer());
29108     },
29109     
29110     onUploaderClick : function(e)
29111     {
29112         e.preventDefault();
29113      
29114         if(this.fireEvent('beforeselectfile', this) != false){
29115             this.selectorEl.dom.click();
29116         }
29117         
29118     },
29119     
29120     onFileSelected : function(e)
29121     {
29122         e.preventDefault();
29123         
29124         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29125             return;
29126         }
29127         
29128         Roo.each(this.selectorEl.dom.files, function(file){
29129             if(this.fireEvent('inspect', this, file) != false){
29130                 this.files.push(file);
29131             }
29132         }, this);
29133         
29134         this.queue();
29135         
29136     },
29137     
29138     queue : function()
29139     {
29140         this.selectorEl.dom.value = '';
29141         
29142         if(!this.files || !this.files.length){
29143             return;
29144         }
29145         
29146         if(this.boxes > 0 && this.files.length > this.boxes){
29147             this.files = this.files.slice(0, this.boxes);
29148         }
29149         
29150         this.uploader.show();
29151         
29152         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29153             this.uploader.hide();
29154         }
29155         
29156         var _this = this;
29157         
29158         var files = [];
29159         
29160         var docs = [];
29161         
29162         Roo.each(this.files, function(file){
29163             
29164             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29165                 var f = this.renderPreview(file);
29166                 files.push(f);
29167                 return;
29168             }
29169             
29170             if(file.type.indexOf('image') != -1){
29171                 this.delegates.push(
29172                     (function(){
29173                         _this.process(file);
29174                     }).createDelegate(this)
29175                 );
29176         
29177                 return;
29178             }
29179             
29180             docs.push(
29181                 (function(){
29182                     _this.process(file);
29183                 }).createDelegate(this)
29184             );
29185             
29186         }, this);
29187         
29188         this.files = files;
29189         
29190         this.delegates = this.delegates.concat(docs);
29191         
29192         if(!this.delegates.length){
29193             this.refresh();
29194             return;
29195         }
29196         
29197         this.progressBar.aria_valuemax = this.delegates.length;
29198         
29199         this.arrange();
29200         
29201         return;
29202     },
29203     
29204     arrange : function()
29205     {
29206         if(!this.delegates.length){
29207             this.progressDialog.hide();
29208             this.refresh();
29209             return;
29210         }
29211         
29212         var delegate = this.delegates.shift();
29213         
29214         this.progressDialog.show();
29215         
29216         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29217         
29218         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29219         
29220         delegate();
29221     },
29222     
29223     refresh : function()
29224     {
29225         this.uploader.show();
29226         
29227         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29228             this.uploader.hide();
29229         }
29230         
29231         Roo.isTouch ? this.closable(false) : this.closable(true);
29232         
29233         this.fireEvent('refresh', this);
29234     },
29235     
29236     onRemove : function(e, el, o)
29237     {
29238         e.preventDefault();
29239         
29240         this.fireEvent('remove', this, o);
29241         
29242     },
29243     
29244     remove : function(o)
29245     {
29246         var files = [];
29247         
29248         Roo.each(this.files, function(file){
29249             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29250                 files.push(file);
29251                 return;
29252             }
29253
29254             o.target.remove();
29255
29256         }, this);
29257         
29258         this.files = files;
29259         
29260         this.refresh();
29261     },
29262     
29263     clear : function()
29264     {
29265         Roo.each(this.files, function(file){
29266             if(!file.target){
29267                 return;
29268             }
29269             
29270             file.target.remove();
29271
29272         }, this);
29273         
29274         this.files = [];
29275         
29276         this.refresh();
29277     },
29278     
29279     onClick : function(e, el, o)
29280     {
29281         e.preventDefault();
29282         
29283         this.fireEvent('click', this, o);
29284         
29285     },
29286     
29287     closable : function(closable)
29288     {
29289         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29290             
29291             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29292             
29293             if(closable){
29294                 el.show();
29295                 return;
29296             }
29297             
29298             el.hide();
29299             
29300         }, this);
29301     },
29302     
29303     xhrOnLoad : function(xhr)
29304     {
29305         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29306             el.remove();
29307         }, this);
29308         
29309         if (xhr.readyState !== 4) {
29310             this.arrange();
29311             this.fireEvent('exception', this, xhr);
29312             return;
29313         }
29314
29315         var response = Roo.decode(xhr.responseText);
29316         
29317         if(!response.success){
29318             this.arrange();
29319             this.fireEvent('exception', this, xhr);
29320             return;
29321         }
29322         
29323         var file = this.renderPreview(response.data);
29324         
29325         this.files.push(file);
29326         
29327         this.arrange();
29328         
29329         this.fireEvent('afterupload', this, xhr);
29330         
29331     },
29332     
29333     xhrOnError : function(xhr)
29334     {
29335         Roo.log('xhr on error');
29336         
29337         var response = Roo.decode(xhr.responseText);
29338           
29339         Roo.log(response);
29340         
29341         this.arrange();
29342     },
29343     
29344     process : function(file)
29345     {
29346         if(this.fireEvent('process', this, file) !== false){
29347             if(this.editable && file.type.indexOf('image') != -1){
29348                 this.fireEvent('edit', this, file);
29349                 return;
29350             }
29351
29352             this.uploadStart(file, false);
29353
29354             return;
29355         }
29356         
29357     },
29358     
29359     uploadStart : function(file, crop)
29360     {
29361         this.xhr = new XMLHttpRequest();
29362         
29363         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29364             this.arrange();
29365             return;
29366         }
29367         
29368         file.xhr = this.xhr;
29369             
29370         this.managerEl.createChild({
29371             tag : 'div',
29372             cls : 'roo-document-manager-loading',
29373             cn : [
29374                 {
29375                     tag : 'div',
29376                     tooltip : file.name,
29377                     cls : 'roo-document-manager-thumb',
29378                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29379                 }
29380             ]
29381
29382         });
29383
29384         this.xhr.open(this.method, this.url, true);
29385         
29386         var headers = {
29387             "Accept": "application/json",
29388             "Cache-Control": "no-cache",
29389             "X-Requested-With": "XMLHttpRequest"
29390         };
29391         
29392         for (var headerName in headers) {
29393             var headerValue = headers[headerName];
29394             if (headerValue) {
29395                 this.xhr.setRequestHeader(headerName, headerValue);
29396             }
29397         }
29398         
29399         var _this = this;
29400         
29401         this.xhr.onload = function()
29402         {
29403             _this.xhrOnLoad(_this.xhr);
29404         }
29405         
29406         this.xhr.onerror = function()
29407         {
29408             _this.xhrOnError(_this.xhr);
29409         }
29410         
29411         var formData = new FormData();
29412
29413         formData.append('returnHTML', 'NO');
29414         
29415         if(crop){
29416             formData.append('crop', crop);
29417         }
29418         
29419         formData.append(this.paramName, file, file.name);
29420         
29421         var options = {
29422             file : file, 
29423             manually : false
29424         };
29425         
29426         if(this.fireEvent('prepare', this, formData, options) != false){
29427             
29428             if(options.manually){
29429                 return;
29430             }
29431             
29432             this.xhr.send(formData);
29433             return;
29434         };
29435         
29436         this.uploadCancel();
29437     },
29438     
29439     uploadCancel : function()
29440     {
29441         if (this.xhr) {
29442             this.xhr.abort();
29443         }
29444         
29445         this.delegates = [];
29446         
29447         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29448             el.remove();
29449         }, this);
29450         
29451         this.arrange();
29452     },
29453     
29454     renderPreview : function(file)
29455     {
29456         if(typeof(file.target) != 'undefined' && file.target){
29457             return file;
29458         }
29459         
29460         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29461         
29462         var previewEl = this.managerEl.createChild({
29463             tag : 'div',
29464             cls : 'roo-document-manager-preview',
29465             cn : [
29466                 {
29467                     tag : 'div',
29468                     tooltip : file[this.toolTipName],
29469                     cls : 'roo-document-manager-thumb',
29470                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29471                 },
29472                 {
29473                     tag : 'button',
29474                     cls : 'close',
29475                     html : '<i class="fa fa-times-circle"></i>'
29476                 }
29477             ]
29478         });
29479
29480         var close = previewEl.select('button.close', true).first();
29481
29482         close.on('click', this.onRemove, this, file);
29483
29484         file.target = previewEl;
29485
29486         var image = previewEl.select('img', true).first();
29487         
29488         var _this = this;
29489         
29490         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29491         
29492         image.on('click', this.onClick, this, file);
29493         
29494         this.fireEvent('previewrendered', this, file);
29495         
29496         return file;
29497         
29498     },
29499     
29500     onPreviewLoad : function(file, image)
29501     {
29502         if(typeof(file.target) == 'undefined' || !file.target){
29503             return;
29504         }
29505         
29506         var width = image.dom.naturalWidth || image.dom.width;
29507         var height = image.dom.naturalHeight || image.dom.height;
29508         
29509         if(!this.previewResize) {
29510             return;
29511         }
29512         
29513         if(width > height){
29514             file.target.addClass('wide');
29515             return;
29516         }
29517         
29518         file.target.addClass('tall');
29519         return;
29520         
29521     },
29522     
29523     uploadFromSource : function(file, crop)
29524     {
29525         this.xhr = new XMLHttpRequest();
29526         
29527         this.managerEl.createChild({
29528             tag : 'div',
29529             cls : 'roo-document-manager-loading',
29530             cn : [
29531                 {
29532                     tag : 'div',
29533                     tooltip : file.name,
29534                     cls : 'roo-document-manager-thumb',
29535                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29536                 }
29537             ]
29538
29539         });
29540
29541         this.xhr.open(this.method, this.url, true);
29542         
29543         var headers = {
29544             "Accept": "application/json",
29545             "Cache-Control": "no-cache",
29546             "X-Requested-With": "XMLHttpRequest"
29547         };
29548         
29549         for (var headerName in headers) {
29550             var headerValue = headers[headerName];
29551             if (headerValue) {
29552                 this.xhr.setRequestHeader(headerName, headerValue);
29553             }
29554         }
29555         
29556         var _this = this;
29557         
29558         this.xhr.onload = function()
29559         {
29560             _this.xhrOnLoad(_this.xhr);
29561         }
29562         
29563         this.xhr.onerror = function()
29564         {
29565             _this.xhrOnError(_this.xhr);
29566         }
29567         
29568         var formData = new FormData();
29569
29570         formData.append('returnHTML', 'NO');
29571         
29572         formData.append('crop', crop);
29573         
29574         if(typeof(file.filename) != 'undefined'){
29575             formData.append('filename', file.filename);
29576         }
29577         
29578         if(typeof(file.mimetype) != 'undefined'){
29579             formData.append('mimetype', file.mimetype);
29580         }
29581         
29582         Roo.log(formData);
29583         
29584         if(this.fireEvent('prepare', this, formData) != false){
29585             this.xhr.send(formData);
29586         };
29587     }
29588 });
29589
29590 /*
29591 * Licence: LGPL
29592 */
29593
29594 /**
29595  * @class Roo.bootstrap.DocumentViewer
29596  * @extends Roo.bootstrap.Component
29597  * Bootstrap DocumentViewer class
29598  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29599  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29600  * 
29601  * @constructor
29602  * Create a new DocumentViewer
29603  * @param {Object} config The config object
29604  */
29605
29606 Roo.bootstrap.DocumentViewer = function(config){
29607     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29608     
29609     this.addEvents({
29610         /**
29611          * @event initial
29612          * Fire after initEvent
29613          * @param {Roo.bootstrap.DocumentViewer} this
29614          */
29615         "initial" : true,
29616         /**
29617          * @event click
29618          * Fire after click
29619          * @param {Roo.bootstrap.DocumentViewer} this
29620          */
29621         "click" : true,
29622         /**
29623          * @event download
29624          * Fire after download button
29625          * @param {Roo.bootstrap.DocumentViewer} this
29626          */
29627         "download" : true,
29628         /**
29629          * @event trash
29630          * Fire after trash button
29631          * @param {Roo.bootstrap.DocumentViewer} this
29632          */
29633         "trash" : true
29634         
29635     });
29636 };
29637
29638 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29639     
29640     showDownload : true,
29641     
29642     showTrash : true,
29643     
29644     getAutoCreate : function()
29645     {
29646         var cfg = {
29647             tag : 'div',
29648             cls : 'roo-document-viewer',
29649             cn : [
29650                 {
29651                     tag : 'div',
29652                     cls : 'roo-document-viewer-body',
29653                     cn : [
29654                         {
29655                             tag : 'div',
29656                             cls : 'roo-document-viewer-thumb',
29657                             cn : [
29658                                 {
29659                                     tag : 'img',
29660                                     cls : 'roo-document-viewer-image'
29661                                 }
29662                             ]
29663                         }
29664                     ]
29665                 },
29666                 {
29667                     tag : 'div',
29668                     cls : 'roo-document-viewer-footer',
29669                     cn : {
29670                         tag : 'div',
29671                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29672                         cn : [
29673                             {
29674                                 tag : 'div',
29675                                 cls : 'btn-group roo-document-viewer-download',
29676                                 cn : [
29677                                     {
29678                                         tag : 'button',
29679                                         cls : 'btn btn-default',
29680                                         html : '<i class="fa fa-download"></i>'
29681                                     }
29682                                 ]
29683                             },
29684                             {
29685                                 tag : 'div',
29686                                 cls : 'btn-group roo-document-viewer-trash',
29687                                 cn : [
29688                                     {
29689                                         tag : 'button',
29690                                         cls : 'btn btn-default',
29691                                         html : '<i class="fa fa-trash"></i>'
29692                                     }
29693                                 ]
29694                             }
29695                         ]
29696                     }
29697                 }
29698             ]
29699         };
29700         
29701         return cfg;
29702     },
29703     
29704     initEvents : function()
29705     {
29706         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29707         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29708         
29709         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29710         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29711         
29712         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29713         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29714         
29715         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29716         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29717         
29718         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29719         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29720         
29721         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29722         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29723         
29724         this.bodyEl.on('click', this.onClick, this);
29725         this.downloadBtn.on('click', this.onDownload, this);
29726         this.trashBtn.on('click', this.onTrash, this);
29727         
29728         this.downloadBtn.hide();
29729         this.trashBtn.hide();
29730         
29731         if(this.showDownload){
29732             this.downloadBtn.show();
29733         }
29734         
29735         if(this.showTrash){
29736             this.trashBtn.show();
29737         }
29738         
29739         if(!this.showDownload && !this.showTrash) {
29740             this.footerEl.hide();
29741         }
29742         
29743     },
29744     
29745     initial : function()
29746     {
29747         this.fireEvent('initial', this);
29748         
29749     },
29750     
29751     onClick : function(e)
29752     {
29753         e.preventDefault();
29754         
29755         this.fireEvent('click', this);
29756     },
29757     
29758     onDownload : function(e)
29759     {
29760         e.preventDefault();
29761         
29762         this.fireEvent('download', this);
29763     },
29764     
29765     onTrash : function(e)
29766     {
29767         e.preventDefault();
29768         
29769         this.fireEvent('trash', this);
29770     }
29771     
29772 });
29773 /*
29774  * - LGPL
29775  *
29776  * nav progress bar
29777  * 
29778  */
29779
29780 /**
29781  * @class Roo.bootstrap.NavProgressBar
29782  * @extends Roo.bootstrap.Component
29783  * Bootstrap NavProgressBar class
29784  * 
29785  * @constructor
29786  * Create a new nav progress bar
29787  * @param {Object} config The config object
29788  */
29789
29790 Roo.bootstrap.NavProgressBar = function(config){
29791     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29792
29793     this.bullets = this.bullets || [];
29794    
29795 //    Roo.bootstrap.NavProgressBar.register(this);
29796      this.addEvents({
29797         /**
29798              * @event changed
29799              * Fires when the active item changes
29800              * @param {Roo.bootstrap.NavProgressBar} this
29801              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29802              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29803          */
29804         'changed': true
29805      });
29806     
29807 };
29808
29809 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29810     
29811     bullets : [],
29812     barItems : [],
29813     
29814     getAutoCreate : function()
29815     {
29816         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29817         
29818         cfg = {
29819             tag : 'div',
29820             cls : 'roo-navigation-bar-group',
29821             cn : [
29822                 {
29823                     tag : 'div',
29824                     cls : 'roo-navigation-top-bar'
29825                 },
29826                 {
29827                     tag : 'div',
29828                     cls : 'roo-navigation-bullets-bar',
29829                     cn : [
29830                         {
29831                             tag : 'ul',
29832                             cls : 'roo-navigation-bar'
29833                         }
29834                     ]
29835                 },
29836                 
29837                 {
29838                     tag : 'div',
29839                     cls : 'roo-navigation-bottom-bar'
29840                 }
29841             ]
29842             
29843         };
29844         
29845         return cfg;
29846         
29847     },
29848     
29849     initEvents: function() 
29850     {
29851         
29852     },
29853     
29854     onRender : function(ct, position) 
29855     {
29856         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29857         
29858         if(this.bullets.length){
29859             Roo.each(this.bullets, function(b){
29860                this.addItem(b);
29861             }, this);
29862         }
29863         
29864         this.format();
29865         
29866     },
29867     
29868     addItem : function(cfg)
29869     {
29870         var item = new Roo.bootstrap.NavProgressItem(cfg);
29871         
29872         item.parentId = this.id;
29873         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29874         
29875         if(cfg.html){
29876             var top = new Roo.bootstrap.Element({
29877                 tag : 'div',
29878                 cls : 'roo-navigation-bar-text'
29879             });
29880             
29881             var bottom = new Roo.bootstrap.Element({
29882                 tag : 'div',
29883                 cls : 'roo-navigation-bar-text'
29884             });
29885             
29886             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29887             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29888             
29889             var topText = new Roo.bootstrap.Element({
29890                 tag : 'span',
29891                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29892             });
29893             
29894             var bottomText = new Roo.bootstrap.Element({
29895                 tag : 'span',
29896                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29897             });
29898             
29899             topText.onRender(top.el, null);
29900             bottomText.onRender(bottom.el, null);
29901             
29902             item.topEl = top;
29903             item.bottomEl = bottom;
29904         }
29905         
29906         this.barItems.push(item);
29907         
29908         return item;
29909     },
29910     
29911     getActive : function()
29912     {
29913         var active = false;
29914         
29915         Roo.each(this.barItems, function(v){
29916             
29917             if (!v.isActive()) {
29918                 return;
29919             }
29920             
29921             active = v;
29922             return false;
29923             
29924         });
29925         
29926         return active;
29927     },
29928     
29929     setActiveItem : function(item)
29930     {
29931         var prev = false;
29932         
29933         Roo.each(this.barItems, function(v){
29934             if (v.rid == item.rid) {
29935                 return ;
29936             }
29937             
29938             if (v.isActive()) {
29939                 v.setActive(false);
29940                 prev = v;
29941             }
29942         });
29943
29944         item.setActive(true);
29945         
29946         this.fireEvent('changed', this, item, prev);
29947     },
29948     
29949     getBarItem: function(rid)
29950     {
29951         var ret = false;
29952         
29953         Roo.each(this.barItems, function(e) {
29954             if (e.rid != rid) {
29955                 return;
29956             }
29957             
29958             ret =  e;
29959             return false;
29960         });
29961         
29962         return ret;
29963     },
29964     
29965     indexOfItem : function(item)
29966     {
29967         var index = false;
29968         
29969         Roo.each(this.barItems, function(v, i){
29970             
29971             if (v.rid != item.rid) {
29972                 return;
29973             }
29974             
29975             index = i;
29976             return false
29977         });
29978         
29979         return index;
29980     },
29981     
29982     setActiveNext : function()
29983     {
29984         var i = this.indexOfItem(this.getActive());
29985         
29986         if (i > this.barItems.length) {
29987             return;
29988         }
29989         
29990         this.setActiveItem(this.barItems[i+1]);
29991     },
29992     
29993     setActivePrev : function()
29994     {
29995         var i = this.indexOfItem(this.getActive());
29996         
29997         if (i  < 1) {
29998             return;
29999         }
30000         
30001         this.setActiveItem(this.barItems[i-1]);
30002     },
30003     
30004     format : function()
30005     {
30006         if(!this.barItems.length){
30007             return;
30008         }
30009      
30010         var width = 100 / this.barItems.length;
30011         
30012         Roo.each(this.barItems, function(i){
30013             i.el.setStyle('width', width + '%');
30014             i.topEl.el.setStyle('width', width + '%');
30015             i.bottomEl.el.setStyle('width', width + '%');
30016         }, this);
30017         
30018     }
30019     
30020 });
30021 /*
30022  * - LGPL
30023  *
30024  * Nav Progress Item
30025  * 
30026  */
30027
30028 /**
30029  * @class Roo.bootstrap.NavProgressItem
30030  * @extends Roo.bootstrap.Component
30031  * Bootstrap NavProgressItem class
30032  * @cfg {String} rid the reference id
30033  * @cfg {Boolean} active (true|false) Is item active default false
30034  * @cfg {Boolean} disabled (true|false) Is item active default false
30035  * @cfg {String} html
30036  * @cfg {String} position (top|bottom) text position default bottom
30037  * @cfg {String} icon show icon instead of number
30038  * 
30039  * @constructor
30040  * Create a new NavProgressItem
30041  * @param {Object} config The config object
30042  */
30043 Roo.bootstrap.NavProgressItem = function(config){
30044     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30045     this.addEvents({
30046         // raw events
30047         /**
30048          * @event click
30049          * The raw click event for the entire grid.
30050          * @param {Roo.bootstrap.NavProgressItem} this
30051          * @param {Roo.EventObject} e
30052          */
30053         "click" : true
30054     });
30055    
30056 };
30057
30058 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30059     
30060     rid : '',
30061     active : false,
30062     disabled : false,
30063     html : '',
30064     position : 'bottom',
30065     icon : false,
30066     
30067     getAutoCreate : function()
30068     {
30069         var iconCls = 'roo-navigation-bar-item-icon';
30070         
30071         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30072         
30073         var cfg = {
30074             tag: 'li',
30075             cls: 'roo-navigation-bar-item',
30076             cn : [
30077                 {
30078                     tag : 'i',
30079                     cls : iconCls
30080                 }
30081             ]
30082         };
30083         
30084         if(this.active){
30085             cfg.cls += ' active';
30086         }
30087         if(this.disabled){
30088             cfg.cls += ' disabled';
30089         }
30090         
30091         return cfg;
30092     },
30093     
30094     disable : function()
30095     {
30096         this.setDisabled(true);
30097     },
30098     
30099     enable : function()
30100     {
30101         this.setDisabled(false);
30102     },
30103     
30104     initEvents: function() 
30105     {
30106         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30107         
30108         this.iconEl.on('click', this.onClick, this);
30109     },
30110     
30111     onClick : function(e)
30112     {
30113         e.preventDefault();
30114         
30115         if(this.disabled){
30116             return;
30117         }
30118         
30119         if(this.fireEvent('click', this, e) === false){
30120             return;
30121         };
30122         
30123         this.parent().setActiveItem(this);
30124     },
30125     
30126     isActive: function () 
30127     {
30128         return this.active;
30129     },
30130     
30131     setActive : function(state)
30132     {
30133         if(this.active == state){
30134             return;
30135         }
30136         
30137         this.active = state;
30138         
30139         if (state) {
30140             this.el.addClass('active');
30141             return;
30142         }
30143         
30144         this.el.removeClass('active');
30145         
30146         return;
30147     },
30148     
30149     setDisabled : function(state)
30150     {
30151         if(this.disabled == state){
30152             return;
30153         }
30154         
30155         this.disabled = state;
30156         
30157         if (state) {
30158             this.el.addClass('disabled');
30159             return;
30160         }
30161         
30162         this.el.removeClass('disabled');
30163     },
30164     
30165     tooltipEl : function()
30166     {
30167         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30168     }
30169 });
30170  
30171
30172  /*
30173  * - LGPL
30174  *
30175  * FieldLabel
30176  * 
30177  */
30178
30179 /**
30180  * @class Roo.bootstrap.FieldLabel
30181  * @extends Roo.bootstrap.Component
30182  * Bootstrap FieldLabel class
30183  * @cfg {String} html contents of the element
30184  * @cfg {String} tag tag of the element default label
30185  * @cfg {String} cls class of the element
30186  * @cfg {String} target label target 
30187  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30188  * @cfg {String} invalidClass default "text-warning"
30189  * @cfg {String} validClass default "text-success"
30190  * @cfg {String} iconTooltip default "This field is required"
30191  * @cfg {String} indicatorpos (left|right) default left
30192  * 
30193  * @constructor
30194  * Create a new FieldLabel
30195  * @param {Object} config The config object
30196  */
30197
30198 Roo.bootstrap.FieldLabel = function(config){
30199     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30200     
30201     this.addEvents({
30202             /**
30203              * @event invalid
30204              * Fires after the field has been marked as invalid.
30205              * @param {Roo.form.FieldLabel} this
30206              * @param {String} msg The validation message
30207              */
30208             invalid : true,
30209             /**
30210              * @event valid
30211              * Fires after the field has been validated with no errors.
30212              * @param {Roo.form.FieldLabel} this
30213              */
30214             valid : true
30215         });
30216 };
30217
30218 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30219     
30220     tag: 'label',
30221     cls: '',
30222     html: '',
30223     target: '',
30224     allowBlank : true,
30225     invalidClass : 'has-warning',
30226     validClass : 'has-success',
30227     iconTooltip : 'This field is required',
30228     indicatorpos : 'left',
30229     
30230     getAutoCreate : function(){
30231         
30232         var cls = "";
30233         if (!this.allowBlank) {
30234             cls  = "visible";
30235         }
30236         
30237         var cfg = {
30238             tag : this.tag,
30239             cls : 'roo-bootstrap-field-label ' + this.cls,
30240             for : this.target,
30241             cn : [
30242                 {
30243                     tag : 'i',
30244                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30245                     tooltip : this.iconTooltip
30246                 },
30247                 {
30248                     tag : 'span',
30249                     html : this.html
30250                 }
30251             ] 
30252         };
30253         
30254         if(this.indicatorpos == 'right'){
30255             var cfg = {
30256                 tag : this.tag,
30257                 cls : 'roo-bootstrap-field-label ' + this.cls,
30258                 for : this.target,
30259                 cn : [
30260                     {
30261                         tag : 'span',
30262                         html : this.html
30263                     },
30264                     {
30265                         tag : 'i',
30266                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30267                         tooltip : this.iconTooltip
30268                     }
30269                 ] 
30270             };
30271         }
30272         
30273         return cfg;
30274     },
30275     
30276     initEvents: function() 
30277     {
30278         Roo.bootstrap.Element.superclass.initEvents.call(this);
30279         
30280         this.indicator = this.indicatorEl();
30281         
30282         if(this.indicator){
30283             this.indicator.removeClass('visible');
30284             this.indicator.addClass('invisible');
30285         }
30286         
30287         Roo.bootstrap.FieldLabel.register(this);
30288     },
30289     
30290     indicatorEl : function()
30291     {
30292         var indicator = this.el.select('i.roo-required-indicator',true).first();
30293         
30294         if(!indicator){
30295             return false;
30296         }
30297         
30298         return indicator;
30299         
30300     },
30301     
30302     /**
30303      * Mark this field as valid
30304      */
30305     markValid : function()
30306     {
30307         if(this.indicator){
30308             this.indicator.removeClass('visible');
30309             this.indicator.addClass('invisible');
30310         }
30311         
30312         this.el.removeClass(this.invalidClass);
30313         
30314         this.el.addClass(this.validClass);
30315         
30316         this.fireEvent('valid', this);
30317     },
30318     
30319     /**
30320      * Mark this field as invalid
30321      * @param {String} msg The validation message
30322      */
30323     markInvalid : function(msg)
30324     {
30325         if(this.indicator){
30326             this.indicator.removeClass('invisible');
30327             this.indicator.addClass('visible');
30328         }
30329         
30330         this.el.removeClass(this.validClass);
30331         
30332         this.el.addClass(this.invalidClass);
30333         
30334         this.fireEvent('invalid', this, msg);
30335     }
30336     
30337    
30338 });
30339
30340 Roo.apply(Roo.bootstrap.FieldLabel, {
30341     
30342     groups: {},
30343     
30344      /**
30345     * register a FieldLabel Group
30346     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30347     */
30348     register : function(label)
30349     {
30350         if(this.groups.hasOwnProperty(label.target)){
30351             return;
30352         }
30353      
30354         this.groups[label.target] = label;
30355         
30356     },
30357     /**
30358     * fetch a FieldLabel Group based on the target
30359     * @param {string} target
30360     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30361     */
30362     get: function(target) {
30363         if (typeof(this.groups[target]) == 'undefined') {
30364             return false;
30365         }
30366         
30367         return this.groups[target] ;
30368     }
30369 });
30370
30371  
30372
30373  /*
30374  * - LGPL
30375  *
30376  * page DateSplitField.
30377  * 
30378  */
30379
30380
30381 /**
30382  * @class Roo.bootstrap.DateSplitField
30383  * @extends Roo.bootstrap.Component
30384  * Bootstrap DateSplitField class
30385  * @cfg {string} fieldLabel - the label associated
30386  * @cfg {Number} labelWidth set the width of label (0-12)
30387  * @cfg {String} labelAlign (top|left)
30388  * @cfg {Boolean} dayAllowBlank (true|false) default false
30389  * @cfg {Boolean} monthAllowBlank (true|false) default false
30390  * @cfg {Boolean} yearAllowBlank (true|false) default false
30391  * @cfg {string} dayPlaceholder 
30392  * @cfg {string} monthPlaceholder
30393  * @cfg {string} yearPlaceholder
30394  * @cfg {string} dayFormat default 'd'
30395  * @cfg {string} monthFormat default 'm'
30396  * @cfg {string} yearFormat default 'Y'
30397  * @cfg {Number} labellg set the width of label (1-12)
30398  * @cfg {Number} labelmd set the width of label (1-12)
30399  * @cfg {Number} labelsm set the width of label (1-12)
30400  * @cfg {Number} labelxs set the width of label (1-12)
30401
30402  *     
30403  * @constructor
30404  * Create a new DateSplitField
30405  * @param {Object} config The config object
30406  */
30407
30408 Roo.bootstrap.DateSplitField = function(config){
30409     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30410     
30411     this.addEvents({
30412         // raw events
30413          /**
30414          * @event years
30415          * getting the data of years
30416          * @param {Roo.bootstrap.DateSplitField} this
30417          * @param {Object} years
30418          */
30419         "years" : true,
30420         /**
30421          * @event days
30422          * getting the data of days
30423          * @param {Roo.bootstrap.DateSplitField} this
30424          * @param {Object} days
30425          */
30426         "days" : true,
30427         /**
30428          * @event invalid
30429          * Fires after the field has been marked as invalid.
30430          * @param {Roo.form.Field} this
30431          * @param {String} msg The validation message
30432          */
30433         invalid : true,
30434        /**
30435          * @event valid
30436          * Fires after the field has been validated with no errors.
30437          * @param {Roo.form.Field} this
30438          */
30439         valid : true
30440     });
30441 };
30442
30443 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30444     
30445     fieldLabel : '',
30446     labelAlign : 'top',
30447     labelWidth : 3,
30448     dayAllowBlank : false,
30449     monthAllowBlank : false,
30450     yearAllowBlank : false,
30451     dayPlaceholder : '',
30452     monthPlaceholder : '',
30453     yearPlaceholder : '',
30454     dayFormat : 'd',
30455     monthFormat : 'm',
30456     yearFormat : 'Y',
30457     isFormField : true,
30458     labellg : 0,
30459     labelmd : 0,
30460     labelsm : 0,
30461     labelxs : 0,
30462     
30463     getAutoCreate : function()
30464     {
30465         var cfg = {
30466             tag : 'div',
30467             cls : 'row roo-date-split-field-group',
30468             cn : [
30469                 {
30470                     tag : 'input',
30471                     type : 'hidden',
30472                     cls : 'form-hidden-field roo-date-split-field-group-value',
30473                     name : this.name
30474                 }
30475             ]
30476         };
30477         
30478         var labelCls = 'col-md-12';
30479         var contentCls = 'col-md-4';
30480         
30481         if(this.fieldLabel){
30482             
30483             var label = {
30484                 tag : 'div',
30485                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30486                 cn : [
30487                     {
30488                         tag : 'label',
30489                         html : this.fieldLabel
30490                     }
30491                 ]
30492             };
30493             
30494             if(this.labelAlign == 'left'){
30495             
30496                 if(this.labelWidth > 12){
30497                     label.style = "width: " + this.labelWidth + 'px';
30498                 }
30499
30500                 if(this.labelWidth < 13 && this.labelmd == 0){
30501                     this.labelmd = this.labelWidth;
30502                 }
30503
30504                 if(this.labellg > 0){
30505                     labelCls = ' col-lg-' + this.labellg;
30506                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30507                 }
30508
30509                 if(this.labelmd > 0){
30510                     labelCls = ' col-md-' + this.labelmd;
30511                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30512                 }
30513
30514                 if(this.labelsm > 0){
30515                     labelCls = ' col-sm-' + this.labelsm;
30516                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30517                 }
30518
30519                 if(this.labelxs > 0){
30520                     labelCls = ' col-xs-' + this.labelxs;
30521                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30522                 }
30523             }
30524             
30525             label.cls += ' ' + labelCls;
30526             
30527             cfg.cn.push(label);
30528         }
30529         
30530         Roo.each(['day', 'month', 'year'], function(t){
30531             cfg.cn.push({
30532                 tag : 'div',
30533                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30534             });
30535         }, this);
30536         
30537         return cfg;
30538     },
30539     
30540     inputEl: function ()
30541     {
30542         return this.el.select('.roo-date-split-field-group-value', true).first();
30543     },
30544     
30545     onRender : function(ct, position) 
30546     {
30547         var _this = this;
30548         
30549         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30550         
30551         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30552         
30553         this.dayField = new Roo.bootstrap.ComboBox({
30554             allowBlank : this.dayAllowBlank,
30555             alwaysQuery : true,
30556             displayField : 'value',
30557             editable : false,
30558             fieldLabel : '',
30559             forceSelection : true,
30560             mode : 'local',
30561             placeholder : this.dayPlaceholder,
30562             selectOnFocus : true,
30563             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30564             triggerAction : 'all',
30565             typeAhead : true,
30566             valueField : 'value',
30567             store : new Roo.data.SimpleStore({
30568                 data : (function() {    
30569                     var days = [];
30570                     _this.fireEvent('days', _this, days);
30571                     return days;
30572                 })(),
30573                 fields : [ 'value' ]
30574             }),
30575             listeners : {
30576                 select : function (_self, record, index)
30577                 {
30578                     _this.setValue(_this.getValue());
30579                 }
30580             }
30581         });
30582
30583         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30584         
30585         this.monthField = new Roo.bootstrap.MonthField({
30586             after : '<i class=\"fa fa-calendar\"></i>',
30587             allowBlank : this.monthAllowBlank,
30588             placeholder : this.monthPlaceholder,
30589             readOnly : true,
30590             listeners : {
30591                 render : function (_self)
30592                 {
30593                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30594                         e.preventDefault();
30595                         _self.focus();
30596                     });
30597                 },
30598                 select : function (_self, oldvalue, newvalue)
30599                 {
30600                     _this.setValue(_this.getValue());
30601                 }
30602             }
30603         });
30604         
30605         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30606         
30607         this.yearField = new Roo.bootstrap.ComboBox({
30608             allowBlank : this.yearAllowBlank,
30609             alwaysQuery : true,
30610             displayField : 'value',
30611             editable : false,
30612             fieldLabel : '',
30613             forceSelection : true,
30614             mode : 'local',
30615             placeholder : this.yearPlaceholder,
30616             selectOnFocus : true,
30617             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30618             triggerAction : 'all',
30619             typeAhead : true,
30620             valueField : 'value',
30621             store : new Roo.data.SimpleStore({
30622                 data : (function() {
30623                     var years = [];
30624                     _this.fireEvent('years', _this, years);
30625                     return years;
30626                 })(),
30627                 fields : [ 'value' ]
30628             }),
30629             listeners : {
30630                 select : function (_self, record, index)
30631                 {
30632                     _this.setValue(_this.getValue());
30633                 }
30634             }
30635         });
30636
30637         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30638     },
30639     
30640     setValue : function(v, format)
30641     {
30642         this.inputEl.dom.value = v;
30643         
30644         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30645         
30646         var d = Date.parseDate(v, f);
30647         
30648         if(!d){
30649             this.validate();
30650             return;
30651         }
30652         
30653         this.setDay(d.format(this.dayFormat));
30654         this.setMonth(d.format(this.monthFormat));
30655         this.setYear(d.format(this.yearFormat));
30656         
30657         this.validate();
30658         
30659         return;
30660     },
30661     
30662     setDay : function(v)
30663     {
30664         this.dayField.setValue(v);
30665         this.inputEl.dom.value = this.getValue();
30666         this.validate();
30667         return;
30668     },
30669     
30670     setMonth : function(v)
30671     {
30672         this.monthField.setValue(v, true);
30673         this.inputEl.dom.value = this.getValue();
30674         this.validate();
30675         return;
30676     },
30677     
30678     setYear : function(v)
30679     {
30680         this.yearField.setValue(v);
30681         this.inputEl.dom.value = this.getValue();
30682         this.validate();
30683         return;
30684     },
30685     
30686     getDay : function()
30687     {
30688         return this.dayField.getValue();
30689     },
30690     
30691     getMonth : function()
30692     {
30693         return this.monthField.getValue();
30694     },
30695     
30696     getYear : function()
30697     {
30698         return this.yearField.getValue();
30699     },
30700     
30701     getValue : function()
30702     {
30703         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30704         
30705         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30706         
30707         return date;
30708     },
30709     
30710     reset : function()
30711     {
30712         this.setDay('');
30713         this.setMonth('');
30714         this.setYear('');
30715         this.inputEl.dom.value = '';
30716         this.validate();
30717         return;
30718     },
30719     
30720     validate : function()
30721     {
30722         var d = this.dayField.validate();
30723         var m = this.monthField.validate();
30724         var y = this.yearField.validate();
30725         
30726         var valid = true;
30727         
30728         if(
30729                 (!this.dayAllowBlank && !d) ||
30730                 (!this.monthAllowBlank && !m) ||
30731                 (!this.yearAllowBlank && !y)
30732         ){
30733             valid = false;
30734         }
30735         
30736         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30737             return valid;
30738         }
30739         
30740         if(valid){
30741             this.markValid();
30742             return valid;
30743         }
30744         
30745         this.markInvalid();
30746         
30747         return valid;
30748     },
30749     
30750     markValid : function()
30751     {
30752         
30753         var label = this.el.select('label', true).first();
30754         var icon = this.el.select('i.fa-star', true).first();
30755
30756         if(label && icon){
30757             icon.remove();
30758         }
30759         
30760         this.fireEvent('valid', this);
30761     },
30762     
30763      /**
30764      * Mark this field as invalid
30765      * @param {String} msg The validation message
30766      */
30767     markInvalid : function(msg)
30768     {
30769         
30770         var label = this.el.select('label', true).first();
30771         var icon = this.el.select('i.fa-star', true).first();
30772
30773         if(label && !icon){
30774             this.el.select('.roo-date-split-field-label', true).createChild({
30775                 tag : 'i',
30776                 cls : 'text-danger fa fa-lg fa-star',
30777                 tooltip : 'This field is required',
30778                 style : 'margin-right:5px;'
30779             }, label, true);
30780         }
30781         
30782         this.fireEvent('invalid', this, msg);
30783     },
30784     
30785     clearInvalid : function()
30786     {
30787         var label = this.el.select('label', true).first();
30788         var icon = this.el.select('i.fa-star', true).first();
30789
30790         if(label && icon){
30791             icon.remove();
30792         }
30793         
30794         this.fireEvent('valid', this);
30795     },
30796     
30797     getName: function()
30798     {
30799         return this.name;
30800     }
30801     
30802 });
30803
30804  /**
30805  *
30806  * This is based on 
30807  * http://masonry.desandro.com
30808  *
30809  * The idea is to render all the bricks based on vertical width...
30810  *
30811  * The original code extends 'outlayer' - we might need to use that....
30812  * 
30813  */
30814
30815
30816 /**
30817  * @class Roo.bootstrap.LayoutMasonry
30818  * @extends Roo.bootstrap.Component
30819  * Bootstrap Layout Masonry class
30820  * 
30821  * @constructor
30822  * Create a new Element
30823  * @param {Object} config The config object
30824  */
30825
30826 Roo.bootstrap.LayoutMasonry = function(config){
30827     
30828     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30829     
30830     this.bricks = [];
30831     
30832     Roo.bootstrap.LayoutMasonry.register(this);
30833     
30834     this.addEvents({
30835         // raw events
30836         /**
30837          * @event layout
30838          * Fire after layout the items
30839          * @param {Roo.bootstrap.LayoutMasonry} this
30840          * @param {Roo.EventObject} e
30841          */
30842         "layout" : true
30843     });
30844     
30845 };
30846
30847 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30848     
30849     /**
30850      * @cfg {Boolean} isLayoutInstant = no animation?
30851      */   
30852     isLayoutInstant : false, // needed?
30853    
30854     /**
30855      * @cfg {Number} boxWidth  width of the columns
30856      */   
30857     boxWidth : 450,
30858     
30859       /**
30860      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30861      */   
30862     boxHeight : 0,
30863     
30864     /**
30865      * @cfg {Number} padWidth padding below box..
30866      */   
30867     padWidth : 10, 
30868     
30869     /**
30870      * @cfg {Number} gutter gutter width..
30871      */   
30872     gutter : 10,
30873     
30874      /**
30875      * @cfg {Number} maxCols maximum number of columns
30876      */   
30877     
30878     maxCols: 0,
30879     
30880     /**
30881      * @cfg {Boolean} isAutoInitial defalut true
30882      */   
30883     isAutoInitial : true, 
30884     
30885     containerWidth: 0,
30886     
30887     /**
30888      * @cfg {Boolean} isHorizontal defalut false
30889      */   
30890     isHorizontal : false, 
30891
30892     currentSize : null,
30893     
30894     tag: 'div',
30895     
30896     cls: '',
30897     
30898     bricks: null, //CompositeElement
30899     
30900     cols : 1,
30901     
30902     _isLayoutInited : false,
30903     
30904 //    isAlternative : false, // only use for vertical layout...
30905     
30906     /**
30907      * @cfg {Number} alternativePadWidth padding below box..
30908      */   
30909     alternativePadWidth : 50,
30910     
30911     selectedBrick : [],
30912     
30913     getAutoCreate : function(){
30914         
30915         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30916         
30917         var cfg = {
30918             tag: this.tag,
30919             cls: 'blog-masonary-wrapper ' + this.cls,
30920             cn : {
30921                 cls : 'mas-boxes masonary'
30922             }
30923         };
30924         
30925         return cfg;
30926     },
30927     
30928     getChildContainer: function( )
30929     {
30930         if (this.boxesEl) {
30931             return this.boxesEl;
30932         }
30933         
30934         this.boxesEl = this.el.select('.mas-boxes').first();
30935         
30936         return this.boxesEl;
30937     },
30938     
30939     
30940     initEvents : function()
30941     {
30942         var _this = this;
30943         
30944         if(this.isAutoInitial){
30945             Roo.log('hook children rendered');
30946             this.on('childrenrendered', function() {
30947                 Roo.log('children rendered');
30948                 _this.initial();
30949             } ,this);
30950         }
30951     },
30952     
30953     initial : function()
30954     {
30955         this.selectedBrick = [];
30956         
30957         this.currentSize = this.el.getBox(true);
30958         
30959         Roo.EventManager.onWindowResize(this.resize, this); 
30960
30961         if(!this.isAutoInitial){
30962             this.layout();
30963             return;
30964         }
30965         
30966         this.layout();
30967         
30968         return;
30969         //this.layout.defer(500,this);
30970         
30971     },
30972     
30973     resize : function()
30974     {
30975         var cs = this.el.getBox(true);
30976         
30977         if (
30978                 this.currentSize.width == cs.width && 
30979                 this.currentSize.x == cs.x && 
30980                 this.currentSize.height == cs.height && 
30981                 this.currentSize.y == cs.y 
30982         ) {
30983             Roo.log("no change in with or X or Y");
30984             return;
30985         }
30986         
30987         this.currentSize = cs;
30988         
30989         this.layout();
30990         
30991     },
30992     
30993     layout : function()
30994     {   
30995         this._resetLayout();
30996         
30997         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30998         
30999         this.layoutItems( isInstant );
31000       
31001         this._isLayoutInited = true;
31002         
31003         this.fireEvent('layout', this);
31004         
31005     },
31006     
31007     _resetLayout : function()
31008     {
31009         if(this.isHorizontal){
31010             this.horizontalMeasureColumns();
31011             return;
31012         }
31013         
31014         this.verticalMeasureColumns();
31015         
31016     },
31017     
31018     verticalMeasureColumns : function()
31019     {
31020         this.getContainerWidth();
31021         
31022 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31023 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31024 //            return;
31025 //        }
31026         
31027         var boxWidth = this.boxWidth + this.padWidth;
31028         
31029         if(this.containerWidth < this.boxWidth){
31030             boxWidth = this.containerWidth
31031         }
31032         
31033         var containerWidth = this.containerWidth;
31034         
31035         var cols = Math.floor(containerWidth / boxWidth);
31036         
31037         this.cols = Math.max( cols, 1 );
31038         
31039         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31040         
31041         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31042         
31043         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31044         
31045         this.colWidth = boxWidth + avail - this.padWidth;
31046         
31047         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31048         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31049     },
31050     
31051     horizontalMeasureColumns : function()
31052     {
31053         this.getContainerWidth();
31054         
31055         var boxWidth = this.boxWidth;
31056         
31057         if(this.containerWidth < boxWidth){
31058             boxWidth = this.containerWidth;
31059         }
31060         
31061         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31062         
31063         this.el.setHeight(boxWidth);
31064         
31065     },
31066     
31067     getContainerWidth : function()
31068     {
31069         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31070     },
31071     
31072     layoutItems : function( isInstant )
31073     {
31074         Roo.log(this.bricks);
31075         
31076         var items = Roo.apply([], this.bricks);
31077         
31078         if(this.isHorizontal){
31079             this._horizontalLayoutItems( items , isInstant );
31080             return;
31081         }
31082         
31083 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31084 //            this._verticalAlternativeLayoutItems( items , isInstant );
31085 //            return;
31086 //        }
31087         
31088         this._verticalLayoutItems( items , isInstant );
31089         
31090     },
31091     
31092     _verticalLayoutItems : function ( items , isInstant)
31093     {
31094         if ( !items || !items.length ) {
31095             return;
31096         }
31097         
31098         var standard = [
31099             ['xs', 'xs', 'xs', 'tall'],
31100             ['xs', 'xs', 'tall'],
31101             ['xs', 'xs', 'sm'],
31102             ['xs', 'xs', 'xs'],
31103             ['xs', 'tall'],
31104             ['xs', 'sm'],
31105             ['xs', 'xs'],
31106             ['xs'],
31107             
31108             ['sm', 'xs', 'xs'],
31109             ['sm', 'xs'],
31110             ['sm'],
31111             
31112             ['tall', 'xs', 'xs', 'xs'],
31113             ['tall', 'xs', 'xs'],
31114             ['tall', 'xs'],
31115             ['tall']
31116             
31117         ];
31118         
31119         var queue = [];
31120         
31121         var boxes = [];
31122         
31123         var box = [];
31124         
31125         Roo.each(items, function(item, k){
31126             
31127             switch (item.size) {
31128                 // these layouts take up a full box,
31129                 case 'md' :
31130                 case 'md-left' :
31131                 case 'md-right' :
31132                 case 'wide' :
31133                     
31134                     if(box.length){
31135                         boxes.push(box);
31136                         box = [];
31137                     }
31138                     
31139                     boxes.push([item]);
31140                     
31141                     break;
31142                     
31143                 case 'xs' :
31144                 case 'sm' :
31145                 case 'tall' :
31146                     
31147                     box.push(item);
31148                     
31149                     break;
31150                 default :
31151                     break;
31152                     
31153             }
31154             
31155         }, this);
31156         
31157         if(box.length){
31158             boxes.push(box);
31159             box = [];
31160         }
31161         
31162         var filterPattern = function(box, length)
31163         {
31164             if(!box.length){
31165                 return;
31166             }
31167             
31168             var match = false;
31169             
31170             var pattern = box.slice(0, length);
31171             
31172             var format = [];
31173             
31174             Roo.each(pattern, function(i){
31175                 format.push(i.size);
31176             }, this);
31177             
31178             Roo.each(standard, function(s){
31179                 
31180                 if(String(s) != String(format)){
31181                     return;
31182                 }
31183                 
31184                 match = true;
31185                 return false;
31186                 
31187             }, this);
31188             
31189             if(!match && length == 1){
31190                 return;
31191             }
31192             
31193             if(!match){
31194                 filterPattern(box, length - 1);
31195                 return;
31196             }
31197                 
31198             queue.push(pattern);
31199
31200             box = box.slice(length, box.length);
31201
31202             filterPattern(box, 4);
31203
31204             return;
31205             
31206         }
31207         
31208         Roo.each(boxes, function(box, k){
31209             
31210             if(!box.length){
31211                 return;
31212             }
31213             
31214             if(box.length == 1){
31215                 queue.push(box);
31216                 return;
31217             }
31218             
31219             filterPattern(box, 4);
31220             
31221         }, this);
31222         
31223         this._processVerticalLayoutQueue( queue, isInstant );
31224         
31225     },
31226     
31227 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31228 //    {
31229 //        if ( !items || !items.length ) {
31230 //            return;
31231 //        }
31232 //
31233 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31234 //        
31235 //    },
31236     
31237     _horizontalLayoutItems : function ( items , isInstant)
31238     {
31239         if ( !items || !items.length || items.length < 3) {
31240             return;
31241         }
31242         
31243         items.reverse();
31244         
31245         var eItems = items.slice(0, 3);
31246         
31247         items = items.slice(3, items.length);
31248         
31249         var standard = [
31250             ['xs', 'xs', 'xs', 'wide'],
31251             ['xs', 'xs', 'wide'],
31252             ['xs', 'xs', 'sm'],
31253             ['xs', 'xs', 'xs'],
31254             ['xs', 'wide'],
31255             ['xs', 'sm'],
31256             ['xs', 'xs'],
31257             ['xs'],
31258             
31259             ['sm', 'xs', 'xs'],
31260             ['sm', 'xs'],
31261             ['sm'],
31262             
31263             ['wide', 'xs', 'xs', 'xs'],
31264             ['wide', 'xs', 'xs'],
31265             ['wide', 'xs'],
31266             ['wide'],
31267             
31268             ['wide-thin']
31269         ];
31270         
31271         var queue = [];
31272         
31273         var boxes = [];
31274         
31275         var box = [];
31276         
31277         Roo.each(items, function(item, k){
31278             
31279             switch (item.size) {
31280                 case 'md' :
31281                 case 'md-left' :
31282                 case 'md-right' :
31283                 case 'tall' :
31284                     
31285                     if(box.length){
31286                         boxes.push(box);
31287                         box = [];
31288                     }
31289                     
31290                     boxes.push([item]);
31291                     
31292                     break;
31293                     
31294                 case 'xs' :
31295                 case 'sm' :
31296                 case 'wide' :
31297                 case 'wide-thin' :
31298                     
31299                     box.push(item);
31300                     
31301                     break;
31302                 default :
31303                     break;
31304                     
31305             }
31306             
31307         }, this);
31308         
31309         if(box.length){
31310             boxes.push(box);
31311             box = [];
31312         }
31313         
31314         var filterPattern = function(box, length)
31315         {
31316             if(!box.length){
31317                 return;
31318             }
31319             
31320             var match = false;
31321             
31322             var pattern = box.slice(0, length);
31323             
31324             var format = [];
31325             
31326             Roo.each(pattern, function(i){
31327                 format.push(i.size);
31328             }, this);
31329             
31330             Roo.each(standard, function(s){
31331                 
31332                 if(String(s) != String(format)){
31333                     return;
31334                 }
31335                 
31336                 match = true;
31337                 return false;
31338                 
31339             }, this);
31340             
31341             if(!match && length == 1){
31342                 return;
31343             }
31344             
31345             if(!match){
31346                 filterPattern(box, length - 1);
31347                 return;
31348             }
31349                 
31350             queue.push(pattern);
31351
31352             box = box.slice(length, box.length);
31353
31354             filterPattern(box, 4);
31355
31356             return;
31357             
31358         }
31359         
31360         Roo.each(boxes, function(box, k){
31361             
31362             if(!box.length){
31363                 return;
31364             }
31365             
31366             if(box.length == 1){
31367                 queue.push(box);
31368                 return;
31369             }
31370             
31371             filterPattern(box, 4);
31372             
31373         }, this);
31374         
31375         
31376         var prune = [];
31377         
31378         var pos = this.el.getBox(true);
31379         
31380         var minX = pos.x;
31381         
31382         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31383         
31384         var hit_end = false;
31385         
31386         Roo.each(queue, function(box){
31387             
31388             if(hit_end){
31389                 
31390                 Roo.each(box, function(b){
31391                 
31392                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31393                     b.el.hide();
31394
31395                 }, this);
31396
31397                 return;
31398             }
31399             
31400             var mx = 0;
31401             
31402             Roo.each(box, function(b){
31403                 
31404                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31405                 b.el.show();
31406
31407                 mx = Math.max(mx, b.x);
31408                 
31409             }, this);
31410             
31411             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31412             
31413             if(maxX < minX){
31414                 
31415                 Roo.each(box, function(b){
31416                 
31417                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31418                     b.el.hide();
31419                     
31420                 }, this);
31421                 
31422                 hit_end = true;
31423                 
31424                 return;
31425             }
31426             
31427             prune.push(box);
31428             
31429         }, this);
31430         
31431         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31432     },
31433     
31434     /** Sets position of item in DOM
31435     * @param {Element} item
31436     * @param {Number} x - horizontal position
31437     * @param {Number} y - vertical position
31438     * @param {Boolean} isInstant - disables transitions
31439     */
31440     _processVerticalLayoutQueue : function( queue, isInstant )
31441     {
31442         var pos = this.el.getBox(true);
31443         var x = pos.x;
31444         var y = pos.y;
31445         var maxY = [];
31446         
31447         for (var i = 0; i < this.cols; i++){
31448             maxY[i] = pos.y;
31449         }
31450         
31451         Roo.each(queue, function(box, k){
31452             
31453             var col = k % this.cols;
31454             
31455             Roo.each(box, function(b,kk){
31456                 
31457                 b.el.position('absolute');
31458                 
31459                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31460                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31461                 
31462                 if(b.size == 'md-left' || b.size == 'md-right'){
31463                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31464                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31465                 }
31466                 
31467                 b.el.setWidth(width);
31468                 b.el.setHeight(height);
31469                 // iframe?
31470                 b.el.select('iframe',true).setSize(width,height);
31471                 
31472             }, this);
31473             
31474             for (var i = 0; i < this.cols; i++){
31475                 
31476                 if(maxY[i] < maxY[col]){
31477                     col = i;
31478                     continue;
31479                 }
31480                 
31481                 col = Math.min(col, i);
31482                 
31483             }
31484             
31485             x = pos.x + col * (this.colWidth + this.padWidth);
31486             
31487             y = maxY[col];
31488             
31489             var positions = [];
31490             
31491             switch (box.length){
31492                 case 1 :
31493                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31494                     break;
31495                 case 2 :
31496                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31497                     break;
31498                 case 3 :
31499                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31500                     break;
31501                 case 4 :
31502                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31503                     break;
31504                 default :
31505                     break;
31506             }
31507             
31508             Roo.each(box, function(b,kk){
31509                 
31510                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31511                 
31512                 var sz = b.el.getSize();
31513                 
31514                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31515                 
31516             }, this);
31517             
31518         }, this);
31519         
31520         var mY = 0;
31521         
31522         for (var i = 0; i < this.cols; i++){
31523             mY = Math.max(mY, maxY[i]);
31524         }
31525         
31526         this.el.setHeight(mY - pos.y);
31527         
31528     },
31529     
31530 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31531 //    {
31532 //        var pos = this.el.getBox(true);
31533 //        var x = pos.x;
31534 //        var y = pos.y;
31535 //        var maxX = pos.right;
31536 //        
31537 //        var maxHeight = 0;
31538 //        
31539 //        Roo.each(items, function(item, k){
31540 //            
31541 //            var c = k % 2;
31542 //            
31543 //            item.el.position('absolute');
31544 //                
31545 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31546 //
31547 //            item.el.setWidth(width);
31548 //
31549 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31550 //
31551 //            item.el.setHeight(height);
31552 //            
31553 //            if(c == 0){
31554 //                item.el.setXY([x, y], isInstant ? false : true);
31555 //            } else {
31556 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31557 //            }
31558 //            
31559 //            y = y + height + this.alternativePadWidth;
31560 //            
31561 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31562 //            
31563 //        }, this);
31564 //        
31565 //        this.el.setHeight(maxHeight);
31566 //        
31567 //    },
31568     
31569     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31570     {
31571         var pos = this.el.getBox(true);
31572         
31573         var minX = pos.x;
31574         var minY = pos.y;
31575         
31576         var maxX = pos.right;
31577         
31578         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31579         
31580         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31581         
31582         Roo.each(queue, function(box, k){
31583             
31584             Roo.each(box, function(b, kk){
31585                 
31586                 b.el.position('absolute');
31587                 
31588                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31589                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31590                 
31591                 if(b.size == 'md-left' || b.size == 'md-right'){
31592                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31593                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31594                 }
31595                 
31596                 b.el.setWidth(width);
31597                 b.el.setHeight(height);
31598                 
31599             }, this);
31600             
31601             if(!box.length){
31602                 return;
31603             }
31604             
31605             var positions = [];
31606             
31607             switch (box.length){
31608                 case 1 :
31609                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31610                     break;
31611                 case 2 :
31612                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31613                     break;
31614                 case 3 :
31615                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31616                     break;
31617                 case 4 :
31618                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31619                     break;
31620                 default :
31621                     break;
31622             }
31623             
31624             Roo.each(box, function(b,kk){
31625                 
31626                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31627                 
31628                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31629                 
31630             }, this);
31631             
31632         }, this);
31633         
31634     },
31635     
31636     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31637     {
31638         Roo.each(eItems, function(b,k){
31639             
31640             b.size = (k == 0) ? 'sm' : 'xs';
31641             b.x = (k == 0) ? 2 : 1;
31642             b.y = (k == 0) ? 2 : 1;
31643             
31644             b.el.position('absolute');
31645             
31646             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31647                 
31648             b.el.setWidth(width);
31649             
31650             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31651             
31652             b.el.setHeight(height);
31653             
31654         }, this);
31655
31656         var positions = [];
31657         
31658         positions.push({
31659             x : maxX - this.unitWidth * 2 - this.gutter,
31660             y : minY
31661         });
31662         
31663         positions.push({
31664             x : maxX - this.unitWidth,
31665             y : minY + (this.unitWidth + this.gutter) * 2
31666         });
31667         
31668         positions.push({
31669             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31670             y : minY
31671         });
31672         
31673         Roo.each(eItems, function(b,k){
31674             
31675             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31676
31677         }, this);
31678         
31679     },
31680     
31681     getVerticalOneBoxColPositions : function(x, y, box)
31682     {
31683         var pos = [];
31684         
31685         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31686         
31687         if(box[0].size == 'md-left'){
31688             rand = 0;
31689         }
31690         
31691         if(box[0].size == 'md-right'){
31692             rand = 1;
31693         }
31694         
31695         pos.push({
31696             x : x + (this.unitWidth + this.gutter) * rand,
31697             y : y
31698         });
31699         
31700         return pos;
31701     },
31702     
31703     getVerticalTwoBoxColPositions : function(x, y, box)
31704     {
31705         var pos = [];
31706         
31707         if(box[0].size == 'xs'){
31708             
31709             pos.push({
31710                 x : x,
31711                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31712             });
31713
31714             pos.push({
31715                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31716                 y : y
31717             });
31718             
31719             return pos;
31720             
31721         }
31722         
31723         pos.push({
31724             x : x,
31725             y : y
31726         });
31727
31728         pos.push({
31729             x : x + (this.unitWidth + this.gutter) * 2,
31730             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31731         });
31732         
31733         return pos;
31734         
31735     },
31736     
31737     getVerticalThreeBoxColPositions : function(x, y, box)
31738     {
31739         var pos = [];
31740         
31741         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31742             
31743             pos.push({
31744                 x : x,
31745                 y : y
31746             });
31747
31748             pos.push({
31749                 x : x + (this.unitWidth + this.gutter) * 1,
31750                 y : y
31751             });
31752             
31753             pos.push({
31754                 x : x + (this.unitWidth + this.gutter) * 2,
31755                 y : y
31756             });
31757             
31758             return pos;
31759             
31760         }
31761         
31762         if(box[0].size == 'xs' && box[1].size == 'xs'){
31763             
31764             pos.push({
31765                 x : x,
31766                 y : y
31767             });
31768
31769             pos.push({
31770                 x : x,
31771                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31772             });
31773             
31774             pos.push({
31775                 x : x + (this.unitWidth + this.gutter) * 1,
31776                 y : y
31777             });
31778             
31779             return pos;
31780             
31781         }
31782         
31783         pos.push({
31784             x : x,
31785             y : y
31786         });
31787
31788         pos.push({
31789             x : x + (this.unitWidth + this.gutter) * 2,
31790             y : y
31791         });
31792
31793         pos.push({
31794             x : x + (this.unitWidth + this.gutter) * 2,
31795             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31796         });
31797             
31798         return pos;
31799         
31800     },
31801     
31802     getVerticalFourBoxColPositions : function(x, y, box)
31803     {
31804         var pos = [];
31805         
31806         if(box[0].size == 'xs'){
31807             
31808             pos.push({
31809                 x : x,
31810                 y : y
31811             });
31812
31813             pos.push({
31814                 x : x,
31815                 y : y + (this.unitHeight + this.gutter) * 1
31816             });
31817             
31818             pos.push({
31819                 x : x,
31820                 y : y + (this.unitHeight + this.gutter) * 2
31821             });
31822             
31823             pos.push({
31824                 x : x + (this.unitWidth + this.gutter) * 1,
31825                 y : y
31826             });
31827             
31828             return pos;
31829             
31830         }
31831         
31832         pos.push({
31833             x : x,
31834             y : y
31835         });
31836
31837         pos.push({
31838             x : x + (this.unitWidth + this.gutter) * 2,
31839             y : y
31840         });
31841
31842         pos.push({
31843             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31844             y : y + (this.unitHeight + this.gutter) * 1
31845         });
31846
31847         pos.push({
31848             x : x + (this.unitWidth + this.gutter) * 2,
31849             y : y + (this.unitWidth + this.gutter) * 2
31850         });
31851
31852         return pos;
31853         
31854     },
31855     
31856     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31857     {
31858         var pos = [];
31859         
31860         if(box[0].size == 'md-left'){
31861             pos.push({
31862                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31863                 y : minY
31864             });
31865             
31866             return pos;
31867         }
31868         
31869         if(box[0].size == 'md-right'){
31870             pos.push({
31871                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31872                 y : minY + (this.unitWidth + this.gutter) * 1
31873             });
31874             
31875             return pos;
31876         }
31877         
31878         var rand = Math.floor(Math.random() * (4 - box[0].y));
31879         
31880         pos.push({
31881             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31882             y : minY + (this.unitWidth + this.gutter) * rand
31883         });
31884         
31885         return pos;
31886         
31887     },
31888     
31889     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31890     {
31891         var pos = [];
31892         
31893         if(box[0].size == 'xs'){
31894             
31895             pos.push({
31896                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31897                 y : minY
31898             });
31899
31900             pos.push({
31901                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31902                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31903             });
31904             
31905             return pos;
31906             
31907         }
31908         
31909         pos.push({
31910             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31911             y : minY
31912         });
31913
31914         pos.push({
31915             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31916             y : minY + (this.unitWidth + this.gutter) * 2
31917         });
31918         
31919         return pos;
31920         
31921     },
31922     
31923     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31924     {
31925         var pos = [];
31926         
31927         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31928             
31929             pos.push({
31930                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31931                 y : minY
31932             });
31933
31934             pos.push({
31935                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31936                 y : minY + (this.unitWidth + this.gutter) * 1
31937             });
31938             
31939             pos.push({
31940                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31941                 y : minY + (this.unitWidth + this.gutter) * 2
31942             });
31943             
31944             return pos;
31945             
31946         }
31947         
31948         if(box[0].size == 'xs' && box[1].size == 'xs'){
31949             
31950             pos.push({
31951                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31952                 y : minY
31953             });
31954
31955             pos.push({
31956                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31957                 y : minY
31958             });
31959             
31960             pos.push({
31961                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31962                 y : minY + (this.unitWidth + this.gutter) * 1
31963             });
31964             
31965             return pos;
31966             
31967         }
31968         
31969         pos.push({
31970             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31971             y : minY
31972         });
31973
31974         pos.push({
31975             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31976             y : minY + (this.unitWidth + this.gutter) * 2
31977         });
31978
31979         pos.push({
31980             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31981             y : minY + (this.unitWidth + this.gutter) * 2
31982         });
31983             
31984         return pos;
31985         
31986     },
31987     
31988     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31989     {
31990         var pos = [];
31991         
31992         if(box[0].size == 'xs'){
31993             
31994             pos.push({
31995                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31996                 y : minY
31997             });
31998
31999             pos.push({
32000                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32001                 y : minY
32002             });
32003             
32004             pos.push({
32005                 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),
32006                 y : minY
32007             });
32008             
32009             pos.push({
32010                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32011                 y : minY + (this.unitWidth + this.gutter) * 1
32012             });
32013             
32014             return pos;
32015             
32016         }
32017         
32018         pos.push({
32019             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32020             y : minY
32021         });
32022         
32023         pos.push({
32024             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32025             y : minY + (this.unitWidth + this.gutter) * 2
32026         });
32027         
32028         pos.push({
32029             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32030             y : minY + (this.unitWidth + this.gutter) * 2
32031         });
32032         
32033         pos.push({
32034             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),
32035             y : minY + (this.unitWidth + this.gutter) * 2
32036         });
32037
32038         return pos;
32039         
32040     },
32041     
32042     /**
32043     * remove a Masonry Brick
32044     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32045     */
32046     removeBrick : function(brick_id)
32047     {
32048         if (!brick_id) {
32049             return;
32050         }
32051         
32052         for (var i = 0; i<this.bricks.length; i++) {
32053             if (this.bricks[i].id == brick_id) {
32054                 this.bricks.splice(i,1);
32055                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32056                 this.initial();
32057             }
32058         }
32059     },
32060     
32061     /**
32062     * adds a Masonry Brick
32063     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32064     */
32065     addBrick : function(cfg)
32066     {
32067         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32068         //this.register(cn);
32069         cn.parentId = this.id;
32070         cn.render(this.el);
32071         return cn;
32072     },
32073     
32074     /**
32075     * register a Masonry Brick
32076     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32077     */
32078     
32079     register : function(brick)
32080     {
32081         this.bricks.push(brick);
32082         brick.masonryId = this.id;
32083     },
32084     
32085     /**
32086     * clear all the Masonry Brick
32087     */
32088     clearAll : function()
32089     {
32090         this.bricks = [];
32091         //this.getChildContainer().dom.innerHTML = "";
32092         this.el.dom.innerHTML = '';
32093     },
32094     
32095     getSelected : function()
32096     {
32097         if (!this.selectedBrick) {
32098             return false;
32099         }
32100         
32101         return this.selectedBrick;
32102     }
32103 });
32104
32105 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32106     
32107     groups: {},
32108      /**
32109     * register a Masonry Layout
32110     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32111     */
32112     
32113     register : function(layout)
32114     {
32115         this.groups[layout.id] = layout;
32116     },
32117     /**
32118     * fetch a  Masonry Layout based on the masonry layout ID
32119     * @param {string} the masonry layout to add
32120     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32121     */
32122     
32123     get: function(layout_id) {
32124         if (typeof(this.groups[layout_id]) == 'undefined') {
32125             return false;
32126         }
32127         return this.groups[layout_id] ;
32128     }
32129     
32130     
32131     
32132 });
32133
32134  
32135
32136  /**
32137  *
32138  * This is based on 
32139  * http://masonry.desandro.com
32140  *
32141  * The idea is to render all the bricks based on vertical width...
32142  *
32143  * The original code extends 'outlayer' - we might need to use that....
32144  * 
32145  */
32146
32147
32148 /**
32149  * @class Roo.bootstrap.LayoutMasonryAuto
32150  * @extends Roo.bootstrap.Component
32151  * Bootstrap Layout Masonry class
32152  * 
32153  * @constructor
32154  * Create a new Element
32155  * @param {Object} config The config object
32156  */
32157
32158 Roo.bootstrap.LayoutMasonryAuto = function(config){
32159     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32160 };
32161
32162 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32163     
32164       /**
32165      * @cfg {Boolean} isFitWidth  - resize the width..
32166      */   
32167     isFitWidth : false,  // options..
32168     /**
32169      * @cfg {Boolean} isOriginLeft = left align?
32170      */   
32171     isOriginLeft : true,
32172     /**
32173      * @cfg {Boolean} isOriginTop = top align?
32174      */   
32175     isOriginTop : false,
32176     /**
32177      * @cfg {Boolean} isLayoutInstant = no animation?
32178      */   
32179     isLayoutInstant : false, // needed?
32180     /**
32181      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32182      */   
32183     isResizingContainer : true,
32184     /**
32185      * @cfg {Number} columnWidth  width of the columns 
32186      */   
32187     
32188     columnWidth : 0,
32189     
32190     /**
32191      * @cfg {Number} maxCols maximum number of columns
32192      */   
32193     
32194     maxCols: 0,
32195     /**
32196      * @cfg {Number} padHeight padding below box..
32197      */   
32198     
32199     padHeight : 10, 
32200     
32201     /**
32202      * @cfg {Boolean} isAutoInitial defalut true
32203      */   
32204     
32205     isAutoInitial : true, 
32206     
32207     // private?
32208     gutter : 0,
32209     
32210     containerWidth: 0,
32211     initialColumnWidth : 0,
32212     currentSize : null,
32213     
32214     colYs : null, // array.
32215     maxY : 0,
32216     padWidth: 10,
32217     
32218     
32219     tag: 'div',
32220     cls: '',
32221     bricks: null, //CompositeElement
32222     cols : 0, // array?
32223     // element : null, // wrapped now this.el
32224     _isLayoutInited : null, 
32225     
32226     
32227     getAutoCreate : function(){
32228         
32229         var cfg = {
32230             tag: this.tag,
32231             cls: 'blog-masonary-wrapper ' + this.cls,
32232             cn : {
32233                 cls : 'mas-boxes masonary'
32234             }
32235         };
32236         
32237         return cfg;
32238     },
32239     
32240     getChildContainer: function( )
32241     {
32242         if (this.boxesEl) {
32243             return this.boxesEl;
32244         }
32245         
32246         this.boxesEl = this.el.select('.mas-boxes').first();
32247         
32248         return this.boxesEl;
32249     },
32250     
32251     
32252     initEvents : function()
32253     {
32254         var _this = this;
32255         
32256         if(this.isAutoInitial){
32257             Roo.log('hook children rendered');
32258             this.on('childrenrendered', function() {
32259                 Roo.log('children rendered');
32260                 _this.initial();
32261             } ,this);
32262         }
32263         
32264     },
32265     
32266     initial : function()
32267     {
32268         this.reloadItems();
32269
32270         this.currentSize = this.el.getBox(true);
32271
32272         /// was window resize... - let's see if this works..
32273         Roo.EventManager.onWindowResize(this.resize, this); 
32274
32275         if(!this.isAutoInitial){
32276             this.layout();
32277             return;
32278         }
32279         
32280         this.layout.defer(500,this);
32281     },
32282     
32283     reloadItems: function()
32284     {
32285         this.bricks = this.el.select('.masonry-brick', true);
32286         
32287         this.bricks.each(function(b) {
32288             //Roo.log(b.getSize());
32289             if (!b.attr('originalwidth')) {
32290                 b.attr('originalwidth',  b.getSize().width);
32291             }
32292             
32293         });
32294         
32295         Roo.log(this.bricks.elements.length);
32296     },
32297     
32298     resize : function()
32299     {
32300         Roo.log('resize');
32301         var cs = this.el.getBox(true);
32302         
32303         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32304             Roo.log("no change in with or X");
32305             return;
32306         }
32307         this.currentSize = cs;
32308         this.layout();
32309     },
32310     
32311     layout : function()
32312     {
32313          Roo.log('layout');
32314         this._resetLayout();
32315         //this._manageStamps();
32316       
32317         // don't animate first layout
32318         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32319         this.layoutItems( isInstant );
32320       
32321         // flag for initalized
32322         this._isLayoutInited = true;
32323     },
32324     
32325     layoutItems : function( isInstant )
32326     {
32327         //var items = this._getItemsForLayout( this.items );
32328         // original code supports filtering layout items.. we just ignore it..
32329         
32330         this._layoutItems( this.bricks , isInstant );
32331       
32332         this._postLayout();
32333     },
32334     _layoutItems : function ( items , isInstant)
32335     {
32336        //this.fireEvent( 'layout', this, items );
32337     
32338
32339         if ( !items || !items.elements.length ) {
32340           // no items, emit event with empty array
32341             return;
32342         }
32343
32344         var queue = [];
32345         items.each(function(item) {
32346             Roo.log("layout item");
32347             Roo.log(item);
32348             // get x/y object from method
32349             var position = this._getItemLayoutPosition( item );
32350             // enqueue
32351             position.item = item;
32352             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32353             queue.push( position );
32354         }, this);
32355       
32356         this._processLayoutQueue( queue );
32357     },
32358     /** Sets position of item in DOM
32359     * @param {Element} item
32360     * @param {Number} x - horizontal position
32361     * @param {Number} y - vertical position
32362     * @param {Boolean} isInstant - disables transitions
32363     */
32364     _processLayoutQueue : function( queue )
32365     {
32366         for ( var i=0, len = queue.length; i < len; i++ ) {
32367             var obj = queue[i];
32368             obj.item.position('absolute');
32369             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32370         }
32371     },
32372       
32373     
32374     /**
32375     * Any logic you want to do after each layout,
32376     * i.e. size the container
32377     */
32378     _postLayout : function()
32379     {
32380         this.resizeContainer();
32381     },
32382     
32383     resizeContainer : function()
32384     {
32385         if ( !this.isResizingContainer ) {
32386             return;
32387         }
32388         var size = this._getContainerSize();
32389         if ( size ) {
32390             this.el.setSize(size.width,size.height);
32391             this.boxesEl.setSize(size.width,size.height);
32392         }
32393     },
32394     
32395     
32396     
32397     _resetLayout : function()
32398     {
32399         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32400         this.colWidth = this.el.getWidth();
32401         //this.gutter = this.el.getWidth(); 
32402         
32403         this.measureColumns();
32404
32405         // reset column Y
32406         var i = this.cols;
32407         this.colYs = [];
32408         while (i--) {
32409             this.colYs.push( 0 );
32410         }
32411     
32412         this.maxY = 0;
32413     },
32414
32415     measureColumns : function()
32416     {
32417         this.getContainerWidth();
32418       // if columnWidth is 0, default to outerWidth of first item
32419         if ( !this.columnWidth ) {
32420             var firstItem = this.bricks.first();
32421             Roo.log(firstItem);
32422             this.columnWidth  = this.containerWidth;
32423             if (firstItem && firstItem.attr('originalwidth') ) {
32424                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32425             }
32426             // columnWidth fall back to item of first element
32427             Roo.log("set column width?");
32428                         this.initialColumnWidth = this.columnWidth  ;
32429
32430             // if first elem has no width, default to size of container
32431             
32432         }
32433         
32434         
32435         if (this.initialColumnWidth) {
32436             this.columnWidth = this.initialColumnWidth;
32437         }
32438         
32439         
32440             
32441         // column width is fixed at the top - however if container width get's smaller we should
32442         // reduce it...
32443         
32444         // this bit calcs how man columns..
32445             
32446         var columnWidth = this.columnWidth += this.gutter;
32447       
32448         // calculate columns
32449         var containerWidth = this.containerWidth + this.gutter;
32450         
32451         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32452         // fix rounding errors, typically with gutters
32453         var excess = columnWidth - containerWidth % columnWidth;
32454         
32455         
32456         // if overshoot is less than a pixel, round up, otherwise floor it
32457         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32458         cols = Math[ mathMethod ]( cols );
32459         this.cols = Math.max( cols, 1 );
32460         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32461         
32462          // padding positioning..
32463         var totalColWidth = this.cols * this.columnWidth;
32464         var padavail = this.containerWidth - totalColWidth;
32465         // so for 2 columns - we need 3 'pads'
32466         
32467         var padNeeded = (1+this.cols) * this.padWidth;
32468         
32469         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32470         
32471         this.columnWidth += padExtra
32472         //this.padWidth = Math.floor(padavail /  ( this.cols));
32473         
32474         // adjust colum width so that padding is fixed??
32475         
32476         // we have 3 columns ... total = width * 3
32477         // we have X left over... that should be used by 
32478         
32479         //if (this.expandC) {
32480             
32481         //}
32482         
32483         
32484         
32485     },
32486     
32487     getContainerWidth : function()
32488     {
32489        /* // container is parent if fit width
32490         var container = this.isFitWidth ? this.element.parentNode : this.element;
32491         // check that this.size and size are there
32492         // IE8 triggers resize on body size change, so they might not be
32493         
32494         var size = getSize( container );  //FIXME
32495         this.containerWidth = size && size.innerWidth; //FIXME
32496         */
32497          
32498         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32499         
32500     },
32501     
32502     _getItemLayoutPosition : function( item )  // what is item?
32503     {
32504         // we resize the item to our columnWidth..
32505       
32506         item.setWidth(this.columnWidth);
32507         item.autoBoxAdjust  = false;
32508         
32509         var sz = item.getSize();
32510  
32511         // how many columns does this brick span
32512         var remainder = this.containerWidth % this.columnWidth;
32513         
32514         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32515         // round if off by 1 pixel, otherwise use ceil
32516         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32517         colSpan = Math.min( colSpan, this.cols );
32518         
32519         // normally this should be '1' as we dont' currently allow multi width columns..
32520         
32521         var colGroup = this._getColGroup( colSpan );
32522         // get the minimum Y value from the columns
32523         var minimumY = Math.min.apply( Math, colGroup );
32524         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32525         
32526         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32527          
32528         // position the brick
32529         var position = {
32530             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32531             y: this.currentSize.y + minimumY + this.padHeight
32532         };
32533         
32534         Roo.log(position);
32535         // apply setHeight to necessary columns
32536         var setHeight = minimumY + sz.height + this.padHeight;
32537         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32538         
32539         var setSpan = this.cols + 1 - colGroup.length;
32540         for ( var i = 0; i < setSpan; i++ ) {
32541           this.colYs[ shortColIndex + i ] = setHeight ;
32542         }
32543       
32544         return position;
32545     },
32546     
32547     /**
32548      * @param {Number} colSpan - number of columns the element spans
32549      * @returns {Array} colGroup
32550      */
32551     _getColGroup : function( colSpan )
32552     {
32553         if ( colSpan < 2 ) {
32554           // if brick spans only one column, use all the column Ys
32555           return this.colYs;
32556         }
32557       
32558         var colGroup = [];
32559         // how many different places could this brick fit horizontally
32560         var groupCount = this.cols + 1 - colSpan;
32561         // for each group potential horizontal position
32562         for ( var i = 0; i < groupCount; i++ ) {
32563           // make an array of colY values for that one group
32564           var groupColYs = this.colYs.slice( i, i + colSpan );
32565           // and get the max value of the array
32566           colGroup[i] = Math.max.apply( Math, groupColYs );
32567         }
32568         return colGroup;
32569     },
32570     /*
32571     _manageStamp : function( stamp )
32572     {
32573         var stampSize =  stamp.getSize();
32574         var offset = stamp.getBox();
32575         // get the columns that this stamp affects
32576         var firstX = this.isOriginLeft ? offset.x : offset.right;
32577         var lastX = firstX + stampSize.width;
32578         var firstCol = Math.floor( firstX / this.columnWidth );
32579         firstCol = Math.max( 0, firstCol );
32580         
32581         var lastCol = Math.floor( lastX / this.columnWidth );
32582         // lastCol should not go over if multiple of columnWidth #425
32583         lastCol -= lastX % this.columnWidth ? 0 : 1;
32584         lastCol = Math.min( this.cols - 1, lastCol );
32585         
32586         // set colYs to bottom of the stamp
32587         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32588             stampSize.height;
32589             
32590         for ( var i = firstCol; i <= lastCol; i++ ) {
32591           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32592         }
32593     },
32594     */
32595     
32596     _getContainerSize : function()
32597     {
32598         this.maxY = Math.max.apply( Math, this.colYs );
32599         var size = {
32600             height: this.maxY
32601         };
32602       
32603         if ( this.isFitWidth ) {
32604             size.width = this._getContainerFitWidth();
32605         }
32606       
32607         return size;
32608     },
32609     
32610     _getContainerFitWidth : function()
32611     {
32612         var unusedCols = 0;
32613         // count unused columns
32614         var i = this.cols;
32615         while ( --i ) {
32616           if ( this.colYs[i] !== 0 ) {
32617             break;
32618           }
32619           unusedCols++;
32620         }
32621         // fit container to columns that have been used
32622         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32623     },
32624     
32625     needsResizeLayout : function()
32626     {
32627         var previousWidth = this.containerWidth;
32628         this.getContainerWidth();
32629         return previousWidth !== this.containerWidth;
32630     }
32631  
32632 });
32633
32634  
32635
32636  /*
32637  * - LGPL
32638  *
32639  * element
32640  * 
32641  */
32642
32643 /**
32644  * @class Roo.bootstrap.MasonryBrick
32645  * @extends Roo.bootstrap.Component
32646  * Bootstrap MasonryBrick class
32647  * 
32648  * @constructor
32649  * Create a new MasonryBrick
32650  * @param {Object} config The config object
32651  */
32652
32653 Roo.bootstrap.MasonryBrick = function(config){
32654     
32655     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32656     
32657     Roo.bootstrap.MasonryBrick.register(this);
32658     
32659     this.addEvents({
32660         // raw events
32661         /**
32662          * @event click
32663          * When a MasonryBrick is clcik
32664          * @param {Roo.bootstrap.MasonryBrick} this
32665          * @param {Roo.EventObject} e
32666          */
32667         "click" : true
32668     });
32669 };
32670
32671 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32672     
32673     /**
32674      * @cfg {String} title
32675      */   
32676     title : '',
32677     /**
32678      * @cfg {String} html
32679      */   
32680     html : '',
32681     /**
32682      * @cfg {String} bgimage
32683      */   
32684     bgimage : '',
32685     /**
32686      * @cfg {String} videourl
32687      */   
32688     videourl : '',
32689     /**
32690      * @cfg {String} cls
32691      */   
32692     cls : '',
32693     /**
32694      * @cfg {String} href
32695      */   
32696     href : '',
32697     /**
32698      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32699      */   
32700     size : 'xs',
32701     
32702     /**
32703      * @cfg {String} placetitle (center|bottom)
32704      */   
32705     placetitle : '',
32706     
32707     /**
32708      * @cfg {Boolean} isFitContainer defalut true
32709      */   
32710     isFitContainer : true, 
32711     
32712     /**
32713      * @cfg {Boolean} preventDefault defalut false
32714      */   
32715     preventDefault : false, 
32716     
32717     /**
32718      * @cfg {Boolean} inverse defalut false
32719      */   
32720     maskInverse : false, 
32721     
32722     getAutoCreate : function()
32723     {
32724         if(!this.isFitContainer){
32725             return this.getSplitAutoCreate();
32726         }
32727         
32728         var cls = 'masonry-brick masonry-brick-full';
32729         
32730         if(this.href.length){
32731             cls += ' masonry-brick-link';
32732         }
32733         
32734         if(this.bgimage.length){
32735             cls += ' masonry-brick-image';
32736         }
32737         
32738         if(this.maskInverse){
32739             cls += ' mask-inverse';
32740         }
32741         
32742         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32743             cls += ' enable-mask';
32744         }
32745         
32746         if(this.size){
32747             cls += ' masonry-' + this.size + '-brick';
32748         }
32749         
32750         if(this.placetitle.length){
32751             
32752             switch (this.placetitle) {
32753                 case 'center' :
32754                     cls += ' masonry-center-title';
32755                     break;
32756                 case 'bottom' :
32757                     cls += ' masonry-bottom-title';
32758                     break;
32759                 default:
32760                     break;
32761             }
32762             
32763         } else {
32764             if(!this.html.length && !this.bgimage.length){
32765                 cls += ' masonry-center-title';
32766             }
32767
32768             if(!this.html.length && this.bgimage.length){
32769                 cls += ' masonry-bottom-title';
32770             }
32771         }
32772         
32773         if(this.cls){
32774             cls += ' ' + this.cls;
32775         }
32776         
32777         var cfg = {
32778             tag: (this.href.length) ? 'a' : 'div',
32779             cls: cls,
32780             cn: [
32781                 {
32782                     tag: 'div',
32783                     cls: 'masonry-brick-mask'
32784                 },
32785                 {
32786                     tag: 'div',
32787                     cls: 'masonry-brick-paragraph',
32788                     cn: []
32789                 }
32790             ]
32791         };
32792         
32793         if(this.href.length){
32794             cfg.href = this.href;
32795         }
32796         
32797         var cn = cfg.cn[1].cn;
32798         
32799         if(this.title.length){
32800             cn.push({
32801                 tag: 'h4',
32802                 cls: 'masonry-brick-title',
32803                 html: this.title
32804             });
32805         }
32806         
32807         if(this.html.length){
32808             cn.push({
32809                 tag: 'p',
32810                 cls: 'masonry-brick-text',
32811                 html: this.html
32812             });
32813         }
32814         
32815         if (!this.title.length && !this.html.length) {
32816             cfg.cn[1].cls += ' hide';
32817         }
32818         
32819         if(this.bgimage.length){
32820             cfg.cn.push({
32821                 tag: 'img',
32822                 cls: 'masonry-brick-image-view',
32823                 src: this.bgimage
32824             });
32825         }
32826         
32827         if(this.videourl.length){
32828             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32829             // youtube support only?
32830             cfg.cn.push({
32831                 tag: 'iframe',
32832                 cls: 'masonry-brick-image-view',
32833                 src: vurl,
32834                 frameborder : 0,
32835                 allowfullscreen : true
32836             });
32837         }
32838         
32839         return cfg;
32840         
32841     },
32842     
32843     getSplitAutoCreate : function()
32844     {
32845         var cls = 'masonry-brick masonry-brick-split';
32846         
32847         if(this.href.length){
32848             cls += ' masonry-brick-link';
32849         }
32850         
32851         if(this.bgimage.length){
32852             cls += ' masonry-brick-image';
32853         }
32854         
32855         if(this.size){
32856             cls += ' masonry-' + this.size + '-brick';
32857         }
32858         
32859         switch (this.placetitle) {
32860             case 'center' :
32861                 cls += ' masonry-center-title';
32862                 break;
32863             case 'bottom' :
32864                 cls += ' masonry-bottom-title';
32865                 break;
32866             default:
32867                 if(!this.bgimage.length){
32868                     cls += ' masonry-center-title';
32869                 }
32870
32871                 if(this.bgimage.length){
32872                     cls += ' masonry-bottom-title';
32873                 }
32874                 break;
32875         }
32876         
32877         if(this.cls){
32878             cls += ' ' + this.cls;
32879         }
32880         
32881         var cfg = {
32882             tag: (this.href.length) ? 'a' : 'div',
32883             cls: cls,
32884             cn: [
32885                 {
32886                     tag: 'div',
32887                     cls: 'masonry-brick-split-head',
32888                     cn: [
32889                         {
32890                             tag: 'div',
32891                             cls: 'masonry-brick-paragraph',
32892                             cn: []
32893                         }
32894                     ]
32895                 },
32896                 {
32897                     tag: 'div',
32898                     cls: 'masonry-brick-split-body',
32899                     cn: []
32900                 }
32901             ]
32902         };
32903         
32904         if(this.href.length){
32905             cfg.href = this.href;
32906         }
32907         
32908         if(this.title.length){
32909             cfg.cn[0].cn[0].cn.push({
32910                 tag: 'h4',
32911                 cls: 'masonry-brick-title',
32912                 html: this.title
32913             });
32914         }
32915         
32916         if(this.html.length){
32917             cfg.cn[1].cn.push({
32918                 tag: 'p',
32919                 cls: 'masonry-brick-text',
32920                 html: this.html
32921             });
32922         }
32923
32924         if(this.bgimage.length){
32925             cfg.cn[0].cn.push({
32926                 tag: 'img',
32927                 cls: 'masonry-brick-image-view',
32928                 src: this.bgimage
32929             });
32930         }
32931         
32932         if(this.videourl.length){
32933             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32934             // youtube support only?
32935             cfg.cn[0].cn.cn.push({
32936                 tag: 'iframe',
32937                 cls: 'masonry-brick-image-view',
32938                 src: vurl,
32939                 frameborder : 0,
32940                 allowfullscreen : true
32941             });
32942         }
32943         
32944         return cfg;
32945     },
32946     
32947     initEvents: function() 
32948     {
32949         switch (this.size) {
32950             case 'xs' :
32951                 this.x = 1;
32952                 this.y = 1;
32953                 break;
32954             case 'sm' :
32955                 this.x = 2;
32956                 this.y = 2;
32957                 break;
32958             case 'md' :
32959             case 'md-left' :
32960             case 'md-right' :
32961                 this.x = 3;
32962                 this.y = 3;
32963                 break;
32964             case 'tall' :
32965                 this.x = 2;
32966                 this.y = 3;
32967                 break;
32968             case 'wide' :
32969                 this.x = 3;
32970                 this.y = 2;
32971                 break;
32972             case 'wide-thin' :
32973                 this.x = 3;
32974                 this.y = 1;
32975                 break;
32976                         
32977             default :
32978                 break;
32979         }
32980         
32981         if(Roo.isTouch){
32982             this.el.on('touchstart', this.onTouchStart, this);
32983             this.el.on('touchmove', this.onTouchMove, this);
32984             this.el.on('touchend', this.onTouchEnd, this);
32985             this.el.on('contextmenu', this.onContextMenu, this);
32986         } else {
32987             this.el.on('mouseenter'  ,this.enter, this);
32988             this.el.on('mouseleave', this.leave, this);
32989             this.el.on('click', this.onClick, this);
32990         }
32991         
32992         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32993             this.parent().bricks.push(this);   
32994         }
32995         
32996     },
32997     
32998     onClick: function(e, el)
32999     {
33000         var time = this.endTimer - this.startTimer;
33001         // Roo.log(e.preventDefault());
33002         if(Roo.isTouch){
33003             if(time > 1000){
33004                 e.preventDefault();
33005                 return;
33006             }
33007         }
33008         
33009         if(!this.preventDefault){
33010             return;
33011         }
33012         
33013         e.preventDefault();
33014         
33015         if (this.activeClass != '') {
33016             this.selectBrick();
33017         }
33018         
33019         this.fireEvent('click', this, e);
33020     },
33021     
33022     enter: function(e, el)
33023     {
33024         e.preventDefault();
33025         
33026         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33027             return;
33028         }
33029         
33030         if(this.bgimage.length && this.html.length){
33031             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33032         }
33033     },
33034     
33035     leave: function(e, el)
33036     {
33037         e.preventDefault();
33038         
33039         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33040             return;
33041         }
33042         
33043         if(this.bgimage.length && this.html.length){
33044             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33045         }
33046     },
33047     
33048     onTouchStart: function(e, el)
33049     {
33050 //        e.preventDefault();
33051         
33052         this.touchmoved = false;
33053         
33054         if(!this.isFitContainer){
33055             return;
33056         }
33057         
33058         if(!this.bgimage.length || !this.html.length){
33059             return;
33060         }
33061         
33062         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33063         
33064         this.timer = new Date().getTime();
33065         
33066     },
33067     
33068     onTouchMove: function(e, el)
33069     {
33070         this.touchmoved = true;
33071     },
33072     
33073     onContextMenu : function(e,el)
33074     {
33075         e.preventDefault();
33076         e.stopPropagation();
33077         return false;
33078     },
33079     
33080     onTouchEnd: function(e, el)
33081     {
33082 //        e.preventDefault();
33083         
33084         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33085         
33086             this.leave(e,el);
33087             
33088             return;
33089         }
33090         
33091         if(!this.bgimage.length || !this.html.length){
33092             
33093             if(this.href.length){
33094                 window.location.href = this.href;
33095             }
33096             
33097             return;
33098         }
33099         
33100         if(!this.isFitContainer){
33101             return;
33102         }
33103         
33104         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33105         
33106         window.location.href = this.href;
33107     },
33108     
33109     //selection on single brick only
33110     selectBrick : function() {
33111         
33112         if (!this.parentId) {
33113             return;
33114         }
33115         
33116         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33117         var index = m.selectedBrick.indexOf(this.id);
33118         
33119         if ( index > -1) {
33120             m.selectedBrick.splice(index,1);
33121             this.el.removeClass(this.activeClass);
33122             return;
33123         }
33124         
33125         for(var i = 0; i < m.selectedBrick.length; i++) {
33126             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33127             b.el.removeClass(b.activeClass);
33128         }
33129         
33130         m.selectedBrick = [];
33131         
33132         m.selectedBrick.push(this.id);
33133         this.el.addClass(this.activeClass);
33134         return;
33135     },
33136     
33137     isSelected : function(){
33138         return this.el.hasClass(this.activeClass);
33139         
33140     }
33141 });
33142
33143 Roo.apply(Roo.bootstrap.MasonryBrick, {
33144     
33145     //groups: {},
33146     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33147      /**
33148     * register a Masonry Brick
33149     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33150     */
33151     
33152     register : function(brick)
33153     {
33154         //this.groups[brick.id] = brick;
33155         this.groups.add(brick.id, brick);
33156     },
33157     /**
33158     * fetch a  masonry brick based on the masonry brick ID
33159     * @param {string} the masonry brick to add
33160     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33161     */
33162     
33163     get: function(brick_id) 
33164     {
33165         // if (typeof(this.groups[brick_id]) == 'undefined') {
33166         //     return false;
33167         // }
33168         // return this.groups[brick_id] ;
33169         
33170         if(this.groups.key(brick_id)) {
33171             return this.groups.key(brick_id);
33172         }
33173         
33174         return false;
33175     }
33176     
33177     
33178     
33179 });
33180
33181  /*
33182  * - LGPL
33183  *
33184  * element
33185  * 
33186  */
33187
33188 /**
33189  * @class Roo.bootstrap.Brick
33190  * @extends Roo.bootstrap.Component
33191  * Bootstrap Brick class
33192  * 
33193  * @constructor
33194  * Create a new Brick
33195  * @param {Object} config The config object
33196  */
33197
33198 Roo.bootstrap.Brick = function(config){
33199     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33200     
33201     this.addEvents({
33202         // raw events
33203         /**
33204          * @event click
33205          * When a Brick is click
33206          * @param {Roo.bootstrap.Brick} this
33207          * @param {Roo.EventObject} e
33208          */
33209         "click" : true
33210     });
33211 };
33212
33213 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33214     
33215     /**
33216      * @cfg {String} title
33217      */   
33218     title : '',
33219     /**
33220      * @cfg {String} html
33221      */   
33222     html : '',
33223     /**
33224      * @cfg {String} bgimage
33225      */   
33226     bgimage : '',
33227     /**
33228      * @cfg {String} cls
33229      */   
33230     cls : '',
33231     /**
33232      * @cfg {String} href
33233      */   
33234     href : '',
33235     /**
33236      * @cfg {String} video
33237      */   
33238     video : '',
33239     /**
33240      * @cfg {Boolean} square
33241      */   
33242     square : true,
33243     
33244     getAutoCreate : function()
33245     {
33246         var cls = 'roo-brick';
33247         
33248         if(this.href.length){
33249             cls += ' roo-brick-link';
33250         }
33251         
33252         if(this.bgimage.length){
33253             cls += ' roo-brick-image';
33254         }
33255         
33256         if(!this.html.length && !this.bgimage.length){
33257             cls += ' roo-brick-center-title';
33258         }
33259         
33260         if(!this.html.length && this.bgimage.length){
33261             cls += ' roo-brick-bottom-title';
33262         }
33263         
33264         if(this.cls){
33265             cls += ' ' + this.cls;
33266         }
33267         
33268         var cfg = {
33269             tag: (this.href.length) ? 'a' : 'div',
33270             cls: cls,
33271             cn: [
33272                 {
33273                     tag: 'div',
33274                     cls: 'roo-brick-paragraph',
33275                     cn: []
33276                 }
33277             ]
33278         };
33279         
33280         if(this.href.length){
33281             cfg.href = this.href;
33282         }
33283         
33284         var cn = cfg.cn[0].cn;
33285         
33286         if(this.title.length){
33287             cn.push({
33288                 tag: 'h4',
33289                 cls: 'roo-brick-title',
33290                 html: this.title
33291             });
33292         }
33293         
33294         if(this.html.length){
33295             cn.push({
33296                 tag: 'p',
33297                 cls: 'roo-brick-text',
33298                 html: this.html
33299             });
33300         } else {
33301             cn.cls += ' hide';
33302         }
33303         
33304         if(this.bgimage.length){
33305             cfg.cn.push({
33306                 tag: 'img',
33307                 cls: 'roo-brick-image-view',
33308                 src: this.bgimage
33309             });
33310         }
33311         
33312         return cfg;
33313     },
33314     
33315     initEvents: function() 
33316     {
33317         if(this.title.length || this.html.length){
33318             this.el.on('mouseenter'  ,this.enter, this);
33319             this.el.on('mouseleave', this.leave, this);
33320         }
33321         
33322         Roo.EventManager.onWindowResize(this.resize, this); 
33323         
33324         if(this.bgimage.length){
33325             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33326             this.imageEl.on('load', this.onImageLoad, this);
33327             return;
33328         }
33329         
33330         this.resize();
33331     },
33332     
33333     onImageLoad : function()
33334     {
33335         this.resize();
33336     },
33337     
33338     resize : function()
33339     {
33340         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33341         
33342         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33343         
33344         if(this.bgimage.length){
33345             var image = this.el.select('.roo-brick-image-view', true).first();
33346             
33347             image.setWidth(paragraph.getWidth());
33348             
33349             if(this.square){
33350                 image.setHeight(paragraph.getWidth());
33351             }
33352             
33353             this.el.setHeight(image.getHeight());
33354             paragraph.setHeight(image.getHeight());
33355             
33356         }
33357         
33358     },
33359     
33360     enter: function(e, el)
33361     {
33362         e.preventDefault();
33363         
33364         if(this.bgimage.length){
33365             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33366             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33367         }
33368     },
33369     
33370     leave: function(e, el)
33371     {
33372         e.preventDefault();
33373         
33374         if(this.bgimage.length){
33375             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33376             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33377         }
33378     }
33379     
33380 });
33381
33382  
33383
33384  /*
33385  * - LGPL
33386  *
33387  * Number field 
33388  */
33389
33390 /**
33391  * @class Roo.bootstrap.NumberField
33392  * @extends Roo.bootstrap.Input
33393  * Bootstrap NumberField class
33394  * 
33395  * 
33396  * 
33397  * 
33398  * @constructor
33399  * Create a new NumberField
33400  * @param {Object} config The config object
33401  */
33402
33403 Roo.bootstrap.NumberField = function(config){
33404     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33405 };
33406
33407 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33408     
33409     /**
33410      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33411      */
33412     allowDecimals : true,
33413     /**
33414      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33415      */
33416     decimalSeparator : ".",
33417     /**
33418      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33419      */
33420     decimalPrecision : 2,
33421     /**
33422      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33423      */
33424     allowNegative : true,
33425     
33426     /**
33427      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33428      */
33429     allowZero: true,
33430     /**
33431      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33432      */
33433     minValue : Number.NEGATIVE_INFINITY,
33434     /**
33435      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33436      */
33437     maxValue : Number.MAX_VALUE,
33438     /**
33439      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33440      */
33441     minText : "The minimum value for this field is {0}",
33442     /**
33443      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33444      */
33445     maxText : "The maximum value for this field is {0}",
33446     /**
33447      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33448      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33449      */
33450     nanText : "{0} is not a valid number",
33451     /**
33452      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33453      */
33454     thousandsDelimiter : false,
33455     /**
33456      * @cfg {String} valueAlign alignment of value
33457      */
33458     valueAlign : "left",
33459
33460     getAutoCreate : function()
33461     {
33462         var hiddenInput = {
33463             tag: 'input',
33464             type: 'hidden',
33465             id: Roo.id(),
33466             cls: 'hidden-number-input'
33467         };
33468         
33469         if (this.name) {
33470             hiddenInput.name = this.name;
33471         }
33472         
33473         this.name = '';
33474         
33475         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33476         
33477         this.name = hiddenInput.name;
33478         
33479         if(cfg.cn.length > 0) {
33480             cfg.cn.push(hiddenInput);
33481         }
33482         
33483         return cfg;
33484     },
33485
33486     // private
33487     initEvents : function()
33488     {   
33489         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33490         
33491         var allowed = "0123456789";
33492         
33493         if(this.allowDecimals){
33494             allowed += this.decimalSeparator;
33495         }
33496         
33497         if(this.allowNegative){
33498             allowed += "-";
33499         }
33500         
33501         if(this.thousandsDelimiter) {
33502             allowed += ",";
33503         }
33504         
33505         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33506         
33507         var keyPress = function(e){
33508             
33509             var k = e.getKey();
33510             
33511             var c = e.getCharCode();
33512             
33513             if(
33514                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33515                     allowed.indexOf(String.fromCharCode(c)) === -1
33516             ){
33517                 e.stopEvent();
33518                 return;
33519             }
33520             
33521             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33522                 return;
33523             }
33524             
33525             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33526                 e.stopEvent();
33527             }
33528         };
33529         
33530         this.el.on("keypress", keyPress, this);
33531     },
33532     
33533     validateValue : function(value)
33534     {
33535         
33536         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33537             return false;
33538         }
33539         
33540         var num = this.parseValue(value);
33541         
33542         if(isNaN(num)){
33543             this.markInvalid(String.format(this.nanText, value));
33544             return false;
33545         }
33546         
33547         if(num < this.minValue){
33548             this.markInvalid(String.format(this.minText, this.minValue));
33549             return false;
33550         }
33551         
33552         if(num > this.maxValue){
33553             this.markInvalid(String.format(this.maxText, this.maxValue));
33554             return false;
33555         }
33556         
33557         return true;
33558     },
33559
33560     getValue : function()
33561     {
33562         var v = this.hiddenEl().getValue();
33563         
33564         return this.fixPrecision(this.parseValue(v));
33565     },
33566
33567     parseValue : function(value)
33568     {
33569         if(this.thousandsDelimiter) {
33570             value += "";
33571             r = new RegExp(",", "g");
33572             value = value.replace(r, "");
33573         }
33574         
33575         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33576         return isNaN(value) ? '' : value;
33577     },
33578
33579     fixPrecision : function(value)
33580     {
33581         if(this.thousandsDelimiter) {
33582             value += "";
33583             r = new RegExp(",", "g");
33584             value = value.replace(r, "");
33585         }
33586         
33587         var nan = isNaN(value);
33588         
33589         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33590             return nan ? '' : value;
33591         }
33592         return parseFloat(value).toFixed(this.decimalPrecision);
33593     },
33594
33595     setValue : function(v)
33596     {
33597         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33598         
33599         this.value = v;
33600         
33601         if(this.rendered){
33602             
33603             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33604             
33605             this.inputEl().dom.value = (v == '') ? '' :
33606                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33607             
33608             if(!this.allowZero && v === '0') {
33609                 this.hiddenEl().dom.value = '';
33610                 this.inputEl().dom.value = '';
33611             }
33612             
33613             this.validate();
33614         }
33615     },
33616
33617     decimalPrecisionFcn : function(v)
33618     {
33619         return Math.floor(v);
33620     },
33621
33622     beforeBlur : function()
33623     {
33624         var v = this.parseValue(this.getRawValue());
33625         
33626         if(v || v === 0 || v === ''){
33627             this.setValue(v);
33628         }
33629     },
33630     
33631     hiddenEl : function()
33632     {
33633         return this.el.select('input.hidden-number-input',true).first();
33634     }
33635     
33636 });
33637
33638  
33639
33640 /*
33641 * Licence: LGPL
33642 */
33643
33644 /**
33645  * @class Roo.bootstrap.DocumentSlider
33646  * @extends Roo.bootstrap.Component
33647  * Bootstrap DocumentSlider class
33648  * 
33649  * @constructor
33650  * Create a new DocumentViewer
33651  * @param {Object} config The config object
33652  */
33653
33654 Roo.bootstrap.DocumentSlider = function(config){
33655     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33656     
33657     this.files = [];
33658     
33659     this.addEvents({
33660         /**
33661          * @event initial
33662          * Fire after initEvent
33663          * @param {Roo.bootstrap.DocumentSlider} this
33664          */
33665         "initial" : true,
33666         /**
33667          * @event update
33668          * Fire after update
33669          * @param {Roo.bootstrap.DocumentSlider} this
33670          */
33671         "update" : true,
33672         /**
33673          * @event click
33674          * Fire after click
33675          * @param {Roo.bootstrap.DocumentSlider} this
33676          */
33677         "click" : true
33678     });
33679 };
33680
33681 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33682     
33683     files : false,
33684     
33685     indicator : 0,
33686     
33687     getAutoCreate : function()
33688     {
33689         var cfg = {
33690             tag : 'div',
33691             cls : 'roo-document-slider',
33692             cn : [
33693                 {
33694                     tag : 'div',
33695                     cls : 'roo-document-slider-header',
33696                     cn : [
33697                         {
33698                             tag : 'div',
33699                             cls : 'roo-document-slider-header-title'
33700                         }
33701                     ]
33702                 },
33703                 {
33704                     tag : 'div',
33705                     cls : 'roo-document-slider-body',
33706                     cn : [
33707                         {
33708                             tag : 'div',
33709                             cls : 'roo-document-slider-prev',
33710                             cn : [
33711                                 {
33712                                     tag : 'i',
33713                                     cls : 'fa fa-chevron-left'
33714                                 }
33715                             ]
33716                         },
33717                         {
33718                             tag : 'div',
33719                             cls : 'roo-document-slider-thumb',
33720                             cn : [
33721                                 {
33722                                     tag : 'img',
33723                                     cls : 'roo-document-slider-image'
33724                                 }
33725                             ]
33726                         },
33727                         {
33728                             tag : 'div',
33729                             cls : 'roo-document-slider-next',
33730                             cn : [
33731                                 {
33732                                     tag : 'i',
33733                                     cls : 'fa fa-chevron-right'
33734                                 }
33735                             ]
33736                         }
33737                     ]
33738                 }
33739             ]
33740         };
33741         
33742         return cfg;
33743     },
33744     
33745     initEvents : function()
33746     {
33747         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33748         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33749         
33750         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33751         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33752         
33753         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33754         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33755         
33756         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33757         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33758         
33759         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33760         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33761         
33762         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33763         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33764         
33765         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33766         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33767         
33768         this.thumbEl.on('click', this.onClick, this);
33769         
33770         this.prevIndicator.on('click', this.prev, this);
33771         
33772         this.nextIndicator.on('click', this.next, this);
33773         
33774     },
33775     
33776     initial : function()
33777     {
33778         if(this.files.length){
33779             this.indicator = 1;
33780             this.update()
33781         }
33782         
33783         this.fireEvent('initial', this);
33784     },
33785     
33786     update : function()
33787     {
33788         this.imageEl.attr('src', this.files[this.indicator - 1]);
33789         
33790         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33791         
33792         this.prevIndicator.show();
33793         
33794         if(this.indicator == 1){
33795             this.prevIndicator.hide();
33796         }
33797         
33798         this.nextIndicator.show();
33799         
33800         if(this.indicator == this.files.length){
33801             this.nextIndicator.hide();
33802         }
33803         
33804         this.thumbEl.scrollTo('top');
33805         
33806         this.fireEvent('update', this);
33807     },
33808     
33809     onClick : function(e)
33810     {
33811         e.preventDefault();
33812         
33813         this.fireEvent('click', this);
33814     },
33815     
33816     prev : function(e)
33817     {
33818         e.preventDefault();
33819         
33820         this.indicator = Math.max(1, this.indicator - 1);
33821         
33822         this.update();
33823     },
33824     
33825     next : function(e)
33826     {
33827         e.preventDefault();
33828         
33829         this.indicator = Math.min(this.files.length, this.indicator + 1);
33830         
33831         this.update();
33832     }
33833 });
33834 /*
33835  * - LGPL
33836  *
33837  * RadioSet
33838  *
33839  *
33840  */
33841
33842 /**
33843  * @class Roo.bootstrap.RadioSet
33844  * @extends Roo.bootstrap.Input
33845  * Bootstrap RadioSet class
33846  * @cfg {String} indicatorpos (left|right) default left
33847  * @cfg {Boolean} inline (true|false) inline the element (default true)
33848  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33849  * @constructor
33850  * Create a new RadioSet
33851  * @param {Object} config The config object
33852  */
33853
33854 Roo.bootstrap.RadioSet = function(config){
33855     
33856     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33857     
33858     this.radioes = [];
33859     
33860     Roo.bootstrap.RadioSet.register(this);
33861     
33862     this.addEvents({
33863         /**
33864         * @event check
33865         * Fires when the element is checked or unchecked.
33866         * @param {Roo.bootstrap.RadioSet} this This radio
33867         * @param {Roo.bootstrap.Radio} item The checked item
33868         */
33869        check : true,
33870        /**
33871         * @event click
33872         * Fires when the element is click.
33873         * @param {Roo.bootstrap.RadioSet} this This radio set
33874         * @param {Roo.bootstrap.Radio} item The checked item
33875         * @param {Roo.EventObject} e The event object
33876         */
33877        click : true
33878     });
33879     
33880 };
33881
33882 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33883
33884     radioes : false,
33885     
33886     inline : true,
33887     
33888     weight : '',
33889     
33890     indicatorpos : 'left',
33891     
33892     getAutoCreate : function()
33893     {
33894         var label = {
33895             tag : 'label',
33896             cls : 'roo-radio-set-label',
33897             cn : [
33898                 {
33899                     tag : 'span',
33900                     html : this.fieldLabel
33901                 }
33902             ]
33903         };
33904         
33905         if(this.indicatorpos == 'left'){
33906             label.cn.unshift({
33907                 tag : 'i',
33908                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33909                 tooltip : 'This field is required'
33910             });
33911         } else {
33912             label.cn.push({
33913                 tag : 'i',
33914                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33915                 tooltip : 'This field is required'
33916             });
33917         }
33918         
33919         var items = {
33920             tag : 'div',
33921             cls : 'roo-radio-set-items'
33922         };
33923         
33924         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33925         
33926         if (align === 'left' && this.fieldLabel.length) {
33927             
33928             items = {
33929                 cls : "roo-radio-set-right", 
33930                 cn: [
33931                     items
33932                 ]
33933             };
33934             
33935             if(this.labelWidth > 12){
33936                 label.style = "width: " + this.labelWidth + 'px';
33937             }
33938             
33939             if(this.labelWidth < 13 && this.labelmd == 0){
33940                 this.labelmd = this.labelWidth;
33941             }
33942             
33943             if(this.labellg > 0){
33944                 label.cls += ' col-lg-' + this.labellg;
33945                 items.cls += ' col-lg-' + (12 - this.labellg);
33946             }
33947             
33948             if(this.labelmd > 0){
33949                 label.cls += ' col-md-' + this.labelmd;
33950                 items.cls += ' col-md-' + (12 - this.labelmd);
33951             }
33952             
33953             if(this.labelsm > 0){
33954                 label.cls += ' col-sm-' + this.labelsm;
33955                 items.cls += ' col-sm-' + (12 - this.labelsm);
33956             }
33957             
33958             if(this.labelxs > 0){
33959                 label.cls += ' col-xs-' + this.labelxs;
33960                 items.cls += ' col-xs-' + (12 - this.labelxs);
33961             }
33962         }
33963         
33964         var cfg = {
33965             tag : 'div',
33966             cls : 'roo-radio-set',
33967             cn : [
33968                 {
33969                     tag : 'input',
33970                     cls : 'roo-radio-set-input',
33971                     type : 'hidden',
33972                     name : this.name,
33973                     value : this.value ? this.value :  ''
33974                 },
33975                 label,
33976                 items
33977             ]
33978         };
33979         
33980         if(this.weight.length){
33981             cfg.cls += ' roo-radio-' + this.weight;
33982         }
33983         
33984         if(this.inline) {
33985             cfg.cls += ' roo-radio-set-inline';
33986         }
33987         
33988         var settings=this;
33989         ['xs','sm','md','lg'].map(function(size){
33990             if (settings[size]) {
33991                 cfg.cls += ' col-' + size + '-' + settings[size];
33992             }
33993         });
33994         
33995         return cfg;
33996         
33997     },
33998
33999     initEvents : function()
34000     {
34001         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34002         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34003         
34004         if(!this.fieldLabel.length){
34005             this.labelEl.hide();
34006         }
34007         
34008         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34009         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34010         
34011         this.indicator = this.indicatorEl();
34012         
34013         if(this.indicator){
34014             this.indicator.addClass('invisible');
34015         }
34016         
34017         this.originalValue = this.getValue();
34018         
34019     },
34020     
34021     inputEl: function ()
34022     {
34023         return this.el.select('.roo-radio-set-input', true).first();
34024     },
34025     
34026     getChildContainer : function()
34027     {
34028         return this.itemsEl;
34029     },
34030     
34031     register : function(item)
34032     {
34033         this.radioes.push(item);
34034         
34035     },
34036     
34037     validate : function()
34038     {   
34039         if(this.getVisibilityEl().hasClass('hidden')){
34040             return true;
34041         }
34042         
34043         var valid = false;
34044         
34045         Roo.each(this.radioes, function(i){
34046             if(!i.checked){
34047                 return;
34048             }
34049             
34050             valid = true;
34051             return false;
34052         });
34053         
34054         if(this.allowBlank) {
34055             return true;
34056         }
34057         
34058         if(this.disabled || valid){
34059             this.markValid();
34060             return true;
34061         }
34062         
34063         this.markInvalid();
34064         return false;
34065         
34066     },
34067     
34068     markValid : function()
34069     {
34070         if(this.labelEl.isVisible(true)){
34071             this.indicatorEl().removeClass('visible');
34072             this.indicatorEl().addClass('invisible');
34073         }
34074         
34075         this.el.removeClass([this.invalidClass, this.validClass]);
34076         this.el.addClass(this.validClass);
34077         
34078         this.fireEvent('valid', this);
34079     },
34080     
34081     markInvalid : function(msg)
34082     {
34083         if(this.allowBlank || this.disabled){
34084             return;
34085         }
34086         
34087         if(this.labelEl.isVisible(true)){
34088             this.indicatorEl().removeClass('invisible');
34089             this.indicatorEl().addClass('visible');
34090         }
34091         
34092         this.el.removeClass([this.invalidClass, this.validClass]);
34093         this.el.addClass(this.invalidClass);
34094         
34095         this.fireEvent('invalid', this, msg);
34096         
34097     },
34098     
34099     setValue : function(v, suppressEvent)
34100     {   
34101         if(this.value === v){
34102             return;
34103         }
34104         
34105         this.value = v;
34106         
34107         if(this.rendered){
34108             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34109         }
34110         
34111         Roo.each(this.radioes, function(i){
34112             i.checked = false;
34113             i.el.removeClass('checked');
34114         });
34115         
34116         Roo.each(this.radioes, function(i){
34117             
34118             if(i.value === v || i.value.toString() === v.toString()){
34119                 i.checked = true;
34120                 i.el.addClass('checked');
34121                 
34122                 if(suppressEvent !== true){
34123                     this.fireEvent('check', this, i);
34124                 }
34125                 
34126                 return false;
34127             }
34128             
34129         }, this);
34130         
34131         this.validate();
34132     },
34133     
34134     clearInvalid : function(){
34135         
34136         if(!this.el || this.preventMark){
34137             return;
34138         }
34139         
34140         this.el.removeClass([this.invalidClass]);
34141         
34142         this.fireEvent('valid', this);
34143     }
34144     
34145 });
34146
34147 Roo.apply(Roo.bootstrap.RadioSet, {
34148     
34149     groups: {},
34150     
34151     register : function(set)
34152     {
34153         this.groups[set.name] = set;
34154     },
34155     
34156     get: function(name) 
34157     {
34158         if (typeof(this.groups[name]) == 'undefined') {
34159             return false;
34160         }
34161         
34162         return this.groups[name] ;
34163     }
34164     
34165 });
34166 /*
34167  * Based on:
34168  * Ext JS Library 1.1.1
34169  * Copyright(c) 2006-2007, Ext JS, LLC.
34170  *
34171  * Originally Released Under LGPL - original licence link has changed is not relivant.
34172  *
34173  * Fork - LGPL
34174  * <script type="text/javascript">
34175  */
34176
34177
34178 /**
34179  * @class Roo.bootstrap.SplitBar
34180  * @extends Roo.util.Observable
34181  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34182  * <br><br>
34183  * Usage:
34184  * <pre><code>
34185 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34186                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34187 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34188 split.minSize = 100;
34189 split.maxSize = 600;
34190 split.animate = true;
34191 split.on('moved', splitterMoved);
34192 </code></pre>
34193  * @constructor
34194  * Create a new SplitBar
34195  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34196  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34197  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34198  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34199                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34200                         position of the SplitBar).
34201  */
34202 Roo.bootstrap.SplitBar = function(cfg){
34203     
34204     /** @private */
34205     
34206     //{
34207     //  dragElement : elm
34208     //  resizingElement: el,
34209         // optional..
34210     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34211     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34212         // existingProxy ???
34213     //}
34214     
34215     this.el = Roo.get(cfg.dragElement, true);
34216     this.el.dom.unselectable = "on";
34217     /** @private */
34218     this.resizingEl = Roo.get(cfg.resizingElement, true);
34219
34220     /**
34221      * @private
34222      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34223      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34224      * @type Number
34225      */
34226     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34227     
34228     /**
34229      * The minimum size of the resizing element. (Defaults to 0)
34230      * @type Number
34231      */
34232     this.minSize = 0;
34233     
34234     /**
34235      * The maximum size of the resizing element. (Defaults to 2000)
34236      * @type Number
34237      */
34238     this.maxSize = 2000;
34239     
34240     /**
34241      * Whether to animate the transition to the new size
34242      * @type Boolean
34243      */
34244     this.animate = false;
34245     
34246     /**
34247      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34248      * @type Boolean
34249      */
34250     this.useShim = false;
34251     
34252     /** @private */
34253     this.shim = null;
34254     
34255     if(!cfg.existingProxy){
34256         /** @private */
34257         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34258     }else{
34259         this.proxy = Roo.get(cfg.existingProxy).dom;
34260     }
34261     /** @private */
34262     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34263     
34264     /** @private */
34265     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34266     
34267     /** @private */
34268     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34269     
34270     /** @private */
34271     this.dragSpecs = {};
34272     
34273     /**
34274      * @private The adapter to use to positon and resize elements
34275      */
34276     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34277     this.adapter.init(this);
34278     
34279     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34280         /** @private */
34281         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34282         this.el.addClass("roo-splitbar-h");
34283     }else{
34284         /** @private */
34285         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34286         this.el.addClass("roo-splitbar-v");
34287     }
34288     
34289     this.addEvents({
34290         /**
34291          * @event resize
34292          * Fires when the splitter is moved (alias for {@link #event-moved})
34293          * @param {Roo.bootstrap.SplitBar} this
34294          * @param {Number} newSize the new width or height
34295          */
34296         "resize" : true,
34297         /**
34298          * @event moved
34299          * Fires when the splitter is moved
34300          * @param {Roo.bootstrap.SplitBar} this
34301          * @param {Number} newSize the new width or height
34302          */
34303         "moved" : true,
34304         /**
34305          * @event beforeresize
34306          * Fires before the splitter is dragged
34307          * @param {Roo.bootstrap.SplitBar} this
34308          */
34309         "beforeresize" : true,
34310
34311         "beforeapply" : true
34312     });
34313
34314     Roo.util.Observable.call(this);
34315 };
34316
34317 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34318     onStartProxyDrag : function(x, y){
34319         this.fireEvent("beforeresize", this);
34320         if(!this.overlay){
34321             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34322             o.unselectable();
34323             o.enableDisplayMode("block");
34324             // all splitbars share the same overlay
34325             Roo.bootstrap.SplitBar.prototype.overlay = o;
34326         }
34327         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34328         this.overlay.show();
34329         Roo.get(this.proxy).setDisplayed("block");
34330         var size = this.adapter.getElementSize(this);
34331         this.activeMinSize = this.getMinimumSize();;
34332         this.activeMaxSize = this.getMaximumSize();;
34333         var c1 = size - this.activeMinSize;
34334         var c2 = Math.max(this.activeMaxSize - size, 0);
34335         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34336             this.dd.resetConstraints();
34337             this.dd.setXConstraint(
34338                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34339                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34340             );
34341             this.dd.setYConstraint(0, 0);
34342         }else{
34343             this.dd.resetConstraints();
34344             this.dd.setXConstraint(0, 0);
34345             this.dd.setYConstraint(
34346                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34347                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34348             );
34349          }
34350         this.dragSpecs.startSize = size;
34351         this.dragSpecs.startPoint = [x, y];
34352         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34353     },
34354     
34355     /** 
34356      * @private Called after the drag operation by the DDProxy
34357      */
34358     onEndProxyDrag : function(e){
34359         Roo.get(this.proxy).setDisplayed(false);
34360         var endPoint = Roo.lib.Event.getXY(e);
34361         if(this.overlay){
34362             this.overlay.hide();
34363         }
34364         var newSize;
34365         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34366             newSize = this.dragSpecs.startSize + 
34367                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34368                     endPoint[0] - this.dragSpecs.startPoint[0] :
34369                     this.dragSpecs.startPoint[0] - endPoint[0]
34370                 );
34371         }else{
34372             newSize = this.dragSpecs.startSize + 
34373                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34374                     endPoint[1] - this.dragSpecs.startPoint[1] :
34375                     this.dragSpecs.startPoint[1] - endPoint[1]
34376                 );
34377         }
34378         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34379         if(newSize != this.dragSpecs.startSize){
34380             if(this.fireEvent('beforeapply', this, newSize) !== false){
34381                 this.adapter.setElementSize(this, newSize);
34382                 this.fireEvent("moved", this, newSize);
34383                 this.fireEvent("resize", this, newSize);
34384             }
34385         }
34386     },
34387     
34388     /**
34389      * Get the adapter this SplitBar uses
34390      * @return The adapter object
34391      */
34392     getAdapter : function(){
34393         return this.adapter;
34394     },
34395     
34396     /**
34397      * Set the adapter this SplitBar uses
34398      * @param {Object} adapter A SplitBar adapter object
34399      */
34400     setAdapter : function(adapter){
34401         this.adapter = adapter;
34402         this.adapter.init(this);
34403     },
34404     
34405     /**
34406      * Gets the minimum size for the resizing element
34407      * @return {Number} The minimum size
34408      */
34409     getMinimumSize : function(){
34410         return this.minSize;
34411     },
34412     
34413     /**
34414      * Sets the minimum size for the resizing element
34415      * @param {Number} minSize The minimum size
34416      */
34417     setMinimumSize : function(minSize){
34418         this.minSize = minSize;
34419     },
34420     
34421     /**
34422      * Gets the maximum size for the resizing element
34423      * @return {Number} The maximum size
34424      */
34425     getMaximumSize : function(){
34426         return this.maxSize;
34427     },
34428     
34429     /**
34430      * Sets the maximum size for the resizing element
34431      * @param {Number} maxSize The maximum size
34432      */
34433     setMaximumSize : function(maxSize){
34434         this.maxSize = maxSize;
34435     },
34436     
34437     /**
34438      * Sets the initialize size for the resizing element
34439      * @param {Number} size The initial size
34440      */
34441     setCurrentSize : function(size){
34442         var oldAnimate = this.animate;
34443         this.animate = false;
34444         this.adapter.setElementSize(this, size);
34445         this.animate = oldAnimate;
34446     },
34447     
34448     /**
34449      * Destroy this splitbar. 
34450      * @param {Boolean} removeEl True to remove the element
34451      */
34452     destroy : function(removeEl){
34453         if(this.shim){
34454             this.shim.remove();
34455         }
34456         this.dd.unreg();
34457         this.proxy.parentNode.removeChild(this.proxy);
34458         if(removeEl){
34459             this.el.remove();
34460         }
34461     }
34462 });
34463
34464 /**
34465  * @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.
34466  */
34467 Roo.bootstrap.SplitBar.createProxy = function(dir){
34468     var proxy = new Roo.Element(document.createElement("div"));
34469     proxy.unselectable();
34470     var cls = 'roo-splitbar-proxy';
34471     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34472     document.body.appendChild(proxy.dom);
34473     return proxy.dom;
34474 };
34475
34476 /** 
34477  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34478  * Default Adapter. It assumes the splitter and resizing element are not positioned
34479  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34480  */
34481 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34482 };
34483
34484 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34485     // do nothing for now
34486     init : function(s){
34487     
34488     },
34489     /**
34490      * Called before drag operations to get the current size of the resizing element. 
34491      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34492      */
34493      getElementSize : function(s){
34494         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34495             return s.resizingEl.getWidth();
34496         }else{
34497             return s.resizingEl.getHeight();
34498         }
34499     },
34500     
34501     /**
34502      * Called after drag operations to set the size of the resizing element.
34503      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34504      * @param {Number} newSize The new size to set
34505      * @param {Function} onComplete A function to be invoked when resizing is complete
34506      */
34507     setElementSize : function(s, newSize, onComplete){
34508         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34509             if(!s.animate){
34510                 s.resizingEl.setWidth(newSize);
34511                 if(onComplete){
34512                     onComplete(s, newSize);
34513                 }
34514             }else{
34515                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34516             }
34517         }else{
34518             
34519             if(!s.animate){
34520                 s.resizingEl.setHeight(newSize);
34521                 if(onComplete){
34522                     onComplete(s, newSize);
34523                 }
34524             }else{
34525                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34526             }
34527         }
34528     }
34529 };
34530
34531 /** 
34532  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34533  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34534  * Adapter that  moves the splitter element to align with the resized sizing element. 
34535  * Used with an absolute positioned SplitBar.
34536  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34537  * document.body, make sure you assign an id to the body element.
34538  */
34539 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34540     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34541     this.container = Roo.get(container);
34542 };
34543
34544 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34545     init : function(s){
34546         this.basic.init(s);
34547     },
34548     
34549     getElementSize : function(s){
34550         return this.basic.getElementSize(s);
34551     },
34552     
34553     setElementSize : function(s, newSize, onComplete){
34554         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34555     },
34556     
34557     moveSplitter : function(s){
34558         var yes = Roo.bootstrap.SplitBar;
34559         switch(s.placement){
34560             case yes.LEFT:
34561                 s.el.setX(s.resizingEl.getRight());
34562                 break;
34563             case yes.RIGHT:
34564                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34565                 break;
34566             case yes.TOP:
34567                 s.el.setY(s.resizingEl.getBottom());
34568                 break;
34569             case yes.BOTTOM:
34570                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34571                 break;
34572         }
34573     }
34574 };
34575
34576 /**
34577  * Orientation constant - Create a vertical SplitBar
34578  * @static
34579  * @type Number
34580  */
34581 Roo.bootstrap.SplitBar.VERTICAL = 1;
34582
34583 /**
34584  * Orientation constant - Create a horizontal SplitBar
34585  * @static
34586  * @type Number
34587  */
34588 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34589
34590 /**
34591  * Placement constant - The resizing element is to the left of the splitter element
34592  * @static
34593  * @type Number
34594  */
34595 Roo.bootstrap.SplitBar.LEFT = 1;
34596
34597 /**
34598  * Placement constant - The resizing element is to the right of the splitter element
34599  * @static
34600  * @type Number
34601  */
34602 Roo.bootstrap.SplitBar.RIGHT = 2;
34603
34604 /**
34605  * Placement constant - The resizing element is positioned above the splitter element
34606  * @static
34607  * @type Number
34608  */
34609 Roo.bootstrap.SplitBar.TOP = 3;
34610
34611 /**
34612  * Placement constant - The resizing element is positioned under splitter element
34613  * @static
34614  * @type Number
34615  */
34616 Roo.bootstrap.SplitBar.BOTTOM = 4;
34617 Roo.namespace("Roo.bootstrap.layout");/*
34618  * Based on:
34619  * Ext JS Library 1.1.1
34620  * Copyright(c) 2006-2007, Ext JS, LLC.
34621  *
34622  * Originally Released Under LGPL - original licence link has changed is not relivant.
34623  *
34624  * Fork - LGPL
34625  * <script type="text/javascript">
34626  */
34627
34628 /**
34629  * @class Roo.bootstrap.layout.Manager
34630  * @extends Roo.bootstrap.Component
34631  * Base class for layout managers.
34632  */
34633 Roo.bootstrap.layout.Manager = function(config)
34634 {
34635     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34636
34637
34638
34639
34640
34641     /** false to disable window resize monitoring @type Boolean */
34642     this.monitorWindowResize = true;
34643     this.regions = {};
34644     this.addEvents({
34645         /**
34646          * @event layout
34647          * Fires when a layout is performed.
34648          * @param {Roo.LayoutManager} this
34649          */
34650         "layout" : true,
34651         /**
34652          * @event regionresized
34653          * Fires when the user resizes a region.
34654          * @param {Roo.LayoutRegion} region The resized region
34655          * @param {Number} newSize The new size (width for east/west, height for north/south)
34656          */
34657         "regionresized" : true,
34658         /**
34659          * @event regioncollapsed
34660          * Fires when a region is collapsed.
34661          * @param {Roo.LayoutRegion} region The collapsed region
34662          */
34663         "regioncollapsed" : true,
34664         /**
34665          * @event regionexpanded
34666          * Fires when a region is expanded.
34667          * @param {Roo.LayoutRegion} region The expanded region
34668          */
34669         "regionexpanded" : true
34670     });
34671     this.updating = false;
34672
34673     if (config.el) {
34674         this.el = Roo.get(config.el);
34675         this.initEvents();
34676     }
34677
34678 };
34679
34680 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34681
34682
34683     regions : null,
34684
34685     monitorWindowResize : true,
34686
34687
34688     updating : false,
34689
34690
34691     onRender : function(ct, position)
34692     {
34693         if(!this.el){
34694             this.el = Roo.get(ct);
34695             this.initEvents();
34696         }
34697         //this.fireEvent('render',this);
34698     },
34699
34700
34701     initEvents: function()
34702     {
34703
34704
34705         // ie scrollbar fix
34706         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34707             document.body.scroll = "no";
34708         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34709             this.el.position('relative');
34710         }
34711         this.id = this.el.id;
34712         this.el.addClass("roo-layout-container");
34713         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34714         if(this.el.dom != document.body ) {
34715             this.el.on('resize', this.layout,this);
34716             this.el.on('show', this.layout,this);
34717         }
34718
34719     },
34720
34721     /**
34722      * Returns true if this layout is currently being updated
34723      * @return {Boolean}
34724      */
34725     isUpdating : function(){
34726         return this.updating;
34727     },
34728
34729     /**
34730      * Suspend the LayoutManager from doing auto-layouts while
34731      * making multiple add or remove calls
34732      */
34733     beginUpdate : function(){
34734         this.updating = true;
34735     },
34736
34737     /**
34738      * Restore auto-layouts and optionally disable the manager from performing a layout
34739      * @param {Boolean} noLayout true to disable a layout update
34740      */
34741     endUpdate : function(noLayout){
34742         this.updating = false;
34743         if(!noLayout){
34744             this.layout();
34745         }
34746     },
34747
34748     layout: function(){
34749         // abstract...
34750     },
34751
34752     onRegionResized : function(region, newSize){
34753         this.fireEvent("regionresized", region, newSize);
34754         this.layout();
34755     },
34756
34757     onRegionCollapsed : function(region){
34758         this.fireEvent("regioncollapsed", region);
34759     },
34760
34761     onRegionExpanded : function(region){
34762         this.fireEvent("regionexpanded", region);
34763     },
34764
34765     /**
34766      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34767      * performs box-model adjustments.
34768      * @return {Object} The size as an object {width: (the width), height: (the height)}
34769      */
34770     getViewSize : function()
34771     {
34772         var size;
34773         if(this.el.dom != document.body){
34774             size = this.el.getSize();
34775         }else{
34776             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34777         }
34778         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34779         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34780         return size;
34781     },
34782
34783     /**
34784      * Returns the Element this layout is bound to.
34785      * @return {Roo.Element}
34786      */
34787     getEl : function(){
34788         return this.el;
34789     },
34790
34791     /**
34792      * Returns the specified region.
34793      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34794      * @return {Roo.LayoutRegion}
34795      */
34796     getRegion : function(target){
34797         return this.regions[target.toLowerCase()];
34798     },
34799
34800     onWindowResize : function(){
34801         if(this.monitorWindowResize){
34802             this.layout();
34803         }
34804     }
34805 });
34806 /*
34807  * Based on:
34808  * Ext JS Library 1.1.1
34809  * Copyright(c) 2006-2007, Ext JS, LLC.
34810  *
34811  * Originally Released Under LGPL - original licence link has changed is not relivant.
34812  *
34813  * Fork - LGPL
34814  * <script type="text/javascript">
34815  */
34816 /**
34817  * @class Roo.bootstrap.layout.Border
34818  * @extends Roo.bootstrap.layout.Manager
34819  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34820  * please see: examples/bootstrap/nested.html<br><br>
34821  
34822 <b>The container the layout is rendered into can be either the body element or any other element.
34823 If it is not the body element, the container needs to either be an absolute positioned element,
34824 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34825 the container size if it is not the body element.</b>
34826
34827 * @constructor
34828 * Create a new Border
34829 * @param {Object} config Configuration options
34830  */
34831 Roo.bootstrap.layout.Border = function(config){
34832     config = config || {};
34833     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34834     
34835     
34836     
34837     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34838         if(config[region]){
34839             config[region].region = region;
34840             this.addRegion(config[region]);
34841         }
34842     },this);
34843     
34844 };
34845
34846 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34847
34848 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34849     /**
34850      * Creates and adds a new region if it doesn't already exist.
34851      * @param {String} target The target region key (north, south, east, west or center).
34852      * @param {Object} config The regions config object
34853      * @return {BorderLayoutRegion} The new region
34854      */
34855     addRegion : function(config)
34856     {
34857         if(!this.regions[config.region]){
34858             var r = this.factory(config);
34859             this.bindRegion(r);
34860         }
34861         return this.regions[config.region];
34862     },
34863
34864     // private (kinda)
34865     bindRegion : function(r){
34866         this.regions[r.config.region] = r;
34867         
34868         r.on("visibilitychange",    this.layout, this);
34869         r.on("paneladded",          this.layout, this);
34870         r.on("panelremoved",        this.layout, this);
34871         r.on("invalidated",         this.layout, this);
34872         r.on("resized",             this.onRegionResized, this);
34873         r.on("collapsed",           this.onRegionCollapsed, this);
34874         r.on("expanded",            this.onRegionExpanded, this);
34875     },
34876
34877     /**
34878      * Performs a layout update.
34879      */
34880     layout : function()
34881     {
34882         if(this.updating) {
34883             return;
34884         }
34885         
34886         // render all the rebions if they have not been done alreayd?
34887         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34888             if(this.regions[region] && !this.regions[region].bodyEl){
34889                 this.regions[region].onRender(this.el)
34890             }
34891         },this);
34892         
34893         var size = this.getViewSize();
34894         var w = size.width;
34895         var h = size.height;
34896         var centerW = w;
34897         var centerH = h;
34898         var centerY = 0;
34899         var centerX = 0;
34900         //var x = 0, y = 0;
34901
34902         var rs = this.regions;
34903         var north = rs["north"];
34904         var south = rs["south"]; 
34905         var west = rs["west"];
34906         var east = rs["east"];
34907         var center = rs["center"];
34908         //if(this.hideOnLayout){ // not supported anymore
34909             //c.el.setStyle("display", "none");
34910         //}
34911         if(north && north.isVisible()){
34912             var b = north.getBox();
34913             var m = north.getMargins();
34914             b.width = w - (m.left+m.right);
34915             b.x = m.left;
34916             b.y = m.top;
34917             centerY = b.height + b.y + m.bottom;
34918             centerH -= centerY;
34919             north.updateBox(this.safeBox(b));
34920         }
34921         if(south && south.isVisible()){
34922             var b = south.getBox();
34923             var m = south.getMargins();
34924             b.width = w - (m.left+m.right);
34925             b.x = m.left;
34926             var totalHeight = (b.height + m.top + m.bottom);
34927             b.y = h - totalHeight + m.top;
34928             centerH -= totalHeight;
34929             south.updateBox(this.safeBox(b));
34930         }
34931         if(west && west.isVisible()){
34932             var b = west.getBox();
34933             var m = west.getMargins();
34934             b.height = centerH - (m.top+m.bottom);
34935             b.x = m.left;
34936             b.y = centerY + m.top;
34937             var totalWidth = (b.width + m.left + m.right);
34938             centerX += totalWidth;
34939             centerW -= totalWidth;
34940             west.updateBox(this.safeBox(b));
34941         }
34942         if(east && east.isVisible()){
34943             var b = east.getBox();
34944             var m = east.getMargins();
34945             b.height = centerH - (m.top+m.bottom);
34946             var totalWidth = (b.width + m.left + m.right);
34947             b.x = w - totalWidth + m.left;
34948             b.y = centerY + m.top;
34949             centerW -= totalWidth;
34950             east.updateBox(this.safeBox(b));
34951         }
34952         if(center){
34953             var m = center.getMargins();
34954             var centerBox = {
34955                 x: centerX + m.left,
34956                 y: centerY + m.top,
34957                 width: centerW - (m.left+m.right),
34958                 height: centerH - (m.top+m.bottom)
34959             };
34960             //if(this.hideOnLayout){
34961                 //center.el.setStyle("display", "block");
34962             //}
34963             center.updateBox(this.safeBox(centerBox));
34964         }
34965         this.el.repaint();
34966         this.fireEvent("layout", this);
34967     },
34968
34969     // private
34970     safeBox : function(box){
34971         box.width = Math.max(0, box.width);
34972         box.height = Math.max(0, box.height);
34973         return box;
34974     },
34975
34976     /**
34977      * Adds a ContentPanel (or subclass) to this layout.
34978      * @param {String} target The target region key (north, south, east, west or center).
34979      * @param {Roo.ContentPanel} panel The panel to add
34980      * @return {Roo.ContentPanel} The added panel
34981      */
34982     add : function(target, panel){
34983          
34984         target = target.toLowerCase();
34985         return this.regions[target].add(panel);
34986     },
34987
34988     /**
34989      * Remove a ContentPanel (or subclass) to this layout.
34990      * @param {String} target The target region key (north, south, east, west or center).
34991      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34992      * @return {Roo.ContentPanel} The removed panel
34993      */
34994     remove : function(target, panel){
34995         target = target.toLowerCase();
34996         return this.regions[target].remove(panel);
34997     },
34998
34999     /**
35000      * Searches all regions for a panel with the specified id
35001      * @param {String} panelId
35002      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35003      */
35004     findPanel : function(panelId){
35005         var rs = this.regions;
35006         for(var target in rs){
35007             if(typeof rs[target] != "function"){
35008                 var p = rs[target].getPanel(panelId);
35009                 if(p){
35010                     return p;
35011                 }
35012             }
35013         }
35014         return null;
35015     },
35016
35017     /**
35018      * Searches all regions for a panel with the specified id and activates (shows) it.
35019      * @param {String/ContentPanel} panelId The panels id or the panel itself
35020      * @return {Roo.ContentPanel} The shown panel or null
35021      */
35022     showPanel : function(panelId) {
35023       var rs = this.regions;
35024       for(var target in rs){
35025          var r = rs[target];
35026          if(typeof r != "function"){
35027             if(r.hasPanel(panelId)){
35028                return r.showPanel(panelId);
35029             }
35030          }
35031       }
35032       return null;
35033    },
35034
35035    /**
35036      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35037      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35038      */
35039    /*
35040     restoreState : function(provider){
35041         if(!provider){
35042             provider = Roo.state.Manager;
35043         }
35044         var sm = new Roo.LayoutStateManager();
35045         sm.init(this, provider);
35046     },
35047 */
35048  
35049  
35050     /**
35051      * Adds a xtype elements to the layout.
35052      * <pre><code>
35053
35054 layout.addxtype({
35055        xtype : 'ContentPanel',
35056        region: 'west',
35057        items: [ .... ]
35058    }
35059 );
35060
35061 layout.addxtype({
35062         xtype : 'NestedLayoutPanel',
35063         region: 'west',
35064         layout: {
35065            center: { },
35066            west: { }   
35067         },
35068         items : [ ... list of content panels or nested layout panels.. ]
35069    }
35070 );
35071 </code></pre>
35072      * @param {Object} cfg Xtype definition of item to add.
35073      */
35074     addxtype : function(cfg)
35075     {
35076         // basically accepts a pannel...
35077         // can accept a layout region..!?!?
35078         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35079         
35080         
35081         // theory?  children can only be panels??
35082         
35083         //if (!cfg.xtype.match(/Panel$/)) {
35084         //    return false;
35085         //}
35086         var ret = false;
35087         
35088         if (typeof(cfg.region) == 'undefined') {
35089             Roo.log("Failed to add Panel, region was not set");
35090             Roo.log(cfg);
35091             return false;
35092         }
35093         var region = cfg.region;
35094         delete cfg.region;
35095         
35096           
35097         var xitems = [];
35098         if (cfg.items) {
35099             xitems = cfg.items;
35100             delete cfg.items;
35101         }
35102         var nb = false;
35103         
35104         switch(cfg.xtype) 
35105         {
35106             case 'Content':  // ContentPanel (el, cfg)
35107             case 'Scroll':  // ContentPanel (el, cfg)
35108             case 'View': 
35109                 cfg.autoCreate = true;
35110                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35111                 //} else {
35112                 //    var el = this.el.createChild();
35113                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35114                 //}
35115                 
35116                 this.add(region, ret);
35117                 break;
35118             
35119             /*
35120             case 'TreePanel': // our new panel!
35121                 cfg.el = this.el.createChild();
35122                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35123                 this.add(region, ret);
35124                 break;
35125             */
35126             
35127             case 'Nest': 
35128                 // create a new Layout (which is  a Border Layout...
35129                 
35130                 var clayout = cfg.layout;
35131                 clayout.el  = this.el.createChild();
35132                 clayout.items   = clayout.items  || [];
35133                 
35134                 delete cfg.layout;
35135                 
35136                 // replace this exitems with the clayout ones..
35137                 xitems = clayout.items;
35138                  
35139                 // force background off if it's in center...
35140                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35141                     cfg.background = false;
35142                 }
35143                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35144                 
35145                 
35146                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35147                 //console.log('adding nested layout panel '  + cfg.toSource());
35148                 this.add(region, ret);
35149                 nb = {}; /// find first...
35150                 break;
35151             
35152             case 'Grid':
35153                 
35154                 // needs grid and region
35155                 
35156                 //var el = this.getRegion(region).el.createChild();
35157                 /*
35158                  *var el = this.el.createChild();
35159                 // create the grid first...
35160                 cfg.grid.container = el;
35161                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35162                 */
35163                 
35164                 if (region == 'center' && this.active ) {
35165                     cfg.background = false;
35166                 }
35167                 
35168                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35169                 
35170                 this.add(region, ret);
35171                 /*
35172                 if (cfg.background) {
35173                     // render grid on panel activation (if panel background)
35174                     ret.on('activate', function(gp) {
35175                         if (!gp.grid.rendered) {
35176                     //        gp.grid.render(el);
35177                         }
35178                     });
35179                 } else {
35180                   //  cfg.grid.render(el);
35181                 }
35182                 */
35183                 break;
35184            
35185            
35186             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35187                 // it was the old xcomponent building that caused this before.
35188                 // espeically if border is the top element in the tree.
35189                 ret = this;
35190                 break; 
35191                 
35192                     
35193                 
35194                 
35195                 
35196             default:
35197                 /*
35198                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35199                     
35200                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35201                     this.add(region, ret);
35202                 } else {
35203                 */
35204                     Roo.log(cfg);
35205                     throw "Can not add '" + cfg.xtype + "' to Border";
35206                     return null;
35207              
35208                                 
35209              
35210         }
35211         this.beginUpdate();
35212         // add children..
35213         var region = '';
35214         var abn = {};
35215         Roo.each(xitems, function(i)  {
35216             region = nb && i.region ? i.region : false;
35217             
35218             var add = ret.addxtype(i);
35219            
35220             if (region) {
35221                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35222                 if (!i.background) {
35223                     abn[region] = nb[region] ;
35224                 }
35225             }
35226             
35227         });
35228         this.endUpdate();
35229
35230         // make the last non-background panel active..
35231         //if (nb) { Roo.log(abn); }
35232         if (nb) {
35233             
35234             for(var r in abn) {
35235                 region = this.getRegion(r);
35236                 if (region) {
35237                     // tried using nb[r], but it does not work..
35238                      
35239                     region.showPanel(abn[r]);
35240                    
35241                 }
35242             }
35243         }
35244         return ret;
35245         
35246     },
35247     
35248     
35249 // private
35250     factory : function(cfg)
35251     {
35252         
35253         var validRegions = Roo.bootstrap.layout.Border.regions;
35254
35255         var target = cfg.region;
35256         cfg.mgr = this;
35257         
35258         var r = Roo.bootstrap.layout;
35259         Roo.log(target);
35260         switch(target){
35261             case "north":
35262                 return new r.North(cfg);
35263             case "south":
35264                 return new r.South(cfg);
35265             case "east":
35266                 return new r.East(cfg);
35267             case "west":
35268                 return new r.West(cfg);
35269             case "center":
35270                 return new r.Center(cfg);
35271         }
35272         throw 'Layout region "'+target+'" not supported.';
35273     }
35274     
35275     
35276 });
35277  /*
35278  * Based on:
35279  * Ext JS Library 1.1.1
35280  * Copyright(c) 2006-2007, Ext JS, LLC.
35281  *
35282  * Originally Released Under LGPL - original licence link has changed is not relivant.
35283  *
35284  * Fork - LGPL
35285  * <script type="text/javascript">
35286  */
35287  
35288 /**
35289  * @class Roo.bootstrap.layout.Basic
35290  * @extends Roo.util.Observable
35291  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35292  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35293  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35294  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35295  * @cfg {string}   region  the region that it inhabits..
35296  * @cfg {bool}   skipConfig skip config?
35297  * 
35298
35299  */
35300 Roo.bootstrap.layout.Basic = function(config){
35301     
35302     this.mgr = config.mgr;
35303     
35304     this.position = config.region;
35305     
35306     var skipConfig = config.skipConfig;
35307     
35308     this.events = {
35309         /**
35310          * @scope Roo.BasicLayoutRegion
35311          */
35312         
35313         /**
35314          * @event beforeremove
35315          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35316          * @param {Roo.LayoutRegion} this
35317          * @param {Roo.ContentPanel} panel The panel
35318          * @param {Object} e The cancel event object
35319          */
35320         "beforeremove" : true,
35321         /**
35322          * @event invalidated
35323          * Fires when the layout for this region is changed.
35324          * @param {Roo.LayoutRegion} this
35325          */
35326         "invalidated" : true,
35327         /**
35328          * @event visibilitychange
35329          * Fires when this region is shown or hidden 
35330          * @param {Roo.LayoutRegion} this
35331          * @param {Boolean} visibility true or false
35332          */
35333         "visibilitychange" : true,
35334         /**
35335          * @event paneladded
35336          * Fires when a panel is added. 
35337          * @param {Roo.LayoutRegion} this
35338          * @param {Roo.ContentPanel} panel The panel
35339          */
35340         "paneladded" : true,
35341         /**
35342          * @event panelremoved
35343          * Fires when a panel is removed. 
35344          * @param {Roo.LayoutRegion} this
35345          * @param {Roo.ContentPanel} panel The panel
35346          */
35347         "panelremoved" : true,
35348         /**
35349          * @event beforecollapse
35350          * Fires when this region before collapse.
35351          * @param {Roo.LayoutRegion} this
35352          */
35353         "beforecollapse" : true,
35354         /**
35355          * @event collapsed
35356          * Fires when this region is collapsed.
35357          * @param {Roo.LayoutRegion} this
35358          */
35359         "collapsed" : true,
35360         /**
35361          * @event expanded
35362          * Fires when this region is expanded.
35363          * @param {Roo.LayoutRegion} this
35364          */
35365         "expanded" : true,
35366         /**
35367          * @event slideshow
35368          * Fires when this region is slid into view.
35369          * @param {Roo.LayoutRegion} this
35370          */
35371         "slideshow" : true,
35372         /**
35373          * @event slidehide
35374          * Fires when this region slides out of view. 
35375          * @param {Roo.LayoutRegion} this
35376          */
35377         "slidehide" : true,
35378         /**
35379          * @event panelactivated
35380          * Fires when a panel is activated. 
35381          * @param {Roo.LayoutRegion} this
35382          * @param {Roo.ContentPanel} panel The activated panel
35383          */
35384         "panelactivated" : true,
35385         /**
35386          * @event resized
35387          * Fires when the user resizes this region. 
35388          * @param {Roo.LayoutRegion} this
35389          * @param {Number} newSize The new size (width for east/west, height for north/south)
35390          */
35391         "resized" : true
35392     };
35393     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35394     this.panels = new Roo.util.MixedCollection();
35395     this.panels.getKey = this.getPanelId.createDelegate(this);
35396     this.box = null;
35397     this.activePanel = null;
35398     // ensure listeners are added...
35399     
35400     if (config.listeners || config.events) {
35401         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35402             listeners : config.listeners || {},
35403             events : config.events || {}
35404         });
35405     }
35406     
35407     if(skipConfig !== true){
35408         this.applyConfig(config);
35409     }
35410 };
35411
35412 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35413 {
35414     getPanelId : function(p){
35415         return p.getId();
35416     },
35417     
35418     applyConfig : function(config){
35419         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35420         this.config = config;
35421         
35422     },
35423     
35424     /**
35425      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35426      * the width, for horizontal (north, south) the height.
35427      * @param {Number} newSize The new width or height
35428      */
35429     resizeTo : function(newSize){
35430         var el = this.el ? this.el :
35431                  (this.activePanel ? this.activePanel.getEl() : null);
35432         if(el){
35433             switch(this.position){
35434                 case "east":
35435                 case "west":
35436                     el.setWidth(newSize);
35437                     this.fireEvent("resized", this, newSize);
35438                 break;
35439                 case "north":
35440                 case "south":
35441                     el.setHeight(newSize);
35442                     this.fireEvent("resized", this, newSize);
35443                 break;                
35444             }
35445         }
35446     },
35447     
35448     getBox : function(){
35449         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35450     },
35451     
35452     getMargins : function(){
35453         return this.margins;
35454     },
35455     
35456     updateBox : function(box){
35457         this.box = box;
35458         var el = this.activePanel.getEl();
35459         el.dom.style.left = box.x + "px";
35460         el.dom.style.top = box.y + "px";
35461         this.activePanel.setSize(box.width, box.height);
35462     },
35463     
35464     /**
35465      * Returns the container element for this region.
35466      * @return {Roo.Element}
35467      */
35468     getEl : function(){
35469         return this.activePanel;
35470     },
35471     
35472     /**
35473      * Returns true if this region is currently visible.
35474      * @return {Boolean}
35475      */
35476     isVisible : function(){
35477         return this.activePanel ? true : false;
35478     },
35479     
35480     setActivePanel : function(panel){
35481         panel = this.getPanel(panel);
35482         if(this.activePanel && this.activePanel != panel){
35483             this.activePanel.setActiveState(false);
35484             this.activePanel.getEl().setLeftTop(-10000,-10000);
35485         }
35486         this.activePanel = panel;
35487         panel.setActiveState(true);
35488         if(this.box){
35489             panel.setSize(this.box.width, this.box.height);
35490         }
35491         this.fireEvent("panelactivated", this, panel);
35492         this.fireEvent("invalidated");
35493     },
35494     
35495     /**
35496      * Show the specified panel.
35497      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35498      * @return {Roo.ContentPanel} The shown panel or null
35499      */
35500     showPanel : function(panel){
35501         panel = this.getPanel(panel);
35502         if(panel){
35503             this.setActivePanel(panel);
35504         }
35505         return panel;
35506     },
35507     
35508     /**
35509      * Get the active panel for this region.
35510      * @return {Roo.ContentPanel} The active panel or null
35511      */
35512     getActivePanel : function(){
35513         return this.activePanel;
35514     },
35515     
35516     /**
35517      * Add the passed ContentPanel(s)
35518      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35519      * @return {Roo.ContentPanel} The panel added (if only one was added)
35520      */
35521     add : function(panel){
35522         if(arguments.length > 1){
35523             for(var i = 0, len = arguments.length; i < len; i++) {
35524                 this.add(arguments[i]);
35525             }
35526             return null;
35527         }
35528         if(this.hasPanel(panel)){
35529             this.showPanel(panel);
35530             return panel;
35531         }
35532         var el = panel.getEl();
35533         if(el.dom.parentNode != this.mgr.el.dom){
35534             this.mgr.el.dom.appendChild(el.dom);
35535         }
35536         if(panel.setRegion){
35537             panel.setRegion(this);
35538         }
35539         this.panels.add(panel);
35540         el.setStyle("position", "absolute");
35541         if(!panel.background){
35542             this.setActivePanel(panel);
35543             if(this.config.initialSize && this.panels.getCount()==1){
35544                 this.resizeTo(this.config.initialSize);
35545             }
35546         }
35547         this.fireEvent("paneladded", this, panel);
35548         return panel;
35549     },
35550     
35551     /**
35552      * Returns true if the panel is in this region.
35553      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35554      * @return {Boolean}
35555      */
35556     hasPanel : function(panel){
35557         if(typeof panel == "object"){ // must be panel obj
35558             panel = panel.getId();
35559         }
35560         return this.getPanel(panel) ? true : false;
35561     },
35562     
35563     /**
35564      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35565      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35566      * @param {Boolean} preservePanel Overrides the config preservePanel option
35567      * @return {Roo.ContentPanel} The panel that was removed
35568      */
35569     remove : function(panel, preservePanel){
35570         panel = this.getPanel(panel);
35571         if(!panel){
35572             return null;
35573         }
35574         var e = {};
35575         this.fireEvent("beforeremove", this, panel, e);
35576         if(e.cancel === true){
35577             return null;
35578         }
35579         var panelId = panel.getId();
35580         this.panels.removeKey(panelId);
35581         return panel;
35582     },
35583     
35584     /**
35585      * Returns the panel specified or null if it's not in this region.
35586      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35587      * @return {Roo.ContentPanel}
35588      */
35589     getPanel : function(id){
35590         if(typeof id == "object"){ // must be panel obj
35591             return id;
35592         }
35593         return this.panels.get(id);
35594     },
35595     
35596     /**
35597      * Returns this regions position (north/south/east/west/center).
35598      * @return {String} 
35599      */
35600     getPosition: function(){
35601         return this.position;    
35602     }
35603 });/*
35604  * Based on:
35605  * Ext JS Library 1.1.1
35606  * Copyright(c) 2006-2007, Ext JS, LLC.
35607  *
35608  * Originally Released Under LGPL - original licence link has changed is not relivant.
35609  *
35610  * Fork - LGPL
35611  * <script type="text/javascript">
35612  */
35613  
35614 /**
35615  * @class Roo.bootstrap.layout.Region
35616  * @extends Roo.bootstrap.layout.Basic
35617  * This class represents a region in a layout manager.
35618  
35619  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35620  * @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})
35621  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35622  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35623  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35624  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35625  * @cfg {String}    title           The title for the region (overrides panel titles)
35626  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35627  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35628  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35629  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35630  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35631  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35632  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35633  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35634  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35635  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35636
35637  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35638  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35639  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35640  * @cfg {Number}    width           For East/West panels
35641  * @cfg {Number}    height          For North/South panels
35642  * @cfg {Boolean}   split           To show the splitter
35643  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35644  * 
35645  * @cfg {string}   cls             Extra CSS classes to add to region
35646  * 
35647  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35648  * @cfg {string}   region  the region that it inhabits..
35649  *
35650
35651  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35652  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35653
35654  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35655  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35656  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35657  */
35658 Roo.bootstrap.layout.Region = function(config)
35659 {
35660     this.applyConfig(config);
35661
35662     var mgr = config.mgr;
35663     var pos = config.region;
35664     config.skipConfig = true;
35665     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35666     
35667     if (mgr.el) {
35668         this.onRender(mgr.el);   
35669     }
35670      
35671     this.visible = true;
35672     this.collapsed = false;
35673     this.unrendered_panels = [];
35674 };
35675
35676 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35677
35678     position: '', // set by wrapper (eg. north/south etc..)
35679     unrendered_panels : null,  // unrendered panels.
35680     createBody : function(){
35681         /** This region's body element 
35682         * @type Roo.Element */
35683         this.bodyEl = this.el.createChild({
35684                 tag: "div",
35685                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35686         });
35687     },
35688
35689     onRender: function(ctr, pos)
35690     {
35691         var dh = Roo.DomHelper;
35692         /** This region's container element 
35693         * @type Roo.Element */
35694         this.el = dh.append(ctr.dom, {
35695                 tag: "div",
35696                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35697             }, true);
35698         /** This region's title element 
35699         * @type Roo.Element */
35700     
35701         this.titleEl = dh.append(this.el.dom,
35702             {
35703                     tag: "div",
35704                     unselectable: "on",
35705                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35706                     children:[
35707                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35708                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35709                     ]}, true);
35710         
35711         this.titleEl.enableDisplayMode();
35712         /** This region's title text element 
35713         * @type HTMLElement */
35714         this.titleTextEl = this.titleEl.dom.firstChild;
35715         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35716         /*
35717         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35718         this.closeBtn.enableDisplayMode();
35719         this.closeBtn.on("click", this.closeClicked, this);
35720         this.closeBtn.hide();
35721     */
35722         this.createBody(this.config);
35723         if(this.config.hideWhenEmpty){
35724             this.hide();
35725             this.on("paneladded", this.validateVisibility, this);
35726             this.on("panelremoved", this.validateVisibility, this);
35727         }
35728         if(this.autoScroll){
35729             this.bodyEl.setStyle("overflow", "auto");
35730         }else{
35731             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35732         }
35733         //if(c.titlebar !== false){
35734             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35735                 this.titleEl.hide();
35736             }else{
35737                 this.titleEl.show();
35738                 if(this.config.title){
35739                     this.titleTextEl.innerHTML = this.config.title;
35740                 }
35741             }
35742         //}
35743         if(this.config.collapsed){
35744             this.collapse(true);
35745         }
35746         if(this.config.hidden){
35747             this.hide();
35748         }
35749         
35750         if (this.unrendered_panels && this.unrendered_panels.length) {
35751             for (var i =0;i< this.unrendered_panels.length; i++) {
35752                 this.add(this.unrendered_panels[i]);
35753             }
35754             this.unrendered_panels = null;
35755             
35756         }
35757         
35758     },
35759     
35760     applyConfig : function(c)
35761     {
35762         /*
35763          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35764             var dh = Roo.DomHelper;
35765             if(c.titlebar !== false){
35766                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35767                 this.collapseBtn.on("click", this.collapse, this);
35768                 this.collapseBtn.enableDisplayMode();
35769                 /*
35770                 if(c.showPin === true || this.showPin){
35771                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35772                     this.stickBtn.enableDisplayMode();
35773                     this.stickBtn.on("click", this.expand, this);
35774                     this.stickBtn.hide();
35775                 }
35776                 
35777             }
35778             */
35779             /** This region's collapsed element
35780             * @type Roo.Element */
35781             /*
35782              *
35783             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35784                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35785             ]}, true);
35786             
35787             if(c.floatable !== false){
35788                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35789                this.collapsedEl.on("click", this.collapseClick, this);
35790             }
35791
35792             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35793                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35794                    id: "message", unselectable: "on", style:{"float":"left"}});
35795                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35796              }
35797             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35798             this.expandBtn.on("click", this.expand, this);
35799             
35800         }
35801         
35802         if(this.collapseBtn){
35803             this.collapseBtn.setVisible(c.collapsible == true);
35804         }
35805         
35806         this.cmargins = c.cmargins || this.cmargins ||
35807                          (this.position == "west" || this.position == "east" ?
35808                              {top: 0, left: 2, right:2, bottom: 0} :
35809                              {top: 2, left: 0, right:0, bottom: 2});
35810         */
35811         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35812         
35813         
35814         this.bottomTabs = c.tabPosition != "top";
35815         
35816         this.autoScroll = c.autoScroll || false;
35817         
35818         
35819        
35820         
35821         this.duration = c.duration || .30;
35822         this.slideDuration = c.slideDuration || .45;
35823         this.config = c;
35824        
35825     },
35826     /**
35827      * Returns true if this region is currently visible.
35828      * @return {Boolean}
35829      */
35830     isVisible : function(){
35831         return this.visible;
35832     },
35833
35834     /**
35835      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35836      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35837      */
35838     //setCollapsedTitle : function(title){
35839     //    title = title || "&#160;";
35840      //   if(this.collapsedTitleTextEl){
35841       //      this.collapsedTitleTextEl.innerHTML = title;
35842        // }
35843     //},
35844
35845     getBox : function(){
35846         var b;
35847       //  if(!this.collapsed){
35848             b = this.el.getBox(false, true);
35849        // }else{
35850           //  b = this.collapsedEl.getBox(false, true);
35851         //}
35852         return b;
35853     },
35854
35855     getMargins : function(){
35856         return this.margins;
35857         //return this.collapsed ? this.cmargins : this.margins;
35858     },
35859 /*
35860     highlight : function(){
35861         this.el.addClass("x-layout-panel-dragover");
35862     },
35863
35864     unhighlight : function(){
35865         this.el.removeClass("x-layout-panel-dragover");
35866     },
35867 */
35868     updateBox : function(box)
35869     {
35870         if (!this.bodyEl) {
35871             return; // not rendered yet..
35872         }
35873         
35874         this.box = box;
35875         if(!this.collapsed){
35876             this.el.dom.style.left = box.x + "px";
35877             this.el.dom.style.top = box.y + "px";
35878             this.updateBody(box.width, box.height);
35879         }else{
35880             this.collapsedEl.dom.style.left = box.x + "px";
35881             this.collapsedEl.dom.style.top = box.y + "px";
35882             this.collapsedEl.setSize(box.width, box.height);
35883         }
35884         if(this.tabs){
35885             this.tabs.autoSizeTabs();
35886         }
35887     },
35888
35889     updateBody : function(w, h)
35890     {
35891         if(w !== null){
35892             this.el.setWidth(w);
35893             w -= this.el.getBorderWidth("rl");
35894             if(this.config.adjustments){
35895                 w += this.config.adjustments[0];
35896             }
35897         }
35898         if(h !== null && h > 0){
35899             this.el.setHeight(h);
35900             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35901             h -= this.el.getBorderWidth("tb");
35902             if(this.config.adjustments){
35903                 h += this.config.adjustments[1];
35904             }
35905             this.bodyEl.setHeight(h);
35906             if(this.tabs){
35907                 h = this.tabs.syncHeight(h);
35908             }
35909         }
35910         if(this.panelSize){
35911             w = w !== null ? w : this.panelSize.width;
35912             h = h !== null ? h : this.panelSize.height;
35913         }
35914         if(this.activePanel){
35915             var el = this.activePanel.getEl();
35916             w = w !== null ? w : el.getWidth();
35917             h = h !== null ? h : el.getHeight();
35918             this.panelSize = {width: w, height: h};
35919             this.activePanel.setSize(w, h);
35920         }
35921         if(Roo.isIE && this.tabs){
35922             this.tabs.el.repaint();
35923         }
35924     },
35925
35926     /**
35927      * Returns the container element for this region.
35928      * @return {Roo.Element}
35929      */
35930     getEl : function(){
35931         return this.el;
35932     },
35933
35934     /**
35935      * Hides this region.
35936      */
35937     hide : function(){
35938         //if(!this.collapsed){
35939             this.el.dom.style.left = "-2000px";
35940             this.el.hide();
35941         //}else{
35942          //   this.collapsedEl.dom.style.left = "-2000px";
35943          //   this.collapsedEl.hide();
35944        // }
35945         this.visible = false;
35946         this.fireEvent("visibilitychange", this, false);
35947     },
35948
35949     /**
35950      * Shows this region if it was previously hidden.
35951      */
35952     show : function(){
35953         //if(!this.collapsed){
35954             this.el.show();
35955         //}else{
35956         //    this.collapsedEl.show();
35957        // }
35958         this.visible = true;
35959         this.fireEvent("visibilitychange", this, true);
35960     },
35961 /*
35962     closeClicked : function(){
35963         if(this.activePanel){
35964             this.remove(this.activePanel);
35965         }
35966     },
35967
35968     collapseClick : function(e){
35969         if(this.isSlid){
35970            e.stopPropagation();
35971            this.slideIn();
35972         }else{
35973            e.stopPropagation();
35974            this.slideOut();
35975         }
35976     },
35977 */
35978     /**
35979      * Collapses this region.
35980      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35981      */
35982     /*
35983     collapse : function(skipAnim, skipCheck = false){
35984         if(this.collapsed) {
35985             return;
35986         }
35987         
35988         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35989             
35990             this.collapsed = true;
35991             if(this.split){
35992                 this.split.el.hide();
35993             }
35994             if(this.config.animate && skipAnim !== true){
35995                 this.fireEvent("invalidated", this);
35996                 this.animateCollapse();
35997             }else{
35998                 this.el.setLocation(-20000,-20000);
35999                 this.el.hide();
36000                 this.collapsedEl.show();
36001                 this.fireEvent("collapsed", this);
36002                 this.fireEvent("invalidated", this);
36003             }
36004         }
36005         
36006     },
36007 */
36008     animateCollapse : function(){
36009         // overridden
36010     },
36011
36012     /**
36013      * Expands this region if it was previously collapsed.
36014      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36015      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36016      */
36017     /*
36018     expand : function(e, skipAnim){
36019         if(e) {
36020             e.stopPropagation();
36021         }
36022         if(!this.collapsed || this.el.hasActiveFx()) {
36023             return;
36024         }
36025         if(this.isSlid){
36026             this.afterSlideIn();
36027             skipAnim = true;
36028         }
36029         this.collapsed = false;
36030         if(this.config.animate && skipAnim !== true){
36031             this.animateExpand();
36032         }else{
36033             this.el.show();
36034             if(this.split){
36035                 this.split.el.show();
36036             }
36037             this.collapsedEl.setLocation(-2000,-2000);
36038             this.collapsedEl.hide();
36039             this.fireEvent("invalidated", this);
36040             this.fireEvent("expanded", this);
36041         }
36042     },
36043 */
36044     animateExpand : function(){
36045         // overridden
36046     },
36047
36048     initTabs : function()
36049     {
36050         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36051         
36052         var ts = new Roo.bootstrap.panel.Tabs({
36053                 el: this.bodyEl.dom,
36054                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36055                 disableTooltips: this.config.disableTabTips,
36056                 toolbar : this.config.toolbar
36057             });
36058         
36059         if(this.config.hideTabs){
36060             ts.stripWrap.setDisplayed(false);
36061         }
36062         this.tabs = ts;
36063         ts.resizeTabs = this.config.resizeTabs === true;
36064         ts.minTabWidth = this.config.minTabWidth || 40;
36065         ts.maxTabWidth = this.config.maxTabWidth || 250;
36066         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36067         ts.monitorResize = false;
36068         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36069         ts.bodyEl.addClass('roo-layout-tabs-body');
36070         this.panels.each(this.initPanelAsTab, this);
36071     },
36072
36073     initPanelAsTab : function(panel){
36074         var ti = this.tabs.addTab(
36075             panel.getEl().id,
36076             panel.getTitle(),
36077             null,
36078             this.config.closeOnTab && panel.isClosable(),
36079             panel.tpl
36080         );
36081         if(panel.tabTip !== undefined){
36082             ti.setTooltip(panel.tabTip);
36083         }
36084         ti.on("activate", function(){
36085               this.setActivePanel(panel);
36086         }, this);
36087         
36088         if(this.config.closeOnTab){
36089             ti.on("beforeclose", function(t, e){
36090                 e.cancel = true;
36091                 this.remove(panel);
36092             }, this);
36093         }
36094         
36095         panel.tabItem = ti;
36096         
36097         return ti;
36098     },
36099
36100     updatePanelTitle : function(panel, title)
36101     {
36102         if(this.activePanel == panel){
36103             this.updateTitle(title);
36104         }
36105         if(this.tabs){
36106             var ti = this.tabs.getTab(panel.getEl().id);
36107             ti.setText(title);
36108             if(panel.tabTip !== undefined){
36109                 ti.setTooltip(panel.tabTip);
36110             }
36111         }
36112     },
36113
36114     updateTitle : function(title){
36115         if(this.titleTextEl && !this.config.title){
36116             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36117         }
36118     },
36119
36120     setActivePanel : function(panel)
36121     {
36122         panel = this.getPanel(panel);
36123         if(this.activePanel && this.activePanel != panel){
36124             if(this.activePanel.setActiveState(false) === false){
36125                 return;
36126             }
36127         }
36128         this.activePanel = panel;
36129         panel.setActiveState(true);
36130         if(this.panelSize){
36131             panel.setSize(this.panelSize.width, this.panelSize.height);
36132         }
36133         if(this.closeBtn){
36134             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36135         }
36136         this.updateTitle(panel.getTitle());
36137         if(this.tabs){
36138             this.fireEvent("invalidated", this);
36139         }
36140         this.fireEvent("panelactivated", this, panel);
36141     },
36142
36143     /**
36144      * Shows the specified panel.
36145      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36146      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36147      */
36148     showPanel : function(panel)
36149     {
36150         panel = this.getPanel(panel);
36151         if(panel){
36152             if(this.tabs){
36153                 var tab = this.tabs.getTab(panel.getEl().id);
36154                 if(tab.isHidden()){
36155                     this.tabs.unhideTab(tab.id);
36156                 }
36157                 tab.activate();
36158             }else{
36159                 this.setActivePanel(panel);
36160             }
36161         }
36162         return panel;
36163     },
36164
36165     /**
36166      * Get the active panel for this region.
36167      * @return {Roo.ContentPanel} The active panel or null
36168      */
36169     getActivePanel : function(){
36170         return this.activePanel;
36171     },
36172
36173     validateVisibility : function(){
36174         if(this.panels.getCount() < 1){
36175             this.updateTitle("&#160;");
36176             this.closeBtn.hide();
36177             this.hide();
36178         }else{
36179             if(!this.isVisible()){
36180                 this.show();
36181             }
36182         }
36183     },
36184
36185     /**
36186      * Adds the passed ContentPanel(s) to this region.
36187      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36188      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36189      */
36190     add : function(panel)
36191     {
36192         if(arguments.length > 1){
36193             for(var i = 0, len = arguments.length; i < len; i++) {
36194                 this.add(arguments[i]);
36195             }
36196             return null;
36197         }
36198         
36199         // if we have not been rendered yet, then we can not really do much of this..
36200         if (!this.bodyEl) {
36201             this.unrendered_panels.push(panel);
36202             return panel;
36203         }
36204         
36205         
36206         
36207         
36208         if(this.hasPanel(panel)){
36209             this.showPanel(panel);
36210             return panel;
36211         }
36212         panel.setRegion(this);
36213         this.panels.add(panel);
36214        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36215             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36216             // and hide them... ???
36217             this.bodyEl.dom.appendChild(panel.getEl().dom);
36218             if(panel.background !== true){
36219                 this.setActivePanel(panel);
36220             }
36221             this.fireEvent("paneladded", this, panel);
36222             return panel;
36223         }
36224         */
36225         if(!this.tabs){
36226             this.initTabs();
36227         }else{
36228             this.initPanelAsTab(panel);
36229         }
36230         
36231         
36232         if(panel.background !== true){
36233             this.tabs.activate(panel.getEl().id);
36234         }
36235         this.fireEvent("paneladded", this, panel);
36236         return panel;
36237     },
36238
36239     /**
36240      * Hides the tab for the specified panel.
36241      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36242      */
36243     hidePanel : function(panel){
36244         if(this.tabs && (panel = this.getPanel(panel))){
36245             this.tabs.hideTab(panel.getEl().id);
36246         }
36247     },
36248
36249     /**
36250      * Unhides the tab for a previously hidden panel.
36251      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36252      */
36253     unhidePanel : function(panel){
36254         if(this.tabs && (panel = this.getPanel(panel))){
36255             this.tabs.unhideTab(panel.getEl().id);
36256         }
36257     },
36258
36259     clearPanels : function(){
36260         while(this.panels.getCount() > 0){
36261              this.remove(this.panels.first());
36262         }
36263     },
36264
36265     /**
36266      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36267      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36268      * @param {Boolean} preservePanel Overrides the config preservePanel option
36269      * @return {Roo.ContentPanel} The panel that was removed
36270      */
36271     remove : function(panel, preservePanel)
36272     {
36273         panel = this.getPanel(panel);
36274         if(!panel){
36275             return null;
36276         }
36277         var e = {};
36278         this.fireEvent("beforeremove", this, panel, e);
36279         if(e.cancel === true){
36280             return null;
36281         }
36282         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36283         var panelId = panel.getId();
36284         this.panels.removeKey(panelId);
36285         if(preservePanel){
36286             document.body.appendChild(panel.getEl().dom);
36287         }
36288         if(this.tabs){
36289             this.tabs.removeTab(panel.getEl().id);
36290         }else if (!preservePanel){
36291             this.bodyEl.dom.removeChild(panel.getEl().dom);
36292         }
36293         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36294             var p = this.panels.first();
36295             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36296             tempEl.appendChild(p.getEl().dom);
36297             this.bodyEl.update("");
36298             this.bodyEl.dom.appendChild(p.getEl().dom);
36299             tempEl = null;
36300             this.updateTitle(p.getTitle());
36301             this.tabs = null;
36302             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36303             this.setActivePanel(p);
36304         }
36305         panel.setRegion(null);
36306         if(this.activePanel == panel){
36307             this.activePanel = null;
36308         }
36309         if(this.config.autoDestroy !== false && preservePanel !== true){
36310             try{panel.destroy();}catch(e){}
36311         }
36312         this.fireEvent("panelremoved", this, panel);
36313         return panel;
36314     },
36315
36316     /**
36317      * Returns the TabPanel component used by this region
36318      * @return {Roo.TabPanel}
36319      */
36320     getTabs : function(){
36321         return this.tabs;
36322     },
36323
36324     createTool : function(parentEl, className){
36325         var btn = Roo.DomHelper.append(parentEl, {
36326             tag: "div",
36327             cls: "x-layout-tools-button",
36328             children: [ {
36329                 tag: "div",
36330                 cls: "roo-layout-tools-button-inner " + className,
36331                 html: "&#160;"
36332             }]
36333         }, true);
36334         btn.addClassOnOver("roo-layout-tools-button-over");
36335         return btn;
36336     }
36337 });/*
36338  * Based on:
36339  * Ext JS Library 1.1.1
36340  * Copyright(c) 2006-2007, Ext JS, LLC.
36341  *
36342  * Originally Released Under LGPL - original licence link has changed is not relivant.
36343  *
36344  * Fork - LGPL
36345  * <script type="text/javascript">
36346  */
36347  
36348
36349
36350 /**
36351  * @class Roo.SplitLayoutRegion
36352  * @extends Roo.LayoutRegion
36353  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36354  */
36355 Roo.bootstrap.layout.Split = function(config){
36356     this.cursor = config.cursor;
36357     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36358 };
36359
36360 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36361 {
36362     splitTip : "Drag to resize.",
36363     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36364     useSplitTips : false,
36365
36366     applyConfig : function(config){
36367         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36368     },
36369     
36370     onRender : function(ctr,pos) {
36371         
36372         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36373         if(!this.config.split){
36374             return;
36375         }
36376         if(!this.split){
36377             
36378             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36379                             tag: "div",
36380                             id: this.el.id + "-split",
36381                             cls: "roo-layout-split roo-layout-split-"+this.position,
36382                             html: "&#160;"
36383             });
36384             /** The SplitBar for this region 
36385             * @type Roo.SplitBar */
36386             // does not exist yet...
36387             Roo.log([this.position, this.orientation]);
36388             
36389             this.split = new Roo.bootstrap.SplitBar({
36390                 dragElement : splitEl,
36391                 resizingElement: this.el,
36392                 orientation : this.orientation
36393             });
36394             
36395             this.split.on("moved", this.onSplitMove, this);
36396             this.split.useShim = this.config.useShim === true;
36397             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36398             if(this.useSplitTips){
36399                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36400             }
36401             //if(config.collapsible){
36402             //    this.split.el.on("dblclick", this.collapse,  this);
36403             //}
36404         }
36405         if(typeof this.config.minSize != "undefined"){
36406             this.split.minSize = this.config.minSize;
36407         }
36408         if(typeof this.config.maxSize != "undefined"){
36409             this.split.maxSize = this.config.maxSize;
36410         }
36411         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36412             this.hideSplitter();
36413         }
36414         
36415     },
36416
36417     getHMaxSize : function(){
36418          var cmax = this.config.maxSize || 10000;
36419          var center = this.mgr.getRegion("center");
36420          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36421     },
36422
36423     getVMaxSize : function(){
36424          var cmax = this.config.maxSize || 10000;
36425          var center = this.mgr.getRegion("center");
36426          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36427     },
36428
36429     onSplitMove : function(split, newSize){
36430         this.fireEvent("resized", this, newSize);
36431     },
36432     
36433     /** 
36434      * Returns the {@link Roo.SplitBar} for this region.
36435      * @return {Roo.SplitBar}
36436      */
36437     getSplitBar : function(){
36438         return this.split;
36439     },
36440     
36441     hide : function(){
36442         this.hideSplitter();
36443         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36444     },
36445
36446     hideSplitter : function(){
36447         if(this.split){
36448             this.split.el.setLocation(-2000,-2000);
36449             this.split.el.hide();
36450         }
36451     },
36452
36453     show : function(){
36454         if(this.split){
36455             this.split.el.show();
36456         }
36457         Roo.bootstrap.layout.Split.superclass.show.call(this);
36458     },
36459     
36460     beforeSlide: function(){
36461         if(Roo.isGecko){// firefox overflow auto bug workaround
36462             this.bodyEl.clip();
36463             if(this.tabs) {
36464                 this.tabs.bodyEl.clip();
36465             }
36466             if(this.activePanel){
36467                 this.activePanel.getEl().clip();
36468                 
36469                 if(this.activePanel.beforeSlide){
36470                     this.activePanel.beforeSlide();
36471                 }
36472             }
36473         }
36474     },
36475     
36476     afterSlide : function(){
36477         if(Roo.isGecko){// firefox overflow auto bug workaround
36478             this.bodyEl.unclip();
36479             if(this.tabs) {
36480                 this.tabs.bodyEl.unclip();
36481             }
36482             if(this.activePanel){
36483                 this.activePanel.getEl().unclip();
36484                 if(this.activePanel.afterSlide){
36485                     this.activePanel.afterSlide();
36486                 }
36487             }
36488         }
36489     },
36490
36491     initAutoHide : function(){
36492         if(this.autoHide !== false){
36493             if(!this.autoHideHd){
36494                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36495                 this.autoHideHd = {
36496                     "mouseout": function(e){
36497                         if(!e.within(this.el, true)){
36498                             st.delay(500);
36499                         }
36500                     },
36501                     "mouseover" : function(e){
36502                         st.cancel();
36503                     },
36504                     scope : this
36505                 };
36506             }
36507             this.el.on(this.autoHideHd);
36508         }
36509     },
36510
36511     clearAutoHide : function(){
36512         if(this.autoHide !== false){
36513             this.el.un("mouseout", this.autoHideHd.mouseout);
36514             this.el.un("mouseover", this.autoHideHd.mouseover);
36515         }
36516     },
36517
36518     clearMonitor : function(){
36519         Roo.get(document).un("click", this.slideInIf, this);
36520     },
36521
36522     // these names are backwards but not changed for compat
36523     slideOut : function(){
36524         if(this.isSlid || this.el.hasActiveFx()){
36525             return;
36526         }
36527         this.isSlid = true;
36528         if(this.collapseBtn){
36529             this.collapseBtn.hide();
36530         }
36531         this.closeBtnState = this.closeBtn.getStyle('display');
36532         this.closeBtn.hide();
36533         if(this.stickBtn){
36534             this.stickBtn.show();
36535         }
36536         this.el.show();
36537         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36538         this.beforeSlide();
36539         this.el.setStyle("z-index", 10001);
36540         this.el.slideIn(this.getSlideAnchor(), {
36541             callback: function(){
36542                 this.afterSlide();
36543                 this.initAutoHide();
36544                 Roo.get(document).on("click", this.slideInIf, this);
36545                 this.fireEvent("slideshow", this);
36546             },
36547             scope: this,
36548             block: true
36549         });
36550     },
36551
36552     afterSlideIn : function(){
36553         this.clearAutoHide();
36554         this.isSlid = false;
36555         this.clearMonitor();
36556         this.el.setStyle("z-index", "");
36557         if(this.collapseBtn){
36558             this.collapseBtn.show();
36559         }
36560         this.closeBtn.setStyle('display', this.closeBtnState);
36561         if(this.stickBtn){
36562             this.stickBtn.hide();
36563         }
36564         this.fireEvent("slidehide", this);
36565     },
36566
36567     slideIn : function(cb){
36568         if(!this.isSlid || this.el.hasActiveFx()){
36569             Roo.callback(cb);
36570             return;
36571         }
36572         this.isSlid = false;
36573         this.beforeSlide();
36574         this.el.slideOut(this.getSlideAnchor(), {
36575             callback: function(){
36576                 this.el.setLeftTop(-10000, -10000);
36577                 this.afterSlide();
36578                 this.afterSlideIn();
36579                 Roo.callback(cb);
36580             },
36581             scope: this,
36582             block: true
36583         });
36584     },
36585     
36586     slideInIf : function(e){
36587         if(!e.within(this.el)){
36588             this.slideIn();
36589         }
36590     },
36591
36592     animateCollapse : function(){
36593         this.beforeSlide();
36594         this.el.setStyle("z-index", 20000);
36595         var anchor = this.getSlideAnchor();
36596         this.el.slideOut(anchor, {
36597             callback : function(){
36598                 this.el.setStyle("z-index", "");
36599                 this.collapsedEl.slideIn(anchor, {duration:.3});
36600                 this.afterSlide();
36601                 this.el.setLocation(-10000,-10000);
36602                 this.el.hide();
36603                 this.fireEvent("collapsed", this);
36604             },
36605             scope: this,
36606             block: true
36607         });
36608     },
36609
36610     animateExpand : function(){
36611         this.beforeSlide();
36612         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36613         this.el.setStyle("z-index", 20000);
36614         this.collapsedEl.hide({
36615             duration:.1
36616         });
36617         this.el.slideIn(this.getSlideAnchor(), {
36618             callback : function(){
36619                 this.el.setStyle("z-index", "");
36620                 this.afterSlide();
36621                 if(this.split){
36622                     this.split.el.show();
36623                 }
36624                 this.fireEvent("invalidated", this);
36625                 this.fireEvent("expanded", this);
36626             },
36627             scope: this,
36628             block: true
36629         });
36630     },
36631
36632     anchors : {
36633         "west" : "left",
36634         "east" : "right",
36635         "north" : "top",
36636         "south" : "bottom"
36637     },
36638
36639     sanchors : {
36640         "west" : "l",
36641         "east" : "r",
36642         "north" : "t",
36643         "south" : "b"
36644     },
36645
36646     canchors : {
36647         "west" : "tl-tr",
36648         "east" : "tr-tl",
36649         "north" : "tl-bl",
36650         "south" : "bl-tl"
36651     },
36652
36653     getAnchor : function(){
36654         return this.anchors[this.position];
36655     },
36656
36657     getCollapseAnchor : function(){
36658         return this.canchors[this.position];
36659     },
36660
36661     getSlideAnchor : function(){
36662         return this.sanchors[this.position];
36663     },
36664
36665     getAlignAdj : function(){
36666         var cm = this.cmargins;
36667         switch(this.position){
36668             case "west":
36669                 return [0, 0];
36670             break;
36671             case "east":
36672                 return [0, 0];
36673             break;
36674             case "north":
36675                 return [0, 0];
36676             break;
36677             case "south":
36678                 return [0, 0];
36679             break;
36680         }
36681     },
36682
36683     getExpandAdj : function(){
36684         var c = this.collapsedEl, cm = this.cmargins;
36685         switch(this.position){
36686             case "west":
36687                 return [-(cm.right+c.getWidth()+cm.left), 0];
36688             break;
36689             case "east":
36690                 return [cm.right+c.getWidth()+cm.left, 0];
36691             break;
36692             case "north":
36693                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36694             break;
36695             case "south":
36696                 return [0, cm.top+cm.bottom+c.getHeight()];
36697             break;
36698         }
36699     }
36700 });/*
36701  * Based on:
36702  * Ext JS Library 1.1.1
36703  * Copyright(c) 2006-2007, Ext JS, LLC.
36704  *
36705  * Originally Released Under LGPL - original licence link has changed is not relivant.
36706  *
36707  * Fork - LGPL
36708  * <script type="text/javascript">
36709  */
36710 /*
36711  * These classes are private internal classes
36712  */
36713 Roo.bootstrap.layout.Center = function(config){
36714     config.region = "center";
36715     Roo.bootstrap.layout.Region.call(this, config);
36716     this.visible = true;
36717     this.minWidth = config.minWidth || 20;
36718     this.minHeight = config.minHeight || 20;
36719 };
36720
36721 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36722     hide : function(){
36723         // center panel can't be hidden
36724     },
36725     
36726     show : function(){
36727         // center panel can't be hidden
36728     },
36729     
36730     getMinWidth: function(){
36731         return this.minWidth;
36732     },
36733     
36734     getMinHeight: function(){
36735         return this.minHeight;
36736     }
36737 });
36738
36739
36740
36741
36742  
36743
36744
36745
36746
36747
36748 Roo.bootstrap.layout.North = function(config)
36749 {
36750     config.region = 'north';
36751     config.cursor = 'n-resize';
36752     
36753     Roo.bootstrap.layout.Split.call(this, config);
36754     
36755     
36756     if(this.split){
36757         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36758         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36759         this.split.el.addClass("roo-layout-split-v");
36760     }
36761     var size = config.initialSize || config.height;
36762     if(typeof size != "undefined"){
36763         this.el.setHeight(size);
36764     }
36765 };
36766 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36767 {
36768     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36769     
36770     
36771     
36772     getBox : function(){
36773         if(this.collapsed){
36774             return this.collapsedEl.getBox();
36775         }
36776         var box = this.el.getBox();
36777         if(this.split){
36778             box.height += this.split.el.getHeight();
36779         }
36780         return box;
36781     },
36782     
36783     updateBox : function(box){
36784         if(this.split && !this.collapsed){
36785             box.height -= this.split.el.getHeight();
36786             this.split.el.setLeft(box.x);
36787             this.split.el.setTop(box.y+box.height);
36788             this.split.el.setWidth(box.width);
36789         }
36790         if(this.collapsed){
36791             this.updateBody(box.width, null);
36792         }
36793         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36794     }
36795 });
36796
36797
36798
36799
36800
36801 Roo.bootstrap.layout.South = function(config){
36802     config.region = 'south';
36803     config.cursor = 's-resize';
36804     Roo.bootstrap.layout.Split.call(this, config);
36805     if(this.split){
36806         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36807         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36808         this.split.el.addClass("roo-layout-split-v");
36809     }
36810     var size = config.initialSize || config.height;
36811     if(typeof size != "undefined"){
36812         this.el.setHeight(size);
36813     }
36814 };
36815
36816 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36817     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36818     getBox : function(){
36819         if(this.collapsed){
36820             return this.collapsedEl.getBox();
36821         }
36822         var box = this.el.getBox();
36823         if(this.split){
36824             var sh = this.split.el.getHeight();
36825             box.height += sh;
36826             box.y -= sh;
36827         }
36828         return box;
36829     },
36830     
36831     updateBox : function(box){
36832         if(this.split && !this.collapsed){
36833             var sh = this.split.el.getHeight();
36834             box.height -= sh;
36835             box.y += sh;
36836             this.split.el.setLeft(box.x);
36837             this.split.el.setTop(box.y-sh);
36838             this.split.el.setWidth(box.width);
36839         }
36840         if(this.collapsed){
36841             this.updateBody(box.width, null);
36842         }
36843         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36844     }
36845 });
36846
36847 Roo.bootstrap.layout.East = function(config){
36848     config.region = "east";
36849     config.cursor = "e-resize";
36850     Roo.bootstrap.layout.Split.call(this, config);
36851     if(this.split){
36852         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36853         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36854         this.split.el.addClass("roo-layout-split-h");
36855     }
36856     var size = config.initialSize || config.width;
36857     if(typeof size != "undefined"){
36858         this.el.setWidth(size);
36859     }
36860 };
36861 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36862     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36863     getBox : function(){
36864         if(this.collapsed){
36865             return this.collapsedEl.getBox();
36866         }
36867         var box = this.el.getBox();
36868         if(this.split){
36869             var sw = this.split.el.getWidth();
36870             box.width += sw;
36871             box.x -= sw;
36872         }
36873         return box;
36874     },
36875
36876     updateBox : function(box){
36877         if(this.split && !this.collapsed){
36878             var sw = this.split.el.getWidth();
36879             box.width -= sw;
36880             this.split.el.setLeft(box.x);
36881             this.split.el.setTop(box.y);
36882             this.split.el.setHeight(box.height);
36883             box.x += sw;
36884         }
36885         if(this.collapsed){
36886             this.updateBody(null, box.height);
36887         }
36888         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36889     }
36890 });
36891
36892 Roo.bootstrap.layout.West = function(config){
36893     config.region = "west";
36894     config.cursor = "w-resize";
36895     
36896     Roo.bootstrap.layout.Split.call(this, config);
36897     if(this.split){
36898         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36899         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36900         this.split.el.addClass("roo-layout-split-h");
36901     }
36902     
36903 };
36904 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36905     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36906     
36907     onRender: function(ctr, pos)
36908     {
36909         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36910         var size = this.config.initialSize || this.config.width;
36911         if(typeof size != "undefined"){
36912             this.el.setWidth(size);
36913         }
36914     },
36915     
36916     getBox : function(){
36917         if(this.collapsed){
36918             return this.collapsedEl.getBox();
36919         }
36920         var box = this.el.getBox();
36921         if(this.split){
36922             box.width += this.split.el.getWidth();
36923         }
36924         return box;
36925     },
36926     
36927     updateBox : function(box){
36928         if(this.split && !this.collapsed){
36929             var sw = this.split.el.getWidth();
36930             box.width -= sw;
36931             this.split.el.setLeft(box.x+box.width);
36932             this.split.el.setTop(box.y);
36933             this.split.el.setHeight(box.height);
36934         }
36935         if(this.collapsed){
36936             this.updateBody(null, box.height);
36937         }
36938         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36939     }
36940 });
36941 Roo.namespace("Roo.bootstrap.panel");/*
36942  * Based on:
36943  * Ext JS Library 1.1.1
36944  * Copyright(c) 2006-2007, Ext JS, LLC.
36945  *
36946  * Originally Released Under LGPL - original licence link has changed is not relivant.
36947  *
36948  * Fork - LGPL
36949  * <script type="text/javascript">
36950  */
36951 /**
36952  * @class Roo.ContentPanel
36953  * @extends Roo.util.Observable
36954  * A basic ContentPanel element.
36955  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36956  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36957  * @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
36958  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36959  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36960  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36961  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36962  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36963  * @cfg {String} title          The title for this panel
36964  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36965  * @cfg {String} url            Calls {@link #setUrl} with this value
36966  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36967  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36968  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36969  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36970  * @cfg {Boolean} badges render the badges
36971
36972  * @constructor
36973  * Create a new ContentPanel.
36974  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36975  * @param {String/Object} config A string to set only the title or a config object
36976  * @param {String} content (optional) Set the HTML content for this panel
36977  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36978  */
36979 Roo.bootstrap.panel.Content = function( config){
36980     
36981     this.tpl = config.tpl || false;
36982     
36983     var el = config.el;
36984     var content = config.content;
36985
36986     if(config.autoCreate){ // xtype is available if this is called from factory
36987         el = Roo.id();
36988     }
36989     this.el = Roo.get(el);
36990     if(!this.el && config && config.autoCreate){
36991         if(typeof config.autoCreate == "object"){
36992             if(!config.autoCreate.id){
36993                 config.autoCreate.id = config.id||el;
36994             }
36995             this.el = Roo.DomHelper.append(document.body,
36996                         config.autoCreate, true);
36997         }else{
36998             var elcfg =  {   tag: "div",
36999                             cls: "roo-layout-inactive-content",
37000                             id: config.id||el
37001                             };
37002             if (config.html) {
37003                 elcfg.html = config.html;
37004                 
37005             }
37006                         
37007             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37008         }
37009     } 
37010     this.closable = false;
37011     this.loaded = false;
37012     this.active = false;
37013    
37014       
37015     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37016         
37017         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37018         
37019         this.wrapEl = this.el; //this.el.wrap();
37020         var ti = [];
37021         if (config.toolbar.items) {
37022             ti = config.toolbar.items ;
37023             delete config.toolbar.items ;
37024         }
37025         
37026         var nitems = [];
37027         this.toolbar.render(this.wrapEl, 'before');
37028         for(var i =0;i < ti.length;i++) {
37029           //  Roo.log(['add child', items[i]]);
37030             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37031         }
37032         this.toolbar.items = nitems;
37033         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37034         delete config.toolbar;
37035         
37036     }
37037     /*
37038     // xtype created footer. - not sure if will work as we normally have to render first..
37039     if (this.footer && !this.footer.el && this.footer.xtype) {
37040         if (!this.wrapEl) {
37041             this.wrapEl = this.el.wrap();
37042         }
37043     
37044         this.footer.container = this.wrapEl.createChild();
37045          
37046         this.footer = Roo.factory(this.footer, Roo);
37047         
37048     }
37049     */
37050     
37051      if(typeof config == "string"){
37052         this.title = config;
37053     }else{
37054         Roo.apply(this, config);
37055     }
37056     
37057     if(this.resizeEl){
37058         this.resizeEl = Roo.get(this.resizeEl, true);
37059     }else{
37060         this.resizeEl = this.el;
37061     }
37062     // handle view.xtype
37063     
37064  
37065     
37066     
37067     this.addEvents({
37068         /**
37069          * @event activate
37070          * Fires when this panel is activated. 
37071          * @param {Roo.ContentPanel} this
37072          */
37073         "activate" : true,
37074         /**
37075          * @event deactivate
37076          * Fires when this panel is activated. 
37077          * @param {Roo.ContentPanel} this
37078          */
37079         "deactivate" : true,
37080
37081         /**
37082          * @event resize
37083          * Fires when this panel is resized if fitToFrame is true.
37084          * @param {Roo.ContentPanel} this
37085          * @param {Number} width The width after any component adjustments
37086          * @param {Number} height The height after any component adjustments
37087          */
37088         "resize" : true,
37089         
37090          /**
37091          * @event render
37092          * Fires when this tab is created
37093          * @param {Roo.ContentPanel} this
37094          */
37095         "render" : true
37096         
37097         
37098         
37099     });
37100     
37101
37102     
37103     
37104     if(this.autoScroll){
37105         this.resizeEl.setStyle("overflow", "auto");
37106     } else {
37107         // fix randome scrolling
37108         //this.el.on('scroll', function() {
37109         //    Roo.log('fix random scolling');
37110         //    this.scrollTo('top',0); 
37111         //});
37112     }
37113     content = content || this.content;
37114     if(content){
37115         this.setContent(content);
37116     }
37117     if(config && config.url){
37118         this.setUrl(this.url, this.params, this.loadOnce);
37119     }
37120     
37121     
37122     
37123     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37124     
37125     if (this.view && typeof(this.view.xtype) != 'undefined') {
37126         this.view.el = this.el.appendChild(document.createElement("div"));
37127         this.view = Roo.factory(this.view); 
37128         this.view.render  &&  this.view.render(false, '');  
37129     }
37130     
37131     
37132     this.fireEvent('render', this);
37133 };
37134
37135 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37136     
37137     tabTip : '',
37138     
37139     setRegion : function(region){
37140         this.region = region;
37141         this.setActiveClass(region && !this.background);
37142     },
37143     
37144     
37145     setActiveClass: function(state)
37146     {
37147         if(state){
37148            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37149            this.el.setStyle('position','relative');
37150         }else{
37151            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37152            this.el.setStyle('position', 'absolute');
37153         } 
37154     },
37155     
37156     /**
37157      * Returns the toolbar for this Panel if one was configured. 
37158      * @return {Roo.Toolbar} 
37159      */
37160     getToolbar : function(){
37161         return this.toolbar;
37162     },
37163     
37164     setActiveState : function(active)
37165     {
37166         this.active = active;
37167         this.setActiveClass(active);
37168         if(!active){
37169             if(this.fireEvent("deactivate", this) === false){
37170                 return false;
37171             }
37172             return true;
37173         }
37174         this.fireEvent("activate", this);
37175         return true;
37176     },
37177     /**
37178      * Updates this panel's element
37179      * @param {String} content The new content
37180      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37181     */
37182     setContent : function(content, loadScripts){
37183         this.el.update(content, loadScripts);
37184     },
37185
37186     ignoreResize : function(w, h){
37187         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37188             return true;
37189         }else{
37190             this.lastSize = {width: w, height: h};
37191             return false;
37192         }
37193     },
37194     /**
37195      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37196      * @return {Roo.UpdateManager} The UpdateManager
37197      */
37198     getUpdateManager : function(){
37199         return this.el.getUpdateManager();
37200     },
37201      /**
37202      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37203      * @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:
37204 <pre><code>
37205 panel.load({
37206     url: "your-url.php",
37207     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37208     callback: yourFunction,
37209     scope: yourObject, //(optional scope)
37210     discardUrl: false,
37211     nocache: false,
37212     text: "Loading...",
37213     timeout: 30,
37214     scripts: false
37215 });
37216 </code></pre>
37217      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37218      * 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.
37219      * @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}
37220      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37221      * @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.
37222      * @return {Roo.ContentPanel} this
37223      */
37224     load : function(){
37225         var um = this.el.getUpdateManager();
37226         um.update.apply(um, arguments);
37227         return this;
37228     },
37229
37230
37231     /**
37232      * 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.
37233      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37234      * @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)
37235      * @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)
37236      * @return {Roo.UpdateManager} The UpdateManager
37237      */
37238     setUrl : function(url, params, loadOnce){
37239         if(this.refreshDelegate){
37240             this.removeListener("activate", this.refreshDelegate);
37241         }
37242         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37243         this.on("activate", this.refreshDelegate);
37244         return this.el.getUpdateManager();
37245     },
37246     
37247     _handleRefresh : function(url, params, loadOnce){
37248         if(!loadOnce || !this.loaded){
37249             var updater = this.el.getUpdateManager();
37250             updater.update(url, params, this._setLoaded.createDelegate(this));
37251         }
37252     },
37253     
37254     _setLoaded : function(){
37255         this.loaded = true;
37256     }, 
37257     
37258     /**
37259      * Returns this panel's id
37260      * @return {String} 
37261      */
37262     getId : function(){
37263         return this.el.id;
37264     },
37265     
37266     /** 
37267      * Returns this panel's element - used by regiosn to add.
37268      * @return {Roo.Element} 
37269      */
37270     getEl : function(){
37271         return this.wrapEl || this.el;
37272     },
37273     
37274    
37275     
37276     adjustForComponents : function(width, height)
37277     {
37278         //Roo.log('adjustForComponents ');
37279         if(this.resizeEl != this.el){
37280             width -= this.el.getFrameWidth('lr');
37281             height -= this.el.getFrameWidth('tb');
37282         }
37283         if(this.toolbar){
37284             var te = this.toolbar.getEl();
37285             te.setWidth(width);
37286             height -= te.getHeight();
37287         }
37288         if(this.footer){
37289             var te = this.footer.getEl();
37290             te.setWidth(width);
37291             height -= te.getHeight();
37292         }
37293         
37294         
37295         if(this.adjustments){
37296             width += this.adjustments[0];
37297             height += this.adjustments[1];
37298         }
37299         return {"width": width, "height": height};
37300     },
37301     
37302     setSize : function(width, height){
37303         if(this.fitToFrame && !this.ignoreResize(width, height)){
37304             if(this.fitContainer && this.resizeEl != this.el){
37305                 this.el.setSize(width, height);
37306             }
37307             var size = this.adjustForComponents(width, height);
37308             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37309             this.fireEvent('resize', this, size.width, size.height);
37310         }
37311     },
37312     
37313     /**
37314      * Returns this panel's title
37315      * @return {String} 
37316      */
37317     getTitle : function(){
37318         
37319         if (typeof(this.title) != 'object') {
37320             return this.title;
37321         }
37322         
37323         var t = '';
37324         for (var k in this.title) {
37325             if (!this.title.hasOwnProperty(k)) {
37326                 continue;
37327             }
37328             
37329             if (k.indexOf('-') >= 0) {
37330                 var s = k.split('-');
37331                 for (var i = 0; i<s.length; i++) {
37332                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37333                 }
37334             } else {
37335                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37336             }
37337         }
37338         return t;
37339     },
37340     
37341     /**
37342      * Set this panel's title
37343      * @param {String} title
37344      */
37345     setTitle : function(title){
37346         this.title = title;
37347         if(this.region){
37348             this.region.updatePanelTitle(this, title);
37349         }
37350     },
37351     
37352     /**
37353      * Returns true is this panel was configured to be closable
37354      * @return {Boolean} 
37355      */
37356     isClosable : function(){
37357         return this.closable;
37358     },
37359     
37360     beforeSlide : function(){
37361         this.el.clip();
37362         this.resizeEl.clip();
37363     },
37364     
37365     afterSlide : function(){
37366         this.el.unclip();
37367         this.resizeEl.unclip();
37368     },
37369     
37370     /**
37371      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37372      *   Will fail silently if the {@link #setUrl} method has not been called.
37373      *   This does not activate the panel, just updates its content.
37374      */
37375     refresh : function(){
37376         if(this.refreshDelegate){
37377            this.loaded = false;
37378            this.refreshDelegate();
37379         }
37380     },
37381     
37382     /**
37383      * Destroys this panel
37384      */
37385     destroy : function(){
37386         this.el.removeAllListeners();
37387         var tempEl = document.createElement("span");
37388         tempEl.appendChild(this.el.dom);
37389         tempEl.innerHTML = "";
37390         this.el.remove();
37391         this.el = null;
37392     },
37393     
37394     /**
37395      * form - if the content panel contains a form - this is a reference to it.
37396      * @type {Roo.form.Form}
37397      */
37398     form : false,
37399     /**
37400      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37401      *    This contains a reference to it.
37402      * @type {Roo.View}
37403      */
37404     view : false,
37405     
37406       /**
37407      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37408      * <pre><code>
37409
37410 layout.addxtype({
37411        xtype : 'Form',
37412        items: [ .... ]
37413    }
37414 );
37415
37416 </code></pre>
37417      * @param {Object} cfg Xtype definition of item to add.
37418      */
37419     
37420     
37421     getChildContainer: function () {
37422         return this.getEl();
37423     }
37424     
37425     
37426     /*
37427         var  ret = new Roo.factory(cfg);
37428         return ret;
37429         
37430         
37431         // add form..
37432         if (cfg.xtype.match(/^Form$/)) {
37433             
37434             var el;
37435             //if (this.footer) {
37436             //    el = this.footer.container.insertSibling(false, 'before');
37437             //} else {
37438                 el = this.el.createChild();
37439             //}
37440
37441             this.form = new  Roo.form.Form(cfg);
37442             
37443             
37444             if ( this.form.allItems.length) {
37445                 this.form.render(el.dom);
37446             }
37447             return this.form;
37448         }
37449         // should only have one of theses..
37450         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37451             // views.. should not be just added - used named prop 'view''
37452             
37453             cfg.el = this.el.appendChild(document.createElement("div"));
37454             // factory?
37455             
37456             var ret = new Roo.factory(cfg);
37457              
37458              ret.render && ret.render(false, ''); // render blank..
37459             this.view = ret;
37460             return ret;
37461         }
37462         return false;
37463     }
37464     \*/
37465 });
37466  
37467 /**
37468  * @class Roo.bootstrap.panel.Grid
37469  * @extends Roo.bootstrap.panel.Content
37470  * @constructor
37471  * Create a new GridPanel.
37472  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37473  * @param {Object} config A the config object
37474   
37475  */
37476
37477
37478
37479 Roo.bootstrap.panel.Grid = function(config)
37480 {
37481     
37482       
37483     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37484         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37485
37486     config.el = this.wrapper;
37487     //this.el = this.wrapper;
37488     
37489       if (config.container) {
37490         // ctor'ed from a Border/panel.grid
37491         
37492         
37493         this.wrapper.setStyle("overflow", "hidden");
37494         this.wrapper.addClass('roo-grid-container');
37495
37496     }
37497     
37498     
37499     if(config.toolbar){
37500         var tool_el = this.wrapper.createChild();    
37501         this.toolbar = Roo.factory(config.toolbar);
37502         var ti = [];
37503         if (config.toolbar.items) {
37504             ti = config.toolbar.items ;
37505             delete config.toolbar.items ;
37506         }
37507         
37508         var nitems = [];
37509         this.toolbar.render(tool_el);
37510         for(var i =0;i < ti.length;i++) {
37511           //  Roo.log(['add child', items[i]]);
37512             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37513         }
37514         this.toolbar.items = nitems;
37515         
37516         delete config.toolbar;
37517     }
37518     
37519     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37520     config.grid.scrollBody = true;;
37521     config.grid.monitorWindowResize = false; // turn off autosizing
37522     config.grid.autoHeight = false;
37523     config.grid.autoWidth = false;
37524     
37525     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37526     
37527     if (config.background) {
37528         // render grid on panel activation (if panel background)
37529         this.on('activate', function(gp) {
37530             if (!gp.grid.rendered) {
37531                 gp.grid.render(this.wrapper);
37532                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37533             }
37534         });
37535             
37536     } else {
37537         this.grid.render(this.wrapper);
37538         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37539
37540     }
37541     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37542     // ??? needed ??? config.el = this.wrapper;
37543     
37544     
37545     
37546   
37547     // xtype created footer. - not sure if will work as we normally have to render first..
37548     if (this.footer && !this.footer.el && this.footer.xtype) {
37549         
37550         var ctr = this.grid.getView().getFooterPanel(true);
37551         this.footer.dataSource = this.grid.dataSource;
37552         this.footer = Roo.factory(this.footer, Roo);
37553         this.footer.render(ctr);
37554         
37555     }
37556     
37557     
37558     
37559     
37560      
37561 };
37562
37563 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37564     getId : function(){
37565         return this.grid.id;
37566     },
37567     
37568     /**
37569      * Returns the grid for this panel
37570      * @return {Roo.bootstrap.Table} 
37571      */
37572     getGrid : function(){
37573         return this.grid;    
37574     },
37575     
37576     setSize : function(width, height){
37577         if(!this.ignoreResize(width, height)){
37578             var grid = this.grid;
37579             var size = this.adjustForComponents(width, height);
37580             var gridel = grid.getGridEl();
37581             gridel.setSize(size.width, size.height);
37582             /*
37583             var thd = grid.getGridEl().select('thead',true).first();
37584             var tbd = grid.getGridEl().select('tbody', true).first();
37585             if (tbd) {
37586                 tbd.setSize(width, height - thd.getHeight());
37587             }
37588             */
37589             grid.autoSize();
37590         }
37591     },
37592      
37593     
37594     
37595     beforeSlide : function(){
37596         this.grid.getView().scroller.clip();
37597     },
37598     
37599     afterSlide : function(){
37600         this.grid.getView().scroller.unclip();
37601     },
37602     
37603     destroy : function(){
37604         this.grid.destroy();
37605         delete this.grid;
37606         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37607     }
37608 });
37609
37610 /**
37611  * @class Roo.bootstrap.panel.Nest
37612  * @extends Roo.bootstrap.panel.Content
37613  * @constructor
37614  * Create a new Panel, that can contain a layout.Border.
37615  * 
37616  * 
37617  * @param {Roo.BorderLayout} layout The layout for this panel
37618  * @param {String/Object} config A string to set only the title or a config object
37619  */
37620 Roo.bootstrap.panel.Nest = function(config)
37621 {
37622     // construct with only one argument..
37623     /* FIXME - implement nicer consturctors
37624     if (layout.layout) {
37625         config = layout;
37626         layout = config.layout;
37627         delete config.layout;
37628     }
37629     if (layout.xtype && !layout.getEl) {
37630         // then layout needs constructing..
37631         layout = Roo.factory(layout, Roo);
37632     }
37633     */
37634     
37635     config.el =  config.layout.getEl();
37636     
37637     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37638     
37639     config.layout.monitorWindowResize = false; // turn off autosizing
37640     this.layout = config.layout;
37641     this.layout.getEl().addClass("roo-layout-nested-layout");
37642     
37643     
37644     
37645     
37646 };
37647
37648 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37649
37650     setSize : function(width, height){
37651         if(!this.ignoreResize(width, height)){
37652             var size = this.adjustForComponents(width, height);
37653             var el = this.layout.getEl();
37654             if (size.height < 1) {
37655                 el.setWidth(size.width);   
37656             } else {
37657                 el.setSize(size.width, size.height);
37658             }
37659             var touch = el.dom.offsetWidth;
37660             this.layout.layout();
37661             // ie requires a double layout on the first pass
37662             if(Roo.isIE && !this.initialized){
37663                 this.initialized = true;
37664                 this.layout.layout();
37665             }
37666         }
37667     },
37668     
37669     // activate all subpanels if not currently active..
37670     
37671     setActiveState : function(active){
37672         this.active = active;
37673         this.setActiveClass(active);
37674         
37675         if(!active){
37676             this.fireEvent("deactivate", this);
37677             return;
37678         }
37679         
37680         this.fireEvent("activate", this);
37681         // not sure if this should happen before or after..
37682         if (!this.layout) {
37683             return; // should not happen..
37684         }
37685         var reg = false;
37686         for (var r in this.layout.regions) {
37687             reg = this.layout.getRegion(r);
37688             if (reg.getActivePanel()) {
37689                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37690                 reg.setActivePanel(reg.getActivePanel());
37691                 continue;
37692             }
37693             if (!reg.panels.length) {
37694                 continue;
37695             }
37696             reg.showPanel(reg.getPanel(0));
37697         }
37698         
37699         
37700         
37701         
37702     },
37703     
37704     /**
37705      * Returns the nested BorderLayout for this panel
37706      * @return {Roo.BorderLayout} 
37707      */
37708     getLayout : function(){
37709         return this.layout;
37710     },
37711     
37712      /**
37713      * Adds a xtype elements to the layout of the nested panel
37714      * <pre><code>
37715
37716 panel.addxtype({
37717        xtype : 'ContentPanel',
37718        region: 'west',
37719        items: [ .... ]
37720    }
37721 );
37722
37723 panel.addxtype({
37724         xtype : 'NestedLayoutPanel',
37725         region: 'west',
37726         layout: {
37727            center: { },
37728            west: { }   
37729         },
37730         items : [ ... list of content panels or nested layout panels.. ]
37731    }
37732 );
37733 </code></pre>
37734      * @param {Object} cfg Xtype definition of item to add.
37735      */
37736     addxtype : function(cfg) {
37737         return this.layout.addxtype(cfg);
37738     
37739     }
37740 });        /*
37741  * Based on:
37742  * Ext JS Library 1.1.1
37743  * Copyright(c) 2006-2007, Ext JS, LLC.
37744  *
37745  * Originally Released Under LGPL - original licence link has changed is not relivant.
37746  *
37747  * Fork - LGPL
37748  * <script type="text/javascript">
37749  */
37750 /**
37751  * @class Roo.TabPanel
37752  * @extends Roo.util.Observable
37753  * A lightweight tab container.
37754  * <br><br>
37755  * Usage:
37756  * <pre><code>
37757 // basic tabs 1, built from existing content
37758 var tabs = new Roo.TabPanel("tabs1");
37759 tabs.addTab("script", "View Script");
37760 tabs.addTab("markup", "View Markup");
37761 tabs.activate("script");
37762
37763 // more advanced tabs, built from javascript
37764 var jtabs = new Roo.TabPanel("jtabs");
37765 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37766
37767 // set up the UpdateManager
37768 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37769 var updater = tab2.getUpdateManager();
37770 updater.setDefaultUrl("ajax1.htm");
37771 tab2.on('activate', updater.refresh, updater, true);
37772
37773 // Use setUrl for Ajax loading
37774 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37775 tab3.setUrl("ajax2.htm", null, true);
37776
37777 // Disabled tab
37778 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37779 tab4.disable();
37780
37781 jtabs.activate("jtabs-1");
37782  * </code></pre>
37783  * @constructor
37784  * Create a new TabPanel.
37785  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37786  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37787  */
37788 Roo.bootstrap.panel.Tabs = function(config){
37789     /**
37790     * The container element for this TabPanel.
37791     * @type Roo.Element
37792     */
37793     this.el = Roo.get(config.el);
37794     delete config.el;
37795     if(config){
37796         if(typeof config == "boolean"){
37797             this.tabPosition = config ? "bottom" : "top";
37798         }else{
37799             Roo.apply(this, config);
37800         }
37801     }
37802     
37803     if(this.tabPosition == "bottom"){
37804         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37805         this.el.addClass("roo-tabs-bottom");
37806     }
37807     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37808     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37809     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37810     if(Roo.isIE){
37811         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37812     }
37813     if(this.tabPosition != "bottom"){
37814         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37815          * @type Roo.Element
37816          */
37817         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37818         this.el.addClass("roo-tabs-top");
37819     }
37820     this.items = [];
37821
37822     this.bodyEl.setStyle("position", "relative");
37823
37824     this.active = null;
37825     this.activateDelegate = this.activate.createDelegate(this);
37826
37827     this.addEvents({
37828         /**
37829          * @event tabchange
37830          * Fires when the active tab changes
37831          * @param {Roo.TabPanel} this
37832          * @param {Roo.TabPanelItem} activePanel The new active tab
37833          */
37834         "tabchange": true,
37835         /**
37836          * @event beforetabchange
37837          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37838          * @param {Roo.TabPanel} this
37839          * @param {Object} e Set cancel to true on this object to cancel the tab change
37840          * @param {Roo.TabPanelItem} tab The tab being changed to
37841          */
37842         "beforetabchange" : true
37843     });
37844
37845     Roo.EventManager.onWindowResize(this.onResize, this);
37846     this.cpad = this.el.getPadding("lr");
37847     this.hiddenCount = 0;
37848
37849
37850     // toolbar on the tabbar support...
37851     if (this.toolbar) {
37852         alert("no toolbar support yet");
37853         this.toolbar  = false;
37854         /*
37855         var tcfg = this.toolbar;
37856         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37857         this.toolbar = new Roo.Toolbar(tcfg);
37858         if (Roo.isSafari) {
37859             var tbl = tcfg.container.child('table', true);
37860             tbl.setAttribute('width', '100%');
37861         }
37862         */
37863         
37864     }
37865    
37866
37867
37868     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37869 };
37870
37871 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37872     /*
37873      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37874      */
37875     tabPosition : "top",
37876     /*
37877      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37878      */
37879     currentTabWidth : 0,
37880     /*
37881      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37882      */
37883     minTabWidth : 40,
37884     /*
37885      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37886      */
37887     maxTabWidth : 250,
37888     /*
37889      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37890      */
37891     preferredTabWidth : 175,
37892     /*
37893      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37894      */
37895     resizeTabs : false,
37896     /*
37897      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37898      */
37899     monitorResize : true,
37900     /*
37901      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37902      */
37903     toolbar : false,
37904
37905     /**
37906      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37907      * @param {String} id The id of the div to use <b>or create</b>
37908      * @param {String} text The text for the tab
37909      * @param {String} content (optional) Content to put in the TabPanelItem body
37910      * @param {Boolean} closable (optional) True to create a close icon on the tab
37911      * @return {Roo.TabPanelItem} The created TabPanelItem
37912      */
37913     addTab : function(id, text, content, closable, tpl)
37914     {
37915         var item = new Roo.bootstrap.panel.TabItem({
37916             panel: this,
37917             id : id,
37918             text : text,
37919             closable : closable,
37920             tpl : tpl
37921         });
37922         this.addTabItem(item);
37923         if(content){
37924             item.setContent(content);
37925         }
37926         return item;
37927     },
37928
37929     /**
37930      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37931      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37932      * @return {Roo.TabPanelItem}
37933      */
37934     getTab : function(id){
37935         return this.items[id];
37936     },
37937
37938     /**
37939      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37940      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37941      */
37942     hideTab : function(id){
37943         var t = this.items[id];
37944         if(!t.isHidden()){
37945            t.setHidden(true);
37946            this.hiddenCount++;
37947            this.autoSizeTabs();
37948         }
37949     },
37950
37951     /**
37952      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37953      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37954      */
37955     unhideTab : function(id){
37956         var t = this.items[id];
37957         if(t.isHidden()){
37958            t.setHidden(false);
37959            this.hiddenCount--;
37960            this.autoSizeTabs();
37961         }
37962     },
37963
37964     /**
37965      * Adds an existing {@link Roo.TabPanelItem}.
37966      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37967      */
37968     addTabItem : function(item){
37969         this.items[item.id] = item;
37970         this.items.push(item);
37971       //  if(this.resizeTabs){
37972     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37973   //         this.autoSizeTabs();
37974 //        }else{
37975 //            item.autoSize();
37976        // }
37977     },
37978
37979     /**
37980      * Removes a {@link Roo.TabPanelItem}.
37981      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37982      */
37983     removeTab : function(id){
37984         var items = this.items;
37985         var tab = items[id];
37986         if(!tab) { return; }
37987         var index = items.indexOf(tab);
37988         if(this.active == tab && items.length > 1){
37989             var newTab = this.getNextAvailable(index);
37990             if(newTab) {
37991                 newTab.activate();
37992             }
37993         }
37994         this.stripEl.dom.removeChild(tab.pnode.dom);
37995         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37996             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37997         }
37998         items.splice(index, 1);
37999         delete this.items[tab.id];
38000         tab.fireEvent("close", tab);
38001         tab.purgeListeners();
38002         this.autoSizeTabs();
38003     },
38004
38005     getNextAvailable : function(start){
38006         var items = this.items;
38007         var index = start;
38008         // look for a next tab that will slide over to
38009         // replace the one being removed
38010         while(index < items.length){
38011             var item = items[++index];
38012             if(item && !item.isHidden()){
38013                 return item;
38014             }
38015         }
38016         // if one isn't found select the previous tab (on the left)
38017         index = start;
38018         while(index >= 0){
38019             var item = items[--index];
38020             if(item && !item.isHidden()){
38021                 return item;
38022             }
38023         }
38024         return null;
38025     },
38026
38027     /**
38028      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38029      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38030      */
38031     disableTab : function(id){
38032         var tab = this.items[id];
38033         if(tab && this.active != tab){
38034             tab.disable();
38035         }
38036     },
38037
38038     /**
38039      * Enables a {@link Roo.TabPanelItem} that is disabled.
38040      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38041      */
38042     enableTab : function(id){
38043         var tab = this.items[id];
38044         tab.enable();
38045     },
38046
38047     /**
38048      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38049      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38050      * @return {Roo.TabPanelItem} The TabPanelItem.
38051      */
38052     activate : function(id){
38053         var tab = this.items[id];
38054         if(!tab){
38055             return null;
38056         }
38057         if(tab == this.active || tab.disabled){
38058             return tab;
38059         }
38060         var e = {};
38061         this.fireEvent("beforetabchange", this, e, tab);
38062         if(e.cancel !== true && !tab.disabled){
38063             if(this.active){
38064                 this.active.hide();
38065             }
38066             this.active = this.items[id];
38067             this.active.show();
38068             this.fireEvent("tabchange", this, this.active);
38069         }
38070         return tab;
38071     },
38072
38073     /**
38074      * Gets the active {@link Roo.TabPanelItem}.
38075      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38076      */
38077     getActiveTab : function(){
38078         return this.active;
38079     },
38080
38081     /**
38082      * Updates the tab body element to fit the height of the container element
38083      * for overflow scrolling
38084      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38085      */
38086     syncHeight : function(targetHeight){
38087         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38088         var bm = this.bodyEl.getMargins();
38089         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38090         this.bodyEl.setHeight(newHeight);
38091         return newHeight;
38092     },
38093
38094     onResize : function(){
38095         if(this.monitorResize){
38096             this.autoSizeTabs();
38097         }
38098     },
38099
38100     /**
38101      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38102      */
38103     beginUpdate : function(){
38104         this.updating = true;
38105     },
38106
38107     /**
38108      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38109      */
38110     endUpdate : function(){
38111         this.updating = false;
38112         this.autoSizeTabs();
38113     },
38114
38115     /**
38116      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38117      */
38118     autoSizeTabs : function(){
38119         var count = this.items.length;
38120         var vcount = count - this.hiddenCount;
38121         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38122             return;
38123         }
38124         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38125         var availWidth = Math.floor(w / vcount);
38126         var b = this.stripBody;
38127         if(b.getWidth() > w){
38128             var tabs = this.items;
38129             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38130             if(availWidth < this.minTabWidth){
38131                 /*if(!this.sleft){    // incomplete scrolling code
38132                     this.createScrollButtons();
38133                 }
38134                 this.showScroll();
38135                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38136             }
38137         }else{
38138             if(this.currentTabWidth < this.preferredTabWidth){
38139                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38140             }
38141         }
38142     },
38143
38144     /**
38145      * Returns the number of tabs in this TabPanel.
38146      * @return {Number}
38147      */
38148      getCount : function(){
38149          return this.items.length;
38150      },
38151
38152     /**
38153      * Resizes all the tabs to the passed width
38154      * @param {Number} The new width
38155      */
38156     setTabWidth : function(width){
38157         this.currentTabWidth = width;
38158         for(var i = 0, len = this.items.length; i < len; i++) {
38159                 if(!this.items[i].isHidden()) {
38160                 this.items[i].setWidth(width);
38161             }
38162         }
38163     },
38164
38165     /**
38166      * Destroys this TabPanel
38167      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38168      */
38169     destroy : function(removeEl){
38170         Roo.EventManager.removeResizeListener(this.onResize, this);
38171         for(var i = 0, len = this.items.length; i < len; i++){
38172             this.items[i].purgeListeners();
38173         }
38174         if(removeEl === true){
38175             this.el.update("");
38176             this.el.remove();
38177         }
38178     },
38179     
38180     createStrip : function(container)
38181     {
38182         var strip = document.createElement("nav");
38183         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38184         container.appendChild(strip);
38185         return strip;
38186     },
38187     
38188     createStripList : function(strip)
38189     {
38190         // div wrapper for retard IE
38191         // returns the "tr" element.
38192         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38193         //'<div class="x-tabs-strip-wrap">'+
38194           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38195           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38196         return strip.firstChild; //.firstChild.firstChild.firstChild;
38197     },
38198     createBody : function(container)
38199     {
38200         var body = document.createElement("div");
38201         Roo.id(body, "tab-body");
38202         //Roo.fly(body).addClass("x-tabs-body");
38203         Roo.fly(body).addClass("tab-content");
38204         container.appendChild(body);
38205         return body;
38206     },
38207     createItemBody :function(bodyEl, id){
38208         var body = Roo.getDom(id);
38209         if(!body){
38210             body = document.createElement("div");
38211             body.id = id;
38212         }
38213         //Roo.fly(body).addClass("x-tabs-item-body");
38214         Roo.fly(body).addClass("tab-pane");
38215          bodyEl.insertBefore(body, bodyEl.firstChild);
38216         return body;
38217     },
38218     /** @private */
38219     createStripElements :  function(stripEl, text, closable, tpl)
38220     {
38221         var td = document.createElement("li"); // was td..
38222         
38223         
38224         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38225         
38226         
38227         stripEl.appendChild(td);
38228         /*if(closable){
38229             td.className = "x-tabs-closable";
38230             if(!this.closeTpl){
38231                 this.closeTpl = new Roo.Template(
38232                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38233                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38234                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38235                 );
38236             }
38237             var el = this.closeTpl.overwrite(td, {"text": text});
38238             var close = el.getElementsByTagName("div")[0];
38239             var inner = el.getElementsByTagName("em")[0];
38240             return {"el": el, "close": close, "inner": inner};
38241         } else {
38242         */
38243         // not sure what this is..
38244 //            if(!this.tabTpl){
38245                 //this.tabTpl = new Roo.Template(
38246                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38247                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38248                 //);
38249 //                this.tabTpl = new Roo.Template(
38250 //                   '<a href="#">' +
38251 //                   '<span unselectable="on"' +
38252 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38253 //                            ' >{text}</span></a>'
38254 //                );
38255 //                
38256 //            }
38257
38258
38259             var template = tpl || this.tabTpl || false;
38260             
38261             if(!template){
38262                 
38263                 template = new Roo.Template(
38264                    '<a href="#">' +
38265                    '<span unselectable="on"' +
38266                             (this.disableTooltips ? '' : ' title="{text}"') +
38267                             ' >{text}</span></a>'
38268                 );
38269             }
38270             
38271             switch (typeof(template)) {
38272                 case 'object' :
38273                     break;
38274                 case 'string' :
38275                     template = new Roo.Template(template);
38276                     break;
38277                 default :
38278                     break;
38279             }
38280             
38281             var el = template.overwrite(td, {"text": text});
38282             
38283             var inner = el.getElementsByTagName("span")[0];
38284             
38285             return {"el": el, "inner": inner};
38286             
38287     }
38288         
38289     
38290 });
38291
38292 /**
38293  * @class Roo.TabPanelItem
38294  * @extends Roo.util.Observable
38295  * Represents an individual item (tab plus body) in a TabPanel.
38296  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38297  * @param {String} id The id of this TabPanelItem
38298  * @param {String} text The text for the tab of this TabPanelItem
38299  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38300  */
38301 Roo.bootstrap.panel.TabItem = function(config){
38302     /**
38303      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38304      * @type Roo.TabPanel
38305      */
38306     this.tabPanel = config.panel;
38307     /**
38308      * The id for this TabPanelItem
38309      * @type String
38310      */
38311     this.id = config.id;
38312     /** @private */
38313     this.disabled = false;
38314     /** @private */
38315     this.text = config.text;
38316     /** @private */
38317     this.loaded = false;
38318     this.closable = config.closable;
38319
38320     /**
38321      * The body element for this TabPanelItem.
38322      * @type Roo.Element
38323      */
38324     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38325     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38326     this.bodyEl.setStyle("display", "block");
38327     this.bodyEl.setStyle("zoom", "1");
38328     //this.hideAction();
38329
38330     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38331     /** @private */
38332     this.el = Roo.get(els.el);
38333     this.inner = Roo.get(els.inner, true);
38334     this.textEl = Roo.get(this.el.dom.firstChild, true);
38335     this.pnode = Roo.get(els.el.parentNode, true);
38336 //    this.el.on("mousedown", this.onTabMouseDown, this);
38337     this.el.on("click", this.onTabClick, this);
38338     /** @private */
38339     if(config.closable){
38340         var c = Roo.get(els.close, true);
38341         c.dom.title = this.closeText;
38342         c.addClassOnOver("close-over");
38343         c.on("click", this.closeClick, this);
38344      }
38345
38346     this.addEvents({
38347          /**
38348          * @event activate
38349          * Fires when this tab becomes the active tab.
38350          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38351          * @param {Roo.TabPanelItem} this
38352          */
38353         "activate": true,
38354         /**
38355          * @event beforeclose
38356          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38357          * @param {Roo.TabPanelItem} this
38358          * @param {Object} e Set cancel to true on this object to cancel the close.
38359          */
38360         "beforeclose": true,
38361         /**
38362          * @event close
38363          * Fires when this tab is closed.
38364          * @param {Roo.TabPanelItem} this
38365          */
38366          "close": true,
38367         /**
38368          * @event deactivate
38369          * Fires when this tab is no longer the active tab.
38370          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38371          * @param {Roo.TabPanelItem} this
38372          */
38373          "deactivate" : true
38374     });
38375     this.hidden = false;
38376
38377     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38378 };
38379
38380 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38381            {
38382     purgeListeners : function(){
38383        Roo.util.Observable.prototype.purgeListeners.call(this);
38384        this.el.removeAllListeners();
38385     },
38386     /**
38387      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38388      */
38389     show : function(){
38390         this.pnode.addClass("active");
38391         this.showAction();
38392         if(Roo.isOpera){
38393             this.tabPanel.stripWrap.repaint();
38394         }
38395         this.fireEvent("activate", this.tabPanel, this);
38396     },
38397
38398     /**
38399      * Returns true if this tab is the active tab.
38400      * @return {Boolean}
38401      */
38402     isActive : function(){
38403         return this.tabPanel.getActiveTab() == this;
38404     },
38405
38406     /**
38407      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38408      */
38409     hide : function(){
38410         this.pnode.removeClass("active");
38411         this.hideAction();
38412         this.fireEvent("deactivate", this.tabPanel, this);
38413     },
38414
38415     hideAction : function(){
38416         this.bodyEl.hide();
38417         this.bodyEl.setStyle("position", "absolute");
38418         this.bodyEl.setLeft("-20000px");
38419         this.bodyEl.setTop("-20000px");
38420     },
38421
38422     showAction : function(){
38423         this.bodyEl.setStyle("position", "relative");
38424         this.bodyEl.setTop("");
38425         this.bodyEl.setLeft("");
38426         this.bodyEl.show();
38427     },
38428
38429     /**
38430      * Set the tooltip for the tab.
38431      * @param {String} tooltip The tab's tooltip
38432      */
38433     setTooltip : function(text){
38434         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38435             this.textEl.dom.qtip = text;
38436             this.textEl.dom.removeAttribute('title');
38437         }else{
38438             this.textEl.dom.title = text;
38439         }
38440     },
38441
38442     onTabClick : function(e){
38443         e.preventDefault();
38444         this.tabPanel.activate(this.id);
38445     },
38446
38447     onTabMouseDown : function(e){
38448         e.preventDefault();
38449         this.tabPanel.activate(this.id);
38450     },
38451 /*
38452     getWidth : function(){
38453         return this.inner.getWidth();
38454     },
38455
38456     setWidth : function(width){
38457         var iwidth = width - this.pnode.getPadding("lr");
38458         this.inner.setWidth(iwidth);
38459         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38460         this.pnode.setWidth(width);
38461     },
38462 */
38463     /**
38464      * Show or hide the tab
38465      * @param {Boolean} hidden True to hide or false to show.
38466      */
38467     setHidden : function(hidden){
38468         this.hidden = hidden;
38469         this.pnode.setStyle("display", hidden ? "none" : "");
38470     },
38471
38472     /**
38473      * Returns true if this tab is "hidden"
38474      * @return {Boolean}
38475      */
38476     isHidden : function(){
38477         return this.hidden;
38478     },
38479
38480     /**
38481      * Returns the text for this tab
38482      * @return {String}
38483      */
38484     getText : function(){
38485         return this.text;
38486     },
38487     /*
38488     autoSize : function(){
38489         //this.el.beginMeasure();
38490         this.textEl.setWidth(1);
38491         /*
38492          *  #2804 [new] Tabs in Roojs
38493          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38494          */
38495         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38496         //this.el.endMeasure();
38497     //},
38498
38499     /**
38500      * Sets the text for the tab (Note: this also sets the tooltip text)
38501      * @param {String} text The tab's text and tooltip
38502      */
38503     setText : function(text){
38504         this.text = text;
38505         this.textEl.update(text);
38506         this.setTooltip(text);
38507         //if(!this.tabPanel.resizeTabs){
38508         //    this.autoSize();
38509         //}
38510     },
38511     /**
38512      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38513      */
38514     activate : function(){
38515         this.tabPanel.activate(this.id);
38516     },
38517
38518     /**
38519      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38520      */
38521     disable : function(){
38522         if(this.tabPanel.active != this){
38523             this.disabled = true;
38524             this.pnode.addClass("disabled");
38525         }
38526     },
38527
38528     /**
38529      * Enables this TabPanelItem if it was previously disabled.
38530      */
38531     enable : function(){
38532         this.disabled = false;
38533         this.pnode.removeClass("disabled");
38534     },
38535
38536     /**
38537      * Sets the content for this TabPanelItem.
38538      * @param {String} content The content
38539      * @param {Boolean} loadScripts true to look for and load scripts
38540      */
38541     setContent : function(content, loadScripts){
38542         this.bodyEl.update(content, loadScripts);
38543     },
38544
38545     /**
38546      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38547      * @return {Roo.UpdateManager} The UpdateManager
38548      */
38549     getUpdateManager : function(){
38550         return this.bodyEl.getUpdateManager();
38551     },
38552
38553     /**
38554      * Set a URL to be used to load the content for this TabPanelItem.
38555      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38556      * @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)
38557      * @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)
38558      * @return {Roo.UpdateManager} The UpdateManager
38559      */
38560     setUrl : function(url, params, loadOnce){
38561         if(this.refreshDelegate){
38562             this.un('activate', this.refreshDelegate);
38563         }
38564         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38565         this.on("activate", this.refreshDelegate);
38566         return this.bodyEl.getUpdateManager();
38567     },
38568
38569     /** @private */
38570     _handleRefresh : function(url, params, loadOnce){
38571         if(!loadOnce || !this.loaded){
38572             var updater = this.bodyEl.getUpdateManager();
38573             updater.update(url, params, this._setLoaded.createDelegate(this));
38574         }
38575     },
38576
38577     /**
38578      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38579      *   Will fail silently if the setUrl method has not been called.
38580      *   This does not activate the panel, just updates its content.
38581      */
38582     refresh : function(){
38583         if(this.refreshDelegate){
38584            this.loaded = false;
38585            this.refreshDelegate();
38586         }
38587     },
38588
38589     /** @private */
38590     _setLoaded : function(){
38591         this.loaded = true;
38592     },
38593
38594     /** @private */
38595     closeClick : function(e){
38596         var o = {};
38597         e.stopEvent();
38598         this.fireEvent("beforeclose", this, o);
38599         if(o.cancel !== true){
38600             this.tabPanel.removeTab(this.id);
38601         }
38602     },
38603     /**
38604      * The text displayed in the tooltip for the close icon.
38605      * @type String
38606      */
38607     closeText : "Close this tab"
38608 });
38609 /**
38610 *    This script refer to:
38611 *    Title: International Telephone Input
38612 *    Author: Jack O'Connor
38613 *    Code version:  v12.1.12
38614 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38615 **/
38616
38617 Roo.bootstrap.PhoneInputData = function() {
38618     var d = [
38619       [
38620         "Afghanistan (‫افغانستان‬‎)",
38621         "af",
38622         "93"
38623       ],
38624       [
38625         "Albania (Shqipëri)",
38626         "al",
38627         "355"
38628       ],
38629       [
38630         "Algeria (‫الجزائر‬‎)",
38631         "dz",
38632         "213"
38633       ],
38634       [
38635         "American Samoa",
38636         "as",
38637         "1684"
38638       ],
38639       [
38640         "Andorra",
38641         "ad",
38642         "376"
38643       ],
38644       [
38645         "Angola",
38646         "ao",
38647         "244"
38648       ],
38649       [
38650         "Anguilla",
38651         "ai",
38652         "1264"
38653       ],
38654       [
38655         "Antigua and Barbuda",
38656         "ag",
38657         "1268"
38658       ],
38659       [
38660         "Argentina",
38661         "ar",
38662         "54"
38663       ],
38664       [
38665         "Armenia (Հայաստան)",
38666         "am",
38667         "374"
38668       ],
38669       [
38670         "Aruba",
38671         "aw",
38672         "297"
38673       ],
38674       [
38675         "Australia",
38676         "au",
38677         "61",
38678         0
38679       ],
38680       [
38681         "Austria (Österreich)",
38682         "at",
38683         "43"
38684       ],
38685       [
38686         "Azerbaijan (Azərbaycan)",
38687         "az",
38688         "994"
38689       ],
38690       [
38691         "Bahamas",
38692         "bs",
38693         "1242"
38694       ],
38695       [
38696         "Bahrain (‫البحرين‬‎)",
38697         "bh",
38698         "973"
38699       ],
38700       [
38701         "Bangladesh (বাংলাদেশ)",
38702         "bd",
38703         "880"
38704       ],
38705       [
38706         "Barbados",
38707         "bb",
38708         "1246"
38709       ],
38710       [
38711         "Belarus (Беларусь)",
38712         "by",
38713         "375"
38714       ],
38715       [
38716         "Belgium (België)",
38717         "be",
38718         "32"
38719       ],
38720       [
38721         "Belize",
38722         "bz",
38723         "501"
38724       ],
38725       [
38726         "Benin (Bénin)",
38727         "bj",
38728         "229"
38729       ],
38730       [
38731         "Bermuda",
38732         "bm",
38733         "1441"
38734       ],
38735       [
38736         "Bhutan (འབྲུག)",
38737         "bt",
38738         "975"
38739       ],
38740       [
38741         "Bolivia",
38742         "bo",
38743         "591"
38744       ],
38745       [
38746         "Bosnia and Herzegovina (Босна и Херцеговина)",
38747         "ba",
38748         "387"
38749       ],
38750       [
38751         "Botswana",
38752         "bw",
38753         "267"
38754       ],
38755       [
38756         "Brazil (Brasil)",
38757         "br",
38758         "55"
38759       ],
38760       [
38761         "British Indian Ocean Territory",
38762         "io",
38763         "246"
38764       ],
38765       [
38766         "British Virgin Islands",
38767         "vg",
38768         "1284"
38769       ],
38770       [
38771         "Brunei",
38772         "bn",
38773         "673"
38774       ],
38775       [
38776         "Bulgaria (България)",
38777         "bg",
38778         "359"
38779       ],
38780       [
38781         "Burkina Faso",
38782         "bf",
38783         "226"
38784       ],
38785       [
38786         "Burundi (Uburundi)",
38787         "bi",
38788         "257"
38789       ],
38790       [
38791         "Cambodia (កម្ពុជា)",
38792         "kh",
38793         "855"
38794       ],
38795       [
38796         "Cameroon (Cameroun)",
38797         "cm",
38798         "237"
38799       ],
38800       [
38801         "Canada",
38802         "ca",
38803         "1",
38804         1,
38805         ["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"]
38806       ],
38807       [
38808         "Cape Verde (Kabu Verdi)",
38809         "cv",
38810         "238"
38811       ],
38812       [
38813         "Caribbean Netherlands",
38814         "bq",
38815         "599",
38816         1
38817       ],
38818       [
38819         "Cayman Islands",
38820         "ky",
38821         "1345"
38822       ],
38823       [
38824         "Central African Republic (République centrafricaine)",
38825         "cf",
38826         "236"
38827       ],
38828       [
38829         "Chad (Tchad)",
38830         "td",
38831         "235"
38832       ],
38833       [
38834         "Chile",
38835         "cl",
38836         "56"
38837       ],
38838       [
38839         "China (中国)",
38840         "cn",
38841         "86"
38842       ],
38843       [
38844         "Christmas Island",
38845         "cx",
38846         "61",
38847         2
38848       ],
38849       [
38850         "Cocos (Keeling) Islands",
38851         "cc",
38852         "61",
38853         1
38854       ],
38855       [
38856         "Colombia",
38857         "co",
38858         "57"
38859       ],
38860       [
38861         "Comoros (‫جزر القمر‬‎)",
38862         "km",
38863         "269"
38864       ],
38865       [
38866         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38867         "cd",
38868         "243"
38869       ],
38870       [
38871         "Congo (Republic) (Congo-Brazzaville)",
38872         "cg",
38873         "242"
38874       ],
38875       [
38876         "Cook Islands",
38877         "ck",
38878         "682"
38879       ],
38880       [
38881         "Costa Rica",
38882         "cr",
38883         "506"
38884       ],
38885       [
38886         "Côte d’Ivoire",
38887         "ci",
38888         "225"
38889       ],
38890       [
38891         "Croatia (Hrvatska)",
38892         "hr",
38893         "385"
38894       ],
38895       [
38896         "Cuba",
38897         "cu",
38898         "53"
38899       ],
38900       [
38901         "Curaçao",
38902         "cw",
38903         "599",
38904         0
38905       ],
38906       [
38907         "Cyprus (Κύπρος)",
38908         "cy",
38909         "357"
38910       ],
38911       [
38912         "Czech Republic (Česká republika)",
38913         "cz",
38914         "420"
38915       ],
38916       [
38917         "Denmark (Danmark)",
38918         "dk",
38919         "45"
38920       ],
38921       [
38922         "Djibouti",
38923         "dj",
38924         "253"
38925       ],
38926       [
38927         "Dominica",
38928         "dm",
38929         "1767"
38930       ],
38931       [
38932         "Dominican Republic (República Dominicana)",
38933         "do",
38934         "1",
38935         2,
38936         ["809", "829", "849"]
38937       ],
38938       [
38939         "Ecuador",
38940         "ec",
38941         "593"
38942       ],
38943       [
38944         "Egypt (‫مصر‬‎)",
38945         "eg",
38946         "20"
38947       ],
38948       [
38949         "El Salvador",
38950         "sv",
38951         "503"
38952       ],
38953       [
38954         "Equatorial Guinea (Guinea Ecuatorial)",
38955         "gq",
38956         "240"
38957       ],
38958       [
38959         "Eritrea",
38960         "er",
38961         "291"
38962       ],
38963       [
38964         "Estonia (Eesti)",
38965         "ee",
38966         "372"
38967       ],
38968       [
38969         "Ethiopia",
38970         "et",
38971         "251"
38972       ],
38973       [
38974         "Falkland Islands (Islas Malvinas)",
38975         "fk",
38976         "500"
38977       ],
38978       [
38979         "Faroe Islands (Føroyar)",
38980         "fo",
38981         "298"
38982       ],
38983       [
38984         "Fiji",
38985         "fj",
38986         "679"
38987       ],
38988       [
38989         "Finland (Suomi)",
38990         "fi",
38991         "358",
38992         0
38993       ],
38994       [
38995         "France",
38996         "fr",
38997         "33"
38998       ],
38999       [
39000         "French Guiana (Guyane française)",
39001         "gf",
39002         "594"
39003       ],
39004       [
39005         "French Polynesia (Polynésie française)",
39006         "pf",
39007         "689"
39008       ],
39009       [
39010         "Gabon",
39011         "ga",
39012         "241"
39013       ],
39014       [
39015         "Gambia",
39016         "gm",
39017         "220"
39018       ],
39019       [
39020         "Georgia (საქართველო)",
39021         "ge",
39022         "995"
39023       ],
39024       [
39025         "Germany (Deutschland)",
39026         "de",
39027         "49"
39028       ],
39029       [
39030         "Ghana (Gaana)",
39031         "gh",
39032         "233"
39033       ],
39034       [
39035         "Gibraltar",
39036         "gi",
39037         "350"
39038       ],
39039       [
39040         "Greece (Ελλάδα)",
39041         "gr",
39042         "30"
39043       ],
39044       [
39045         "Greenland (Kalaallit Nunaat)",
39046         "gl",
39047         "299"
39048       ],
39049       [
39050         "Grenada",
39051         "gd",
39052         "1473"
39053       ],
39054       [
39055         "Guadeloupe",
39056         "gp",
39057         "590",
39058         0
39059       ],
39060       [
39061         "Guam",
39062         "gu",
39063         "1671"
39064       ],
39065       [
39066         "Guatemala",
39067         "gt",
39068         "502"
39069       ],
39070       [
39071         "Guernsey",
39072         "gg",
39073         "44",
39074         1
39075       ],
39076       [
39077         "Guinea (Guinée)",
39078         "gn",
39079         "224"
39080       ],
39081       [
39082         "Guinea-Bissau (Guiné Bissau)",
39083         "gw",
39084         "245"
39085       ],
39086       [
39087         "Guyana",
39088         "gy",
39089         "592"
39090       ],
39091       [
39092         "Haiti",
39093         "ht",
39094         "509"
39095       ],
39096       [
39097         "Honduras",
39098         "hn",
39099         "504"
39100       ],
39101       [
39102         "Hong Kong (香港)",
39103         "hk",
39104         "852"
39105       ],
39106       [
39107         "Hungary (Magyarország)",
39108         "hu",
39109         "36"
39110       ],
39111       [
39112         "Iceland (Ísland)",
39113         "is",
39114         "354"
39115       ],
39116       [
39117         "India (भारत)",
39118         "in",
39119         "91"
39120       ],
39121       [
39122         "Indonesia",
39123         "id",
39124         "62"
39125       ],
39126       [
39127         "Iran (‫ایران‬‎)",
39128         "ir",
39129         "98"
39130       ],
39131       [
39132         "Iraq (‫العراق‬‎)",
39133         "iq",
39134         "964"
39135       ],
39136       [
39137         "Ireland",
39138         "ie",
39139         "353"
39140       ],
39141       [
39142         "Isle of Man",
39143         "im",
39144         "44",
39145         2
39146       ],
39147       [
39148         "Israel (‫ישראל‬‎)",
39149         "il",
39150         "972"
39151       ],
39152       [
39153         "Italy (Italia)",
39154         "it",
39155         "39",
39156         0
39157       ],
39158       [
39159         "Jamaica",
39160         "jm",
39161         "1876"
39162       ],
39163       [
39164         "Japan (日本)",
39165         "jp",
39166         "81"
39167       ],
39168       [
39169         "Jersey",
39170         "je",
39171         "44",
39172         3
39173       ],
39174       [
39175         "Jordan (‫الأردن‬‎)",
39176         "jo",
39177         "962"
39178       ],
39179       [
39180         "Kazakhstan (Казахстан)",
39181         "kz",
39182         "7",
39183         1
39184       ],
39185       [
39186         "Kenya",
39187         "ke",
39188         "254"
39189       ],
39190       [
39191         "Kiribati",
39192         "ki",
39193         "686"
39194       ],
39195       [
39196         "Kosovo",
39197         "xk",
39198         "383"
39199       ],
39200       [
39201         "Kuwait (‫الكويت‬‎)",
39202         "kw",
39203         "965"
39204       ],
39205       [
39206         "Kyrgyzstan (Кыргызстан)",
39207         "kg",
39208         "996"
39209       ],
39210       [
39211         "Laos (ລາວ)",
39212         "la",
39213         "856"
39214       ],
39215       [
39216         "Latvia (Latvija)",
39217         "lv",
39218         "371"
39219       ],
39220       [
39221         "Lebanon (‫لبنان‬‎)",
39222         "lb",
39223         "961"
39224       ],
39225       [
39226         "Lesotho",
39227         "ls",
39228         "266"
39229       ],
39230       [
39231         "Liberia",
39232         "lr",
39233         "231"
39234       ],
39235       [
39236         "Libya (‫ليبيا‬‎)",
39237         "ly",
39238         "218"
39239       ],
39240       [
39241         "Liechtenstein",
39242         "li",
39243         "423"
39244       ],
39245       [
39246         "Lithuania (Lietuva)",
39247         "lt",
39248         "370"
39249       ],
39250       [
39251         "Luxembourg",
39252         "lu",
39253         "352"
39254       ],
39255       [
39256         "Macau (澳門)",
39257         "mo",
39258         "853"
39259       ],
39260       [
39261         "Macedonia (FYROM) (Македонија)",
39262         "mk",
39263         "389"
39264       ],
39265       [
39266         "Madagascar (Madagasikara)",
39267         "mg",
39268         "261"
39269       ],
39270       [
39271         "Malawi",
39272         "mw",
39273         "265"
39274       ],
39275       [
39276         "Malaysia",
39277         "my",
39278         "60"
39279       ],
39280       [
39281         "Maldives",
39282         "mv",
39283         "960"
39284       ],
39285       [
39286         "Mali",
39287         "ml",
39288         "223"
39289       ],
39290       [
39291         "Malta",
39292         "mt",
39293         "356"
39294       ],
39295       [
39296         "Marshall Islands",
39297         "mh",
39298         "692"
39299       ],
39300       [
39301         "Martinique",
39302         "mq",
39303         "596"
39304       ],
39305       [
39306         "Mauritania (‫موريتانيا‬‎)",
39307         "mr",
39308         "222"
39309       ],
39310       [
39311         "Mauritius (Moris)",
39312         "mu",
39313         "230"
39314       ],
39315       [
39316         "Mayotte",
39317         "yt",
39318         "262",
39319         1
39320       ],
39321       [
39322         "Mexico (México)",
39323         "mx",
39324         "52"
39325       ],
39326       [
39327         "Micronesia",
39328         "fm",
39329         "691"
39330       ],
39331       [
39332         "Moldova (Republica Moldova)",
39333         "md",
39334         "373"
39335       ],
39336       [
39337         "Monaco",
39338         "mc",
39339         "377"
39340       ],
39341       [
39342         "Mongolia (Монгол)",
39343         "mn",
39344         "976"
39345       ],
39346       [
39347         "Montenegro (Crna Gora)",
39348         "me",
39349         "382"
39350       ],
39351       [
39352         "Montserrat",
39353         "ms",
39354         "1664"
39355       ],
39356       [
39357         "Morocco (‫المغرب‬‎)",
39358         "ma",
39359         "212",
39360         0
39361       ],
39362       [
39363         "Mozambique (Moçambique)",
39364         "mz",
39365         "258"
39366       ],
39367       [
39368         "Myanmar (Burma) (မြန်မာ)",
39369         "mm",
39370         "95"
39371       ],
39372       [
39373         "Namibia (Namibië)",
39374         "na",
39375         "264"
39376       ],
39377       [
39378         "Nauru",
39379         "nr",
39380         "674"
39381       ],
39382       [
39383         "Nepal (नेपाल)",
39384         "np",
39385         "977"
39386       ],
39387       [
39388         "Netherlands (Nederland)",
39389         "nl",
39390         "31"
39391       ],
39392       [
39393         "New Caledonia (Nouvelle-Calédonie)",
39394         "nc",
39395         "687"
39396       ],
39397       [
39398         "New Zealand",
39399         "nz",
39400         "64"
39401       ],
39402       [
39403         "Nicaragua",
39404         "ni",
39405         "505"
39406       ],
39407       [
39408         "Niger (Nijar)",
39409         "ne",
39410         "227"
39411       ],
39412       [
39413         "Nigeria",
39414         "ng",
39415         "234"
39416       ],
39417       [
39418         "Niue",
39419         "nu",
39420         "683"
39421       ],
39422       [
39423         "Norfolk Island",
39424         "nf",
39425         "672"
39426       ],
39427       [
39428         "North Korea (조선 민주주의 인민 공화국)",
39429         "kp",
39430         "850"
39431       ],
39432       [
39433         "Northern Mariana Islands",
39434         "mp",
39435         "1670"
39436       ],
39437       [
39438         "Norway (Norge)",
39439         "no",
39440         "47",
39441         0
39442       ],
39443       [
39444         "Oman (‫عُمان‬‎)",
39445         "om",
39446         "968"
39447       ],
39448       [
39449         "Pakistan (‫پاکستان‬‎)",
39450         "pk",
39451         "92"
39452       ],
39453       [
39454         "Palau",
39455         "pw",
39456         "680"
39457       ],
39458       [
39459         "Palestine (‫فلسطين‬‎)",
39460         "ps",
39461         "970"
39462       ],
39463       [
39464         "Panama (Panamá)",
39465         "pa",
39466         "507"
39467       ],
39468       [
39469         "Papua New Guinea",
39470         "pg",
39471         "675"
39472       ],
39473       [
39474         "Paraguay",
39475         "py",
39476         "595"
39477       ],
39478       [
39479         "Peru (Perú)",
39480         "pe",
39481         "51"
39482       ],
39483       [
39484         "Philippines",
39485         "ph",
39486         "63"
39487       ],
39488       [
39489         "Poland (Polska)",
39490         "pl",
39491         "48"
39492       ],
39493       [
39494         "Portugal",
39495         "pt",
39496         "351"
39497       ],
39498       [
39499         "Puerto Rico",
39500         "pr",
39501         "1",
39502         3,
39503         ["787", "939"]
39504       ],
39505       [
39506         "Qatar (‫قطر‬‎)",
39507         "qa",
39508         "974"
39509       ],
39510       [
39511         "Réunion (La Réunion)",
39512         "re",
39513         "262",
39514         0
39515       ],
39516       [
39517         "Romania (România)",
39518         "ro",
39519         "40"
39520       ],
39521       [
39522         "Russia (Россия)",
39523         "ru",
39524         "7",
39525         0
39526       ],
39527       [
39528         "Rwanda",
39529         "rw",
39530         "250"
39531       ],
39532       [
39533         "Saint Barthélemy",
39534         "bl",
39535         "590",
39536         1
39537       ],
39538       [
39539         "Saint Helena",
39540         "sh",
39541         "290"
39542       ],
39543       [
39544         "Saint Kitts and Nevis",
39545         "kn",
39546         "1869"
39547       ],
39548       [
39549         "Saint Lucia",
39550         "lc",
39551         "1758"
39552       ],
39553       [
39554         "Saint Martin (Saint-Martin (partie française))",
39555         "mf",
39556         "590",
39557         2
39558       ],
39559       [
39560         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39561         "pm",
39562         "508"
39563       ],
39564       [
39565         "Saint Vincent and the Grenadines",
39566         "vc",
39567         "1784"
39568       ],
39569       [
39570         "Samoa",
39571         "ws",
39572         "685"
39573       ],
39574       [
39575         "San Marino",
39576         "sm",
39577         "378"
39578       ],
39579       [
39580         "São Tomé and Príncipe (São Tomé e Príncipe)",
39581         "st",
39582         "239"
39583       ],
39584       [
39585         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39586         "sa",
39587         "966"
39588       ],
39589       [
39590         "Senegal (Sénégal)",
39591         "sn",
39592         "221"
39593       ],
39594       [
39595         "Serbia (Србија)",
39596         "rs",
39597         "381"
39598       ],
39599       [
39600         "Seychelles",
39601         "sc",
39602         "248"
39603       ],
39604       [
39605         "Sierra Leone",
39606         "sl",
39607         "232"
39608       ],
39609       [
39610         "Singapore",
39611         "sg",
39612         "65"
39613       ],
39614       [
39615         "Sint Maarten",
39616         "sx",
39617         "1721"
39618       ],
39619       [
39620         "Slovakia (Slovensko)",
39621         "sk",
39622         "421"
39623       ],
39624       [
39625         "Slovenia (Slovenija)",
39626         "si",
39627         "386"
39628       ],
39629       [
39630         "Solomon Islands",
39631         "sb",
39632         "677"
39633       ],
39634       [
39635         "Somalia (Soomaaliya)",
39636         "so",
39637         "252"
39638       ],
39639       [
39640         "South Africa",
39641         "za",
39642         "27"
39643       ],
39644       [
39645         "South Korea (대한민국)",
39646         "kr",
39647         "82"
39648       ],
39649       [
39650         "South Sudan (‫جنوب السودان‬‎)",
39651         "ss",
39652         "211"
39653       ],
39654       [
39655         "Spain (España)",
39656         "es",
39657         "34"
39658       ],
39659       [
39660         "Sri Lanka (ශ්‍රී ලංකාව)",
39661         "lk",
39662         "94"
39663       ],
39664       [
39665         "Sudan (‫السودان‬‎)",
39666         "sd",
39667         "249"
39668       ],
39669       [
39670         "Suriname",
39671         "sr",
39672         "597"
39673       ],
39674       [
39675         "Svalbard and Jan Mayen",
39676         "sj",
39677         "47",
39678         1
39679       ],
39680       [
39681         "Swaziland",
39682         "sz",
39683         "268"
39684       ],
39685       [
39686         "Sweden (Sverige)",
39687         "se",
39688         "46"
39689       ],
39690       [
39691         "Switzerland (Schweiz)",
39692         "ch",
39693         "41"
39694       ],
39695       [
39696         "Syria (‫سوريا‬‎)",
39697         "sy",
39698         "963"
39699       ],
39700       [
39701         "Taiwan (台灣)",
39702         "tw",
39703         "886"
39704       ],
39705       [
39706         "Tajikistan",
39707         "tj",
39708         "992"
39709       ],
39710       [
39711         "Tanzania",
39712         "tz",
39713         "255"
39714       ],
39715       [
39716         "Thailand (ไทย)",
39717         "th",
39718         "66"
39719       ],
39720       [
39721         "Timor-Leste",
39722         "tl",
39723         "670"
39724       ],
39725       [
39726         "Togo",
39727         "tg",
39728         "228"
39729       ],
39730       [
39731         "Tokelau",
39732         "tk",
39733         "690"
39734       ],
39735       [
39736         "Tonga",
39737         "to",
39738         "676"
39739       ],
39740       [
39741         "Trinidad and Tobago",
39742         "tt",
39743         "1868"
39744       ],
39745       [
39746         "Tunisia (‫تونس‬‎)",
39747         "tn",
39748         "216"
39749       ],
39750       [
39751         "Turkey (Türkiye)",
39752         "tr",
39753         "90"
39754       ],
39755       [
39756         "Turkmenistan",
39757         "tm",
39758         "993"
39759       ],
39760       [
39761         "Turks and Caicos Islands",
39762         "tc",
39763         "1649"
39764       ],
39765       [
39766         "Tuvalu",
39767         "tv",
39768         "688"
39769       ],
39770       [
39771         "U.S. Virgin Islands",
39772         "vi",
39773         "1340"
39774       ],
39775       [
39776         "Uganda",
39777         "ug",
39778         "256"
39779       ],
39780       [
39781         "Ukraine (Україна)",
39782         "ua",
39783         "380"
39784       ],
39785       [
39786         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39787         "ae",
39788         "971"
39789       ],
39790       [
39791         "United Kingdom",
39792         "gb",
39793         "44",
39794         0
39795       ],
39796       [
39797         "United States",
39798         "us",
39799         "1",
39800         0
39801       ],
39802       [
39803         "Uruguay",
39804         "uy",
39805         "598"
39806       ],
39807       [
39808         "Uzbekistan (Oʻzbekiston)",
39809         "uz",
39810         "998"
39811       ],
39812       [
39813         "Vanuatu",
39814         "vu",
39815         "678"
39816       ],
39817       [
39818         "Vatican City (Città del Vaticano)",
39819         "va",
39820         "39",
39821         1
39822       ],
39823       [
39824         "Venezuela",
39825         "ve",
39826         "58"
39827       ],
39828       [
39829         "Vietnam (Việt Nam)",
39830         "vn",
39831         "84"
39832       ],
39833       [
39834         "Wallis and Futuna (Wallis-et-Futuna)",
39835         "wf",
39836         "681"
39837       ],
39838       [
39839         "Western Sahara (‫الصحراء الغربية‬‎)",
39840         "eh",
39841         "212",
39842         1
39843       ],
39844       [
39845         "Yemen (‫اليمن‬‎)",
39846         "ye",
39847         "967"
39848       ],
39849       [
39850         "Zambia",
39851         "zm",
39852         "260"
39853       ],
39854       [
39855         "Zimbabwe",
39856         "zw",
39857         "263"
39858       ],
39859       [
39860         "Åland Islands",
39861         "ax",
39862         "358",
39863         1
39864       ]
39865   ];
39866   
39867   return d;
39868 }/**
39869 *    This script refer to:
39870 *    Title: International Telephone Input
39871 *    Author: Jack O'Connor
39872 *    Code version:  v12.1.12
39873 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39874 **/
39875
39876 /**
39877  * @class Roo.bootstrap.PhoneInput
39878  * @extends Roo.bootstrap.TriggerField
39879  * An input with International dial-code selection
39880  
39881  * @cfg {String} defaultDialCode default '+852'
39882  * @cfg {Array} preferedCountries default []
39883   
39884  * @constructor
39885  * Create a new PhoneInput.
39886  * @param {Object} config Configuration options
39887  */
39888
39889 Roo.bootstrap.PhoneInput = function(config) {
39890     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39891 };
39892
39893 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39894         
39895         listWidth: undefined,
39896         
39897         selectedClass: 'active',
39898         
39899         invalidClass : "has-warning",
39900         
39901         validClass: 'has-success',
39902         
39903         allowed: '0123456789',
39904         
39905         max_length: 15,
39906         
39907         /**
39908          * @cfg {String} defaultDialCode The default dial code when initializing the input
39909          */
39910         defaultDialCode: '+852',
39911         
39912         /**
39913          * @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
39914          */
39915         preferedCountries: false,
39916         
39917         getAutoCreate : function()
39918         {
39919             var data = Roo.bootstrap.PhoneInputData();
39920             var align = this.labelAlign || this.parentLabelAlign();
39921             var id = Roo.id();
39922             
39923             this.allCountries = [];
39924             this.dialCodeMapping = [];
39925             
39926             for (var i = 0; i < data.length; i++) {
39927               var c = data[i];
39928               this.allCountries[i] = {
39929                 name: c[0],
39930                 iso2: c[1],
39931                 dialCode: c[2],
39932                 priority: c[3] || 0,
39933                 areaCodes: c[4] || null
39934               };
39935               this.dialCodeMapping[c[2]] = {
39936                   name: c[0],
39937                   iso2: c[1],
39938                   priority: c[3] || 0,
39939                   areaCodes: c[4] || null
39940               };
39941             }
39942             
39943             var cfg = {
39944                 cls: 'form-group',
39945                 cn: []
39946             };
39947             
39948             var input =  {
39949                 tag: 'input',
39950                 id : id,
39951                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39952                 maxlength: this.max_length,
39953                 cls : 'form-control tel-input',
39954                 autocomplete: 'new-password'
39955             };
39956             
39957             var hiddenInput = {
39958                 tag: 'input',
39959                 type: 'hidden',
39960                 cls: 'hidden-tel-input'
39961             };
39962             
39963             if (this.name) {
39964                 hiddenInput.name = this.name;
39965             }
39966             
39967             if (this.disabled) {
39968                 input.disabled = true;
39969             }
39970             
39971             var flag_container = {
39972                 tag: 'div',
39973                 cls: 'flag-box',
39974                 cn: [
39975                     {
39976                         tag: 'div',
39977                         cls: 'flag'
39978                     },
39979                     {
39980                         tag: 'div',
39981                         cls: 'caret'
39982                     }
39983                 ]
39984             };
39985             
39986             var box = {
39987                 tag: 'div',
39988                 cls: this.hasFeedback ? 'has-feedback' : '',
39989                 cn: [
39990                     hiddenInput,
39991                     input,
39992                     {
39993                         tag: 'input',
39994                         cls: 'dial-code-holder',
39995                         disabled: true
39996                     }
39997                 ]
39998             };
39999             
40000             var container = {
40001                 cls: 'roo-select2-container input-group',
40002                 cn: [
40003                     flag_container,
40004                     box
40005                 ]
40006             };
40007             
40008             if (this.fieldLabel.length) {
40009                 var indicator = {
40010                     tag: 'i',
40011                     tooltip: 'This field is required'
40012                 };
40013                 
40014                 var label = {
40015                     tag: 'label',
40016                     'for':  id,
40017                     cls: 'control-label',
40018                     cn: []
40019                 };
40020                 
40021                 var label_text = {
40022                     tag: 'span',
40023                     html: this.fieldLabel
40024                 };
40025                 
40026                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40027                 label.cn = [
40028                     indicator,
40029                     label_text
40030                 ];
40031                 
40032                 if(this.indicatorpos == 'right') {
40033                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40034                     label.cn = [
40035                         label_text,
40036                         indicator
40037                     ];
40038                 }
40039                 
40040                 if(align == 'left') {
40041                     container = {
40042                         tag: 'div',
40043                         cn: [
40044                             container
40045                         ]
40046                     };
40047                     
40048                     if(this.labelWidth > 12){
40049                         label.style = "width: " + this.labelWidth + 'px';
40050                     }
40051                     if(this.labelWidth < 13 && this.labelmd == 0){
40052                         this.labelmd = this.labelWidth;
40053                     }
40054                     if(this.labellg > 0){
40055                         label.cls += ' col-lg-' + this.labellg;
40056                         input.cls += ' col-lg-' + (12 - this.labellg);
40057                     }
40058                     if(this.labelmd > 0){
40059                         label.cls += ' col-md-' + this.labelmd;
40060                         container.cls += ' col-md-' + (12 - this.labelmd);
40061                     }
40062                     if(this.labelsm > 0){
40063                         label.cls += ' col-sm-' + this.labelsm;
40064                         container.cls += ' col-sm-' + (12 - this.labelsm);
40065                     }
40066                     if(this.labelxs > 0){
40067                         label.cls += ' col-xs-' + this.labelxs;
40068                         container.cls += ' col-xs-' + (12 - this.labelxs);
40069                     }
40070                 }
40071             }
40072             
40073             cfg.cn = [
40074                 label,
40075                 container
40076             ];
40077             
40078             var settings = this;
40079             
40080             ['xs','sm','md','lg'].map(function(size){
40081                 if (settings[size]) {
40082                     cfg.cls += ' col-' + size + '-' + settings[size];
40083                 }
40084             });
40085             
40086             this.store = new Roo.data.Store({
40087                 proxy : new Roo.data.MemoryProxy({}),
40088                 reader : new Roo.data.JsonReader({
40089                     fields : [
40090                         {
40091                             'name' : 'name',
40092                             'type' : 'string'
40093                         },
40094                         {
40095                             'name' : 'iso2',
40096                             'type' : 'string'
40097                         },
40098                         {
40099                             'name' : 'dialCode',
40100                             'type' : 'string'
40101                         },
40102                         {
40103                             'name' : 'priority',
40104                             'type' : 'string'
40105                         },
40106                         {
40107                             'name' : 'areaCodes',
40108                             'type' : 'string'
40109                         }
40110                     ]
40111                 })
40112             });
40113             
40114             if(!this.preferedCountries) {
40115                 this.preferedCountries = [
40116                     'hk',
40117                     'gb',
40118                     'us'
40119                 ];
40120             }
40121             
40122             var p = this.preferedCountries.reverse();
40123             
40124             if(p) {
40125                 for (var i = 0; i < p.length; i++) {
40126                     for (var j = 0; j < this.allCountries.length; j++) {
40127                         if(this.allCountries[j].iso2 == p[i]) {
40128                             var t = this.allCountries[j];
40129                             this.allCountries.splice(j,1);
40130                             this.allCountries.unshift(t);
40131                         }
40132                     } 
40133                 }
40134             }
40135             
40136             this.store.proxy.data = {
40137                 success: true,
40138                 data: this.allCountries
40139             };
40140             
40141             return cfg;
40142         },
40143         
40144         initEvents : function()
40145         {
40146             this.createList();
40147             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40148             
40149             this.indicator = this.indicatorEl();
40150             this.flag = this.flagEl();
40151             this.dialCodeHolder = this.dialCodeHolderEl();
40152             
40153             this.trigger = this.el.select('div.flag-box',true).first();
40154             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40155             
40156             var _this = this;
40157             
40158             (function(){
40159                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40160                 _this.list.setWidth(lw);
40161             }).defer(100);
40162             
40163             this.list.on('mouseover', this.onViewOver, this);
40164             this.list.on('mousemove', this.onViewMove, this);
40165             this.inputEl().on("keyup", this.onKeyUp, this);
40166             this.inputEl().on("keypress", this.onKeyPress, this);
40167             
40168             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40169
40170             this.view = new Roo.View(this.list, this.tpl, {
40171                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40172             });
40173             
40174             this.view.on('click', this.onViewClick, this);
40175             this.setValue(this.defaultDialCode);
40176         },
40177         
40178         onTriggerClick : function(e)
40179         {
40180             Roo.log('trigger click');
40181             if(this.disabled){
40182                 return;
40183             }
40184             
40185             if(this.isExpanded()){
40186                 this.collapse();
40187                 this.hasFocus = false;
40188             }else {
40189                 this.store.load({});
40190                 this.hasFocus = true;
40191                 this.expand();
40192             }
40193         },
40194         
40195         isExpanded : function()
40196         {
40197             return this.list.isVisible();
40198         },
40199         
40200         collapse : function()
40201         {
40202             if(!this.isExpanded()){
40203                 return;
40204             }
40205             this.list.hide();
40206             Roo.get(document).un('mousedown', this.collapseIf, this);
40207             Roo.get(document).un('mousewheel', this.collapseIf, this);
40208             this.fireEvent('collapse', this);
40209             this.validate();
40210         },
40211         
40212         expand : function()
40213         {
40214             Roo.log('expand');
40215
40216             if(this.isExpanded() || !this.hasFocus){
40217                 return;
40218             }
40219             
40220             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40221             this.list.setWidth(lw);
40222             
40223             this.list.show();
40224             this.restrictHeight();
40225             
40226             Roo.get(document).on('mousedown', this.collapseIf, this);
40227             Roo.get(document).on('mousewheel', this.collapseIf, this);
40228             
40229             this.fireEvent('expand', this);
40230         },
40231         
40232         restrictHeight : function()
40233         {
40234             this.list.alignTo(this.inputEl(), this.listAlign);
40235             this.list.alignTo(this.inputEl(), this.listAlign);
40236         },
40237         
40238         onViewOver : function(e, t)
40239         {
40240             if(this.inKeyMode){
40241                 return;
40242             }
40243             var item = this.view.findItemFromChild(t);
40244             
40245             if(item){
40246                 var index = this.view.indexOf(item);
40247                 this.select(index, false);
40248             }
40249         },
40250
40251         // private
40252         onViewClick : function(view, doFocus, el, e)
40253         {
40254             var index = this.view.getSelectedIndexes()[0];
40255             
40256             var r = this.store.getAt(index);
40257             
40258             if(r){
40259                 this.onSelect(r, index);
40260             }
40261             if(doFocus !== false && !this.blockFocus){
40262                 this.inputEl().focus();
40263             }
40264         },
40265         
40266         onViewMove : function(e, t)
40267         {
40268             this.inKeyMode = false;
40269         },
40270         
40271         select : function(index, scrollIntoView)
40272         {
40273             this.selectedIndex = index;
40274             this.view.select(index);
40275             if(scrollIntoView !== false){
40276                 var el = this.view.getNode(index);
40277                 if(el){
40278                     this.list.scrollChildIntoView(el, false);
40279                 }
40280             }
40281         },
40282         
40283         createList : function()
40284         {
40285             this.list = Roo.get(document.body).createChild({
40286                 tag: 'ul',
40287                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40288                 style: 'display:none'
40289             });
40290             
40291             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40292         },
40293         
40294         collapseIf : function(e)
40295         {
40296             var in_combo  = e.within(this.el);
40297             var in_list =  e.within(this.list);
40298             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40299             
40300             if (in_combo || in_list || is_list) {
40301                 return;
40302             }
40303             this.collapse();
40304         },
40305         
40306         onSelect : function(record, index)
40307         {
40308             if(this.fireEvent('beforeselect', this, record, index) !== false){
40309                 
40310                 this.setFlagClass(record.data.iso2);
40311                 this.setDialCode(record.data.dialCode);
40312                 this.hasFocus = false;
40313                 this.collapse();
40314                 this.fireEvent('select', this, record, index);
40315             }
40316         },
40317         
40318         flagEl : function()
40319         {
40320             var flag = this.el.select('div.flag',true).first();
40321             if(!flag){
40322                 return false;
40323             }
40324             return flag;
40325         },
40326         
40327         dialCodeHolderEl : function()
40328         {
40329             var d = this.el.select('input.dial-code-holder',true).first();
40330             if(!d){
40331                 return false;
40332             }
40333             return d;
40334         },
40335         
40336         setDialCode : function(v)
40337         {
40338             this.dialCodeHolder.dom.value = '+'+v;
40339         },
40340         
40341         setFlagClass : function(n)
40342         {
40343             this.flag.dom.className = 'flag '+n;
40344         },
40345         
40346         getValue : function()
40347         {
40348             var v = this.inputEl().getValue();
40349             if(this.dialCodeHolder) {
40350                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40351             }
40352             return v;
40353         },
40354         
40355         setValue : function(v)
40356         {
40357             var d = this.getDialCode(v);
40358             
40359             //invalid dial code
40360             if(v.length == 0 || !d || d.length == 0) {
40361                 if(this.rendered){
40362                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40363                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40364                 }
40365                 return;
40366             }
40367             
40368             //valid dial code
40369             this.setFlagClass(this.dialCodeMapping[d].iso2);
40370             this.setDialCode(d);
40371             this.inputEl().dom.value = v.replace('+'+d,'');
40372             this.hiddenEl().dom.value = this.getValue();
40373             
40374             this.validate();
40375         },
40376         
40377         getDialCode : function(v)
40378         {
40379             v = v ||  '';
40380             
40381             if (v.length == 0) {
40382                 return this.dialCodeHolder.dom.value;
40383             }
40384             
40385             var dialCode = "";
40386             if (v.charAt(0) != "+") {
40387                 return false;
40388             }
40389             var numericChars = "";
40390             for (var i = 1; i < v.length; i++) {
40391               var c = v.charAt(i);
40392               if (!isNaN(c)) {
40393                 numericChars += c;
40394                 if (this.dialCodeMapping[numericChars]) {
40395                   dialCode = v.substr(1, i);
40396                 }
40397                 if (numericChars.length == 4) {
40398                   break;
40399                 }
40400               }
40401             }
40402             return dialCode;
40403         },
40404         
40405         reset : function()
40406         {
40407             this.setValue(this.defaultDialCode);
40408             this.validate();
40409         },
40410         
40411         hiddenEl : function()
40412         {
40413             return this.el.select('input.hidden-tel-input',true).first();
40414         },
40415         
40416         // after setting val
40417         onKeyUp : function(e){
40418             this.setValue(this.getValue());
40419         },
40420         
40421         onKeyPress : function(e){
40422             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40423                 e.stopEvent();
40424             }
40425         }
40426         
40427 });
40428 /**
40429  * @class Roo.bootstrap.MoneyField
40430  * @extends Roo.bootstrap.ComboBox
40431  * Bootstrap MoneyField class
40432  * 
40433  * @constructor
40434  * Create a new MoneyField.
40435  * @param {Object} config Configuration options
40436  */
40437
40438 Roo.bootstrap.MoneyField = function(config) {
40439     
40440     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40441     
40442 };
40443
40444 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40445     
40446     /**
40447      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40448      */
40449     allowDecimals : true,
40450     /**
40451      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40452      */
40453     decimalSeparator : ".",
40454     /**
40455      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40456      */
40457     decimalPrecision : 0,
40458     /**
40459      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40460      */
40461     allowNegative : true,
40462     /**
40463      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40464      */
40465     allowZero: true,
40466     /**
40467      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40468      */
40469     minValue : Number.NEGATIVE_INFINITY,
40470     /**
40471      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40472      */
40473     maxValue : Number.MAX_VALUE,
40474     /**
40475      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40476      */
40477     minText : "The minimum value for this field is {0}",
40478     /**
40479      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40480      */
40481     maxText : "The maximum value for this field is {0}",
40482     /**
40483      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40484      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40485      */
40486     nanText : "{0} is not a valid number",
40487     /**
40488      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40489      */
40490     castInt : true,
40491     /**
40492      * @cfg {String} defaults currency of the MoneyField
40493      * value should be in lkey
40494      */
40495     defaultCurrency : false,
40496     /**
40497      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40498      */
40499     thousandsDelimiter : false,
40500     /**
40501      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40502      */
40503     max_length: false,
40504     
40505     inputlg : 9,
40506     inputmd : 9,
40507     inputsm : 9,
40508     inputxs : 6,
40509     
40510     store : false,
40511     
40512     getAutoCreate : function()
40513     {
40514         var align = this.labelAlign || this.parentLabelAlign();
40515         
40516         var id = Roo.id();
40517
40518         var cfg = {
40519             cls: 'form-group',
40520             cn: []
40521         };
40522
40523         var input =  {
40524             tag: 'input',
40525             id : id,
40526             cls : 'form-control roo-money-amount-input',
40527             autocomplete: 'new-password'
40528         };
40529         
40530         var hiddenInput = {
40531             tag: 'input',
40532             type: 'hidden',
40533             id: Roo.id(),
40534             cls: 'hidden-number-input'
40535         };
40536         
40537         if(this.max_length) {
40538             input.maxlength = this.max_length; 
40539         }
40540         
40541         if (this.name) {
40542             hiddenInput.name = this.name;
40543         }
40544
40545         if (this.disabled) {
40546             input.disabled = true;
40547         }
40548
40549         var clg = 12 - this.inputlg;
40550         var cmd = 12 - this.inputmd;
40551         var csm = 12 - this.inputsm;
40552         var cxs = 12 - this.inputxs;
40553         
40554         var container = {
40555             tag : 'div',
40556             cls : 'row roo-money-field',
40557             cn : [
40558                 {
40559                     tag : 'div',
40560                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40561                     cn : [
40562                         {
40563                             tag : 'div',
40564                             cls: 'roo-select2-container input-group',
40565                             cn: [
40566                                 {
40567                                     tag : 'input',
40568                                     cls : 'form-control roo-money-currency-input',
40569                                     autocomplete: 'new-password',
40570                                     readOnly : 1,
40571                                     name : this.currencyName
40572                                 },
40573                                 {
40574                                     tag :'span',
40575                                     cls : 'input-group-addon',
40576                                     cn : [
40577                                         {
40578                                             tag: 'span',
40579                                             cls: 'caret'
40580                                         }
40581                                     ]
40582                                 }
40583                             ]
40584                         }
40585                     ]
40586                 },
40587                 {
40588                     tag : 'div',
40589                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40590                     cn : [
40591                         {
40592                             tag: 'div',
40593                             cls: this.hasFeedback ? 'has-feedback' : '',
40594                             cn: [
40595                                 input
40596                             ]
40597                         }
40598                     ]
40599                 }
40600             ]
40601             
40602         };
40603         
40604         if (this.fieldLabel.length) {
40605             var indicator = {
40606                 tag: 'i',
40607                 tooltip: 'This field is required'
40608             };
40609
40610             var label = {
40611                 tag: 'label',
40612                 'for':  id,
40613                 cls: 'control-label',
40614                 cn: []
40615             };
40616
40617             var label_text = {
40618                 tag: 'span',
40619                 html: this.fieldLabel
40620             };
40621
40622             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40623             label.cn = [
40624                 indicator,
40625                 label_text
40626             ];
40627
40628             if(this.indicatorpos == 'right') {
40629                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40630                 label.cn = [
40631                     label_text,
40632                     indicator
40633                 ];
40634             }
40635
40636             if(align == 'left') {
40637                 container = {
40638                     tag: 'div',
40639                     cn: [
40640                         container
40641                     ]
40642                 };
40643
40644                 if(this.labelWidth > 12){
40645                     label.style = "width: " + this.labelWidth + 'px';
40646                 }
40647                 if(this.labelWidth < 13 && this.labelmd == 0){
40648                     this.labelmd = this.labelWidth;
40649                 }
40650                 if(this.labellg > 0){
40651                     label.cls += ' col-lg-' + this.labellg;
40652                     input.cls += ' col-lg-' + (12 - this.labellg);
40653                 }
40654                 if(this.labelmd > 0){
40655                     label.cls += ' col-md-' + this.labelmd;
40656                     container.cls += ' col-md-' + (12 - this.labelmd);
40657                 }
40658                 if(this.labelsm > 0){
40659                     label.cls += ' col-sm-' + this.labelsm;
40660                     container.cls += ' col-sm-' + (12 - this.labelsm);
40661                 }
40662                 if(this.labelxs > 0){
40663                     label.cls += ' col-xs-' + this.labelxs;
40664                     container.cls += ' col-xs-' + (12 - this.labelxs);
40665                 }
40666             }
40667         }
40668
40669         cfg.cn = [
40670             label,
40671             container,
40672             hiddenInput
40673         ];
40674         
40675         var settings = this;
40676
40677         ['xs','sm','md','lg'].map(function(size){
40678             if (settings[size]) {
40679                 cfg.cls += ' col-' + size + '-' + settings[size];
40680             }
40681         });
40682         
40683         return cfg;
40684     },
40685     
40686     initEvents : function()
40687     {
40688         this.indicator = this.indicatorEl();
40689         
40690         this.initCurrencyEvent();
40691         
40692         this.initNumberEvent();
40693     },
40694     
40695     initCurrencyEvent : function()
40696     {
40697         if (!this.store) {
40698             throw "can not find store for combo";
40699         }
40700         
40701         this.store = Roo.factory(this.store, Roo.data);
40702         this.store.parent = this;
40703         
40704         this.createList();
40705         
40706         this.triggerEl = this.el.select('.input-group-addon', true).first();
40707         
40708         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40709         
40710         var _this = this;
40711         
40712         (function(){
40713             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40714             _this.list.setWidth(lw);
40715         }).defer(100);
40716         
40717         this.list.on('mouseover', this.onViewOver, this);
40718         this.list.on('mousemove', this.onViewMove, this);
40719         this.list.on('scroll', this.onViewScroll, this);
40720         
40721         if(!this.tpl){
40722             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40723         }
40724         
40725         this.view = new Roo.View(this.list, this.tpl, {
40726             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40727         });
40728         
40729         this.view.on('click', this.onViewClick, this);
40730         
40731         this.store.on('beforeload', this.onBeforeLoad, this);
40732         this.store.on('load', this.onLoad, this);
40733         this.store.on('loadexception', this.onLoadException, this);
40734         
40735         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40736             "up" : function(e){
40737                 this.inKeyMode = true;
40738                 this.selectPrev();
40739             },
40740
40741             "down" : function(e){
40742                 if(!this.isExpanded()){
40743                     this.onTriggerClick();
40744                 }else{
40745                     this.inKeyMode = true;
40746                     this.selectNext();
40747                 }
40748             },
40749
40750             "enter" : function(e){
40751                 this.collapse();
40752                 
40753                 if(this.fireEvent("specialkey", this, e)){
40754                     this.onViewClick(false);
40755                 }
40756                 
40757                 return true;
40758             },
40759
40760             "esc" : function(e){
40761                 this.collapse();
40762             },
40763
40764             "tab" : function(e){
40765                 this.collapse();
40766                 
40767                 if(this.fireEvent("specialkey", this, e)){
40768                     this.onViewClick(false);
40769                 }
40770                 
40771                 return true;
40772             },
40773
40774             scope : this,
40775
40776             doRelay : function(foo, bar, hname){
40777                 if(hname == 'down' || this.scope.isExpanded()){
40778                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40779                 }
40780                 return true;
40781             },
40782
40783             forceKeyDown: true
40784         });
40785         
40786         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40787         
40788     },
40789     
40790     initNumberEvent : function(e)
40791     {
40792         this.inputEl().on("keydown" , this.fireKey,  this);
40793         this.inputEl().on("focus", this.onFocus,  this);
40794         this.inputEl().on("blur", this.onBlur,  this);
40795         
40796         this.inputEl().relayEvent('keyup', this);
40797         
40798         if(this.indicator){
40799             this.indicator.addClass('invisible');
40800         }
40801  
40802         this.originalValue = this.getValue();
40803         
40804         if(this.validationEvent == 'keyup'){
40805             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40806             this.inputEl().on('keyup', this.filterValidation, this);
40807         }
40808         else if(this.validationEvent !== false){
40809             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40810         }
40811         
40812         if(this.selectOnFocus){
40813             this.on("focus", this.preFocus, this);
40814             
40815         }
40816         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40817             this.inputEl().on("keypress", this.filterKeys, this);
40818         } else {
40819             this.inputEl().relayEvent('keypress', this);
40820         }
40821         
40822         var allowed = "0123456789";
40823         
40824         if(this.allowDecimals){
40825             allowed += this.decimalSeparator;
40826         }
40827         
40828         if(this.allowNegative){
40829             allowed += "-";
40830         }
40831         
40832         if(this.thousandsDelimiter) {
40833             allowed += ",";
40834         }
40835         
40836         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40837         
40838         var keyPress = function(e){
40839             
40840             var k = e.getKey();
40841             
40842             var c = e.getCharCode();
40843             
40844             if(
40845                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40846                     allowed.indexOf(String.fromCharCode(c)) === -1
40847             ){
40848                 e.stopEvent();
40849                 return;
40850             }
40851             
40852             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40853                 return;
40854             }
40855             
40856             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40857                 e.stopEvent();
40858             }
40859         };
40860         
40861         this.inputEl().on("keypress", keyPress, this);
40862         
40863     },
40864     
40865     onTriggerClick : function(e)
40866     {   
40867         if(this.disabled){
40868             return;
40869         }
40870         
40871         this.page = 0;
40872         this.loadNext = false;
40873         
40874         if(this.isExpanded()){
40875             this.collapse();
40876             return;
40877         }
40878         
40879         this.hasFocus = true;
40880         
40881         if(this.triggerAction == 'all') {
40882             this.doQuery(this.allQuery, true);
40883             return;
40884         }
40885         
40886         this.doQuery(this.getRawValue());
40887     },
40888     
40889     getCurrency : function()
40890     {   
40891         var v = this.currencyEl().getValue();
40892         
40893         return v;
40894     },
40895     
40896     restrictHeight : function()
40897     {
40898         this.list.alignTo(this.currencyEl(), this.listAlign);
40899         this.list.alignTo(this.currencyEl(), this.listAlign);
40900     },
40901     
40902     onViewClick : function(view, doFocus, el, e)
40903     {
40904         var index = this.view.getSelectedIndexes()[0];
40905         
40906         var r = this.store.getAt(index);
40907         
40908         if(r){
40909             this.onSelect(r, index);
40910         }
40911     },
40912     
40913     onSelect : function(record, index){
40914         
40915         if(this.fireEvent('beforeselect', this, record, index) !== false){
40916         
40917             this.setFromCurrencyData(index > -1 ? record.data : false);
40918             
40919             this.collapse();
40920             
40921             this.fireEvent('select', this, record, index);
40922         }
40923     },
40924     
40925     setFromCurrencyData : function(o)
40926     {
40927         var currency = '';
40928         
40929         this.lastCurrency = o;
40930         
40931         if (this.currencyField) {
40932             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40933         } else {
40934             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40935         }
40936         
40937         this.lastSelectionText = currency;
40938         
40939         //setting default currency
40940         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40941             this.setCurrency(this.defaultCurrency);
40942             return;
40943         }
40944         
40945         this.setCurrency(currency);
40946     },
40947     
40948     setFromData : function(o)
40949     {
40950         var c = {};
40951         
40952         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40953         
40954         this.setFromCurrencyData(c);
40955         
40956         var value = '';
40957         
40958         if (this.name) {
40959             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40960         } else {
40961             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40962         }
40963         
40964         this.setValue(value);
40965         
40966     },
40967     
40968     setCurrency : function(v)
40969     {   
40970         this.currencyValue = v;
40971         
40972         if(this.rendered){
40973             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40974             this.validate();
40975         }
40976     },
40977     
40978     setValue : function(v)
40979     {
40980         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40981         
40982         this.value = v;
40983         
40984         if(this.rendered){
40985             
40986             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40987             
40988             this.inputEl().dom.value = (v == '') ? '' :
40989                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40990             
40991             if(!this.allowZero && v === '0') {
40992                 this.hiddenEl().dom.value = '';
40993                 this.inputEl().dom.value = '';
40994             }
40995             
40996             this.validate();
40997         }
40998     },
40999     
41000     getRawValue : function()
41001     {
41002         var v = this.inputEl().getValue();
41003         
41004         return v;
41005     },
41006     
41007     getValue : function()
41008     {
41009         return this.fixPrecision(this.parseValue(this.getRawValue()));
41010     },
41011     
41012     parseValue : function(value)
41013     {
41014         if(this.thousandsDelimiter) {
41015             value += "";
41016             r = new RegExp(",", "g");
41017             value = value.replace(r, "");
41018         }
41019         
41020         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41021         return isNaN(value) ? '' : value;
41022         
41023     },
41024     
41025     fixPrecision : function(value)
41026     {
41027         if(this.thousandsDelimiter) {
41028             value += "";
41029             r = new RegExp(",", "g");
41030             value = value.replace(r, "");
41031         }
41032         
41033         var nan = isNaN(value);
41034         
41035         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41036             return nan ? '' : value;
41037         }
41038         return parseFloat(value).toFixed(this.decimalPrecision);
41039     },
41040     
41041     decimalPrecisionFcn : function(v)
41042     {
41043         return Math.floor(v);
41044     },
41045     
41046     validateValue : function(value)
41047     {
41048         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41049             return false;
41050         }
41051         
41052         var num = this.parseValue(value);
41053         
41054         if(isNaN(num)){
41055             this.markInvalid(String.format(this.nanText, value));
41056             return false;
41057         }
41058         
41059         if(num < this.minValue){
41060             this.markInvalid(String.format(this.minText, this.minValue));
41061             return false;
41062         }
41063         
41064         if(num > this.maxValue){
41065             this.markInvalid(String.format(this.maxText, this.maxValue));
41066             return false;
41067         }
41068         
41069         return true;
41070     },
41071     
41072     validate : function()
41073     {
41074         if(this.disabled || this.allowBlank){
41075             this.markValid();
41076             return true;
41077         }
41078         
41079         var currency = this.getCurrency();
41080         
41081         if(this.validateValue(this.getRawValue()) && currency.length){
41082             this.markValid();
41083             return true;
41084         }
41085         
41086         this.markInvalid();
41087         return false;
41088     },
41089     
41090     getName: function()
41091     {
41092         return this.name;
41093     },
41094     
41095     beforeBlur : function()
41096     {
41097         if(!this.castInt){
41098             return;
41099         }
41100         
41101         var v = this.parseValue(this.getRawValue());
41102         
41103         if(v || v == 0){
41104             this.setValue(v);
41105         }
41106     },
41107     
41108     onBlur : function()
41109     {
41110         this.beforeBlur();
41111         
41112         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41113             //this.el.removeClass(this.focusClass);
41114         }
41115         
41116         this.hasFocus = false;
41117         
41118         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41119             this.validate();
41120         }
41121         
41122         var v = this.getValue();
41123         
41124         if(String(v) !== String(this.startValue)){
41125             this.fireEvent('change', this, v, this.startValue);
41126         }
41127         
41128         this.fireEvent("blur", this);
41129     },
41130     
41131     inputEl : function()
41132     {
41133         return this.el.select('.roo-money-amount-input', true).first();
41134     },
41135     
41136     currencyEl : function()
41137     {
41138         return this.el.select('.roo-money-currency-input', true).first();
41139     },
41140     
41141     hiddenEl : function()
41142     {
41143         return this.el.select('input.hidden-number-input',true).first();
41144     }
41145     
41146 });