Roo/bootstrap/PagingToolbar.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
585  * @cfg {String} size ( lg | sm | xs)
586  * @cfg {String} tag ( a | input | submit)
587  * @cfg {String} href empty or href
588  * @cfg {Boolean} disabled default false;
589  * @cfg {Boolean} isClose default false;
590  * @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)
591  * @cfg {String} badge text for badge
592  * @cfg {String} theme (default|glow)  
593  * @cfg {Boolean} inverse dark themed version
594  * @cfg {Boolean} toggle is it a slidy toggle button
595  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
596  * @cfg {String} ontext text for on slidy toggle state
597  * @cfg {String} offtext text for off slidy toggle state
598  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
599  * @cfg {Boolean} removeClass remove the standard class..
600  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
601  * 
602  * @constructor
603  * Create a new button
604  * @param {Object} config The config object
605  */
606
607
608 Roo.bootstrap.Button = function(config){
609     Roo.bootstrap.Button.superclass.constructor.call(this, config);
610     this.weightClass = ["btn-default", 
611                        "btn-primary", 
612                        "btn-success", 
613                        "btn-info", 
614                        "btn-warning",
615                        "btn-danger",
616                        "btn-link"
617                       ],  
618     this.addEvents({
619         // raw events
620         /**
621          * @event click
622          * When a butotn is pressed
623          * @param {Roo.bootstrap.Button} btn
624          * @param {Roo.EventObject} e
625          */
626         "click" : true,
627          /**
628          * @event toggle
629          * After the button has been toggles
630          * @param {Roo.bootstrap.Button} btn
631          * @param {Roo.EventObject} e
632          * @param {boolean} pressed (also available as button.pressed)
633          */
634         "toggle" : true
635     });
636 };
637
638 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
639     html: false,
640     active: false,
641     weight: '',
642     size: '',
643     tag: 'button',
644     href: '',
645     disabled: false,
646     isClose: false,
647     glyphicon: '',
648     badge: '',
649     theme: 'default',
650     inverse: false,
651     
652     toggle: false,
653     ontext: 'ON',
654     offtext: 'OFF',
655     defaulton: true,
656     preventDefault: true,
657     removeClass: false,
658     name: false,
659     target: false,
660      
661     pressed : null,
662      
663     
664     getAutoCreate : function(){
665         
666         var cfg = {
667             tag : 'button',
668             cls : 'roo-button',
669             html: ''
670         };
671         
672         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
673             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
674             this.tag = 'button';
675         } else {
676             cfg.tag = this.tag;
677         }
678         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
679         
680         if (this.toggle == true) {
681             cfg={
682                 tag: 'div',
683                 cls: 'slider-frame roo-button',
684                 cn: [
685                     {
686                         tag: 'span',
687                         'data-on-text':'ON',
688                         'data-off-text':'OFF',
689                         cls: 'slider-button',
690                         html: this.offtext
691                     }
692                 ]
693             };
694             
695             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696                 cfg.cls += ' '+this.weight;
697             }
698             
699             return cfg;
700         }
701         
702         if (this.isClose) {
703             cfg.cls += ' close';
704             
705             cfg["aria-hidden"] = true;
706             
707             cfg.html = "&times;";
708             
709             return cfg;
710         }
711         
712          
713         if (this.theme==='default') {
714             cfg.cls = 'btn roo-button';
715             
716             //if (this.parentType != 'Navbar') {
717             this.weight = this.weight.length ?  this.weight : 'default';
718             //}
719             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
720                 
721                 cfg.cls += ' btn-' + this.weight;
722             }
723         } else if (this.theme==='glow') {
724             
725             cfg.tag = 'a';
726             cfg.cls = 'btn-glow roo-button';
727             
728             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
729                 
730                 cfg.cls += ' ' + this.weight;
731             }
732         }
733    
734         
735         if (this.inverse) {
736             this.cls += ' inverse';
737         }
738         
739         
740         if (this.active || this.pressed === true) {
741             cfg.cls += ' active';
742         }
743         
744         if (this.disabled) {
745             cfg.disabled = 'disabled';
746         }
747         
748         if (this.items) {
749             Roo.log('changing to ul' );
750             cfg.tag = 'ul';
751             this.glyphicon = 'caret';
752         }
753         
754         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
755          
756         //gsRoo.log(this.parentType);
757         if (this.parentType === 'Navbar' && !this.parent().bar) {
758             Roo.log('changing to li?');
759             
760             cfg.tag = 'li';
761             
762             cfg.cls = '';
763             cfg.cn =  [{
764                 tag : 'a',
765                 cls : 'roo-button',
766                 html : this.html,
767                 href : this.href || '#'
768             }];
769             if (this.menu) {
770                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
771                 cfg.cls += ' dropdown';
772             }   
773             
774             delete cfg.html;
775             
776         }
777         
778        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
779         
780         if (this.glyphicon) {
781             cfg.html = ' ' + cfg.html;
782             
783             cfg.cn = [
784                 {
785                     tag: 'span',
786                     cls: 'glyphicon glyphicon-' + this.glyphicon
787                 }
788             ];
789         }
790         
791         if (this.badge) {
792             cfg.html += ' ';
793             
794             cfg.tag = 'a';
795             
796 //            cfg.cls='btn roo-button';
797             
798             cfg.href=this.href;
799             
800             var value = cfg.html;
801             
802             if(this.glyphicon){
803                 value = {
804                             tag: 'span',
805                             cls: 'glyphicon glyphicon-' + this.glyphicon,
806                             html: this.html
807                         };
808                 
809             }
810             
811             cfg.cn = [
812                 value,
813                 {
814                     tag: 'span',
815                     cls: 'badge',
816                     html: this.badge
817                 }
818             ];
819             
820             cfg.html='';
821         }
822         
823         if (this.menu) {
824             cfg.cls += ' dropdown';
825             cfg.html = typeof(cfg.html) != 'undefined' ?
826                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
827         }
828         
829         if (cfg.tag !== 'a' && this.href !== '') {
830             throw "Tag must be a to set href.";
831         } else if (this.href.length > 0) {
832             cfg.href = this.href;
833         }
834         
835         if(this.removeClass){
836             cfg.cls = '';
837         }
838         
839         if(this.target){
840             cfg.target = this.target;
841         }
842         
843         return cfg;
844     },
845     initEvents: function() {
846        // Roo.log('init events?');
847 //        Roo.log(this.el.dom);
848         // add the menu...
849         
850         if (typeof (this.menu) != 'undefined') {
851             this.menu.parentType = this.xtype;
852             this.menu.triggerEl = this.el;
853             this.addxtype(Roo.apply({}, this.menu));
854         }
855
856
857        if (this.el.hasClass('roo-button')) {
858             this.el.on('click', this.onClick, this);
859        } else {
860             this.el.select('.roo-button').on('click', this.onClick, this);
861        }
862        
863        if(this.removeClass){
864            this.el.on('click', this.onClick, this);
865        }
866        
867        this.el.enableDisplayMode();
868         
869     },
870     onClick : function(e)
871     {
872         if (this.disabled) {
873             return;
874         }
875         
876         Roo.log('button on click ');
877         if(this.preventDefault){
878             e.preventDefault();
879         }
880         
881         if (this.pressed === true || this.pressed === false) {
882             this.toggleActive(e);
883         }
884         
885         
886         this.fireEvent('click', this, e);
887     },
888     
889     /**
890      * Enables this button
891      */
892     enable : function()
893     {
894         this.disabled = false;
895         this.el.removeClass('disabled');
896     },
897     
898     /**
899      * Disable this button
900      */
901     disable : function()
902     {
903         this.disabled = true;
904         this.el.addClass('disabled');
905     },
906      /**
907      * sets the active state on/off, 
908      * @param {Boolean} state (optional) Force a particular state
909      */
910     setActive : function(v) {
911         
912         this.el[v ? 'addClass' : 'removeClass']('active');
913         this.pressed = v;
914     },
915      /**
916      * toggles the current active state 
917      */
918     toggleActive : function(e)
919     {
920         this.setActive(!this.pressed);
921         this.fireEvent('toggle', this, e, !this.pressed);
922     },
923      /**
924      * get the current active state
925      * @return {boolean} true if it's active
926      */
927     isActive : function()
928     {
929         return this.el.hasClass('active');
930     },
931     /**
932      * set the text of the first selected button
933      */
934     setText : function(str)
935     {
936         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
937     },
938     /**
939      * get the text of the first selected button
940      */
941     getText : function()
942     {
943         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
944     },
945     
946     setWeight : function(str)
947     {
948         this.el.removeClass(this.weightClass);
949         this.el.addClass('btn-' + str);        
950     }
951     
952     
953 });
954
955  /*
956  * - LGPL
957  *
958  * column
959  * 
960  */
961
962 /**
963  * @class Roo.bootstrap.Column
964  * @extends Roo.bootstrap.Component
965  * Bootstrap Column class
966  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
967  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
968  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
969  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
970  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
971  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
972  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
973  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
974  *
975  * 
976  * @cfg {Boolean} hidden (true|false) hide the element
977  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
978  * @cfg {String} fa (ban|check|...) font awesome icon
979  * @cfg {Number} fasize (1|2|....) font awsome size
980
981  * @cfg {String} icon (info-sign|check|...) glyphicon name
982
983  * @cfg {String} html content of column.
984  * 
985  * @constructor
986  * Create a new Column
987  * @param {Object} config The config object
988  */
989
990 Roo.bootstrap.Column = function(config){
991     Roo.bootstrap.Column.superclass.constructor.call(this, config);
992 };
993
994 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
995     
996     xs: false,
997     sm: false,
998     md: false,
999     lg: false,
1000     xsoff: false,
1001     smoff: false,
1002     mdoff: false,
1003     lgoff: false,
1004     html: '',
1005     offset: 0,
1006     alert: false,
1007     fa: false,
1008     icon : false,
1009     hidden : false,
1010     fasize : 1,
1011     
1012     getAutoCreate : function(){
1013         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1014         
1015         cfg = {
1016             tag: 'div',
1017             cls: 'column'
1018         };
1019         
1020         var settings=this;
1021         ['xs','sm','md','lg'].map(function(size){
1022             //Roo.log( size + ':' + settings[size]);
1023             
1024             if (settings[size+'off'] !== false) {
1025                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1026             }
1027             
1028             if (settings[size] === false) {
1029                 return;
1030             }
1031             
1032             if (!settings[size]) { // 0 = hidden
1033                 cfg.cls += ' hidden-' + size;
1034                 return;
1035             }
1036             cfg.cls += ' col-' + size + '-' + settings[size];
1037             
1038         });
1039         
1040         if (this.hidden) {
1041             cfg.cls += ' hidden';
1042         }
1043         
1044         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1045             cfg.cls +=' alert alert-' + this.alert;
1046         }
1047         
1048         
1049         if (this.html.length) {
1050             cfg.html = this.html;
1051         }
1052         if (this.fa) {
1053             var fasize = '';
1054             if (this.fasize > 1) {
1055                 fasize = ' fa-' + this.fasize + 'x';
1056             }
1057             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1058             
1059             
1060         }
1061         if (this.icon) {
1062             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1063         }
1064         
1065         return cfg;
1066     }
1067    
1068 });
1069
1070  
1071
1072  /*
1073  * - LGPL
1074  *
1075  * page container.
1076  * 
1077  */
1078
1079
1080 /**
1081  * @class Roo.bootstrap.Container
1082  * @extends Roo.bootstrap.Component
1083  * Bootstrap Container class
1084  * @cfg {Boolean} jumbotron is it a jumbotron element
1085  * @cfg {String} html content of element
1086  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1087  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1088  * @cfg {String} header content of header (for panel)
1089  * @cfg {String} footer content of footer (for panel)
1090  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1091  * @cfg {String} tag (header|aside|section) type of HTML tag.
1092  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1093  * @cfg {String} fa font awesome icon
1094  * @cfg {String} icon (info-sign|check|...) glyphicon name
1095  * @cfg {Boolean} hidden (true|false) hide the element
1096  * @cfg {Boolean} expandable (true|false) default false
1097  * @cfg {Boolean} expanded (true|false) default true
1098  * @cfg {String} rheader contet on the right of header
1099  * @cfg {Boolean} clickable (true|false) default false
1100
1101  *     
1102  * @constructor
1103  * Create a new Container
1104  * @param {Object} config The config object
1105  */
1106
1107 Roo.bootstrap.Container = function(config){
1108     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1109     
1110     this.addEvents({
1111         // raw events
1112          /**
1113          * @event expand
1114          * After the panel has been expand
1115          * 
1116          * @param {Roo.bootstrap.Container} this
1117          */
1118         "expand" : true,
1119         /**
1120          * @event collapse
1121          * After the panel has been collapsed
1122          * 
1123          * @param {Roo.bootstrap.Container} this
1124          */
1125         "collapse" : true,
1126         /**
1127          * @event click
1128          * When a element is chick
1129          * @param {Roo.bootstrap.Container} this
1130          * @param {Roo.EventObject} e
1131          */
1132         "click" : true
1133     });
1134 };
1135
1136 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1137     
1138     jumbotron : false,
1139     well: '',
1140     panel : '',
1141     header: '',
1142     footer : '',
1143     sticky: '',
1144     tag : false,
1145     alert : false,
1146     fa: false,
1147     icon : false,
1148     expandable : false,
1149     rheader : '',
1150     expanded : true,
1151     clickable: false,
1152   
1153      
1154     getChildContainer : function() {
1155         
1156         if(!this.el){
1157             return false;
1158         }
1159         
1160         if (this.panel.length) {
1161             return this.el.select('.panel-body',true).first();
1162         }
1163         
1164         return this.el;
1165     },
1166     
1167     
1168     getAutoCreate : function(){
1169         
1170         var cfg = {
1171             tag : this.tag || 'div',
1172             html : '',
1173             cls : ''
1174         };
1175         if (this.jumbotron) {
1176             cfg.cls = 'jumbotron';
1177         }
1178         
1179         
1180         
1181         // - this is applied by the parent..
1182         //if (this.cls) {
1183         //    cfg.cls = this.cls + '';
1184         //}
1185         
1186         if (this.sticky.length) {
1187             
1188             var bd = Roo.get(document.body);
1189             if (!bd.hasClass('bootstrap-sticky')) {
1190                 bd.addClass('bootstrap-sticky');
1191                 Roo.select('html',true).setStyle('height', '100%');
1192             }
1193              
1194             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1195         }
1196         
1197         
1198         if (this.well.length) {
1199             switch (this.well) {
1200                 case 'lg':
1201                 case 'sm':
1202                     cfg.cls +=' well well-' +this.well;
1203                     break;
1204                 default:
1205                     cfg.cls +=' well';
1206                     break;
1207             }
1208         }
1209         
1210         if (this.hidden) {
1211             cfg.cls += ' hidden';
1212         }
1213         
1214         
1215         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1216             cfg.cls +=' alert alert-' + this.alert;
1217         }
1218         
1219         var body = cfg;
1220         
1221         if (this.panel.length) {
1222             cfg.cls += ' panel panel-' + this.panel;
1223             cfg.cn = [];
1224             if (this.header.length) {
1225                 
1226                 var h = [];
1227                 
1228                 if(this.expandable){
1229                     
1230                     cfg.cls = cfg.cls + ' expandable';
1231                     
1232                     h.push({
1233                         tag: 'i',
1234                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1235                     });
1236                     
1237                 }
1238                 
1239                 h.push(
1240                     {
1241                         tag: 'span',
1242                         cls : 'panel-title',
1243                         html : (this.expandable ? '&nbsp;' : '') + this.header
1244                     },
1245                     {
1246                         tag: 'span',
1247                         cls: 'panel-header-right',
1248                         html: this.rheader
1249                     }
1250                 );
1251                 
1252                 cfg.cn.push({
1253                     cls : 'panel-heading',
1254                     style : this.expandable ? 'cursor: pointer' : '',
1255                     cn : h
1256                 });
1257                 
1258             }
1259             
1260             body = false;
1261             cfg.cn.push({
1262                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1263                 html : this.html
1264             });
1265             
1266             
1267             if (this.footer.length) {
1268                 cfg.cn.push({
1269                     cls : 'panel-footer',
1270                     html : this.footer
1271                     
1272                 });
1273             }
1274             
1275         }
1276         
1277         if (body) {
1278             body.html = this.html || cfg.html;
1279             // prefix with the icons..
1280             if (this.fa) {
1281                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1282             }
1283             if (this.icon) {
1284                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1285             }
1286             
1287             
1288         }
1289         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1290             cfg.cls =  'container';
1291         }
1292         
1293         return cfg;
1294     },
1295     
1296     initEvents: function() 
1297     {
1298         if(this.expandable){
1299             var headerEl = this.headerEl();
1300         
1301             if(headerEl){
1302                 headerEl.on('click', this.onToggleClick, this);
1303             }
1304         }
1305         
1306         if(this.clickable){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310     },
1311     
1312     onToggleClick : function()
1313     {
1314         var headerEl = this.headerEl();
1315         
1316         if(!headerEl){
1317             return;
1318         }
1319         
1320         if(this.expanded){
1321             this.collapse();
1322             return;
1323         }
1324         
1325         this.expand();
1326     },
1327     
1328     expand : function()
1329     {
1330         if(this.fireEvent('expand', this)) {
1331             
1332             this.expanded = true;
1333             
1334             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1335             
1336             this.el.select('.panel-body',true).first().removeClass('hide');
1337             
1338             var toggleEl = this.toggleEl();
1339
1340             if(!toggleEl){
1341                 return;
1342             }
1343
1344             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1345         }
1346         
1347     },
1348     
1349     collapse : function()
1350     {
1351         if(this.fireEvent('collapse', this)) {
1352             
1353             this.expanded = false;
1354             
1355             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1356             this.el.select('.panel-body',true).first().addClass('hide');
1357         
1358             var toggleEl = this.toggleEl();
1359
1360             if(!toggleEl){
1361                 return;
1362             }
1363
1364             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1365         }
1366     },
1367     
1368     toggleEl : function()
1369     {
1370         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1371             return;
1372         }
1373         
1374         return this.el.select('.panel-heading .fa',true).first();
1375     },
1376     
1377     headerEl : function()
1378     {
1379         if(!this.el || !this.panel.length || !this.header.length){
1380             return;
1381         }
1382         
1383         return this.el.select('.panel-heading',true).first()
1384     },
1385     
1386     bodyEl : function()
1387     {
1388         if(!this.el || !this.panel.length){
1389             return;
1390         }
1391         
1392         return this.el.select('.panel-body',true).first()
1393     },
1394     
1395     titleEl : function()
1396     {
1397         if(!this.el || !this.panel.length || !this.header.length){
1398             return;
1399         }
1400         
1401         return this.el.select('.panel-title',true).first();
1402     },
1403     
1404     setTitle : function(v)
1405     {
1406         var titleEl = this.titleEl();
1407         
1408         if(!titleEl){
1409             return;
1410         }
1411         
1412         titleEl.dom.innerHTML = v;
1413     },
1414     
1415     getTitle : function()
1416     {
1417         
1418         var titleEl = this.titleEl();
1419         
1420         if(!titleEl){
1421             return '';
1422         }
1423         
1424         return titleEl.dom.innerHTML;
1425     },
1426     
1427     setRightTitle : function(v)
1428     {
1429         var t = this.el.select('.panel-header-right',true).first();
1430         
1431         if(!t){
1432             return;
1433         }
1434         
1435         t.dom.innerHTML = v;
1436     },
1437     
1438     onClick : function(e)
1439     {
1440         e.preventDefault();
1441         
1442         this.fireEvent('click', this, e);
1443     }
1444 });
1445
1446  /*
1447  * - LGPL
1448  *
1449  * image
1450  * 
1451  */
1452
1453
1454 /**
1455  * @class Roo.bootstrap.Img
1456  * @extends Roo.bootstrap.Component
1457  * Bootstrap Img class
1458  * @cfg {Boolean} imgResponsive false | true
1459  * @cfg {String} border rounded | circle | thumbnail
1460  * @cfg {String} src image source
1461  * @cfg {String} alt image alternative text
1462  * @cfg {String} href a tag href
1463  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1464  * @cfg {String} xsUrl xs image source
1465  * @cfg {String} smUrl sm image source
1466  * @cfg {String} mdUrl md image source
1467  * @cfg {String} lgUrl lg image source
1468  * 
1469  * @constructor
1470  * Create a new Input
1471  * @param {Object} config The config object
1472  */
1473
1474 Roo.bootstrap.Img = function(config){
1475     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1476     
1477     this.addEvents({
1478         // img events
1479         /**
1480          * @event click
1481          * The img click event for the img.
1482          * @param {Roo.EventObject} e
1483          */
1484         "click" : true
1485     });
1486 };
1487
1488 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1489     
1490     imgResponsive: true,
1491     border: '',
1492     src: 'about:blank',
1493     href: false,
1494     target: false,
1495     xsUrl: '',
1496     smUrl: '',
1497     mdUrl: '',
1498     lgUrl: '',
1499
1500     getAutoCreate : function()
1501     {   
1502         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1503             return this.createSingleImg();
1504         }
1505         
1506         var cfg = {
1507             tag: 'div',
1508             cls: 'roo-image-responsive-group',
1509             cn: []
1510         };
1511         var _this = this;
1512         
1513         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1514             
1515             if(!_this[size + 'Url']){
1516                 return;
1517             }
1518             
1519             var img = {
1520                 tag: 'img',
1521                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1522                 html: _this.html || cfg.html,
1523                 src: _this[size + 'Url']
1524             };
1525             
1526             img.cls += ' roo-image-responsive-' + size;
1527             
1528             var s = ['xs', 'sm', 'md', 'lg'];
1529             
1530             s.splice(s.indexOf(size), 1);
1531             
1532             Roo.each(s, function(ss){
1533                 img.cls += ' hidden-' + ss;
1534             });
1535             
1536             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1537                 cfg.cls += ' img-' + _this.border;
1538             }
1539             
1540             if(_this.alt){
1541                 cfg.alt = _this.alt;
1542             }
1543             
1544             if(_this.href){
1545                 var a = {
1546                     tag: 'a',
1547                     href: _this.href,
1548                     cn: [
1549                         img
1550                     ]
1551                 };
1552
1553                 if(this.target){
1554                     a.target = _this.target;
1555                 }
1556             }
1557             
1558             cfg.cn.push((_this.href) ? a : img);
1559             
1560         });
1561         
1562         return cfg;
1563     },
1564     
1565     createSingleImg : function()
1566     {
1567         var cfg = {
1568             tag: 'img',
1569             cls: (this.imgResponsive) ? 'img-responsive' : '',
1570             html : null,
1571             src : 'about:blank'  // just incase src get's set to undefined?!?
1572         };
1573         
1574         cfg.html = this.html || cfg.html;
1575         
1576         cfg.src = this.src || cfg.src;
1577         
1578         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1579             cfg.cls += ' img-' + this.border;
1580         }
1581         
1582         if(this.alt){
1583             cfg.alt = this.alt;
1584         }
1585         
1586         if(this.href){
1587             var a = {
1588                 tag: 'a',
1589                 href: this.href,
1590                 cn: [
1591                     cfg
1592                 ]
1593             };
1594             
1595             if(this.target){
1596                 a.target = this.target;
1597             }
1598             
1599         }
1600         
1601         return (this.href) ? a : cfg;
1602     },
1603     
1604     initEvents: function() 
1605     {
1606         if(!this.href){
1607             this.el.on('click', this.onClick, this);
1608         }
1609         
1610     },
1611     
1612     onClick : function(e)
1613     {
1614         Roo.log('img onclick');
1615         this.fireEvent('click', this, e);
1616     },
1617     /**
1618      * Sets the url of the image - used to update it
1619      * @param {String} url the url of the image
1620      */
1621     
1622     setSrc : function(url)
1623     {
1624         this.src =  url;
1625         
1626         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1627             this.el.dom.src =  url;
1628             return;
1629         }
1630         
1631         this.el.select('img', true).first().dom.src =  url;
1632     }
1633     
1634     
1635    
1636 });
1637
1638  /*
1639  * - LGPL
1640  *
1641  * image
1642  * 
1643  */
1644
1645
1646 /**
1647  * @class Roo.bootstrap.Link
1648  * @extends Roo.bootstrap.Component
1649  * Bootstrap Link Class
1650  * @cfg {String} alt image alternative text
1651  * @cfg {String} href a tag href
1652  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1653  * @cfg {String} html the content of the link.
1654  * @cfg {String} anchor name for the anchor link
1655  * @cfg {String} fa - favicon
1656
1657  * @cfg {Boolean} preventDefault (true | false) default false
1658
1659  * 
1660  * @constructor
1661  * Create a new Input
1662  * @param {Object} config The config object
1663  */
1664
1665 Roo.bootstrap.Link = function(config){
1666     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1667     
1668     this.addEvents({
1669         // img events
1670         /**
1671          * @event click
1672          * The img click event for the img.
1673          * @param {Roo.EventObject} e
1674          */
1675         "click" : true
1676     });
1677 };
1678
1679 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1680     
1681     href: false,
1682     target: false,
1683     preventDefault: false,
1684     anchor : false,
1685     alt : false,
1686     fa: false,
1687
1688
1689     getAutoCreate : function()
1690     {
1691         var html = this.html || '';
1692         
1693         if (this.fa !== false) {
1694             html = '<i class="fa fa-' + this.fa + '"></i>';
1695         }
1696         var cfg = {
1697             tag: 'a'
1698         };
1699         // anchor's do not require html/href...
1700         if (this.anchor === false) {
1701             cfg.html = html;
1702             cfg.href = this.href || '#';
1703         } else {
1704             cfg.name = this.anchor;
1705             if (this.html !== false || this.fa !== false) {
1706                 cfg.html = html;
1707             }
1708             if (this.href !== false) {
1709                 cfg.href = this.href;
1710             }
1711         }
1712         
1713         if(this.alt !== false){
1714             cfg.alt = this.alt;
1715         }
1716         
1717         
1718         if(this.target !== false) {
1719             cfg.target = this.target;
1720         }
1721         
1722         return cfg;
1723     },
1724     
1725     initEvents: function() {
1726         
1727         if(!this.href || this.preventDefault){
1728             this.el.on('click', this.onClick, this);
1729         }
1730     },
1731     
1732     onClick : function(e)
1733     {
1734         if(this.preventDefault){
1735             e.preventDefault();
1736         }
1737         //Roo.log('img onclick');
1738         this.fireEvent('click', this, e);
1739     }
1740    
1741 });
1742
1743  /*
1744  * - LGPL
1745  *
1746  * header
1747  * 
1748  */
1749
1750 /**
1751  * @class Roo.bootstrap.Header
1752  * @extends Roo.bootstrap.Component
1753  * Bootstrap Header class
1754  * @cfg {String} html content of header
1755  * @cfg {Number} level (1|2|3|4|5|6) default 1
1756  * 
1757  * @constructor
1758  * Create a new Header
1759  * @param {Object} config The config object
1760  */
1761
1762
1763 Roo.bootstrap.Header  = function(config){
1764     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1765 };
1766
1767 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1768     
1769     //href : false,
1770     html : false,
1771     level : 1,
1772     
1773     
1774     
1775     getAutoCreate : function(){
1776         
1777         
1778         
1779         var cfg = {
1780             tag: 'h' + (1 *this.level),
1781             html: this.html || ''
1782         } ;
1783         
1784         return cfg;
1785     }
1786    
1787 });
1788
1789  
1790
1791  /*
1792  * Based on:
1793  * Ext JS Library 1.1.1
1794  * Copyright(c) 2006-2007, Ext JS, LLC.
1795  *
1796  * Originally Released Under LGPL - original licence link has changed is not relivant.
1797  *
1798  * Fork - LGPL
1799  * <script type="text/javascript">
1800  */
1801  
1802 /**
1803  * @class Roo.bootstrap.MenuMgr
1804  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1805  * @singleton
1806  */
1807 Roo.bootstrap.MenuMgr = function(){
1808    var menus, active, groups = {}, attached = false, lastShow = new Date();
1809
1810    // private - called when first menu is created
1811    function init(){
1812        menus = {};
1813        active = new Roo.util.MixedCollection();
1814        Roo.get(document).addKeyListener(27, function(){
1815            if(active.length > 0){
1816                hideAll();
1817            }
1818        });
1819    }
1820
1821    // private
1822    function hideAll(){
1823        if(active && active.length > 0){
1824            var c = active.clone();
1825            c.each(function(m){
1826                m.hide();
1827            });
1828        }
1829    }
1830
1831    // private
1832    function onHide(m){
1833        active.remove(m);
1834        if(active.length < 1){
1835            Roo.get(document).un("mouseup", onMouseDown);
1836             
1837            attached = false;
1838        }
1839    }
1840
1841    // private
1842    function onShow(m){
1843        var last = active.last();
1844        lastShow = new Date();
1845        active.add(m);
1846        if(!attached){
1847           Roo.get(document).on("mouseup", onMouseDown);
1848            
1849            attached = true;
1850        }
1851        if(m.parentMenu){
1852           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1853           m.parentMenu.activeChild = m;
1854        }else if(last && last.isVisible()){
1855           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1856        }
1857    }
1858
1859    // private
1860    function onBeforeHide(m){
1861        if(m.activeChild){
1862            m.activeChild.hide();
1863        }
1864        if(m.autoHideTimer){
1865            clearTimeout(m.autoHideTimer);
1866            delete m.autoHideTimer;
1867        }
1868    }
1869
1870    // private
1871    function onBeforeShow(m){
1872        var pm = m.parentMenu;
1873        if(!pm && !m.allowOtherMenus){
1874            hideAll();
1875        }else if(pm && pm.activeChild && active != m){
1876            pm.activeChild.hide();
1877        }
1878    }
1879
1880    // private this should really trigger on mouseup..
1881    function onMouseDown(e){
1882         Roo.log("on Mouse Up");
1883         
1884         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1885             Roo.log("MenuManager hideAll");
1886             hideAll();
1887             e.stopEvent();
1888         }
1889         
1890         
1891    }
1892
1893    // private
1894    function onBeforeCheck(mi, state){
1895        if(state){
1896            var g = groups[mi.group];
1897            for(var i = 0, l = g.length; i < l; i++){
1898                if(g[i] != mi){
1899                    g[i].setChecked(false);
1900                }
1901            }
1902        }
1903    }
1904
1905    return {
1906
1907        /**
1908         * Hides all menus that are currently visible
1909         */
1910        hideAll : function(){
1911             hideAll();  
1912        },
1913
1914        // private
1915        register : function(menu){
1916            if(!menus){
1917                init();
1918            }
1919            menus[menu.id] = menu;
1920            menu.on("beforehide", onBeforeHide);
1921            menu.on("hide", onHide);
1922            menu.on("beforeshow", onBeforeShow);
1923            menu.on("show", onShow);
1924            var g = menu.group;
1925            if(g && menu.events["checkchange"]){
1926                if(!groups[g]){
1927                    groups[g] = [];
1928                }
1929                groups[g].push(menu);
1930                menu.on("checkchange", onCheck);
1931            }
1932        },
1933
1934         /**
1935          * Returns a {@link Roo.menu.Menu} object
1936          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1937          * be used to generate and return a new Menu instance.
1938          */
1939        get : function(menu){
1940            if(typeof menu == "string"){ // menu id
1941                return menus[menu];
1942            }else if(menu.events){  // menu instance
1943                return menu;
1944            }
1945            /*else if(typeof menu.length == 'number'){ // array of menu items?
1946                return new Roo.bootstrap.Menu({items:menu});
1947            }else{ // otherwise, must be a config
1948                return new Roo.bootstrap.Menu(menu);
1949            }
1950            */
1951            return false;
1952        },
1953
1954        // private
1955        unregister : function(menu){
1956            delete menus[menu.id];
1957            menu.un("beforehide", onBeforeHide);
1958            menu.un("hide", onHide);
1959            menu.un("beforeshow", onBeforeShow);
1960            menu.un("show", onShow);
1961            var g = menu.group;
1962            if(g && menu.events["checkchange"]){
1963                groups[g].remove(menu);
1964                menu.un("checkchange", onCheck);
1965            }
1966        },
1967
1968        // private
1969        registerCheckable : function(menuItem){
1970            var g = menuItem.group;
1971            if(g){
1972                if(!groups[g]){
1973                    groups[g] = [];
1974                }
1975                groups[g].push(menuItem);
1976                menuItem.on("beforecheckchange", onBeforeCheck);
1977            }
1978        },
1979
1980        // private
1981        unregisterCheckable : function(menuItem){
1982            var g = menuItem.group;
1983            if(g){
1984                groups[g].remove(menuItem);
1985                menuItem.un("beforecheckchange", onBeforeCheck);
1986            }
1987        }
1988    };
1989 }();/*
1990  * - LGPL
1991  *
1992  * menu
1993  * 
1994  */
1995
1996 /**
1997  * @class Roo.bootstrap.Menu
1998  * @extends Roo.bootstrap.Component
1999  * Bootstrap Menu class - container for MenuItems
2000  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2001  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2002  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2003  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2004  * 
2005  * @constructor
2006  * Create a new Menu
2007  * @param {Object} config The config object
2008  */
2009
2010
2011 Roo.bootstrap.Menu = function(config){
2012     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2013     if (this.registerMenu && this.type != 'treeview')  {
2014         Roo.bootstrap.MenuMgr.register(this);
2015     }
2016     
2017     
2018     this.addEvents({
2019         /**
2020          * @event beforeshow
2021          * Fires before this menu is displayed
2022          * @param {Roo.menu.Menu} this
2023          */
2024         beforeshow : true,
2025         /**
2026          * @event beforehide
2027          * Fires before this menu is hidden
2028          * @param {Roo.menu.Menu} this
2029          */
2030         beforehide : true,
2031         /**
2032          * @event show
2033          * Fires after this menu is displayed
2034          * @param {Roo.menu.Menu} this
2035          */
2036         show : true,
2037         /**
2038          * @event hide
2039          * Fires after this menu is hidden
2040          * @param {Roo.menu.Menu} this
2041          */
2042         hide : true,
2043         /**
2044          * @event click
2045          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2046          * @param {Roo.menu.Menu} this
2047          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2048          * @param {Roo.EventObject} e
2049          */
2050         click : true,
2051         /**
2052          * @event mouseover
2053          * Fires when the mouse is hovering over this menu
2054          * @param {Roo.menu.Menu} this
2055          * @param {Roo.EventObject} e
2056          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2057          */
2058         mouseover : true,
2059         /**
2060          * @event mouseout
2061          * Fires when the mouse exits this menu
2062          * @param {Roo.menu.Menu} this
2063          * @param {Roo.EventObject} e
2064          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2065          */
2066         mouseout : true,
2067         /**
2068          * @event itemclick
2069          * Fires when a menu item contained in this menu is clicked
2070          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2071          * @param {Roo.EventObject} e
2072          */
2073         itemclick: true
2074     });
2075     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2076 };
2077
2078 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2079     
2080    /// html : false,
2081     //align : '',
2082     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2083     type: false,
2084     /**
2085      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2086      */
2087     registerMenu : true,
2088     
2089     menuItems :false, // stores the menu items..
2090     
2091     hidden:true,
2092         
2093     parentMenu : false,
2094     
2095     stopEvent : true,
2096     
2097     isLink : false,
2098     
2099     getChildContainer : function() {
2100         return this.el;  
2101     },
2102     
2103     getAutoCreate : function(){
2104          
2105         //if (['right'].indexOf(this.align)!==-1) {
2106         //    cfg.cn[1].cls += ' pull-right'
2107         //}
2108         
2109         
2110         var cfg = {
2111             tag : 'ul',
2112             cls : 'dropdown-menu' ,
2113             style : 'z-index:1000'
2114             
2115         };
2116         
2117         if (this.type === 'submenu') {
2118             cfg.cls = 'submenu active';
2119         }
2120         if (this.type === 'treeview') {
2121             cfg.cls = 'treeview-menu';
2122         }
2123         
2124         return cfg;
2125     },
2126     initEvents : function() {
2127         
2128        // Roo.log("ADD event");
2129        // Roo.log(this.triggerEl.dom);
2130         
2131         this.triggerEl.on('click', this.onTriggerClick, this);
2132         
2133         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2134         
2135         
2136         if (this.triggerEl.hasClass('nav-item')) {
2137             // dropdown toggle on the 'a' in BS4?
2138             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2139         } else {
2140             this.triggerEl.addClass('dropdown-toggle');
2141         }
2142         if (Roo.isTouch) {
2143             this.el.on('touchstart'  , this.onTouch, this);
2144         }
2145         this.el.on('click' , this.onClick, this);
2146
2147         this.el.on("mouseover", this.onMouseOver, this);
2148         this.el.on("mouseout", this.onMouseOut, this);
2149         
2150     },
2151     
2152     findTargetItem : function(e)
2153     {
2154         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2155         if(!t){
2156             return false;
2157         }
2158         //Roo.log(t);         Roo.log(t.id);
2159         if(t && t.id){
2160             //Roo.log(this.menuitems);
2161             return this.menuitems.get(t.id);
2162             
2163             //return this.items.get(t.menuItemId);
2164         }
2165         
2166         return false;
2167     },
2168     
2169     onTouch : function(e) 
2170     {
2171         Roo.log("menu.onTouch");
2172         //e.stopEvent(); this make the user popdown broken
2173         this.onClick(e);
2174     },
2175     
2176     onClick : function(e)
2177     {
2178         Roo.log("menu.onClick");
2179         
2180         var t = this.findTargetItem(e);
2181         if(!t || t.isContainer){
2182             return;
2183         }
2184         Roo.log(e);
2185         /*
2186         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2187             if(t == this.activeItem && t.shouldDeactivate(e)){
2188                 this.activeItem.deactivate();
2189                 delete this.activeItem;
2190                 return;
2191             }
2192             if(t.canActivate){
2193                 this.setActiveItem(t, true);
2194             }
2195             return;
2196             
2197             
2198         }
2199         */
2200        
2201         Roo.log('pass click event');
2202         
2203         t.onClick(e);
2204         
2205         this.fireEvent("click", this, t, e);
2206         
2207         var _this = this;
2208         
2209         if(!t.href.length || t.href == '#'){
2210             (function() { _this.hide(); }).defer(100);
2211         }
2212         
2213     },
2214     
2215     onMouseOver : function(e){
2216         var t  = this.findTargetItem(e);
2217         //Roo.log(t);
2218         //if(t){
2219         //    if(t.canActivate && !t.disabled){
2220         //        this.setActiveItem(t, true);
2221         //    }
2222         //}
2223         
2224         this.fireEvent("mouseover", this, e, t);
2225     },
2226     isVisible : function(){
2227         return !this.hidden;
2228     },
2229      onMouseOut : function(e){
2230         var t  = this.findTargetItem(e);
2231         
2232         //if(t ){
2233         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2234         //        this.activeItem.deactivate();
2235         //        delete this.activeItem;
2236         //    }
2237         //}
2238         this.fireEvent("mouseout", this, e, t);
2239     },
2240     
2241     
2242     /**
2243      * Displays this menu relative to another element
2244      * @param {String/HTMLElement/Roo.Element} element The element to align to
2245      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2246      * the element (defaults to this.defaultAlign)
2247      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2248      */
2249     show : function(el, pos, parentMenu){
2250         this.parentMenu = parentMenu;
2251         if(!this.el){
2252             this.render();
2253         }
2254         this.fireEvent("beforeshow", this);
2255         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2256     },
2257      /**
2258      * Displays this menu at a specific xy position
2259      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2260      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2261      */
2262     showAt : function(xy, parentMenu, /* private: */_e){
2263         this.parentMenu = parentMenu;
2264         if(!this.el){
2265             this.render();
2266         }
2267         if(_e !== false){
2268             this.fireEvent("beforeshow", this);
2269             //xy = this.el.adjustForConstraints(xy);
2270         }
2271         
2272         //this.el.show();
2273         this.hideMenuItems();
2274         this.hidden = false;
2275         this.triggerEl.addClass('open');
2276         this.el.addClass('show');
2277         
2278         // reassign x when hitting right
2279         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2280             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2281         }
2282         
2283         // reassign y when hitting bottom
2284         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2285             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2286         }
2287         
2288         // but the list may align on trigger left or trigger top... should it be a properity?
2289         
2290         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2291             this.el.setXY(xy);
2292         }
2293         
2294         this.focus();
2295         this.fireEvent("show", this);
2296     },
2297     
2298     focus : function(){
2299         return;
2300         if(!this.hidden){
2301             this.doFocus.defer(50, this);
2302         }
2303     },
2304
2305     doFocus : function(){
2306         if(!this.hidden){
2307             this.focusEl.focus();
2308         }
2309     },
2310
2311     /**
2312      * Hides this menu and optionally all parent menus
2313      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2314      */
2315     hide : function(deep)
2316     {
2317         
2318         this.hideMenuItems();
2319         if(this.el && this.isVisible()){
2320             this.fireEvent("beforehide", this);
2321             if(this.activeItem){
2322                 this.activeItem.deactivate();
2323                 this.activeItem = null;
2324             }
2325             this.triggerEl.removeClass('open');;
2326             this.el.removeClass('show');
2327             this.hidden = true;
2328             this.fireEvent("hide", this);
2329         }
2330         if(deep === true && this.parentMenu){
2331             this.parentMenu.hide(true);
2332         }
2333     },
2334     
2335     onTriggerClick : function(e)
2336     {
2337         Roo.log('trigger click');
2338         
2339         var target = e.getTarget();
2340         
2341         Roo.log(target.nodeName.toLowerCase());
2342         
2343         if(target.nodeName.toLowerCase() === 'i'){
2344             e.preventDefault();
2345         }
2346         
2347     },
2348     
2349     onTriggerPress  : function(e)
2350     {
2351         Roo.log('trigger press');
2352         //Roo.log(e.getTarget());
2353        // Roo.log(this.triggerEl.dom);
2354        
2355         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2356         var pel = Roo.get(e.getTarget());
2357         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2358             Roo.log('is treeview or dropdown?');
2359             return;
2360         }
2361         
2362         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2363             return;
2364         }
2365         
2366         if (this.isVisible()) {
2367             Roo.log('hide');
2368             this.hide();
2369         } else {
2370             Roo.log('show');
2371             this.show(this.triggerEl, false, false);
2372         }
2373         
2374         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2375             e.stopEvent();
2376         }
2377         
2378     },
2379        
2380     
2381     hideMenuItems : function()
2382     {
2383         Roo.log("hide Menu Items");
2384         if (!this.el) { 
2385             return;
2386         }
2387         //$(backdrop).remove()
2388         this.el.select('.open',true).each(function(aa) {
2389             
2390             aa.removeClass('open');
2391           //var parent = getParent($(this))
2392           //var relatedTarget = { relatedTarget: this }
2393           
2394            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2395           //if (e.isDefaultPrevented()) return
2396            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2397         });
2398     },
2399     addxtypeChild : function (tree, cntr) {
2400         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2401           
2402         this.menuitems.add(comp);
2403         return comp;
2404
2405     },
2406     getEl : function()
2407     {
2408         Roo.log(this.el);
2409         return this.el;
2410     },
2411     
2412     clear : function()
2413     {
2414         this.getEl().dom.innerHTML = '';
2415         this.menuitems.clear();
2416     }
2417 });
2418
2419  
2420  /*
2421  * - LGPL
2422  *
2423  * menu item
2424  * 
2425  */
2426
2427
2428 /**
2429  * @class Roo.bootstrap.MenuItem
2430  * @extends Roo.bootstrap.Component
2431  * Bootstrap MenuItem class
2432  * @cfg {String} html the menu label
2433  * @cfg {String} href the link
2434  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2435  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2436  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2437  * @cfg {String} fa favicon to show on left of menu item.
2438  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2439  * 
2440  * 
2441  * @constructor
2442  * Create a new MenuItem
2443  * @param {Object} config The config object
2444  */
2445
2446
2447 Roo.bootstrap.MenuItem = function(config){
2448     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2449     this.addEvents({
2450         // raw events
2451         /**
2452          * @event click
2453          * The raw click event for the entire grid.
2454          * @param {Roo.bootstrap.MenuItem} this
2455          * @param {Roo.EventObject} e
2456          */
2457         "click" : true
2458     });
2459 };
2460
2461 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2462     
2463     href : false,
2464     html : false,
2465     preventDefault: false,
2466     isContainer : false,
2467     active : false,
2468     fa: false,
2469     
2470     getAutoCreate : function(){
2471         
2472         if(this.isContainer){
2473             return {
2474                 tag: 'li',
2475                 cls: 'dropdown-menu-item dropdown-item'
2476             };
2477         }
2478         var ctag = {
2479             tag: 'span',
2480             html: 'Link'
2481         };
2482         
2483         var anc = {
2484             tag : 'a',
2485             href : '#',
2486             cn : [  ]
2487         };
2488         
2489         if (this.fa !== false) {
2490             anc.cn.push({
2491                 tag : 'i',
2492                 cls : 'fa fa-' + this.fa
2493             });
2494         }
2495         
2496         anc.cn.push(ctag);
2497         
2498         
2499         var cfg= {
2500             tag: 'li',
2501             cls: 'dropdown-menu-item dropdown-item',
2502             cn: [ anc ]
2503         };
2504         if (this.parent().type == 'treeview') {
2505             cfg.cls = 'treeview-menu';
2506         }
2507         if (this.active) {
2508             cfg.cls += ' active';
2509         }
2510         
2511         
2512         
2513         anc.href = this.href || cfg.cn[0].href ;
2514         ctag.html = this.html || cfg.cn[0].html ;
2515         return cfg;
2516     },
2517     
2518     initEvents: function()
2519     {
2520         if (this.parent().type == 'treeview') {
2521             this.el.select('a').on('click', this.onClick, this);
2522         }
2523         
2524         if (this.menu) {
2525             this.menu.parentType = this.xtype;
2526             this.menu.triggerEl = this.el;
2527             this.menu = this.addxtype(Roo.apply({}, this.menu));
2528         }
2529         
2530     },
2531     onClick : function(e)
2532     {
2533         Roo.log('item on click ');
2534         
2535         if(this.preventDefault){
2536             e.preventDefault();
2537         }
2538         //this.parent().hideMenuItems();
2539         
2540         this.fireEvent('click', this, e);
2541     },
2542     getEl : function()
2543     {
2544         return this.el;
2545     } 
2546 });
2547
2548  
2549
2550  /*
2551  * - LGPL
2552  *
2553  * menu separator
2554  * 
2555  */
2556
2557
2558 /**
2559  * @class Roo.bootstrap.MenuSeparator
2560  * @extends Roo.bootstrap.Component
2561  * Bootstrap MenuSeparator class
2562  * 
2563  * @constructor
2564  * Create a new MenuItem
2565  * @param {Object} config The config object
2566  */
2567
2568
2569 Roo.bootstrap.MenuSeparator = function(config){
2570     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2571 };
2572
2573 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2574     
2575     getAutoCreate : function(){
2576         var cfg = {
2577             cls: 'divider',
2578             tag : 'li'
2579         };
2580         
2581         return cfg;
2582     }
2583    
2584 });
2585
2586  
2587
2588  
2589 /*
2590 * Licence: LGPL
2591 */
2592
2593 /**
2594  * @class Roo.bootstrap.Modal
2595  * @extends Roo.bootstrap.Component
2596  * Bootstrap Modal class
2597  * @cfg {String} title Title of dialog
2598  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2599  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2600  * @cfg {Boolean} specificTitle default false
2601  * @cfg {Array} buttons Array of buttons or standard button set..
2602  * @cfg {String} buttonPosition (left|right|center) default right
2603  * @cfg {Boolean} animate default true
2604  * @cfg {Boolean} allow_close default true
2605  * @cfg {Boolean} fitwindow default false
2606  * @cfg {String} size (sm|lg) default empty
2607  * @cfg {Number} max_width set the max width of modal
2608  *
2609  *
2610  * @constructor
2611  * Create a new Modal Dialog
2612  * @param {Object} config The config object
2613  */
2614
2615 Roo.bootstrap.Modal = function(config){
2616     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2617     this.addEvents({
2618         // raw events
2619         /**
2620          * @event btnclick
2621          * The raw btnclick event for the button
2622          * @param {Roo.EventObject} e
2623          */
2624         "btnclick" : true,
2625         /**
2626          * @event resize
2627          * Fire when dialog resize
2628          * @param {Roo.bootstrap.Modal} this
2629          * @param {Roo.EventObject} e
2630          */
2631         "resize" : true
2632     });
2633     this.buttons = this.buttons || [];
2634
2635     if (this.tmpl) {
2636         this.tmpl = Roo.factory(this.tmpl);
2637     }
2638
2639 };
2640
2641 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2642
2643     title : 'test dialog',
2644
2645     buttons : false,
2646
2647     // set on load...
2648
2649     html: false,
2650
2651     tmp: false,
2652
2653     specificTitle: false,
2654
2655     buttonPosition: 'right',
2656
2657     allow_close : true,
2658
2659     animate : true,
2660
2661     fitwindow: false,
2662     
2663      // private
2664     dialogEl: false,
2665     bodyEl:  false,
2666     footerEl:  false,
2667     titleEl:  false,
2668     closeEl:  false,
2669
2670     size: '',
2671     
2672     max_width: 0,
2673     
2674     max_height: 0,
2675     
2676     fit_content: false,
2677
2678     onRender : function(ct, position)
2679     {
2680         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2681
2682         if(!this.el){
2683             var cfg = Roo.apply({},  this.getAutoCreate());
2684             cfg.id = Roo.id();
2685             //if(!cfg.name){
2686             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2687             //}
2688             //if (!cfg.name.length) {
2689             //    delete cfg.name;
2690            // }
2691             if (this.cls) {
2692                 cfg.cls += ' ' + this.cls;
2693             }
2694             if (this.style) {
2695                 cfg.style = this.style;
2696             }
2697             this.el = Roo.get(document.body).createChild(cfg, position);
2698         }
2699         //var type = this.el.dom.type;
2700
2701
2702         if(this.tabIndex !== undefined){
2703             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2704         }
2705
2706         this.dialogEl = this.el.select('.modal-dialog',true).first();
2707         this.bodyEl = this.el.select('.modal-body',true).first();
2708         this.closeEl = this.el.select('.modal-header .close', true).first();
2709         this.headerEl = this.el.select('.modal-header',true).first();
2710         this.titleEl = this.el.select('.modal-title',true).first();
2711         this.footerEl = this.el.select('.modal-footer',true).first();
2712
2713         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2714         
2715         //this.el.addClass("x-dlg-modal");
2716
2717         if (this.buttons.length) {
2718             Roo.each(this.buttons, function(bb) {
2719                 var b = Roo.apply({}, bb);
2720                 b.xns = b.xns || Roo.bootstrap;
2721                 b.xtype = b.xtype || 'Button';
2722                 if (typeof(b.listeners) == 'undefined') {
2723                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2724                 }
2725
2726                 var btn = Roo.factory(b);
2727
2728                 btn.render(this.el.select('.modal-footer div').first());
2729
2730             },this);
2731         }
2732         // render the children.
2733         var nitems = [];
2734
2735         if(typeof(this.items) != 'undefined'){
2736             var items = this.items;
2737             delete this.items;
2738
2739             for(var i =0;i < items.length;i++) {
2740                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2741             }
2742         }
2743
2744         this.items = nitems;
2745
2746         // where are these used - they used to be body/close/footer
2747
2748
2749         this.initEvents();
2750         //this.el.addClass([this.fieldClass, this.cls]);
2751
2752     },
2753
2754     getAutoCreate : function()
2755     {
2756         var bdy = {
2757                 cls : 'modal-body',
2758                 html : this.html || ''
2759         };
2760
2761         var title = {
2762             tag: 'h4',
2763             cls : 'modal-title',
2764             html : this.title
2765         };
2766
2767         if(this.specificTitle){
2768             title = this.title;
2769
2770         };
2771
2772         var header = [];
2773         if (this.allow_close) {
2774             header.push({
2775                 tag: 'button',
2776                 cls : 'close',
2777                 html : '&times'
2778             });
2779         }
2780
2781         header.push(title);
2782
2783         var size = '';
2784
2785         if(this.size.length){
2786             size = 'modal-' + this.size;
2787         }
2788
2789         var modal = {
2790             cls: "modal",
2791              cn : [
2792                 {
2793                     cls: "modal-dialog " + size,
2794                     cn : [
2795                         {
2796                             cls : "modal-content",
2797                             cn : [
2798                                 {
2799                                     cls : 'modal-header',
2800                                     cn : header
2801                                 },
2802                                 bdy,
2803                                 {
2804                                     cls : 'modal-footer',
2805                                     cn : [
2806                                         {
2807                                             tag: 'div',
2808                                             cls: 'btn-' + this.buttonPosition
2809                                         }
2810                                     ]
2811
2812                                 }
2813
2814
2815                             ]
2816
2817                         }
2818                     ]
2819
2820                 }
2821             ]
2822         };
2823
2824         if(this.animate){
2825             modal.cls += ' fade';
2826         }
2827
2828         return modal;
2829
2830     },
2831     getChildContainer : function() {
2832
2833          return this.bodyEl;
2834
2835     },
2836     getButtonContainer : function() {
2837          return this.el.select('.modal-footer div',true).first();
2838
2839     },
2840     initEvents : function()
2841     {
2842         if (this.allow_close) {
2843             this.closeEl.on('click', this.hide, this);
2844         }
2845         Roo.EventManager.onWindowResize(this.resize, this, true);
2846
2847
2848     },
2849
2850     resize : function()
2851     {
2852         this.maskEl.setSize(
2853             Roo.lib.Dom.getViewWidth(true),
2854             Roo.lib.Dom.getViewHeight(true)
2855         );
2856         
2857         if (this.fitwindow) {
2858             this.setSize(
2859                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2860                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2861             );
2862             return;
2863         }
2864         
2865         if(this.max_width !== 0) {
2866             
2867             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2868             
2869             if(this.height) {
2870                 this.setSize(w, this.height);
2871                 return;
2872             }
2873             
2874             if(this.max_height) {
2875                 this.setSize(w,Math.min(
2876                     this.max_height,
2877                     Roo.lib.Dom.getViewportHeight(true) - 60
2878                 ));
2879                 
2880                 return;
2881             }
2882             
2883             if(!this.fit_content) {
2884                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2885                 return;
2886             }
2887             
2888             this.setSize(w, Math.min(
2889                 60 +
2890                 this.headerEl.getHeight() + 
2891                 this.footerEl.getHeight() + 
2892                 this.getChildHeight(this.bodyEl.dom.childNodes),
2893                 Roo.lib.Dom.getViewportHeight(true) - 60)
2894             );
2895         }
2896         
2897     },
2898
2899     setSize : function(w,h)
2900     {
2901         if (!w && !h) {
2902             return;
2903         }
2904         
2905         this.resizeTo(w,h);
2906     },
2907
2908     show : function() {
2909
2910         if (!this.rendered) {
2911             this.render();
2912         }
2913
2914         //this.el.setStyle('display', 'block');
2915         this.el.removeClass('hideing');        
2916         this.el.addClass('show');
2917  
2918         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2919             var _this = this;
2920             (function(){
2921                 this.el.addClass('in');
2922             }).defer(50, this);
2923         }else{
2924             this.el.addClass('in');
2925         }
2926
2927         // not sure how we can show data in here..
2928         //if (this.tmpl) {
2929         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2930         //}
2931
2932         Roo.get(document.body).addClass("x-body-masked");
2933         
2934         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2935         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2936         this.maskEl.addClass('show');
2937         
2938         this.resize();
2939         
2940         this.fireEvent('show', this);
2941
2942         // set zindex here - otherwise it appears to be ignored...
2943         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2944
2945         (function () {
2946             this.items.forEach( function(e) {
2947                 e.layout ? e.layout() : false;
2948
2949             });
2950         }).defer(100,this);
2951
2952     },
2953     hide : function()
2954     {
2955         if(this.fireEvent("beforehide", this) !== false){
2956             this.maskEl.removeClass('show');
2957             Roo.get(document.body).removeClass("x-body-masked");
2958             this.el.removeClass('in');
2959             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2960
2961             if(this.animate){ // why
2962                 this.el.addClass('hideing');
2963                 (function(){
2964                     if (!this.el.hasClass('hideing')) {
2965                         return; // it's been shown again...
2966                     }
2967                     this.el.removeClass('show');
2968                     this.el.removeClass('hideing');
2969                 }).defer(150,this);
2970                 
2971             }else{
2972                  this.el.removeClass('show');
2973             }
2974             this.fireEvent('hide', this);
2975         }
2976     },
2977     isVisible : function()
2978     {
2979         
2980         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2981         
2982     },
2983
2984     addButton : function(str, cb)
2985     {
2986
2987
2988         var b = Roo.apply({}, { html : str } );
2989         b.xns = b.xns || Roo.bootstrap;
2990         b.xtype = b.xtype || 'Button';
2991         if (typeof(b.listeners) == 'undefined') {
2992             b.listeners = { click : cb.createDelegate(this)  };
2993         }
2994
2995         var btn = Roo.factory(b);
2996
2997         btn.render(this.el.select('.modal-footer div').first());
2998
2999         return btn;
3000
3001     },
3002
3003     setDefaultButton : function(btn)
3004     {
3005         //this.el.select('.modal-footer').()
3006     },
3007     diff : false,
3008
3009     resizeTo: function(w,h)
3010     {
3011         // skip.. ?? why??
3012
3013         this.dialogEl.setWidth(w);
3014         if (this.diff === false) {
3015             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3016         }
3017
3018         this.bodyEl.setHeight(h - this.diff);
3019
3020         this.fireEvent('resize', this);
3021
3022     },
3023     setContentSize  : function(w, h)
3024     {
3025
3026     },
3027     onButtonClick: function(btn,e)
3028     {
3029         //Roo.log([a,b,c]);
3030         this.fireEvent('btnclick', btn.name, e);
3031     },
3032      /**
3033      * Set the title of the Dialog
3034      * @param {String} str new Title
3035      */
3036     setTitle: function(str) {
3037         this.titleEl.dom.innerHTML = str;
3038     },
3039     /**
3040      * Set the body of the Dialog
3041      * @param {String} str new Title
3042      */
3043     setBody: function(str) {
3044         this.bodyEl.dom.innerHTML = str;
3045     },
3046     /**
3047      * Set the body of the Dialog using the template
3048      * @param {Obj} data - apply this data to the template and replace the body contents.
3049      */
3050     applyBody: function(obj)
3051     {
3052         if (!this.tmpl) {
3053             Roo.log("Error - using apply Body without a template");
3054             //code
3055         }
3056         this.tmpl.overwrite(this.bodyEl, obj);
3057     },
3058     
3059     getChildHeight : function(child_nodes)
3060     {
3061         if(
3062             !child_nodes ||
3063             child_nodes.length == 0
3064         ) {
3065             return;
3066         }
3067         
3068         var child_height = 0;
3069         
3070         for(var i = 0; i < child_nodes.length; i++) {
3071             
3072             /*
3073             * for modal with tabs...
3074             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3075                 
3076                 var layout_childs = child_nodes[i].childNodes;
3077                 
3078                 for(var j = 0; j < layout_childs.length; j++) {
3079                     
3080                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3081                         
3082                         var layout_body_childs = layout_childs[j].childNodes;
3083                         
3084                         for(var k = 0; k < layout_body_childs.length; k++) {
3085                             
3086                             if(layout_body_childs[k].classList.contains('navbar')) {
3087                                 child_height += layout_body_childs[k].offsetHeight;
3088                                 continue;
3089                             }
3090                             
3091                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3092                                 
3093                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3094                                 
3095                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3096                                     
3097                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3098                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3099                                         continue;
3100                                     }
3101                                     
3102                                 }
3103                                 
3104                             }
3105                             
3106                         }
3107                     }
3108                 }
3109                 continue;
3110             }
3111             */
3112             
3113             child_height += child_nodes[i].offsetHeight;
3114             // Roo.log(child_nodes[i].offsetHeight);
3115         }
3116         
3117         return child_height;
3118     }
3119
3120 });
3121
3122
3123 Roo.apply(Roo.bootstrap.Modal,  {
3124     /**
3125          * Button config that displays a single OK button
3126          * @type Object
3127          */
3128         OK :  [{
3129             name : 'ok',
3130             weight : 'primary',
3131             html : 'OK'
3132         }],
3133         /**
3134          * Button config that displays Yes and No buttons
3135          * @type Object
3136          */
3137         YESNO : [
3138             {
3139                 name  : 'no',
3140                 html : 'No'
3141             },
3142             {
3143                 name  :'yes',
3144                 weight : 'primary',
3145                 html : 'Yes'
3146             }
3147         ],
3148
3149         /**
3150          * Button config that displays OK and Cancel buttons
3151          * @type Object
3152          */
3153         OKCANCEL : [
3154             {
3155                name : 'cancel',
3156                 html : 'Cancel'
3157             },
3158             {
3159                 name : 'ok',
3160                 weight : 'primary',
3161                 html : 'OK'
3162             }
3163         ],
3164         /**
3165          * Button config that displays Yes, No and Cancel buttons
3166          * @type Object
3167          */
3168         YESNOCANCEL : [
3169             {
3170                 name : 'yes',
3171                 weight : 'primary',
3172                 html : 'Yes'
3173             },
3174             {
3175                 name : 'no',
3176                 html : 'No'
3177             },
3178             {
3179                 name : 'cancel',
3180                 html : 'Cancel'
3181             }
3182         ],
3183         
3184         zIndex : 10001
3185 });
3186 /*
3187  * - LGPL
3188  *
3189  * messagebox - can be used as a replace
3190  * 
3191  */
3192 /**
3193  * @class Roo.MessageBox
3194  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3195  * Example usage:
3196  *<pre><code>
3197 // Basic alert:
3198 Roo.Msg.alert('Status', 'Changes saved successfully.');
3199
3200 // Prompt for user data:
3201 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3202     if (btn == 'ok'){
3203         // process text value...
3204     }
3205 });
3206
3207 // Show a dialog using config options:
3208 Roo.Msg.show({
3209    title:'Save Changes?',
3210    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3211    buttons: Roo.Msg.YESNOCANCEL,
3212    fn: processResult,
3213    animEl: 'elId'
3214 });
3215 </code></pre>
3216  * @singleton
3217  */
3218 Roo.bootstrap.MessageBox = function(){
3219     var dlg, opt, mask, waitTimer;
3220     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3221     var buttons, activeTextEl, bwidth;
3222
3223     
3224     // private
3225     var handleButton = function(button){
3226         dlg.hide();
3227         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3228     };
3229
3230     // private
3231     var handleHide = function(){
3232         if(opt && opt.cls){
3233             dlg.el.removeClass(opt.cls);
3234         }
3235         //if(waitTimer){
3236         //    Roo.TaskMgr.stop(waitTimer);
3237         //    waitTimer = null;
3238         //}
3239     };
3240
3241     // private
3242     var updateButtons = function(b){
3243         var width = 0;
3244         if(!b){
3245             buttons["ok"].hide();
3246             buttons["cancel"].hide();
3247             buttons["yes"].hide();
3248             buttons["no"].hide();
3249             //dlg.footer.dom.style.display = 'none';
3250             return width;
3251         }
3252         dlg.footerEl.dom.style.display = '';
3253         for(var k in buttons){
3254             if(typeof buttons[k] != "function"){
3255                 if(b[k]){
3256                     buttons[k].show();
3257                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3258                     width += buttons[k].el.getWidth()+15;
3259                 }else{
3260                     buttons[k].hide();
3261                 }
3262             }
3263         }
3264         return width;
3265     };
3266
3267     // private
3268     var handleEsc = function(d, k, e){
3269         if(opt && opt.closable !== false){
3270             dlg.hide();
3271         }
3272         if(e){
3273             e.stopEvent();
3274         }
3275     };
3276
3277     return {
3278         /**
3279          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3280          * @return {Roo.BasicDialog} The BasicDialog element
3281          */
3282         getDialog : function(){
3283            if(!dlg){
3284                 dlg = new Roo.bootstrap.Modal( {
3285                     //draggable: true,
3286                     //resizable:false,
3287                     //constraintoviewport:false,
3288                     //fixedcenter:true,
3289                     //collapsible : false,
3290                     //shim:true,
3291                     //modal: true,
3292                 //    width: 'auto',
3293                   //  height:100,
3294                     //buttonAlign:"center",
3295                     closeClick : function(){
3296                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3297                             handleButton("no");
3298                         }else{
3299                             handleButton("cancel");
3300                         }
3301                     }
3302                 });
3303                 dlg.render();
3304                 dlg.on("hide", handleHide);
3305                 mask = dlg.mask;
3306                 //dlg.addKeyListener(27, handleEsc);
3307                 buttons = {};
3308                 this.buttons = buttons;
3309                 var bt = this.buttonText;
3310                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3311                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3312                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3313                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3314                 //Roo.log(buttons);
3315                 bodyEl = dlg.bodyEl.createChild({
3316
3317                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3318                         '<textarea class="roo-mb-textarea"></textarea>' +
3319                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3320                 });
3321                 msgEl = bodyEl.dom.firstChild;
3322                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3323                 textboxEl.enableDisplayMode();
3324                 textboxEl.addKeyListener([10,13], function(){
3325                     if(dlg.isVisible() && opt && opt.buttons){
3326                         if(opt.buttons.ok){
3327                             handleButton("ok");
3328                         }else if(opt.buttons.yes){
3329                             handleButton("yes");
3330                         }
3331                     }
3332                 });
3333                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3334                 textareaEl.enableDisplayMode();
3335                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3336                 progressEl.enableDisplayMode();
3337                 
3338                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3339                 var pf = progressEl.dom.firstChild;
3340                 if (pf) {
3341                     pp = Roo.get(pf.firstChild);
3342                     pp.setHeight(pf.offsetHeight);
3343                 }
3344                 
3345             }
3346             return dlg;
3347         },
3348
3349         /**
3350          * Updates the message box body text
3351          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3352          * the XHTML-compliant non-breaking space character '&amp;#160;')
3353          * @return {Roo.MessageBox} This message box
3354          */
3355         updateText : function(text)
3356         {
3357             if(!dlg.isVisible() && !opt.width){
3358                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3359                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3360             }
3361             msgEl.innerHTML = text || '&#160;';
3362       
3363             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3364             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3365             var w = Math.max(
3366                     Math.min(opt.width || cw , this.maxWidth), 
3367                     Math.max(opt.minWidth || this.minWidth, bwidth)
3368             );
3369             if(opt.prompt){
3370                 activeTextEl.setWidth(w);
3371             }
3372             if(dlg.isVisible()){
3373                 dlg.fixedcenter = false;
3374             }
3375             // to big, make it scroll. = But as usual stupid IE does not support
3376             // !important..
3377             
3378             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3379                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3380                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3381             } else {
3382                 bodyEl.dom.style.height = '';
3383                 bodyEl.dom.style.overflowY = '';
3384             }
3385             if (cw > w) {
3386                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3387             } else {
3388                 bodyEl.dom.style.overflowX = '';
3389             }
3390             
3391             dlg.setContentSize(w, bodyEl.getHeight());
3392             if(dlg.isVisible()){
3393                 dlg.fixedcenter = true;
3394             }
3395             return this;
3396         },
3397
3398         /**
3399          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3400          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3401          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3402          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3403          * @return {Roo.MessageBox} This message box
3404          */
3405         updateProgress : function(value, text){
3406             if(text){
3407                 this.updateText(text);
3408             }
3409             
3410             if (pp) { // weird bug on my firefox - for some reason this is not defined
3411                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3412                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3413             }
3414             return this;
3415         },        
3416
3417         /**
3418          * Returns true if the message box is currently displayed
3419          * @return {Boolean} True if the message box is visible, else false
3420          */
3421         isVisible : function(){
3422             return dlg && dlg.isVisible();  
3423         },
3424
3425         /**
3426          * Hides the message box if it is displayed
3427          */
3428         hide : function(){
3429             if(this.isVisible()){
3430                 dlg.hide();
3431             }  
3432         },
3433
3434         /**
3435          * Displays a new message box, or reinitializes an existing message box, based on the config options
3436          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3437          * The following config object properties are supported:
3438          * <pre>
3439 Property    Type             Description
3440 ----------  ---------------  ------------------------------------------------------------------------------------
3441 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3442                                    closes (defaults to undefined)
3443 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3444                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3445 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3446                                    progress and wait dialogs will ignore this property and always hide the
3447                                    close button as they can only be closed programmatically.
3448 cls               String           A custom CSS class to apply to the message box element
3449 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3450                                    displayed (defaults to 75)
3451 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3452                                    function will be btn (the name of the button that was clicked, if applicable,
3453                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3454                                    Progress and wait dialogs will ignore this option since they do not respond to
3455                                    user actions and can only be closed programmatically, so any required function
3456                                    should be called by the same code after it closes the dialog.
3457 icon              String           A CSS class that provides a background image to be used as an icon for
3458                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3459 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3460 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3461 modal             Boolean          False to allow user interaction with the page while the message box is
3462                                    displayed (defaults to true)
3463 msg               String           A string that will replace the existing message box body text (defaults
3464                                    to the XHTML-compliant non-breaking space character '&#160;')
3465 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3466 progress          Boolean          True to display a progress bar (defaults to false)
3467 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3468 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3469 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3470 title             String           The title text
3471 value             String           The string value to set into the active textbox element if displayed
3472 wait              Boolean          True to display a progress bar (defaults to false)
3473 width             Number           The width of the dialog in pixels
3474 </pre>
3475          *
3476          * Example usage:
3477          * <pre><code>
3478 Roo.Msg.show({
3479    title: 'Address',
3480    msg: 'Please enter your address:',
3481    width: 300,
3482    buttons: Roo.MessageBox.OKCANCEL,
3483    multiline: true,
3484    fn: saveAddress,
3485    animEl: 'addAddressBtn'
3486 });
3487 </code></pre>
3488          * @param {Object} config Configuration options
3489          * @return {Roo.MessageBox} This message box
3490          */
3491         show : function(options)
3492         {
3493             
3494             // this causes nightmares if you show one dialog after another
3495             // especially on callbacks..
3496              
3497             if(this.isVisible()){
3498                 
3499                 this.hide();
3500                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3501                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3502                 Roo.log("New Dialog Message:" +  options.msg )
3503                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3504                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3505                 
3506             }
3507             var d = this.getDialog();
3508             opt = options;
3509             d.setTitle(opt.title || "&#160;");
3510             d.closeEl.setDisplayed(opt.closable !== false);
3511             activeTextEl = textboxEl;
3512             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3513             if(opt.prompt){
3514                 if(opt.multiline){
3515                     textboxEl.hide();
3516                     textareaEl.show();
3517                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3518                         opt.multiline : this.defaultTextHeight);
3519                     activeTextEl = textareaEl;
3520                 }else{
3521                     textboxEl.show();
3522                     textareaEl.hide();
3523                 }
3524             }else{
3525                 textboxEl.hide();
3526                 textareaEl.hide();
3527             }
3528             progressEl.setDisplayed(opt.progress === true);
3529             this.updateProgress(0);
3530             activeTextEl.dom.value = opt.value || "";
3531             if(opt.prompt){
3532                 dlg.setDefaultButton(activeTextEl);
3533             }else{
3534                 var bs = opt.buttons;
3535                 var db = null;
3536                 if(bs && bs.ok){
3537                     db = buttons["ok"];
3538                 }else if(bs && bs.yes){
3539                     db = buttons["yes"];
3540                 }
3541                 dlg.setDefaultButton(db);
3542             }
3543             bwidth = updateButtons(opt.buttons);
3544             this.updateText(opt.msg);
3545             if(opt.cls){
3546                 d.el.addClass(opt.cls);
3547             }
3548             d.proxyDrag = opt.proxyDrag === true;
3549             d.modal = opt.modal !== false;
3550             d.mask = opt.modal !== false ? mask : false;
3551             if(!d.isVisible()){
3552                 // force it to the end of the z-index stack so it gets a cursor in FF
3553                 document.body.appendChild(dlg.el.dom);
3554                 d.animateTarget = null;
3555                 d.show(options.animEl);
3556             }
3557             return this;
3558         },
3559
3560         /**
3561          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3562          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3563          * and closing the message box when the process is complete.
3564          * @param {String} title The title bar text
3565          * @param {String} msg The message box body text
3566          * @return {Roo.MessageBox} This message box
3567          */
3568         progress : function(title, msg){
3569             this.show({
3570                 title : title,
3571                 msg : msg,
3572                 buttons: false,
3573                 progress:true,
3574                 closable:false,
3575                 minWidth: this.minProgressWidth,
3576                 modal : true
3577             });
3578             return this;
3579         },
3580
3581         /**
3582          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3583          * If a callback function is passed it will be called after the user clicks the button, and the
3584          * id of the button that was clicked will be passed as the only parameter to the callback
3585          * (could also be the top-right close button).
3586          * @param {String} title The title bar text
3587          * @param {String} msg The message box body text
3588          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3589          * @param {Object} scope (optional) The scope of the callback function
3590          * @return {Roo.MessageBox} This message box
3591          */
3592         alert : function(title, msg, fn, scope)
3593         {
3594             this.show({
3595                 title : title,
3596                 msg : msg,
3597                 buttons: this.OK,
3598                 fn: fn,
3599                 closable : false,
3600                 scope : scope,
3601                 modal : true
3602             });
3603             return this;
3604         },
3605
3606         /**
3607          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3608          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3609          * You are responsible for closing the message box when the process is complete.
3610          * @param {String} msg The message box body text
3611          * @param {String} title (optional) The title bar text
3612          * @return {Roo.MessageBox} This message box
3613          */
3614         wait : function(msg, title){
3615             this.show({
3616                 title : title,
3617                 msg : msg,
3618                 buttons: false,
3619                 closable:false,
3620                 progress:true,
3621                 modal:true,
3622                 width:300,
3623                 wait:true
3624             });
3625             waitTimer = Roo.TaskMgr.start({
3626                 run: function(i){
3627                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3628                 },
3629                 interval: 1000
3630             });
3631             return this;
3632         },
3633
3634         /**
3635          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3636          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3637          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3638          * @param {String} title The title bar text
3639          * @param {String} msg The message box body text
3640          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3641          * @param {Object} scope (optional) The scope of the callback function
3642          * @return {Roo.MessageBox} This message box
3643          */
3644         confirm : function(title, msg, fn, scope){
3645             this.show({
3646                 title : title,
3647                 msg : msg,
3648                 buttons: this.YESNO,
3649                 fn: fn,
3650                 scope : scope,
3651                 modal : true
3652             });
3653             return this;
3654         },
3655
3656         /**
3657          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3658          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3659          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3660          * (could also be the top-right close button) and the text that was entered will be passed as the two
3661          * parameters to the callback.
3662          * @param {String} title The title bar text
3663          * @param {String} msg The message box body text
3664          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3665          * @param {Object} scope (optional) The scope of the callback function
3666          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3667          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3668          * @return {Roo.MessageBox} This message box
3669          */
3670         prompt : function(title, msg, fn, scope, multiline){
3671             this.show({
3672                 title : title,
3673                 msg : msg,
3674                 buttons: this.OKCANCEL,
3675                 fn: fn,
3676                 minWidth:250,
3677                 scope : scope,
3678                 prompt:true,
3679                 multiline: multiline,
3680                 modal : true
3681             });
3682             return this;
3683         },
3684
3685         /**
3686          * Button config that displays a single OK button
3687          * @type Object
3688          */
3689         OK : {ok:true},
3690         /**
3691          * Button config that displays Yes and No buttons
3692          * @type Object
3693          */
3694         YESNO : {yes:true, no:true},
3695         /**
3696          * Button config that displays OK and Cancel buttons
3697          * @type Object
3698          */
3699         OKCANCEL : {ok:true, cancel:true},
3700         /**
3701          * Button config that displays Yes, No and Cancel buttons
3702          * @type Object
3703          */
3704         YESNOCANCEL : {yes:true, no:true, cancel:true},
3705
3706         /**
3707          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3708          * @type Number
3709          */
3710         defaultTextHeight : 75,
3711         /**
3712          * The maximum width in pixels of the message box (defaults to 600)
3713          * @type Number
3714          */
3715         maxWidth : 600,
3716         /**
3717          * The minimum width in pixels of the message box (defaults to 100)
3718          * @type Number
3719          */
3720         minWidth : 100,
3721         /**
3722          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3723          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3724          * @type Number
3725          */
3726         minProgressWidth : 250,
3727         /**
3728          * An object containing the default button text strings that can be overriden for localized language support.
3729          * Supported properties are: ok, cancel, yes and no.
3730          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3731          * @type Object
3732          */
3733         buttonText : {
3734             ok : "OK",
3735             cancel : "Cancel",
3736             yes : "Yes",
3737             no : "No"
3738         }
3739     };
3740 }();
3741
3742 /**
3743  * Shorthand for {@link Roo.MessageBox}
3744  */
3745 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3746 Roo.Msg = Roo.Msg || Roo.MessageBox;
3747 /*
3748  * - LGPL
3749  *
3750  * navbar
3751  * 
3752  */
3753
3754 /**
3755  * @class Roo.bootstrap.Navbar
3756  * @extends Roo.bootstrap.Component
3757  * Bootstrap Navbar class
3758
3759  * @constructor
3760  * Create a new Navbar
3761  * @param {Object} config The config object
3762  */
3763
3764
3765 Roo.bootstrap.Navbar = function(config){
3766     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3767     this.addEvents({
3768         // raw events
3769         /**
3770          * @event beforetoggle
3771          * Fire before toggle the menu
3772          * @param {Roo.EventObject} e
3773          */
3774         "beforetoggle" : true
3775     });
3776 };
3777
3778 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3779     
3780     
3781    
3782     // private
3783     navItems : false,
3784     loadMask : false,
3785     
3786     
3787     getAutoCreate : function(){
3788         
3789         
3790         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3791         
3792     },
3793     
3794     initEvents :function ()
3795     {
3796         //Roo.log(this.el.select('.navbar-toggle',true));
3797         this.el.select('.navbar-toggle',true).on('click', function() {
3798             if(this.fireEvent('beforetoggle', this) !== false){
3799                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3800             }
3801             
3802         }, this);
3803         
3804         var mark = {
3805             tag: "div",
3806             cls:"x-dlg-mask"
3807         };
3808         
3809         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3810         
3811         var size = this.el.getSize();
3812         this.maskEl.setSize(size.width, size.height);
3813         this.maskEl.enableDisplayMode("block");
3814         this.maskEl.hide();
3815         
3816         if(this.loadMask){
3817             this.maskEl.show();
3818         }
3819     },
3820     
3821     
3822     getChildContainer : function()
3823     {
3824         if (this.el.select('.collapse').getCount()) {
3825             return this.el.select('.collapse',true).first();
3826         }
3827         
3828         return this.el;
3829     },
3830     
3831     mask : function()
3832     {
3833         this.maskEl.show();
3834     },
3835     
3836     unmask : function()
3837     {
3838         this.maskEl.hide();
3839     } 
3840     
3841     
3842     
3843     
3844 });
3845
3846
3847
3848  
3849
3850  /*
3851  * - LGPL
3852  *
3853  * navbar
3854  * 
3855  */
3856
3857 /**
3858  * @class Roo.bootstrap.NavSimplebar
3859  * @extends Roo.bootstrap.Navbar
3860  * Bootstrap Sidebar class
3861  *
3862  * @cfg {Boolean} inverse is inverted color
3863  * 
3864  * @cfg {String} type (nav | pills | tabs)
3865  * @cfg {Boolean} arrangement stacked | justified
3866  * @cfg {String} align (left | right) alignment
3867  * 
3868  * @cfg {Boolean} main (true|false) main nav bar? default false
3869  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3870  * 
3871  * @cfg {String} tag (header|footer|nav|div) default is nav 
3872
3873  * 
3874  * 
3875  * 
3876  * @constructor
3877  * Create a new Sidebar
3878  * @param {Object} config The config object
3879  */
3880
3881
3882 Roo.bootstrap.NavSimplebar = function(config){
3883     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3884 };
3885
3886 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3887     
3888     inverse: false,
3889     
3890     type: false,
3891     arrangement: '',
3892     align : false,
3893     
3894     
3895     
3896     main : false,
3897     
3898     
3899     tag : false,
3900     
3901     
3902     getAutoCreate : function(){
3903         
3904         
3905         var cfg = {
3906             tag : this.tag || 'div',
3907             cls : 'navbar'
3908         };
3909           
3910         
3911         cfg.cn = [
3912             {
3913                 cls: 'nav',
3914                 tag : 'ul'
3915             }
3916         ];
3917         
3918          
3919         this.type = this.type || 'nav';
3920         if (['tabs','pills'].indexOf(this.type)!==-1) {
3921             cfg.cn[0].cls += ' nav-' + this.type
3922         
3923         
3924         } else {
3925             if (this.type!=='nav') {
3926                 Roo.log('nav type must be nav/tabs/pills')
3927             }
3928             cfg.cn[0].cls += ' navbar-nav'
3929         }
3930         
3931         
3932         
3933         
3934         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3935             cfg.cn[0].cls += ' nav-' + this.arrangement;
3936         }
3937         
3938         
3939         if (this.align === 'right') {
3940             cfg.cn[0].cls += ' navbar-right';
3941         }
3942         
3943         if (this.inverse) {
3944             cfg.cls += ' navbar-inverse';
3945             
3946         }
3947         
3948         
3949         return cfg;
3950     
3951         
3952     }
3953     
3954     
3955     
3956 });
3957
3958
3959
3960  
3961
3962  
3963        /*
3964  * - LGPL
3965  *
3966  * navbar
3967  * navbar-fixed-top
3968  * navbar-expand-md  fixed-top 
3969  */
3970
3971 /**
3972  * @class Roo.bootstrap.NavHeaderbar
3973  * @extends Roo.bootstrap.NavSimplebar
3974  * Bootstrap Sidebar class
3975  *
3976  * @cfg {String} brand what is brand
3977  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3978  * @cfg {String} brand_href href of the brand
3979  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3980  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3981  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3982  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3983  * 
3984  * @constructor
3985  * Create a new Sidebar
3986  * @param {Object} config The config object
3987  */
3988
3989
3990 Roo.bootstrap.NavHeaderbar = function(config){
3991     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3992       
3993 };
3994
3995 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3996     
3997     position: '',
3998     brand: '',
3999     brand_href: false,
4000     srButton : true,
4001     autohide : false,
4002     desktopCenter : false,
4003    
4004     
4005     getAutoCreate : function(){
4006         
4007         var   cfg = {
4008             tag: this.nav || 'nav',
4009             cls: 'navbar navbar-expand-md',
4010             role: 'navigation',
4011             cn: []
4012         };
4013         
4014         var cn = cfg.cn;
4015         if (this.desktopCenter) {
4016             cn.push({cls : 'container', cn : []});
4017             cn = cn[0].cn;
4018         }
4019         
4020         if(this.srButton){
4021             cn.push({
4022                 tag: 'div',
4023                 cls: 'navbar-header',
4024                 cn: [
4025                     {
4026                         tag: 'button',
4027                         type: 'button',
4028                         cls: 'navbar-toggle navbar-toggler',
4029                         'data-toggle': 'collapse',
4030                         cn: [
4031                             {
4032                                 tag: 'span',
4033                                 cls: 'sr-only',
4034                                 html: 'Toggle navigation'
4035                             },
4036                             {
4037                                 tag: 'span',
4038                                 cls: 'icon-bar navbar-toggler-icon'
4039                             },
4040                             {
4041                                 tag: 'span',
4042                                 cls: 'icon-bar'
4043                             },
4044                             {
4045                                 tag: 'span',
4046                                 cls: 'icon-bar'
4047                             }
4048                         ]
4049                     }
4050                 ]
4051             });
4052         }
4053         
4054         cn.push({
4055             tag: 'div',
4056             cls: 'collapse navbar-collapse',
4057             cn : []
4058         });
4059         
4060         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4061         
4062         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4063             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4064             
4065             // tag can override this..
4066             
4067             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4068         }
4069         
4070         if (this.brand !== '') {
4071             cn[0].cn.push({
4072                 tag: 'a',
4073                 href: this.brand_href ? this.brand_href : '#',
4074                 cls: 'navbar-brand',
4075                 cn: [
4076                 this.brand
4077                 ]
4078             });
4079         }
4080         
4081         if(this.main){
4082             cfg.cls += ' main-nav';
4083         }
4084         
4085         
4086         return cfg;
4087
4088         
4089     },
4090     getHeaderChildContainer : function()
4091     {
4092         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4093             return this.el.select('.navbar-header',true).first();
4094         }
4095         
4096         return this.getChildContainer();
4097     },
4098     
4099     
4100     initEvents : function()
4101     {
4102         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4103         
4104         if (this.autohide) {
4105             
4106             var prevScroll = 0;
4107             var ft = this.el;
4108             
4109             Roo.get(document).on('scroll',function(e) {
4110                 var ns = Roo.get(document).getScroll().top;
4111                 var os = prevScroll;
4112                 prevScroll = ns;
4113                 
4114                 if(ns > os){
4115                     ft.removeClass('slideDown');
4116                     ft.addClass('slideUp');
4117                     return;
4118                 }
4119                 ft.removeClass('slideUp');
4120                 ft.addClass('slideDown');
4121                  
4122               
4123           },this);
4124         }
4125     }    
4126     
4127 });
4128
4129
4130
4131  
4132
4133  /*
4134  * - LGPL
4135  *
4136  * navbar
4137  * 
4138  */
4139
4140 /**
4141  * @class Roo.bootstrap.NavSidebar
4142  * @extends Roo.bootstrap.Navbar
4143  * Bootstrap Sidebar class
4144  * 
4145  * @constructor
4146  * Create a new Sidebar
4147  * @param {Object} config The config object
4148  */
4149
4150
4151 Roo.bootstrap.NavSidebar = function(config){
4152     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4153 };
4154
4155 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4156     
4157     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4158     
4159     getAutoCreate : function(){
4160         
4161         
4162         return  {
4163             tag: 'div',
4164             cls: 'sidebar sidebar-nav'
4165         };
4166     
4167         
4168     }
4169     
4170     
4171     
4172 });
4173
4174
4175
4176  
4177
4178  /*
4179  * - LGPL
4180  *
4181  * nav group
4182  * 
4183  */
4184
4185 /**
4186  * @class Roo.bootstrap.NavGroup
4187  * @extends Roo.bootstrap.Component
4188  * Bootstrap NavGroup class
4189  * @cfg {String} align (left|right)
4190  * @cfg {Boolean} inverse
4191  * @cfg {String} type (nav|pills|tab) default nav
4192  * @cfg {String} navId - reference Id for navbar.
4193
4194  * 
4195  * @constructor
4196  * Create a new nav group
4197  * @param {Object} config The config object
4198  */
4199
4200 Roo.bootstrap.NavGroup = function(config){
4201     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4202     this.navItems = [];
4203    
4204     Roo.bootstrap.NavGroup.register(this);
4205      this.addEvents({
4206         /**
4207              * @event changed
4208              * Fires when the active item changes
4209              * @param {Roo.bootstrap.NavGroup} this
4210              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4211              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4212          */
4213         'changed': true
4214      });
4215     
4216 };
4217
4218 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4219     
4220     align: '',
4221     inverse: false,
4222     form: false,
4223     type: 'nav',
4224     navId : '',
4225     // private
4226     
4227     navItems : false, 
4228     
4229     getAutoCreate : function()
4230     {
4231         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4232         
4233         cfg = {
4234             tag : 'ul',
4235             cls: 'nav' 
4236         };
4237         
4238         if (['tabs','pills'].indexOf(this.type)!==-1) {
4239             cfg.cls += ' nav-' + this.type
4240         } else {
4241             if (this.type!=='nav') {
4242                 Roo.log('nav type must be nav/tabs/pills')
4243             }
4244             cfg.cls += ' navbar-nav mr-auto'
4245         }
4246         
4247         if (this.parent() && this.parent().sidebar) {
4248             cfg = {
4249                 tag: 'ul',
4250                 cls: 'dashboard-menu sidebar-menu'
4251             };
4252             
4253             return cfg;
4254         }
4255         
4256         if (this.form === true) {
4257             cfg = {
4258                 tag: 'form',
4259                 cls: 'navbar-form'
4260             };
4261             
4262             if (this.align === 'right') {
4263                 cfg.cls += ' navbar-right';
4264             } else {
4265                 cfg.cls += ' navbar-left';
4266             }
4267         }
4268         
4269         if (this.align === 'right') {
4270             cfg.cls += ' navbar-right';
4271         }
4272         
4273         if (this.inverse) {
4274             cfg.cls += ' navbar-inverse';
4275             
4276         }
4277         
4278         
4279         return cfg;
4280     },
4281     /**
4282     * sets the active Navigation item
4283     * @param {Roo.bootstrap.NavItem} the new current navitem
4284     */
4285     setActiveItem : function(item)
4286     {
4287         var prev = false;
4288         Roo.each(this.navItems, function(v){
4289             if (v == item) {
4290                 return ;
4291             }
4292             if (v.isActive()) {
4293                 v.setActive(false, true);
4294                 prev = v;
4295                 
4296             }
4297             
4298         });
4299
4300         item.setActive(true, true);
4301         this.fireEvent('changed', this, item, prev);
4302         
4303         
4304     },
4305     /**
4306     * gets the active Navigation item
4307     * @return {Roo.bootstrap.NavItem} the current navitem
4308     */
4309     getActive : function()
4310     {
4311         
4312         var prev = false;
4313         Roo.each(this.navItems, function(v){
4314             
4315             if (v.isActive()) {
4316                 prev = v;
4317                 
4318             }
4319             
4320         });
4321         return prev;
4322     },
4323     
4324     indexOfNav : function()
4325     {
4326         
4327         var prev = false;
4328         Roo.each(this.navItems, function(v,i){
4329             
4330             if (v.isActive()) {
4331                 prev = i;
4332                 
4333             }
4334             
4335         });
4336         return prev;
4337     },
4338     /**
4339     * adds a Navigation item
4340     * @param {Roo.bootstrap.NavItem} the navitem to add
4341     */
4342     addItem : function(cfg)
4343     {
4344         var cn = new Roo.bootstrap.NavItem(cfg);
4345         this.register(cn);
4346         cn.parentId = this.id;
4347         cn.onRender(this.el, null);
4348         return cn;
4349     },
4350     /**
4351     * register a Navigation item
4352     * @param {Roo.bootstrap.NavItem} the navitem to add
4353     */
4354     register : function(item)
4355     {
4356         this.navItems.push( item);
4357         item.navId = this.navId;
4358     
4359     },
4360     
4361     /**
4362     * clear all the Navigation item
4363     */
4364    
4365     clearAll : function()
4366     {
4367         this.navItems = [];
4368         this.el.dom.innerHTML = '';
4369     },
4370     
4371     getNavItem: function(tabId)
4372     {
4373         var ret = false;
4374         Roo.each(this.navItems, function(e) {
4375             if (e.tabId == tabId) {
4376                ret =  e;
4377                return false;
4378             }
4379             return true;
4380             
4381         });
4382         return ret;
4383     },
4384     
4385     setActiveNext : function()
4386     {
4387         var i = this.indexOfNav(this.getActive());
4388         if (i > this.navItems.length) {
4389             return;
4390         }
4391         this.setActiveItem(this.navItems[i+1]);
4392     },
4393     setActivePrev : function()
4394     {
4395         var i = this.indexOfNav(this.getActive());
4396         if (i  < 1) {
4397             return;
4398         }
4399         this.setActiveItem(this.navItems[i-1]);
4400     },
4401     clearWasActive : function(except) {
4402         Roo.each(this.navItems, function(e) {
4403             if (e.tabId != except.tabId && e.was_active) {
4404                e.was_active = false;
4405                return false;
4406             }
4407             return true;
4408             
4409         });
4410     },
4411     getWasActive : function ()
4412     {
4413         var r = false;
4414         Roo.each(this.navItems, function(e) {
4415             if (e.was_active) {
4416                r = e;
4417                return false;
4418             }
4419             return true;
4420             
4421         });
4422         return r;
4423     }
4424     
4425     
4426 });
4427
4428  
4429 Roo.apply(Roo.bootstrap.NavGroup, {
4430     
4431     groups: {},
4432      /**
4433     * register a Navigation Group
4434     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4435     */
4436     register : function(navgrp)
4437     {
4438         this.groups[navgrp.navId] = navgrp;
4439         
4440     },
4441     /**
4442     * fetch a Navigation Group based on the navigation ID
4443     * @param {string} the navgroup to add
4444     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4445     */
4446     get: function(navId) {
4447         if (typeof(this.groups[navId]) == 'undefined') {
4448             return false;
4449             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4450         }
4451         return this.groups[navId] ;
4452     }
4453     
4454     
4455     
4456 });
4457
4458  /*
4459  * - LGPL
4460  *
4461  * row
4462  * 
4463  */
4464
4465 /**
4466  * @class Roo.bootstrap.NavItem
4467  * @extends Roo.bootstrap.Component
4468  * Bootstrap Navbar.NavItem class
4469  * @cfg {String} href  link to
4470  * @cfg {String} html content of button
4471  * @cfg {String} badge text inside badge
4472  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4473  * @cfg {String} glyphicon name of glyphicon
4474  * @cfg {String} icon name of font awesome icon
4475  * @cfg {Boolean} active Is item active
4476  * @cfg {Boolean} disabled Is item disabled
4477  
4478  * @cfg {Boolean} preventDefault (true | false) default false
4479  * @cfg {String} tabId the tab that this item activates.
4480  * @cfg {String} tagtype (a|span) render as a href or span?
4481  * @cfg {Boolean} animateRef (true|false) link to element default false  
4482   
4483  * @constructor
4484  * Create a new Navbar Item
4485  * @param {Object} config The config object
4486  */
4487 Roo.bootstrap.NavItem = function(config){
4488     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4489     this.addEvents({
4490         // raw events
4491         /**
4492          * @event click
4493          * The raw click event for the entire grid.
4494          * @param {Roo.EventObject} e
4495          */
4496         "click" : true,
4497          /**
4498             * @event changed
4499             * Fires when the active item active state changes
4500             * @param {Roo.bootstrap.NavItem} this
4501             * @param {boolean} state the new state
4502              
4503          */
4504         'changed': true,
4505         /**
4506             * @event scrollto
4507             * Fires when scroll to element
4508             * @param {Roo.bootstrap.NavItem} this
4509             * @param {Object} options
4510             * @param {Roo.EventObject} e
4511              
4512          */
4513         'scrollto': true
4514     });
4515    
4516 };
4517
4518 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4519     
4520     href: false,
4521     html: '',
4522     badge: '',
4523     icon: false,
4524     glyphicon: false,
4525     active: false,
4526     preventDefault : false,
4527     tabId : false,
4528     tagtype : 'a',
4529     disabled : false,
4530     animateRef : false,
4531     was_active : false,
4532     
4533     getAutoCreate : function(){
4534          
4535         var cfg = {
4536             tag: 'li',
4537             cls: 'nav-item'
4538             
4539         };
4540         
4541         if (this.active) {
4542             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4543         }
4544         if (this.disabled) {
4545             cfg.cls += ' disabled';
4546         }
4547         
4548         if (this.href || this.html || this.glyphicon || this.icon) {
4549             cfg.cn = [
4550                 {
4551                     tag: this.tagtype,
4552                     href : this.href || "#",
4553                     html: this.html || ''
4554                 }
4555             ];
4556             if (this.tagtype == 'a') {
4557                 cfg.cn[0].cls = 'nav-link';
4558             }
4559             if (this.icon) {
4560                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4561             }
4562
4563             if(this.glyphicon) {
4564                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4565             }
4566             
4567             if (this.menu) {
4568                 
4569                 cfg.cn[0].html += " <span class='caret'></span>";
4570              
4571             }
4572             
4573             if (this.badge !== '') {
4574                  
4575                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4576             }
4577         }
4578         
4579         
4580         
4581         return cfg;
4582     },
4583     initEvents: function() 
4584     {
4585         if (typeof (this.menu) != 'undefined') {
4586             this.menu.parentType = this.xtype;
4587             this.menu.triggerEl = this.el;
4588             this.menu = this.addxtype(Roo.apply({}, this.menu));
4589         }
4590         
4591         this.el.select('a',true).on('click', this.onClick, this);
4592         
4593         if(this.tagtype == 'span'){
4594             this.el.select('span',true).on('click', this.onClick, this);
4595         }
4596        
4597         // at this point parent should be available..
4598         this.parent().register(this);
4599     },
4600     
4601     onClick : function(e)
4602     {
4603         if (e.getTarget('.dropdown-menu-item')) {
4604             // did you click on a menu itemm.... - then don't trigger onclick..
4605             return;
4606         }
4607         
4608         if(
4609                 this.preventDefault || 
4610                 this.href == '#' 
4611         ){
4612             Roo.log("NavItem - prevent Default?");
4613             e.preventDefault();
4614         }
4615         
4616         if (this.disabled) {
4617             return;
4618         }
4619         
4620         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4621         if (tg && tg.transition) {
4622             Roo.log("waiting for the transitionend");
4623             return;
4624         }
4625         
4626         
4627         
4628         //Roo.log("fire event clicked");
4629         if(this.fireEvent('click', this, e) === false){
4630             return;
4631         };
4632         
4633         if(this.tagtype == 'span'){
4634             return;
4635         }
4636         
4637         //Roo.log(this.href);
4638         var ael = this.el.select('a',true).first();
4639         //Roo.log(ael);
4640         
4641         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4642             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4643             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4644                 return; // ignore... - it's a 'hash' to another page.
4645             }
4646             Roo.log("NavItem - prevent Default?");
4647             e.preventDefault();
4648             this.scrollToElement(e);
4649         }
4650         
4651         
4652         var p =  this.parent();
4653    
4654         if (['tabs','pills'].indexOf(p.type)!==-1) {
4655             if (typeof(p.setActiveItem) !== 'undefined') {
4656                 p.setActiveItem(this);
4657             }
4658         }
4659         
4660         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4661         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4662             // remove the collapsed menu expand...
4663             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4664         }
4665     },
4666     
4667     isActive: function () {
4668         return this.active
4669     },
4670     setActive : function(state, fire, is_was_active)
4671     {
4672         if (this.active && !state && this.navId) {
4673             this.was_active = true;
4674             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4675             if (nv) {
4676                 nv.clearWasActive(this);
4677             }
4678             
4679         }
4680         this.active = state;
4681         
4682         if (!state ) {
4683             this.el.removeClass('active');
4684         } else if (!this.el.hasClass('active')) {
4685             this.el.addClass('active');
4686         }
4687         if (fire) {
4688             this.fireEvent('changed', this, state);
4689         }
4690         
4691         // show a panel if it's registered and related..
4692         
4693         if (!this.navId || !this.tabId || !state || is_was_active) {
4694             return;
4695         }
4696         
4697         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4698         if (!tg) {
4699             return;
4700         }
4701         var pan = tg.getPanelByName(this.tabId);
4702         if (!pan) {
4703             return;
4704         }
4705         // if we can not flip to new panel - go back to old nav highlight..
4706         if (false == tg.showPanel(pan)) {
4707             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4708             if (nv) {
4709                 var onav = nv.getWasActive();
4710                 if (onav) {
4711                     onav.setActive(true, false, true);
4712                 }
4713             }
4714             
4715         }
4716         
4717         
4718         
4719     },
4720      // this should not be here...
4721     setDisabled : function(state)
4722     {
4723         this.disabled = state;
4724         if (!state ) {
4725             this.el.removeClass('disabled');
4726         } else if (!this.el.hasClass('disabled')) {
4727             this.el.addClass('disabled');
4728         }
4729         
4730     },
4731     
4732     /**
4733      * Fetch the element to display the tooltip on.
4734      * @return {Roo.Element} defaults to this.el
4735      */
4736     tooltipEl : function()
4737     {
4738         return this.el.select('' + this.tagtype + '', true).first();
4739     },
4740     
4741     scrollToElement : function(e)
4742     {
4743         var c = document.body;
4744         
4745         /*
4746          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4747          */
4748         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4749             c = document.documentElement;
4750         }
4751         
4752         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4753         
4754         if(!target){
4755             return;
4756         }
4757
4758         var o = target.calcOffsetsTo(c);
4759         
4760         var options = {
4761             target : target,
4762             value : o[1]
4763         };
4764         
4765         this.fireEvent('scrollto', this, options, e);
4766         
4767         Roo.get(c).scrollTo('top', options.value, true);
4768         
4769         return;
4770     }
4771 });
4772  
4773
4774  /*
4775  * - LGPL
4776  *
4777  * sidebar item
4778  *
4779  *  li
4780  *    <span> icon </span>
4781  *    <span> text </span>
4782  *    <span>badge </span>
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.NavSidebarItem
4787  * @extends Roo.bootstrap.NavItem
4788  * Bootstrap Navbar.NavSidebarItem class
4789  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4790  * {Boolean} open is the menu open
4791  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4792  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4793  * {String} buttonSize (sm|md|lg)the extra classes for the button
4794  * {Boolean} showArrow show arrow next to the text (default true)
4795  * @constructor
4796  * Create a new Navbar Button
4797  * @param {Object} config The config object
4798  */
4799 Roo.bootstrap.NavSidebarItem = function(config){
4800     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4801     this.addEvents({
4802         // raw events
4803         /**
4804          * @event click
4805          * The raw click event for the entire grid.
4806          * @param {Roo.EventObject} e
4807          */
4808         "click" : true,
4809          /**
4810             * @event changed
4811             * Fires when the active item active state changes
4812             * @param {Roo.bootstrap.NavSidebarItem} this
4813             * @param {boolean} state the new state
4814              
4815          */
4816         'changed': true
4817     });
4818    
4819 };
4820
4821 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4822     
4823     badgeWeight : 'default',
4824     
4825     open: false,
4826     
4827     buttonView : false,
4828     
4829     buttonWeight : 'default',
4830     
4831     buttonSize : 'md',
4832     
4833     showArrow : true,
4834     
4835     getAutoCreate : function(){
4836         
4837         
4838         var a = {
4839                 tag: 'a',
4840                 href : this.href || '#',
4841                 cls: '',
4842                 html : '',
4843                 cn : []
4844         };
4845         
4846         if(this.buttonView){
4847             a = {
4848                 tag: 'button',
4849                 href : this.href || '#',
4850                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4851                 html : this.html,
4852                 cn : []
4853             };
4854         }
4855         
4856         var cfg = {
4857             tag: 'li',
4858             cls: '',
4859             cn: [ a ]
4860         };
4861         
4862         if (this.active) {
4863             cfg.cls += ' active';
4864         }
4865         
4866         if (this.disabled) {
4867             cfg.cls += ' disabled';
4868         }
4869         if (this.open) {
4870             cfg.cls += ' open x-open';
4871         }
4872         // left icon..
4873         if (this.glyphicon || this.icon) {
4874             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4875             a.cn.push({ tag : 'i', cls : c }) ;
4876         }
4877         
4878         if(!this.buttonView){
4879             var span = {
4880                 tag: 'span',
4881                 html : this.html || ''
4882             };
4883
4884             a.cn.push(span);
4885             
4886         }
4887         
4888         if (this.badge !== '') {
4889             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4890         }
4891         
4892         if (this.menu) {
4893             
4894             if(this.showArrow){
4895                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4896             }
4897             
4898             a.cls += ' dropdown-toggle treeview' ;
4899         }
4900         
4901         return cfg;
4902     },
4903     
4904     initEvents : function()
4905     { 
4906         if (typeof (this.menu) != 'undefined') {
4907             this.menu.parentType = this.xtype;
4908             this.menu.triggerEl = this.el;
4909             this.menu = this.addxtype(Roo.apply({}, this.menu));
4910         }
4911         
4912         this.el.on('click', this.onClick, this);
4913         
4914         if(this.badge !== ''){
4915             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4916         }
4917         
4918     },
4919     
4920     onClick : function(e)
4921     {
4922         if(this.disabled){
4923             e.preventDefault();
4924             return;
4925         }
4926         
4927         if(this.preventDefault){
4928             e.preventDefault();
4929         }
4930         
4931         this.fireEvent('click', this);
4932     },
4933     
4934     disable : function()
4935     {
4936         this.setDisabled(true);
4937     },
4938     
4939     enable : function()
4940     {
4941         this.setDisabled(false);
4942     },
4943     
4944     setDisabled : function(state)
4945     {
4946         if(this.disabled == state){
4947             return;
4948         }
4949         
4950         this.disabled = state;
4951         
4952         if (state) {
4953             this.el.addClass('disabled');
4954             return;
4955         }
4956         
4957         this.el.removeClass('disabled');
4958         
4959         return;
4960     },
4961     
4962     setActive : function(state)
4963     {
4964         if(this.active == state){
4965             return;
4966         }
4967         
4968         this.active = state;
4969         
4970         if (state) {
4971             this.el.addClass('active');
4972             return;
4973         }
4974         
4975         this.el.removeClass('active');
4976         
4977         return;
4978     },
4979     
4980     isActive: function () 
4981     {
4982         return this.active;
4983     },
4984     
4985     setBadge : function(str)
4986     {
4987         if(!this.badgeEl){
4988             return;
4989         }
4990         
4991         this.badgeEl.dom.innerHTML = str;
4992     }
4993     
4994    
4995      
4996  
4997 });
4998  
4999
5000  /*
5001  * - LGPL
5002  *
5003  * row
5004  * 
5005  */
5006
5007 /**
5008  * @class Roo.bootstrap.Row
5009  * @extends Roo.bootstrap.Component
5010  * Bootstrap Row class (contains columns...)
5011  * 
5012  * @constructor
5013  * Create a new Row
5014  * @param {Object} config The config object
5015  */
5016
5017 Roo.bootstrap.Row = function(config){
5018     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5019 };
5020
5021 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5022     
5023     getAutoCreate : function(){
5024        return {
5025             cls: 'row clearfix'
5026        };
5027     }
5028     
5029     
5030 });
5031
5032  
5033
5034  /*
5035  * - LGPL
5036  *
5037  * element
5038  * 
5039  */
5040
5041 /**
5042  * @class Roo.bootstrap.Element
5043  * @extends Roo.bootstrap.Component
5044  * Bootstrap Element class
5045  * @cfg {String} html contents of the element
5046  * @cfg {String} tag tag of the element
5047  * @cfg {String} cls class of the element
5048  * @cfg {Boolean} preventDefault (true|false) default false
5049  * @cfg {Boolean} clickable (true|false) default false
5050  * 
5051  * @constructor
5052  * Create a new Element
5053  * @param {Object} config The config object
5054  */
5055
5056 Roo.bootstrap.Element = function(config){
5057     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5058     
5059     this.addEvents({
5060         // raw events
5061         /**
5062          * @event click
5063          * When a element is chick
5064          * @param {Roo.bootstrap.Element} this
5065          * @param {Roo.EventObject} e
5066          */
5067         "click" : true
5068     });
5069 };
5070
5071 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5072     
5073     tag: 'div',
5074     cls: '',
5075     html: '',
5076     preventDefault: false, 
5077     clickable: false,
5078     
5079     getAutoCreate : function(){
5080         
5081         var cfg = {
5082             tag: this.tag,
5083             // cls: this.cls, double assign in parent class Component.js :: onRender
5084             html: this.html
5085         };
5086         
5087         return cfg;
5088     },
5089     
5090     initEvents: function() 
5091     {
5092         Roo.bootstrap.Element.superclass.initEvents.call(this);
5093         
5094         if(this.clickable){
5095             this.el.on('click', this.onClick, this);
5096         }
5097         
5098     },
5099     
5100     onClick : function(e)
5101     {
5102         if(this.preventDefault){
5103             e.preventDefault();
5104         }
5105         
5106         this.fireEvent('click', this, e);
5107     },
5108     
5109     getValue : function()
5110     {
5111         return this.el.dom.innerHTML;
5112     },
5113     
5114     setValue : function(value)
5115     {
5116         this.el.dom.innerHTML = value;
5117     }
5118    
5119 });
5120
5121  
5122
5123  /*
5124  * - LGPL
5125  *
5126  * pagination
5127  * 
5128  */
5129
5130 /**
5131  * @class Roo.bootstrap.Pagination
5132  * @extends Roo.bootstrap.Component
5133  * Bootstrap Pagination class
5134  * @cfg {String} size xs | sm | md | lg
5135  * @cfg {Boolean} inverse false | true
5136  * 
5137  * @constructor
5138  * Create a new Pagination
5139  * @param {Object} config The config object
5140  */
5141
5142 Roo.bootstrap.Pagination = function(config){
5143     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5144 };
5145
5146 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5147     
5148     cls: false,
5149     size: false,
5150     inverse: false,
5151     
5152     getAutoCreate : function(){
5153         var cfg = {
5154             tag: 'ul',
5155                 cls: 'pagination'
5156         };
5157         if (this.inverse) {
5158             cfg.cls += ' inverse';
5159         }
5160         if (this.html) {
5161             cfg.html=this.html;
5162         }
5163         if (this.cls) {
5164             cfg.cls += " " + this.cls;
5165         }
5166         return cfg;
5167     }
5168    
5169 });
5170
5171  
5172
5173  /*
5174  * - LGPL
5175  *
5176  * Pagination item
5177  * 
5178  */
5179
5180
5181 /**
5182  * @class Roo.bootstrap.PaginationItem
5183  * @extends Roo.bootstrap.Component
5184  * Bootstrap PaginationItem class
5185  * @cfg {String} html text
5186  * @cfg {String} href the link
5187  * @cfg {Boolean} preventDefault (true | false) default true
5188  * @cfg {Boolean} active (true | false) default false
5189  * @cfg {Boolean} disabled default false
5190  * 
5191  * 
5192  * @constructor
5193  * Create a new PaginationItem
5194  * @param {Object} config The config object
5195  */
5196
5197
5198 Roo.bootstrap.PaginationItem = function(config){
5199     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5200     this.addEvents({
5201         // raw events
5202         /**
5203          * @event click
5204          * The raw click event for the entire grid.
5205          * @param {Roo.EventObject} e
5206          */
5207         "click" : true
5208     });
5209 };
5210
5211 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5212     
5213     href : false,
5214     html : false,
5215     preventDefault: true,
5216     active : false,
5217     cls : false,
5218     disabled: false,
5219     
5220     getAutoCreate : function(){
5221         var cfg= {
5222             tag: 'li',
5223             cn: [
5224                 {
5225                     tag : 'a',
5226                     href : this.href ? this.href : '#',
5227                     html : this.html ? this.html : ''
5228                 }
5229             ]
5230         };
5231         
5232         if(this.cls){
5233             cfg.cls = this.cls;
5234         }
5235         
5236         if(this.disabled){
5237             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5238         }
5239         
5240         if(this.active){
5241             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5242         }
5243         
5244         return cfg;
5245     },
5246     
5247     initEvents: function() {
5248         
5249         this.el.on('click', this.onClick, this);
5250         
5251     },
5252     onClick : function(e)
5253     {
5254         Roo.log('PaginationItem on click ');
5255         if(this.preventDefault){
5256             e.preventDefault();
5257         }
5258         
5259         if(this.disabled){
5260             return;
5261         }
5262         
5263         this.fireEvent('click', this, e);
5264     }
5265    
5266 });
5267
5268  
5269
5270  /*
5271  * - LGPL
5272  *
5273  * slider
5274  * 
5275  */
5276
5277
5278 /**
5279  * @class Roo.bootstrap.Slider
5280  * @extends Roo.bootstrap.Component
5281  * Bootstrap Slider class
5282  *    
5283  * @constructor
5284  * Create a new Slider
5285  * @param {Object} config The config object
5286  */
5287
5288 Roo.bootstrap.Slider = function(config){
5289     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5290 };
5291
5292 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5293     
5294     getAutoCreate : function(){
5295         
5296         var cfg = {
5297             tag: 'div',
5298             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5299             cn: [
5300                 {
5301                     tag: 'a',
5302                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5303                 }
5304             ]
5305         };
5306         
5307         return cfg;
5308     }
5309    
5310 });
5311
5312  /*
5313  * Based on:
5314  * Ext JS Library 1.1.1
5315  * Copyright(c) 2006-2007, Ext JS, LLC.
5316  *
5317  * Originally Released Under LGPL - original licence link has changed is not relivant.
5318  *
5319  * Fork - LGPL
5320  * <script type="text/javascript">
5321  */
5322  
5323
5324 /**
5325  * @class Roo.grid.ColumnModel
5326  * @extends Roo.util.Observable
5327  * This is the default implementation of a ColumnModel used by the Grid. It defines
5328  * the columns in the grid.
5329  * <br>Usage:<br>
5330  <pre><code>
5331  var colModel = new Roo.grid.ColumnModel([
5332         {header: "Ticker", width: 60, sortable: true, locked: true},
5333         {header: "Company Name", width: 150, sortable: true},
5334         {header: "Market Cap.", width: 100, sortable: true},
5335         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5336         {header: "Employees", width: 100, sortable: true, resizable: false}
5337  ]);
5338  </code></pre>
5339  * <p>
5340  
5341  * The config options listed for this class are options which may appear in each
5342  * individual column definition.
5343  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5344  * @constructor
5345  * @param {Object} config An Array of column config objects. See this class's
5346  * config objects for details.
5347 */
5348 Roo.grid.ColumnModel = function(config){
5349         /**
5350      * The config passed into the constructor
5351      */
5352     this.config = config;
5353     this.lookup = {};
5354
5355     // if no id, create one
5356     // if the column does not have a dataIndex mapping,
5357     // map it to the order it is in the config
5358     for(var i = 0, len = config.length; i < len; i++){
5359         var c = config[i];
5360         if(typeof c.dataIndex == "undefined"){
5361             c.dataIndex = i;
5362         }
5363         if(typeof c.renderer == "string"){
5364             c.renderer = Roo.util.Format[c.renderer];
5365         }
5366         if(typeof c.id == "undefined"){
5367             c.id = Roo.id();
5368         }
5369         if(c.editor && c.editor.xtype){
5370             c.editor  = Roo.factory(c.editor, Roo.grid);
5371         }
5372         if(c.editor && c.editor.isFormField){
5373             c.editor = new Roo.grid.GridEditor(c.editor);
5374         }
5375         this.lookup[c.id] = c;
5376     }
5377
5378     /**
5379      * The width of columns which have no width specified (defaults to 100)
5380      * @type Number
5381      */
5382     this.defaultWidth = 100;
5383
5384     /**
5385      * Default sortable of columns which have no sortable specified (defaults to false)
5386      * @type Boolean
5387      */
5388     this.defaultSortable = false;
5389
5390     this.addEvents({
5391         /**
5392              * @event widthchange
5393              * Fires when the width of a column changes.
5394              * @param {ColumnModel} this
5395              * @param {Number} columnIndex The column index
5396              * @param {Number} newWidth The new width
5397              */
5398             "widthchange": true,
5399         /**
5400              * @event headerchange
5401              * Fires when the text of a header changes.
5402              * @param {ColumnModel} this
5403              * @param {Number} columnIndex The column index
5404              * @param {Number} newText The new header text
5405              */
5406             "headerchange": true,
5407         /**
5408              * @event hiddenchange
5409              * Fires when a column is hidden or "unhidden".
5410              * @param {ColumnModel} this
5411              * @param {Number} columnIndex The column index
5412              * @param {Boolean} hidden true if hidden, false otherwise
5413              */
5414             "hiddenchange": true,
5415             /**
5416          * @event columnmoved
5417          * Fires when a column is moved.
5418          * @param {ColumnModel} this
5419          * @param {Number} oldIndex
5420          * @param {Number} newIndex
5421          */
5422         "columnmoved" : true,
5423         /**
5424          * @event columlockchange
5425          * Fires when a column's locked state is changed
5426          * @param {ColumnModel} this
5427          * @param {Number} colIndex
5428          * @param {Boolean} locked true if locked
5429          */
5430         "columnlockchange" : true
5431     });
5432     Roo.grid.ColumnModel.superclass.constructor.call(this);
5433 };
5434 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5435     /**
5436      * @cfg {String} header The header text to display in the Grid view.
5437      */
5438     /**
5439      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5440      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5441      * specified, the column's index is used as an index into the Record's data Array.
5442      */
5443     /**
5444      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5445      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5446      */
5447     /**
5448      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5449      * Defaults to the value of the {@link #defaultSortable} property.
5450      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5451      */
5452     /**
5453      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5454      */
5455     /**
5456      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5457      */
5458     /**
5459      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5460      */
5461     /**
5462      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5463      */
5464     /**
5465      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5466      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5467      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5468      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5469      */
5470        /**
5471      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5472      */
5473     /**
5474      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5475      */
5476     /**
5477      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5478      */
5479     /**
5480      * @cfg {String} cursor (Optional)
5481      */
5482     /**
5483      * @cfg {String} tooltip (Optional)
5484      */
5485     /**
5486      * @cfg {Number} xs (Optional)
5487      */
5488     /**
5489      * @cfg {Number} sm (Optional)
5490      */
5491     /**
5492      * @cfg {Number} md (Optional)
5493      */
5494     /**
5495      * @cfg {Number} lg (Optional)
5496      */
5497     /**
5498      * Returns the id of the column at the specified index.
5499      * @param {Number} index The column index
5500      * @return {String} the id
5501      */
5502     getColumnId : function(index){
5503         return this.config[index].id;
5504     },
5505
5506     /**
5507      * Returns the column for a specified id.
5508      * @param {String} id The column id
5509      * @return {Object} the column
5510      */
5511     getColumnById : function(id){
5512         return this.lookup[id];
5513     },
5514
5515     
5516     /**
5517      * Returns the column for a specified dataIndex.
5518      * @param {String} dataIndex The column dataIndex
5519      * @return {Object|Boolean} the column or false if not found
5520      */
5521     getColumnByDataIndex: function(dataIndex){
5522         var index = this.findColumnIndex(dataIndex);
5523         return index > -1 ? this.config[index] : false;
5524     },
5525     
5526     /**
5527      * Returns the index for a specified column id.
5528      * @param {String} id The column id
5529      * @return {Number} the index, or -1 if not found
5530      */
5531     getIndexById : function(id){
5532         for(var i = 0, len = this.config.length; i < len; i++){
5533             if(this.config[i].id == id){
5534                 return i;
5535             }
5536         }
5537         return -1;
5538     },
5539     
5540     /**
5541      * Returns the index for a specified column dataIndex.
5542      * @param {String} dataIndex The column dataIndex
5543      * @return {Number} the index, or -1 if not found
5544      */
5545     
5546     findColumnIndex : function(dataIndex){
5547         for(var i = 0, len = this.config.length; i < len; i++){
5548             if(this.config[i].dataIndex == dataIndex){
5549                 return i;
5550             }
5551         }
5552         return -1;
5553     },
5554     
5555     
5556     moveColumn : function(oldIndex, newIndex){
5557         var c = this.config[oldIndex];
5558         this.config.splice(oldIndex, 1);
5559         this.config.splice(newIndex, 0, c);
5560         this.dataMap = null;
5561         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5562     },
5563
5564     isLocked : function(colIndex){
5565         return this.config[colIndex].locked === true;
5566     },
5567
5568     setLocked : function(colIndex, value, suppressEvent){
5569         if(this.isLocked(colIndex) == value){
5570             return;
5571         }
5572         this.config[colIndex].locked = value;
5573         if(!suppressEvent){
5574             this.fireEvent("columnlockchange", this, colIndex, value);
5575         }
5576     },
5577
5578     getTotalLockedWidth : function(){
5579         var totalWidth = 0;
5580         for(var i = 0; i < this.config.length; i++){
5581             if(this.isLocked(i) && !this.isHidden(i)){
5582                 this.totalWidth += this.getColumnWidth(i);
5583             }
5584         }
5585         return totalWidth;
5586     },
5587
5588     getLockedCount : function(){
5589         for(var i = 0, len = this.config.length; i < len; i++){
5590             if(!this.isLocked(i)){
5591                 return i;
5592             }
5593         }
5594         
5595         return this.config.length;
5596     },
5597
5598     /**
5599      * Returns the number of columns.
5600      * @return {Number}
5601      */
5602     getColumnCount : function(visibleOnly){
5603         if(visibleOnly === true){
5604             var c = 0;
5605             for(var i = 0, len = this.config.length; i < len; i++){
5606                 if(!this.isHidden(i)){
5607                     c++;
5608                 }
5609             }
5610             return c;
5611         }
5612         return this.config.length;
5613     },
5614
5615     /**
5616      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5617      * @param {Function} fn
5618      * @param {Object} scope (optional)
5619      * @return {Array} result
5620      */
5621     getColumnsBy : function(fn, scope){
5622         var r = [];
5623         for(var i = 0, len = this.config.length; i < len; i++){
5624             var c = this.config[i];
5625             if(fn.call(scope||this, c, i) === true){
5626                 r[r.length] = c;
5627             }
5628         }
5629         return r;
5630     },
5631
5632     /**
5633      * Returns true if the specified column is sortable.
5634      * @param {Number} col The column index
5635      * @return {Boolean}
5636      */
5637     isSortable : function(col){
5638         if(typeof this.config[col].sortable == "undefined"){
5639             return this.defaultSortable;
5640         }
5641         return this.config[col].sortable;
5642     },
5643
5644     /**
5645      * Returns the rendering (formatting) function defined for the column.
5646      * @param {Number} col The column index.
5647      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5648      */
5649     getRenderer : function(col){
5650         if(!this.config[col].renderer){
5651             return Roo.grid.ColumnModel.defaultRenderer;
5652         }
5653         return this.config[col].renderer;
5654     },
5655
5656     /**
5657      * Sets the rendering (formatting) function for a column.
5658      * @param {Number} col The column index
5659      * @param {Function} fn The function to use to process the cell's raw data
5660      * to return HTML markup for the grid view. The render function is called with
5661      * the following parameters:<ul>
5662      * <li>Data value.</li>
5663      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5664      * <li>css A CSS style string to apply to the table cell.</li>
5665      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5666      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5667      * <li>Row index</li>
5668      * <li>Column index</li>
5669      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5670      */
5671     setRenderer : function(col, fn){
5672         this.config[col].renderer = fn;
5673     },
5674
5675     /**
5676      * Returns the width for the specified column.
5677      * @param {Number} col The column index
5678      * @return {Number}
5679      */
5680     getColumnWidth : function(col){
5681         return this.config[col].width * 1 || this.defaultWidth;
5682     },
5683
5684     /**
5685      * Sets the width for a column.
5686      * @param {Number} col The column index
5687      * @param {Number} width The new width
5688      */
5689     setColumnWidth : function(col, width, suppressEvent){
5690         this.config[col].width = width;
5691         this.totalWidth = null;
5692         if(!suppressEvent){
5693              this.fireEvent("widthchange", this, col, width);
5694         }
5695     },
5696
5697     /**
5698      * Returns the total width of all columns.
5699      * @param {Boolean} includeHidden True to include hidden column widths
5700      * @return {Number}
5701      */
5702     getTotalWidth : function(includeHidden){
5703         if(!this.totalWidth){
5704             this.totalWidth = 0;
5705             for(var i = 0, len = this.config.length; i < len; i++){
5706                 if(includeHidden || !this.isHidden(i)){
5707                     this.totalWidth += this.getColumnWidth(i);
5708                 }
5709             }
5710         }
5711         return this.totalWidth;
5712     },
5713
5714     /**
5715      * Returns the header for the specified column.
5716      * @param {Number} col The column index
5717      * @return {String}
5718      */
5719     getColumnHeader : function(col){
5720         return this.config[col].header;
5721     },
5722
5723     /**
5724      * Sets the header for a column.
5725      * @param {Number} col The column index
5726      * @param {String} header The new header
5727      */
5728     setColumnHeader : function(col, header){
5729         this.config[col].header = header;
5730         this.fireEvent("headerchange", this, col, header);
5731     },
5732
5733     /**
5734      * Returns the tooltip for the specified column.
5735      * @param {Number} col The column index
5736      * @return {String}
5737      */
5738     getColumnTooltip : function(col){
5739             return this.config[col].tooltip;
5740     },
5741     /**
5742      * Sets the tooltip for a column.
5743      * @param {Number} col The column index
5744      * @param {String} tooltip The new tooltip
5745      */
5746     setColumnTooltip : function(col, tooltip){
5747             this.config[col].tooltip = tooltip;
5748     },
5749
5750     /**
5751      * Returns the dataIndex for the specified column.
5752      * @param {Number} col The column index
5753      * @return {Number}
5754      */
5755     getDataIndex : function(col){
5756         return this.config[col].dataIndex;
5757     },
5758
5759     /**
5760      * Sets the dataIndex for a column.
5761      * @param {Number} col The column index
5762      * @param {Number} dataIndex The new dataIndex
5763      */
5764     setDataIndex : function(col, dataIndex){
5765         this.config[col].dataIndex = dataIndex;
5766     },
5767
5768     
5769     
5770     /**
5771      * Returns true if the cell is editable.
5772      * @param {Number} colIndex The column index
5773      * @param {Number} rowIndex The row index - this is nto actually used..?
5774      * @return {Boolean}
5775      */
5776     isCellEditable : function(colIndex, rowIndex){
5777         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5778     },
5779
5780     /**
5781      * Returns the editor defined for the cell/column.
5782      * return false or null to disable editing.
5783      * @param {Number} colIndex The column index
5784      * @param {Number} rowIndex The row index
5785      * @return {Object}
5786      */
5787     getCellEditor : function(colIndex, rowIndex){
5788         return this.config[colIndex].editor;
5789     },
5790
5791     /**
5792      * Sets if a column is editable.
5793      * @param {Number} col The column index
5794      * @param {Boolean} editable True if the column is editable
5795      */
5796     setEditable : function(col, editable){
5797         this.config[col].editable = editable;
5798     },
5799
5800
5801     /**
5802      * Returns true if the column is hidden.
5803      * @param {Number} colIndex The column index
5804      * @return {Boolean}
5805      */
5806     isHidden : function(colIndex){
5807         return this.config[colIndex].hidden;
5808     },
5809
5810
5811     /**
5812      * Returns true if the column width cannot be changed
5813      */
5814     isFixed : function(colIndex){
5815         return this.config[colIndex].fixed;
5816     },
5817
5818     /**
5819      * Returns true if the column can be resized
5820      * @return {Boolean}
5821      */
5822     isResizable : function(colIndex){
5823         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5824     },
5825     /**
5826      * Sets if a column is hidden.
5827      * @param {Number} colIndex The column index
5828      * @param {Boolean} hidden True if the column is hidden
5829      */
5830     setHidden : function(colIndex, hidden){
5831         this.config[colIndex].hidden = hidden;
5832         this.totalWidth = null;
5833         this.fireEvent("hiddenchange", this, colIndex, hidden);
5834     },
5835
5836     /**
5837      * Sets the editor for a column.
5838      * @param {Number} col The column index
5839      * @param {Object} editor The editor object
5840      */
5841     setEditor : function(col, editor){
5842         this.config[col].editor = editor;
5843     }
5844 });
5845
5846 Roo.grid.ColumnModel.defaultRenderer = function(value)
5847 {
5848     if(typeof value == "object") {
5849         return value;
5850     }
5851         if(typeof value == "string" && value.length < 1){
5852             return "&#160;";
5853         }
5854     
5855         return String.format("{0}", value);
5856 };
5857
5858 // Alias for backwards compatibility
5859 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5860 /*
5861  * Based on:
5862  * Ext JS Library 1.1.1
5863  * Copyright(c) 2006-2007, Ext JS, LLC.
5864  *
5865  * Originally Released Under LGPL - original licence link has changed is not relivant.
5866  *
5867  * Fork - LGPL
5868  * <script type="text/javascript">
5869  */
5870  
5871 /**
5872  * @class Roo.LoadMask
5873  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5874  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5875  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5876  * element's UpdateManager load indicator and will be destroyed after the initial load.
5877  * @constructor
5878  * Create a new LoadMask
5879  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5880  * @param {Object} config The config object
5881  */
5882 Roo.LoadMask = function(el, config){
5883     this.el = Roo.get(el);
5884     Roo.apply(this, config);
5885     if(this.store){
5886         this.store.on('beforeload', this.onBeforeLoad, this);
5887         this.store.on('load', this.onLoad, this);
5888         this.store.on('loadexception', this.onLoadException, this);
5889         this.removeMask = false;
5890     }else{
5891         var um = this.el.getUpdateManager();
5892         um.showLoadIndicator = false; // disable the default indicator
5893         um.on('beforeupdate', this.onBeforeLoad, this);
5894         um.on('update', this.onLoad, this);
5895         um.on('failure', this.onLoad, this);
5896         this.removeMask = true;
5897     }
5898 };
5899
5900 Roo.LoadMask.prototype = {
5901     /**
5902      * @cfg {Boolean} removeMask
5903      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5904      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5905      */
5906     /**
5907      * @cfg {String} msg
5908      * The text to display in a centered loading message box (defaults to 'Loading...')
5909      */
5910     msg : 'Loading...',
5911     /**
5912      * @cfg {String} msgCls
5913      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5914      */
5915     msgCls : 'x-mask-loading',
5916
5917     /**
5918      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5919      * @type Boolean
5920      */
5921     disabled: false,
5922
5923     /**
5924      * Disables the mask to prevent it from being displayed
5925      */
5926     disable : function(){
5927        this.disabled = true;
5928     },
5929
5930     /**
5931      * Enables the mask so that it can be displayed
5932      */
5933     enable : function(){
5934         this.disabled = false;
5935     },
5936     
5937     onLoadException : function()
5938     {
5939         Roo.log(arguments);
5940         
5941         if (typeof(arguments[3]) != 'undefined') {
5942             Roo.MessageBox.alert("Error loading",arguments[3]);
5943         } 
5944         /*
5945         try {
5946             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5947                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5948             }   
5949         } catch(e) {
5950             
5951         }
5952         */
5953     
5954         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5955     },
5956     // private
5957     onLoad : function()
5958     {
5959         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5960     },
5961
5962     // private
5963     onBeforeLoad : function(){
5964         if(!this.disabled){
5965             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5966         }
5967     },
5968
5969     // private
5970     destroy : function(){
5971         if(this.store){
5972             this.store.un('beforeload', this.onBeforeLoad, this);
5973             this.store.un('load', this.onLoad, this);
5974             this.store.un('loadexception', this.onLoadException, this);
5975         }else{
5976             var um = this.el.getUpdateManager();
5977             um.un('beforeupdate', this.onBeforeLoad, this);
5978             um.un('update', this.onLoad, this);
5979             um.un('failure', this.onLoad, this);
5980         }
5981     }
5982 };/*
5983  * - LGPL
5984  *
5985  * table
5986  * 
5987  */
5988
5989 /**
5990  * @class Roo.bootstrap.Table
5991  * @extends Roo.bootstrap.Component
5992  * Bootstrap Table class
5993  * @cfg {String} cls table class
5994  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5995  * @cfg {String} bgcolor Specifies the background color for a table
5996  * @cfg {Number} border Specifies whether the table cells should have borders or not
5997  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5998  * @cfg {Number} cellspacing Specifies the space between cells
5999  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6000  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6001  * @cfg {String} sortable Specifies that the table should be sortable
6002  * @cfg {String} summary Specifies a summary of the content of a table
6003  * @cfg {Number} width Specifies the width of a table
6004  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6005  * 
6006  * @cfg {boolean} striped Should the rows be alternative striped
6007  * @cfg {boolean} bordered Add borders to the table
6008  * @cfg {boolean} hover Add hover highlighting
6009  * @cfg {boolean} condensed Format condensed
6010  * @cfg {boolean} responsive Format condensed
6011  * @cfg {Boolean} loadMask (true|false) default false
6012  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6013  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6014  * @cfg {Boolean} rowSelection (true|false) default false
6015  * @cfg {Boolean} cellSelection (true|false) default false
6016  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6017  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6018  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6019  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6020  
6021  * 
6022  * @constructor
6023  * Create a new Table
6024  * @param {Object} config The config object
6025  */
6026
6027 Roo.bootstrap.Table = function(config){
6028     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6029     
6030   
6031     
6032     // BC...
6033     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6034     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6035     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6036     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6037     
6038     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6039     if (this.sm) {
6040         this.sm.grid = this;
6041         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6042         this.sm = this.selModel;
6043         this.sm.xmodule = this.xmodule || false;
6044     }
6045     
6046     if (this.cm && typeof(this.cm.config) == 'undefined') {
6047         this.colModel = new Roo.grid.ColumnModel(this.cm);
6048         this.cm = this.colModel;
6049         this.cm.xmodule = this.xmodule || false;
6050     }
6051     if (this.store) {
6052         this.store= Roo.factory(this.store, Roo.data);
6053         this.ds = this.store;
6054         this.ds.xmodule = this.xmodule || false;
6055          
6056     }
6057     if (this.footer && this.store) {
6058         this.footer.dataSource = this.ds;
6059         this.footer = Roo.factory(this.footer);
6060     }
6061     
6062     /** @private */
6063     this.addEvents({
6064         /**
6065          * @event cellclick
6066          * Fires when a cell is clicked
6067          * @param {Roo.bootstrap.Table} this
6068          * @param {Roo.Element} el
6069          * @param {Number} rowIndex
6070          * @param {Number} columnIndex
6071          * @param {Roo.EventObject} e
6072          */
6073         "cellclick" : true,
6074         /**
6075          * @event celldblclick
6076          * Fires when a cell is double clicked
6077          * @param {Roo.bootstrap.Table} this
6078          * @param {Roo.Element} el
6079          * @param {Number} rowIndex
6080          * @param {Number} columnIndex
6081          * @param {Roo.EventObject} e
6082          */
6083         "celldblclick" : true,
6084         /**
6085          * @event rowclick
6086          * Fires when a row is clicked
6087          * @param {Roo.bootstrap.Table} this
6088          * @param {Roo.Element} el
6089          * @param {Number} rowIndex
6090          * @param {Roo.EventObject} e
6091          */
6092         "rowclick" : true,
6093         /**
6094          * @event rowdblclick
6095          * Fires when a row is double clicked
6096          * @param {Roo.bootstrap.Table} this
6097          * @param {Roo.Element} el
6098          * @param {Number} rowIndex
6099          * @param {Roo.EventObject} e
6100          */
6101         "rowdblclick" : true,
6102         /**
6103          * @event mouseover
6104          * Fires when a mouseover occur
6105          * @param {Roo.bootstrap.Table} this
6106          * @param {Roo.Element} el
6107          * @param {Number} rowIndex
6108          * @param {Number} columnIndex
6109          * @param {Roo.EventObject} e
6110          */
6111         "mouseover" : true,
6112         /**
6113          * @event mouseout
6114          * Fires when a mouseout occur
6115          * @param {Roo.bootstrap.Table} this
6116          * @param {Roo.Element} el
6117          * @param {Number} rowIndex
6118          * @param {Number} columnIndex
6119          * @param {Roo.EventObject} e
6120          */
6121         "mouseout" : true,
6122         /**
6123          * @event rowclass
6124          * Fires when a row is rendered, so you can change add a style to it.
6125          * @param {Roo.bootstrap.Table} this
6126          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6127          */
6128         'rowclass' : true,
6129           /**
6130          * @event rowsrendered
6131          * Fires when all the  rows have been rendered
6132          * @param {Roo.bootstrap.Table} this
6133          */
6134         'rowsrendered' : true,
6135         /**
6136          * @event contextmenu
6137          * The raw contextmenu event for the entire grid.
6138          * @param {Roo.EventObject} e
6139          */
6140         "contextmenu" : true,
6141         /**
6142          * @event rowcontextmenu
6143          * Fires when a row is right clicked
6144          * @param {Roo.bootstrap.Table} this
6145          * @param {Number} rowIndex
6146          * @param {Roo.EventObject} e
6147          */
6148         "rowcontextmenu" : true,
6149         /**
6150          * @event cellcontextmenu
6151          * Fires when a cell is right clicked
6152          * @param {Roo.bootstrap.Table} this
6153          * @param {Number} rowIndex
6154          * @param {Number} cellIndex
6155          * @param {Roo.EventObject} e
6156          */
6157          "cellcontextmenu" : true,
6158          /**
6159          * @event headercontextmenu
6160          * Fires when a header is right clicked
6161          * @param {Roo.bootstrap.Table} this
6162          * @param {Number} columnIndex
6163          * @param {Roo.EventObject} e
6164          */
6165         "headercontextmenu" : true
6166     });
6167 };
6168
6169 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6170     
6171     cls: false,
6172     align: false,
6173     bgcolor: false,
6174     border: false,
6175     cellpadding: false,
6176     cellspacing: false,
6177     frame: false,
6178     rules: false,
6179     sortable: false,
6180     summary: false,
6181     width: false,
6182     striped : false,
6183     scrollBody : false,
6184     bordered: false,
6185     hover:  false,
6186     condensed : false,
6187     responsive : false,
6188     sm : false,
6189     cm : false,
6190     store : false,
6191     loadMask : false,
6192     footerShow : true,
6193     headerShow : true,
6194   
6195     rowSelection : false,
6196     cellSelection : false,
6197     layout : false,
6198     
6199     // Roo.Element - the tbody
6200     mainBody: false,
6201     // Roo.Element - thead element
6202     mainHead: false,
6203     
6204     container: false, // used by gridpanel...
6205     
6206     lazyLoad : false,
6207     
6208     CSS : Roo.util.CSS,
6209     
6210     auto_hide_footer : false,
6211     
6212     getAutoCreate : function()
6213     {
6214         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6215         
6216         cfg = {
6217             tag: 'table',
6218             cls : 'table',
6219             cn : []
6220         };
6221         if (this.scrollBody) {
6222             cfg.cls += ' table-body-fixed';
6223         }    
6224         if (this.striped) {
6225             cfg.cls += ' table-striped';
6226         }
6227         
6228         if (this.hover) {
6229             cfg.cls += ' table-hover';
6230         }
6231         if (this.bordered) {
6232             cfg.cls += ' table-bordered';
6233         }
6234         if (this.condensed) {
6235             cfg.cls += ' table-condensed';
6236         }
6237         if (this.responsive) {
6238             cfg.cls += ' table-responsive';
6239         }
6240         
6241         if (this.cls) {
6242             cfg.cls+=  ' ' +this.cls;
6243         }
6244         
6245         // this lot should be simplifed...
6246         var _t = this;
6247         var cp = [
6248             'align',
6249             'bgcolor',
6250             'border',
6251             'cellpadding',
6252             'cellspacing',
6253             'frame',
6254             'rules',
6255             'sortable',
6256             'summary',
6257             'width'
6258         ].forEach(function(k) {
6259             if (_t[k]) {
6260                 cfg[k] = _t[k];
6261             }
6262         });
6263         
6264         
6265         if (this.layout) {
6266             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6267         }
6268         
6269         if(this.store || this.cm){
6270             if(this.headerShow){
6271                 cfg.cn.push(this.renderHeader());
6272             }
6273             
6274             cfg.cn.push(this.renderBody());
6275             
6276             if(this.footerShow){
6277                 cfg.cn.push(this.renderFooter());
6278             }
6279             // where does this come from?
6280             //cfg.cls+=  ' TableGrid';
6281         }
6282         
6283         return { cn : [ cfg ] };
6284     },
6285     
6286     initEvents : function()
6287     {   
6288         if(!this.store || !this.cm){
6289             return;
6290         }
6291         if (this.selModel) {
6292             this.selModel.initEvents();
6293         }
6294         
6295         
6296         //Roo.log('initEvents with ds!!!!');
6297         
6298         this.mainBody = this.el.select('tbody', true).first();
6299         this.mainHead = this.el.select('thead', true).first();
6300         this.mainFoot = this.el.select('tfoot', true).first();
6301         
6302         
6303         
6304         var _this = this;
6305         
6306         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6307             e.on('click', _this.sort, _this);
6308         });
6309         
6310         this.mainBody.on("click", this.onClick, this);
6311         this.mainBody.on("dblclick", this.onDblClick, this);
6312         
6313         // why is this done????? = it breaks dialogs??
6314         //this.parent().el.setStyle('position', 'relative');
6315         
6316         
6317         if (this.footer) {
6318             this.footer.parentId = this.id;
6319             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6320             
6321             if(this.lazyLoad){
6322                 this.el.select('tfoot tr td').first().addClass('hide');
6323             }
6324         } 
6325         
6326         if(this.loadMask) {
6327             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6328         }
6329         
6330         this.store.on('load', this.onLoad, this);
6331         this.store.on('beforeload', this.onBeforeLoad, this);
6332         this.store.on('update', this.onUpdate, this);
6333         this.store.on('add', this.onAdd, this);
6334         this.store.on("clear", this.clear, this);
6335         
6336         this.el.on("contextmenu", this.onContextMenu, this);
6337         
6338         this.mainBody.on('scroll', this.onBodyScroll, this);
6339         
6340         this.cm.on("headerchange", this.onHeaderChange, this);
6341         
6342         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6343         
6344     },
6345     
6346     onContextMenu : function(e, t)
6347     {
6348         this.processEvent("contextmenu", e);
6349     },
6350     
6351     processEvent : function(name, e)
6352     {
6353         if (name != 'touchstart' ) {
6354             this.fireEvent(name, e);    
6355         }
6356         
6357         var t = e.getTarget();
6358         
6359         var cell = Roo.get(t);
6360         
6361         if(!cell){
6362             return;
6363         }
6364         
6365         if(cell.findParent('tfoot', false, true)){
6366             return;
6367         }
6368         
6369         if(cell.findParent('thead', false, true)){
6370             
6371             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6372                 cell = Roo.get(t).findParent('th', false, true);
6373                 if (!cell) {
6374                     Roo.log("failed to find th in thead?");
6375                     Roo.log(e.getTarget());
6376                     return;
6377                 }
6378             }
6379             
6380             var cellIndex = cell.dom.cellIndex;
6381             
6382             var ename = name == 'touchstart' ? 'click' : name;
6383             this.fireEvent("header" + ename, this, cellIndex, e);
6384             
6385             return;
6386         }
6387         
6388         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6389             cell = Roo.get(t).findParent('td', false, true);
6390             if (!cell) {
6391                 Roo.log("failed to find th in tbody?");
6392                 Roo.log(e.getTarget());
6393                 return;
6394             }
6395         }
6396         
6397         var row = cell.findParent('tr', false, true);
6398         var cellIndex = cell.dom.cellIndex;
6399         var rowIndex = row.dom.rowIndex - 1;
6400         
6401         if(row !== false){
6402             
6403             this.fireEvent("row" + name, this, rowIndex, e);
6404             
6405             if(cell !== false){
6406             
6407                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6408             }
6409         }
6410         
6411     },
6412     
6413     onMouseover : function(e, el)
6414     {
6415         var cell = Roo.get(el);
6416         
6417         if(!cell){
6418             return;
6419         }
6420         
6421         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6422             cell = cell.findParent('td', false, true);
6423         }
6424         
6425         var row = cell.findParent('tr', false, true);
6426         var cellIndex = cell.dom.cellIndex;
6427         var rowIndex = row.dom.rowIndex - 1; // start from 0
6428         
6429         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6430         
6431     },
6432     
6433     onMouseout : function(e, el)
6434     {
6435         var cell = Roo.get(el);
6436         
6437         if(!cell){
6438             return;
6439         }
6440         
6441         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6442             cell = cell.findParent('td', false, true);
6443         }
6444         
6445         var row = cell.findParent('tr', false, true);
6446         var cellIndex = cell.dom.cellIndex;
6447         var rowIndex = row.dom.rowIndex - 1; // start from 0
6448         
6449         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6450         
6451     },
6452     
6453     onClick : function(e, el)
6454     {
6455         var cell = Roo.get(el);
6456         
6457         if(!cell || (!this.cellSelection && !this.rowSelection)){
6458             return;
6459         }
6460         
6461         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462             cell = cell.findParent('td', false, true);
6463         }
6464         
6465         if(!cell || typeof(cell) == 'undefined'){
6466             return;
6467         }
6468         
6469         var row = cell.findParent('tr', false, true);
6470         
6471         if(!row || typeof(row) == 'undefined'){
6472             return;
6473         }
6474         
6475         var cellIndex = cell.dom.cellIndex;
6476         var rowIndex = this.getRowIndex(row);
6477         
6478         // why??? - should these not be based on SelectionModel?
6479         if(this.cellSelection){
6480             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6481         }
6482         
6483         if(this.rowSelection){
6484             this.fireEvent('rowclick', this, row, rowIndex, e);
6485         }
6486         
6487         
6488     },
6489         
6490     onDblClick : function(e,el)
6491     {
6492         var cell = Roo.get(el);
6493         
6494         if(!cell || (!this.cellSelection && !this.rowSelection)){
6495             return;
6496         }
6497         
6498         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6499             cell = cell.findParent('td', false, true);
6500         }
6501         
6502         if(!cell || typeof(cell) == 'undefined'){
6503             return;
6504         }
6505         
6506         var row = cell.findParent('tr', false, true);
6507         
6508         if(!row || typeof(row) == 'undefined'){
6509             return;
6510         }
6511         
6512         var cellIndex = cell.dom.cellIndex;
6513         var rowIndex = this.getRowIndex(row);
6514         
6515         if(this.cellSelection){
6516             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6517         }
6518         
6519         if(this.rowSelection){
6520             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6521         }
6522     },
6523     
6524     sort : function(e,el)
6525     {
6526         var col = Roo.get(el);
6527         
6528         if(!col.hasClass('sortable')){
6529             return;
6530         }
6531         
6532         var sort = col.attr('sort');
6533         var dir = 'ASC';
6534         
6535         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6536             dir = 'DESC';
6537         }
6538         
6539         this.store.sortInfo = {field : sort, direction : dir};
6540         
6541         if (this.footer) {
6542             Roo.log("calling footer first");
6543             this.footer.onClick('first');
6544         } else {
6545         
6546             this.store.load({ params : { start : 0 } });
6547         }
6548     },
6549     
6550     renderHeader : function()
6551     {
6552         var header = {
6553             tag: 'thead',
6554             cn : []
6555         };
6556         
6557         var cm = this.cm;
6558         this.totalWidth = 0;
6559         
6560         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6561             
6562             var config = cm.config[i];
6563             
6564             var c = {
6565                 tag: 'th',
6566                 cls : 'x-hcol-' + i,
6567                 style : '',
6568                 html: cm.getColumnHeader(i)
6569             };
6570             
6571             var hh = '';
6572             
6573             if(typeof(config.sortable) != 'undefined' && config.sortable){
6574                 c.cls = 'sortable';
6575                 c.html = '<i class="glyphicon"></i>' + c.html;
6576             }
6577             
6578             if(typeof(config.lgHeader) != 'undefined'){
6579                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6580             }
6581             
6582             if(typeof(config.mdHeader) != 'undefined'){
6583                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6584             }
6585             
6586             if(typeof(config.smHeader) != 'undefined'){
6587                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6588             }
6589             
6590             if(typeof(config.xsHeader) != 'undefined'){
6591                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6592             }
6593             
6594             if(hh.length){
6595                 c.html = hh;
6596             }
6597             
6598             if(typeof(config.tooltip) != 'undefined'){
6599                 c.tooltip = config.tooltip;
6600             }
6601             
6602             if(typeof(config.colspan) != 'undefined'){
6603                 c.colspan = config.colspan;
6604             }
6605             
6606             if(typeof(config.hidden) != 'undefined' && config.hidden){
6607                 c.style += ' display:none;';
6608             }
6609             
6610             if(typeof(config.dataIndex) != 'undefined'){
6611                 c.sort = config.dataIndex;
6612             }
6613             
6614            
6615             
6616             if(typeof(config.align) != 'undefined' && config.align.length){
6617                 c.style += ' text-align:' + config.align + ';';
6618             }
6619             
6620             if(typeof(config.width) != 'undefined'){
6621                 c.style += ' width:' + config.width + 'px;';
6622                 this.totalWidth += config.width;
6623             } else {
6624                 this.totalWidth += 100; // assume minimum of 100 per column?
6625             }
6626             
6627             if(typeof(config.cls) != 'undefined'){
6628                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6629             }
6630             
6631             ['xs','sm','md','lg'].map(function(size){
6632                 
6633                 if(typeof(config[size]) == 'undefined'){
6634                     return;
6635                 }
6636                 
6637                 if (!config[size]) { // 0 = hidden
6638                     c.cls += ' hidden-' + size;
6639                     return;
6640                 }
6641                 
6642                 c.cls += ' col-' + size + '-' + config[size];
6643
6644             });
6645             
6646             header.cn.push(c)
6647         }
6648         
6649         return header;
6650     },
6651     
6652     renderBody : function()
6653     {
6654         var body = {
6655             tag: 'tbody',
6656             cn : [
6657                 {
6658                     tag: 'tr',
6659                     cn : [
6660                         {
6661                             tag : 'td',
6662                             colspan :  this.cm.getColumnCount()
6663                         }
6664                     ]
6665                 }
6666             ]
6667         };
6668         
6669         return body;
6670     },
6671     
6672     renderFooter : function()
6673     {
6674         var footer = {
6675             tag: 'tfoot',
6676             cn : [
6677                 {
6678                     tag: 'tr',
6679                     cn : [
6680                         {
6681                             tag : 'td',
6682                             colspan :  this.cm.getColumnCount()
6683                         }
6684                     ]
6685                 }
6686             ]
6687         };
6688         
6689         return footer;
6690     },
6691     
6692     
6693     
6694     onLoad : function()
6695     {
6696 //        Roo.log('ds onload');
6697         this.clear();
6698         
6699         var _this = this;
6700         var cm = this.cm;
6701         var ds = this.store;
6702         
6703         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6704             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6705             if (_this.store.sortInfo) {
6706                     
6707                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6708                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6709                 }
6710                 
6711                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6712                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6713                 }
6714             }
6715         });
6716         
6717         var tbody =  this.mainBody;
6718               
6719         if(ds.getCount() > 0){
6720             ds.data.each(function(d,rowIndex){
6721                 var row =  this.renderRow(cm, ds, rowIndex);
6722                 
6723                 tbody.createChild(row);
6724                 
6725                 var _this = this;
6726                 
6727                 if(row.cellObjects.length){
6728                     Roo.each(row.cellObjects, function(r){
6729                         _this.renderCellObject(r);
6730                     })
6731                 }
6732                 
6733             }, this);
6734         }
6735         
6736         var tfoot = this.el.select('tfoot', true).first();
6737         
6738         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6739             
6740             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6741             
6742             var total = this.ds.getTotalCount();
6743             
6744             if(this.footer.pageSize < total){
6745                 this.mainFoot.show();
6746             }
6747         }
6748         
6749         Roo.each(this.el.select('tbody td', true).elements, function(e){
6750             e.on('mouseover', _this.onMouseover, _this);
6751         });
6752         
6753         Roo.each(this.el.select('tbody td', true).elements, function(e){
6754             e.on('mouseout', _this.onMouseout, _this);
6755         });
6756         this.fireEvent('rowsrendered', this);
6757         
6758         this.autoSize();
6759     },
6760     
6761     
6762     onUpdate : function(ds,record)
6763     {
6764         this.refreshRow(record);
6765         this.autoSize();
6766     },
6767     
6768     onRemove : function(ds, record, index, isUpdate){
6769         if(isUpdate !== true){
6770             this.fireEvent("beforerowremoved", this, index, record);
6771         }
6772         var bt = this.mainBody.dom;
6773         
6774         var rows = this.el.select('tbody > tr', true).elements;
6775         
6776         if(typeof(rows[index]) != 'undefined'){
6777             bt.removeChild(rows[index].dom);
6778         }
6779         
6780 //        if(bt.rows[index]){
6781 //            bt.removeChild(bt.rows[index]);
6782 //        }
6783         
6784         if(isUpdate !== true){
6785             //this.stripeRows(index);
6786             //this.syncRowHeights(index, index);
6787             //this.layout();
6788             this.fireEvent("rowremoved", this, index, record);
6789         }
6790     },
6791     
6792     onAdd : function(ds, records, rowIndex)
6793     {
6794         //Roo.log('on Add called');
6795         // - note this does not handle multiple adding very well..
6796         var bt = this.mainBody.dom;
6797         for (var i =0 ; i < records.length;i++) {
6798             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6799             //Roo.log(records[i]);
6800             //Roo.log(this.store.getAt(rowIndex+i));
6801             this.insertRow(this.store, rowIndex + i, false);
6802             return;
6803         }
6804         
6805     },
6806     
6807     
6808     refreshRow : function(record){
6809         var ds = this.store, index;
6810         if(typeof record == 'number'){
6811             index = record;
6812             record = ds.getAt(index);
6813         }else{
6814             index = ds.indexOf(record);
6815         }
6816         this.insertRow(ds, index, true);
6817         this.autoSize();
6818         this.onRemove(ds, record, index+1, true);
6819         this.autoSize();
6820         //this.syncRowHeights(index, index);
6821         //this.layout();
6822         this.fireEvent("rowupdated", this, index, record);
6823     },
6824     
6825     insertRow : function(dm, rowIndex, isUpdate){
6826         
6827         if(!isUpdate){
6828             this.fireEvent("beforerowsinserted", this, rowIndex);
6829         }
6830             //var s = this.getScrollState();
6831         var row = this.renderRow(this.cm, this.store, rowIndex);
6832         // insert before rowIndex..
6833         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6834         
6835         var _this = this;
6836                 
6837         if(row.cellObjects.length){
6838             Roo.each(row.cellObjects, function(r){
6839                 _this.renderCellObject(r);
6840             })
6841         }
6842             
6843         if(!isUpdate){
6844             this.fireEvent("rowsinserted", this, rowIndex);
6845             //this.syncRowHeights(firstRow, lastRow);
6846             //this.stripeRows(firstRow);
6847             //this.layout();
6848         }
6849         
6850     },
6851     
6852     
6853     getRowDom : function(rowIndex)
6854     {
6855         var rows = this.el.select('tbody > tr', true).elements;
6856         
6857         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6858         
6859     },
6860     // returns the object tree for a tr..
6861   
6862     
6863     renderRow : function(cm, ds, rowIndex) 
6864     {
6865         var d = ds.getAt(rowIndex);
6866         
6867         var row = {
6868             tag : 'tr',
6869             cls : 'x-row-' + rowIndex,
6870             cn : []
6871         };
6872             
6873         var cellObjects = [];
6874         
6875         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6876             var config = cm.config[i];
6877             
6878             var renderer = cm.getRenderer(i);
6879             var value = '';
6880             var id = false;
6881             
6882             if(typeof(renderer) !== 'undefined'){
6883                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6884             }
6885             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6886             // and are rendered into the cells after the row is rendered - using the id for the element.
6887             
6888             if(typeof(value) === 'object'){
6889                 id = Roo.id();
6890                 cellObjects.push({
6891                     container : id,
6892                     cfg : value 
6893                 })
6894             }
6895             
6896             var rowcfg = {
6897                 record: d,
6898                 rowIndex : rowIndex,
6899                 colIndex : i,
6900                 rowClass : ''
6901             };
6902
6903             this.fireEvent('rowclass', this, rowcfg);
6904             
6905             var td = {
6906                 tag: 'td',
6907                 cls : rowcfg.rowClass + ' x-col-' + i,
6908                 style: '',
6909                 html: (typeof(value) === 'object') ? '' : value
6910             };
6911             
6912             if (id) {
6913                 td.id = id;
6914             }
6915             
6916             if(typeof(config.colspan) != 'undefined'){
6917                 td.colspan = config.colspan;
6918             }
6919             
6920             if(typeof(config.hidden) != 'undefined' && config.hidden){
6921                 td.style += ' display:none;';
6922             }
6923             
6924             if(typeof(config.align) != 'undefined' && config.align.length){
6925                 td.style += ' text-align:' + config.align + ';';
6926             }
6927             if(typeof(config.valign) != 'undefined' && config.valign.length){
6928                 td.style += ' vertical-align:' + config.valign + ';';
6929             }
6930             
6931             if(typeof(config.width) != 'undefined'){
6932                 td.style += ' width:' +  config.width + 'px;';
6933             }
6934             
6935             if(typeof(config.cursor) != 'undefined'){
6936                 td.style += ' cursor:' +  config.cursor + ';';
6937             }
6938             
6939             if(typeof(config.cls) != 'undefined'){
6940                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6941             }
6942             
6943             ['xs','sm','md','lg'].map(function(size){
6944                 
6945                 if(typeof(config[size]) == 'undefined'){
6946                     return;
6947                 }
6948                 
6949                 if (!config[size]) { // 0 = hidden
6950                     td.cls += ' hidden-' + size;
6951                     return;
6952                 }
6953                 
6954                 td.cls += ' col-' + size + '-' + config[size];
6955
6956             });
6957             
6958             row.cn.push(td);
6959            
6960         }
6961         
6962         row.cellObjects = cellObjects;
6963         
6964         return row;
6965           
6966     },
6967     
6968     
6969     
6970     onBeforeLoad : function()
6971     {
6972         
6973     },
6974      /**
6975      * Remove all rows
6976      */
6977     clear : function()
6978     {
6979         this.el.select('tbody', true).first().dom.innerHTML = '';
6980     },
6981     /**
6982      * Show or hide a row.
6983      * @param {Number} rowIndex to show or hide
6984      * @param {Boolean} state hide
6985      */
6986     setRowVisibility : function(rowIndex, state)
6987     {
6988         var bt = this.mainBody.dom;
6989         
6990         var rows = this.el.select('tbody > tr', true).elements;
6991         
6992         if(typeof(rows[rowIndex]) == 'undefined'){
6993             return;
6994         }
6995         rows[rowIndex].dom.style.display = state ? '' : 'none';
6996     },
6997     
6998     
6999     getSelectionModel : function(){
7000         if(!this.selModel){
7001             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7002         }
7003         return this.selModel;
7004     },
7005     /*
7006      * Render the Roo.bootstrap object from renderder
7007      */
7008     renderCellObject : function(r)
7009     {
7010         var _this = this;
7011         
7012         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7013         
7014         var t = r.cfg.render(r.container);
7015         
7016         if(r.cfg.cn){
7017             Roo.each(r.cfg.cn, function(c){
7018                 var child = {
7019                     container: t.getChildContainer(),
7020                     cfg: c
7021                 };
7022                 _this.renderCellObject(child);
7023             })
7024         }
7025     },
7026     
7027     getRowIndex : function(row)
7028     {
7029         var rowIndex = -1;
7030         
7031         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7032             if(el != row){
7033                 return;
7034             }
7035             
7036             rowIndex = index;
7037         });
7038         
7039         return rowIndex;
7040     },
7041      /**
7042      * Returns the grid's underlying element = used by panel.Grid
7043      * @return {Element} The element
7044      */
7045     getGridEl : function(){
7046         return this.el;
7047     },
7048      /**
7049      * Forces a resize - used by panel.Grid
7050      * @return {Element} The element
7051      */
7052     autoSize : function()
7053     {
7054         //var ctr = Roo.get(this.container.dom.parentElement);
7055         var ctr = Roo.get(this.el.dom);
7056         
7057         var thd = this.getGridEl().select('thead',true).first();
7058         var tbd = this.getGridEl().select('tbody', true).first();
7059         var tfd = this.getGridEl().select('tfoot', true).first();
7060         
7061         var cw = ctr.getWidth();
7062         
7063         if (tbd) {
7064             
7065             tbd.setSize(ctr.getWidth(),
7066                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7067             );
7068             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7069             cw -= barsize;
7070         }
7071         cw = Math.max(cw, this.totalWidth);
7072         this.getGridEl().select('tr',true).setWidth(cw);
7073         // resize 'expandable coloumn?
7074         
7075         return; // we doe not have a view in this design..
7076         
7077     },
7078     onBodyScroll: function()
7079     {
7080         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7081         if(this.mainHead){
7082             this.mainHead.setStyle({
7083                 'position' : 'relative',
7084                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7085             });
7086         }
7087         
7088         if(this.lazyLoad){
7089             
7090             var scrollHeight = this.mainBody.dom.scrollHeight;
7091             
7092             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7093             
7094             var height = this.mainBody.getHeight();
7095             
7096             if(scrollHeight - height == scrollTop) {
7097                 
7098                 var total = this.ds.getTotalCount();
7099                 
7100                 if(this.footer.cursor + this.footer.pageSize < total){
7101                     
7102                     this.footer.ds.load({
7103                         params : {
7104                             start : this.footer.cursor + this.footer.pageSize,
7105                             limit : this.footer.pageSize
7106                         },
7107                         add : true
7108                     });
7109                 }
7110             }
7111             
7112         }
7113     },
7114     
7115     onHeaderChange : function()
7116     {
7117         var header = this.renderHeader();
7118         var table = this.el.select('table', true).first();
7119         
7120         this.mainHead.remove();
7121         this.mainHead = table.createChild(header, this.mainBody, false);
7122     },
7123     
7124     onHiddenChange : function(colModel, colIndex, hidden)
7125     {
7126         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7127         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7128         
7129         this.CSS.updateRule(thSelector, "display", "");
7130         this.CSS.updateRule(tdSelector, "display", "");
7131         
7132         if(hidden){
7133             this.CSS.updateRule(thSelector, "display", "none");
7134             this.CSS.updateRule(tdSelector, "display", "none");
7135         }
7136         
7137         this.onHeaderChange();
7138         this.onLoad();
7139     },
7140     
7141     setColumnWidth: function(col_index, width)
7142     {
7143         // width = "md-2 xs-2..."
7144         if(!this.colModel.config[col_index]) {
7145             return;
7146         }
7147         
7148         var w = width.split(" ");
7149         
7150         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7151         
7152         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7153         
7154         
7155         for(var j = 0; j < w.length; j++) {
7156             
7157             if(!w[j]) {
7158                 continue;
7159             }
7160             
7161             var size_cls = w[j].split("-");
7162             
7163             if(!Number.isInteger(size_cls[1] * 1)) {
7164                 continue;
7165             }
7166             
7167             if(!this.colModel.config[col_index][size_cls[0]]) {
7168                 continue;
7169             }
7170             
7171             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7172                 continue;
7173             }
7174             
7175             h_row[0].classList.replace(
7176                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7177                 "col-"+size_cls[0]+"-"+size_cls[1]
7178             );
7179             
7180             for(var i = 0; i < rows.length; i++) {
7181                 
7182                 var size_cls = w[j].split("-");
7183                 
7184                 if(!Number.isInteger(size_cls[1] * 1)) {
7185                     continue;
7186                 }
7187                 
7188                 if(!this.colModel.config[col_index][size_cls[0]]) {
7189                     continue;
7190                 }
7191                 
7192                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7193                     continue;
7194                 }
7195                 
7196                 rows[i].classList.replace(
7197                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7198                     "col-"+size_cls[0]+"-"+size_cls[1]
7199                 );
7200             }
7201             
7202             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7203         }
7204     }
7205 });
7206
7207  
7208
7209  /*
7210  * - LGPL
7211  *
7212  * table cell
7213  * 
7214  */
7215
7216 /**
7217  * @class Roo.bootstrap.TableCell
7218  * @extends Roo.bootstrap.Component
7219  * Bootstrap TableCell class
7220  * @cfg {String} html cell contain text
7221  * @cfg {String} cls cell class
7222  * @cfg {String} tag cell tag (td|th) default td
7223  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7224  * @cfg {String} align Aligns the content in a cell
7225  * @cfg {String} axis Categorizes cells
7226  * @cfg {String} bgcolor Specifies the background color of a cell
7227  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7228  * @cfg {Number} colspan Specifies the number of columns a cell should span
7229  * @cfg {String} headers Specifies one or more header cells a cell is related to
7230  * @cfg {Number} height Sets the height of a cell
7231  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7232  * @cfg {Number} rowspan Sets the number of rows a cell should span
7233  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7234  * @cfg {String} valign Vertical aligns the content in a cell
7235  * @cfg {Number} width Specifies the width of a cell
7236  * 
7237  * @constructor
7238  * Create a new TableCell
7239  * @param {Object} config The config object
7240  */
7241
7242 Roo.bootstrap.TableCell = function(config){
7243     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7244 };
7245
7246 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7247     
7248     html: false,
7249     cls: false,
7250     tag: false,
7251     abbr: false,
7252     align: false,
7253     axis: false,
7254     bgcolor: false,
7255     charoff: false,
7256     colspan: false,
7257     headers: false,
7258     height: false,
7259     nowrap: false,
7260     rowspan: false,
7261     scope: false,
7262     valign: false,
7263     width: false,
7264     
7265     
7266     getAutoCreate : function(){
7267         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7268         
7269         cfg = {
7270             tag: 'td'
7271         };
7272         
7273         if(this.tag){
7274             cfg.tag = this.tag;
7275         }
7276         
7277         if (this.html) {
7278             cfg.html=this.html
7279         }
7280         if (this.cls) {
7281             cfg.cls=this.cls
7282         }
7283         if (this.abbr) {
7284             cfg.abbr=this.abbr
7285         }
7286         if (this.align) {
7287             cfg.align=this.align
7288         }
7289         if (this.axis) {
7290             cfg.axis=this.axis
7291         }
7292         if (this.bgcolor) {
7293             cfg.bgcolor=this.bgcolor
7294         }
7295         if (this.charoff) {
7296             cfg.charoff=this.charoff
7297         }
7298         if (this.colspan) {
7299             cfg.colspan=this.colspan
7300         }
7301         if (this.headers) {
7302             cfg.headers=this.headers
7303         }
7304         if (this.height) {
7305             cfg.height=this.height
7306         }
7307         if (this.nowrap) {
7308             cfg.nowrap=this.nowrap
7309         }
7310         if (this.rowspan) {
7311             cfg.rowspan=this.rowspan
7312         }
7313         if (this.scope) {
7314             cfg.scope=this.scope
7315         }
7316         if (this.valign) {
7317             cfg.valign=this.valign
7318         }
7319         if (this.width) {
7320             cfg.width=this.width
7321         }
7322         
7323         
7324         return cfg;
7325     }
7326    
7327 });
7328
7329  
7330
7331  /*
7332  * - LGPL
7333  *
7334  * table row
7335  * 
7336  */
7337
7338 /**
7339  * @class Roo.bootstrap.TableRow
7340  * @extends Roo.bootstrap.Component
7341  * Bootstrap TableRow class
7342  * @cfg {String} cls row class
7343  * @cfg {String} align Aligns the content in a table row
7344  * @cfg {String} bgcolor Specifies a background color for a table row
7345  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7346  * @cfg {String} valign Vertical aligns the content in a table row
7347  * 
7348  * @constructor
7349  * Create a new TableRow
7350  * @param {Object} config The config object
7351  */
7352
7353 Roo.bootstrap.TableRow = function(config){
7354     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7355 };
7356
7357 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7358     
7359     cls: false,
7360     align: false,
7361     bgcolor: false,
7362     charoff: false,
7363     valign: false,
7364     
7365     getAutoCreate : function(){
7366         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7367         
7368         cfg = {
7369             tag: 'tr'
7370         };
7371             
7372         if(this.cls){
7373             cfg.cls = this.cls;
7374         }
7375         if(this.align){
7376             cfg.align = this.align;
7377         }
7378         if(this.bgcolor){
7379             cfg.bgcolor = this.bgcolor;
7380         }
7381         if(this.charoff){
7382             cfg.charoff = this.charoff;
7383         }
7384         if(this.valign){
7385             cfg.valign = this.valign;
7386         }
7387         
7388         return cfg;
7389     }
7390    
7391 });
7392
7393  
7394
7395  /*
7396  * - LGPL
7397  *
7398  * table body
7399  * 
7400  */
7401
7402 /**
7403  * @class Roo.bootstrap.TableBody
7404  * @extends Roo.bootstrap.Component
7405  * Bootstrap TableBody class
7406  * @cfg {String} cls element class
7407  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7408  * @cfg {String} align Aligns the content inside the element
7409  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7410  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7411  * 
7412  * @constructor
7413  * Create a new TableBody
7414  * @param {Object} config The config object
7415  */
7416
7417 Roo.bootstrap.TableBody = function(config){
7418     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7419 };
7420
7421 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7422     
7423     cls: false,
7424     tag: false,
7425     align: false,
7426     charoff: false,
7427     valign: false,
7428     
7429     getAutoCreate : function(){
7430         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7431         
7432         cfg = {
7433             tag: 'tbody'
7434         };
7435             
7436         if (this.cls) {
7437             cfg.cls=this.cls
7438         }
7439         if(this.tag){
7440             cfg.tag = this.tag;
7441         }
7442         
7443         if(this.align){
7444             cfg.align = this.align;
7445         }
7446         if(this.charoff){
7447             cfg.charoff = this.charoff;
7448         }
7449         if(this.valign){
7450             cfg.valign = this.valign;
7451         }
7452         
7453         return cfg;
7454     }
7455     
7456     
7457 //    initEvents : function()
7458 //    {
7459 //        
7460 //        if(!this.store){
7461 //            return;
7462 //        }
7463 //        
7464 //        this.store = Roo.factory(this.store, Roo.data);
7465 //        this.store.on('load', this.onLoad, this);
7466 //        
7467 //        this.store.load();
7468 //        
7469 //    },
7470 //    
7471 //    onLoad: function () 
7472 //    {   
7473 //        this.fireEvent('load', this);
7474 //    }
7475 //    
7476 //   
7477 });
7478
7479  
7480
7481  /*
7482  * Based on:
7483  * Ext JS Library 1.1.1
7484  * Copyright(c) 2006-2007, Ext JS, LLC.
7485  *
7486  * Originally Released Under LGPL - original licence link has changed is not relivant.
7487  *
7488  * Fork - LGPL
7489  * <script type="text/javascript">
7490  */
7491
7492 // as we use this in bootstrap.
7493 Roo.namespace('Roo.form');
7494  /**
7495  * @class Roo.form.Action
7496  * Internal Class used to handle form actions
7497  * @constructor
7498  * @param {Roo.form.BasicForm} el The form element or its id
7499  * @param {Object} config Configuration options
7500  */
7501
7502  
7503  
7504 // define the action interface
7505 Roo.form.Action = function(form, options){
7506     this.form = form;
7507     this.options = options || {};
7508 };
7509 /**
7510  * Client Validation Failed
7511  * @const 
7512  */
7513 Roo.form.Action.CLIENT_INVALID = 'client';
7514 /**
7515  * Server Validation Failed
7516  * @const 
7517  */
7518 Roo.form.Action.SERVER_INVALID = 'server';
7519  /**
7520  * Connect to Server Failed
7521  * @const 
7522  */
7523 Roo.form.Action.CONNECT_FAILURE = 'connect';
7524 /**
7525  * Reading Data from Server Failed
7526  * @const 
7527  */
7528 Roo.form.Action.LOAD_FAILURE = 'load';
7529
7530 Roo.form.Action.prototype = {
7531     type : 'default',
7532     failureType : undefined,
7533     response : undefined,
7534     result : undefined,
7535
7536     // interface method
7537     run : function(options){
7538
7539     },
7540
7541     // interface method
7542     success : function(response){
7543
7544     },
7545
7546     // interface method
7547     handleResponse : function(response){
7548
7549     },
7550
7551     // default connection failure
7552     failure : function(response){
7553         
7554         this.response = response;
7555         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7556         this.form.afterAction(this, false);
7557     },
7558
7559     processResponse : function(response){
7560         this.response = response;
7561         if(!response.responseText){
7562             return true;
7563         }
7564         this.result = this.handleResponse(response);
7565         return this.result;
7566     },
7567
7568     // utility functions used internally
7569     getUrl : function(appendParams){
7570         var url = this.options.url || this.form.url || this.form.el.dom.action;
7571         if(appendParams){
7572             var p = this.getParams();
7573             if(p){
7574                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7575             }
7576         }
7577         return url;
7578     },
7579
7580     getMethod : function(){
7581         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7582     },
7583
7584     getParams : function(){
7585         var bp = this.form.baseParams;
7586         var p = this.options.params;
7587         if(p){
7588             if(typeof p == "object"){
7589                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7590             }else if(typeof p == 'string' && bp){
7591                 p += '&' + Roo.urlEncode(bp);
7592             }
7593         }else if(bp){
7594             p = Roo.urlEncode(bp);
7595         }
7596         return p;
7597     },
7598
7599     createCallback : function(){
7600         return {
7601             success: this.success,
7602             failure: this.failure,
7603             scope: this,
7604             timeout: (this.form.timeout*1000),
7605             upload: this.form.fileUpload ? this.success : undefined
7606         };
7607     }
7608 };
7609
7610 Roo.form.Action.Submit = function(form, options){
7611     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7612 };
7613
7614 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7615     type : 'submit',
7616
7617     haveProgress : false,
7618     uploadComplete : false,
7619     
7620     // uploadProgress indicator.
7621     uploadProgress : function()
7622     {
7623         if (!this.form.progressUrl) {
7624             return;
7625         }
7626         
7627         if (!this.haveProgress) {
7628             Roo.MessageBox.progress("Uploading", "Uploading");
7629         }
7630         if (this.uploadComplete) {
7631            Roo.MessageBox.hide();
7632            return;
7633         }
7634         
7635         this.haveProgress = true;
7636    
7637         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7638         
7639         var c = new Roo.data.Connection();
7640         c.request({
7641             url : this.form.progressUrl,
7642             params: {
7643                 id : uid
7644             },
7645             method: 'GET',
7646             success : function(req){
7647                //console.log(data);
7648                 var rdata = false;
7649                 var edata;
7650                 try  {
7651                    rdata = Roo.decode(req.responseText)
7652                 } catch (e) {
7653                     Roo.log("Invalid data from server..");
7654                     Roo.log(edata);
7655                     return;
7656                 }
7657                 if (!rdata || !rdata.success) {
7658                     Roo.log(rdata);
7659                     Roo.MessageBox.alert(Roo.encode(rdata));
7660                     return;
7661                 }
7662                 var data = rdata.data;
7663                 
7664                 if (this.uploadComplete) {
7665                    Roo.MessageBox.hide();
7666                    return;
7667                 }
7668                    
7669                 if (data){
7670                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7671                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7672                     );
7673                 }
7674                 this.uploadProgress.defer(2000,this);
7675             },
7676        
7677             failure: function(data) {
7678                 Roo.log('progress url failed ');
7679                 Roo.log(data);
7680             },
7681             scope : this
7682         });
7683            
7684     },
7685     
7686     
7687     run : function()
7688     {
7689         // run get Values on the form, so it syncs any secondary forms.
7690         this.form.getValues();
7691         
7692         var o = this.options;
7693         var method = this.getMethod();
7694         var isPost = method == 'POST';
7695         if(o.clientValidation === false || this.form.isValid()){
7696             
7697             if (this.form.progressUrl) {
7698                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7699                     (new Date() * 1) + '' + Math.random());
7700                     
7701             } 
7702             
7703             
7704             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7705                 form:this.form.el.dom,
7706                 url:this.getUrl(!isPost),
7707                 method: method,
7708                 params:isPost ? this.getParams() : null,
7709                 isUpload: this.form.fileUpload
7710             }));
7711             
7712             this.uploadProgress();
7713
7714         }else if (o.clientValidation !== false){ // client validation failed
7715             this.failureType = Roo.form.Action.CLIENT_INVALID;
7716             this.form.afterAction(this, false);
7717         }
7718     },
7719
7720     success : function(response)
7721     {
7722         this.uploadComplete= true;
7723         if (this.haveProgress) {
7724             Roo.MessageBox.hide();
7725         }
7726         
7727         
7728         var result = this.processResponse(response);
7729         if(result === true || result.success){
7730             this.form.afterAction(this, true);
7731             return;
7732         }
7733         if(result.errors){
7734             this.form.markInvalid(result.errors);
7735             this.failureType = Roo.form.Action.SERVER_INVALID;
7736         }
7737         this.form.afterAction(this, false);
7738     },
7739     failure : function(response)
7740     {
7741         this.uploadComplete= true;
7742         if (this.haveProgress) {
7743             Roo.MessageBox.hide();
7744         }
7745         
7746         this.response = response;
7747         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7748         this.form.afterAction(this, false);
7749     },
7750     
7751     handleResponse : function(response){
7752         if(this.form.errorReader){
7753             var rs = this.form.errorReader.read(response);
7754             var errors = [];
7755             if(rs.records){
7756                 for(var i = 0, len = rs.records.length; i < len; i++) {
7757                     var r = rs.records[i];
7758                     errors[i] = r.data;
7759                 }
7760             }
7761             if(errors.length < 1){
7762                 errors = null;
7763             }
7764             return {
7765                 success : rs.success,
7766                 errors : errors
7767             };
7768         }
7769         var ret = false;
7770         try {
7771             ret = Roo.decode(response.responseText);
7772         } catch (e) {
7773             ret = {
7774                 success: false,
7775                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7776                 errors : []
7777             };
7778         }
7779         return ret;
7780         
7781     }
7782 });
7783
7784
7785 Roo.form.Action.Load = function(form, options){
7786     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7787     this.reader = this.form.reader;
7788 };
7789
7790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7791     type : 'load',
7792
7793     run : function(){
7794         
7795         Roo.Ajax.request(Roo.apply(
7796                 this.createCallback(), {
7797                     method:this.getMethod(),
7798                     url:this.getUrl(false),
7799                     params:this.getParams()
7800         }));
7801     },
7802
7803     success : function(response){
7804         
7805         var result = this.processResponse(response);
7806         if(result === true || !result.success || !result.data){
7807             this.failureType = Roo.form.Action.LOAD_FAILURE;
7808             this.form.afterAction(this, false);
7809             return;
7810         }
7811         this.form.clearInvalid();
7812         this.form.setValues(result.data);
7813         this.form.afterAction(this, true);
7814     },
7815
7816     handleResponse : function(response){
7817         if(this.form.reader){
7818             var rs = this.form.reader.read(response);
7819             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7820             return {
7821                 success : rs.success,
7822                 data : data
7823             };
7824         }
7825         return Roo.decode(response.responseText);
7826     }
7827 });
7828
7829 Roo.form.Action.ACTION_TYPES = {
7830     'load' : Roo.form.Action.Load,
7831     'submit' : Roo.form.Action.Submit
7832 };/*
7833  * - LGPL
7834  *
7835  * form
7836  *
7837  */
7838
7839 /**
7840  * @class Roo.bootstrap.Form
7841  * @extends Roo.bootstrap.Component
7842  * Bootstrap Form class
7843  * @cfg {String} method  GET | POST (default POST)
7844  * @cfg {String} labelAlign top | left (default top)
7845  * @cfg {String} align left  | right - for navbars
7846  * @cfg {Boolean} loadMask load mask when submit (default true)
7847
7848  *
7849  * @constructor
7850  * Create a new Form
7851  * @param {Object} config The config object
7852  */
7853
7854
7855 Roo.bootstrap.Form = function(config){
7856     
7857     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7858     
7859     Roo.bootstrap.Form.popover.apply();
7860     
7861     this.addEvents({
7862         /**
7863          * @event clientvalidation
7864          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7865          * @param {Form} this
7866          * @param {Boolean} valid true if the form has passed client-side validation
7867          */
7868         clientvalidation: true,
7869         /**
7870          * @event beforeaction
7871          * Fires before any action is performed. Return false to cancel the action.
7872          * @param {Form} this
7873          * @param {Action} action The action to be performed
7874          */
7875         beforeaction: true,
7876         /**
7877          * @event actionfailed
7878          * Fires when an action fails.
7879          * @param {Form} this
7880          * @param {Action} action The action that failed
7881          */
7882         actionfailed : true,
7883         /**
7884          * @event actioncomplete
7885          * Fires when an action is completed.
7886          * @param {Form} this
7887          * @param {Action} action The action that completed
7888          */
7889         actioncomplete : true
7890     });
7891 };
7892
7893 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7894
7895      /**
7896      * @cfg {String} method
7897      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7898      */
7899     method : 'POST',
7900     /**
7901      * @cfg {String} url
7902      * The URL to use for form actions if one isn't supplied in the action options.
7903      */
7904     /**
7905      * @cfg {Boolean} fileUpload
7906      * Set to true if this form is a file upload.
7907      */
7908
7909     /**
7910      * @cfg {Object} baseParams
7911      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7912      */
7913
7914     /**
7915      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7916      */
7917     timeout: 30,
7918     /**
7919      * @cfg {Sting} align (left|right) for navbar forms
7920      */
7921     align : 'left',
7922
7923     // private
7924     activeAction : null,
7925
7926     /**
7927      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7928      * element by passing it or its id or mask the form itself by passing in true.
7929      * @type Mixed
7930      */
7931     waitMsgTarget : false,
7932
7933     loadMask : true,
7934     
7935     /**
7936      * @cfg {Boolean} errorMask (true|false) default false
7937      */
7938     errorMask : false,
7939     
7940     /**
7941      * @cfg {Number} maskOffset Default 100
7942      */
7943     maskOffset : 100,
7944     
7945     /**
7946      * @cfg {Boolean} maskBody
7947      */
7948     maskBody : false,
7949
7950     getAutoCreate : function(){
7951
7952         var cfg = {
7953             tag: 'form',
7954             method : this.method || 'POST',
7955             id : this.id || Roo.id(),
7956             cls : ''
7957         };
7958         if (this.parent().xtype.match(/^Nav/)) {
7959             cfg.cls = 'navbar-form navbar-' + this.align;
7960
7961         }
7962
7963         if (this.labelAlign == 'left' ) {
7964             cfg.cls += ' form-horizontal';
7965         }
7966
7967
7968         return cfg;
7969     },
7970     initEvents : function()
7971     {
7972         this.el.on('submit', this.onSubmit, this);
7973         // this was added as random key presses on the form where triggering form submit.
7974         this.el.on('keypress', function(e) {
7975             if (e.getCharCode() != 13) {
7976                 return true;
7977             }
7978             // we might need to allow it for textareas.. and some other items.
7979             // check e.getTarget().
7980
7981             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7982                 return true;
7983             }
7984
7985             Roo.log("keypress blocked");
7986
7987             e.preventDefault();
7988             return false;
7989         });
7990         
7991     },
7992     // private
7993     onSubmit : function(e){
7994         e.stopEvent();
7995     },
7996
7997      /**
7998      * Returns true if client-side validation on the form is successful.
7999      * @return Boolean
8000      */
8001     isValid : function(){
8002         var items = this.getItems();
8003         var valid = true;
8004         var target = false;
8005         
8006         items.each(function(f){
8007             
8008             if(f.validate()){
8009                 return;
8010             }
8011             
8012             Roo.log('invalid field: ' + f.name);
8013             
8014             valid = false;
8015
8016             if(!target && f.el.isVisible(true)){
8017                 target = f;
8018             }
8019            
8020         });
8021         
8022         if(this.errorMask && !valid){
8023             Roo.bootstrap.Form.popover.mask(this, target);
8024         }
8025         
8026         return valid;
8027     },
8028     
8029     /**
8030      * Returns true if any fields in this form have changed since their original load.
8031      * @return Boolean
8032      */
8033     isDirty : function(){
8034         var dirty = false;
8035         var items = this.getItems();
8036         items.each(function(f){
8037            if(f.isDirty()){
8038                dirty = true;
8039                return false;
8040            }
8041            return true;
8042         });
8043         return dirty;
8044     },
8045      /**
8046      * Performs a predefined action (submit or load) or custom actions you define on this form.
8047      * @param {String} actionName The name of the action type
8048      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8049      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8050      * accept other config options):
8051      * <pre>
8052 Property          Type             Description
8053 ----------------  ---------------  ----------------------------------------------------------------------------------
8054 url               String           The url for the action (defaults to the form's url)
8055 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8056 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8057 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8058                                    validate the form on the client (defaults to false)
8059      * </pre>
8060      * @return {BasicForm} this
8061      */
8062     doAction : function(action, options){
8063         if(typeof action == 'string'){
8064             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8065         }
8066         if(this.fireEvent('beforeaction', this, action) !== false){
8067             this.beforeAction(action);
8068             action.run.defer(100, action);
8069         }
8070         return this;
8071     },
8072
8073     // private
8074     beforeAction : function(action){
8075         var o = action.options;
8076         
8077         if(this.loadMask){
8078             
8079             if(this.maskBody){
8080                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8081             } else {
8082                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8083             }
8084         }
8085         // not really supported yet.. ??
8086
8087         //if(this.waitMsgTarget === true){
8088         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8089         //}else if(this.waitMsgTarget){
8090         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8091         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8092         //}else {
8093         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8094        // }
8095
8096     },
8097
8098     // private
8099     afterAction : function(action, success){
8100         this.activeAction = null;
8101         var o = action.options;
8102
8103         if(this.loadMask){
8104             
8105             if(this.maskBody){
8106                 Roo.get(document.body).unmask();
8107             } else {
8108                 this.el.unmask();
8109             }
8110         }
8111         
8112         //if(this.waitMsgTarget === true){
8113 //            this.el.unmask();
8114         //}else if(this.waitMsgTarget){
8115         //    this.waitMsgTarget.unmask();
8116         //}else{
8117         //    Roo.MessageBox.updateProgress(1);
8118         //    Roo.MessageBox.hide();
8119        // }
8120         //
8121         if(success){
8122             if(o.reset){
8123                 this.reset();
8124             }
8125             Roo.callback(o.success, o.scope, [this, action]);
8126             this.fireEvent('actioncomplete', this, action);
8127
8128         }else{
8129
8130             // failure condition..
8131             // we have a scenario where updates need confirming.
8132             // eg. if a locking scenario exists..
8133             // we look for { errors : { needs_confirm : true }} in the response.
8134             if (
8135                 (typeof(action.result) != 'undefined')  &&
8136                 (typeof(action.result.errors) != 'undefined')  &&
8137                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8138            ){
8139                 var _t = this;
8140                 Roo.log("not supported yet");
8141                  /*
8142
8143                 Roo.MessageBox.confirm(
8144                     "Change requires confirmation",
8145                     action.result.errorMsg,
8146                     function(r) {
8147                         if (r != 'yes') {
8148                             return;
8149                         }
8150                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8151                     }
8152
8153                 );
8154                 */
8155
8156
8157                 return;
8158             }
8159
8160             Roo.callback(o.failure, o.scope, [this, action]);
8161             // show an error message if no failed handler is set..
8162             if (!this.hasListener('actionfailed')) {
8163                 Roo.log("need to add dialog support");
8164                 /*
8165                 Roo.MessageBox.alert("Error",
8166                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8167                         action.result.errorMsg :
8168                         "Saving Failed, please check your entries or try again"
8169                 );
8170                 */
8171             }
8172
8173             this.fireEvent('actionfailed', this, action);
8174         }
8175
8176     },
8177     /**
8178      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8179      * @param {String} id The value to search for
8180      * @return Field
8181      */
8182     findField : function(id){
8183         var items = this.getItems();
8184         var field = items.get(id);
8185         if(!field){
8186              items.each(function(f){
8187                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8188                     field = f;
8189                     return false;
8190                 }
8191                 return true;
8192             });
8193         }
8194         return field || null;
8195     },
8196      /**
8197      * Mark fields in this form invalid in bulk.
8198      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8199      * @return {BasicForm} this
8200      */
8201     markInvalid : function(errors){
8202         if(errors instanceof Array){
8203             for(var i = 0, len = errors.length; i < len; i++){
8204                 var fieldError = errors[i];
8205                 var f = this.findField(fieldError.id);
8206                 if(f){
8207                     f.markInvalid(fieldError.msg);
8208                 }
8209             }
8210         }else{
8211             var field, id;
8212             for(id in errors){
8213                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8214                     field.markInvalid(errors[id]);
8215                 }
8216             }
8217         }
8218         //Roo.each(this.childForms || [], function (f) {
8219         //    f.markInvalid(errors);
8220         //});
8221
8222         return this;
8223     },
8224
8225     /**
8226      * Set values for fields in this form in bulk.
8227      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8228      * @return {BasicForm} this
8229      */
8230     setValues : function(values){
8231         if(values instanceof Array){ // array of objects
8232             for(var i = 0, len = values.length; i < len; i++){
8233                 var v = values[i];
8234                 var f = this.findField(v.id);
8235                 if(f){
8236                     f.setValue(v.value);
8237                     if(this.trackResetOnLoad){
8238                         f.originalValue = f.getValue();
8239                     }
8240                 }
8241             }
8242         }else{ // object hash
8243             var field, id;
8244             for(id in values){
8245                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8246
8247                     if (field.setFromData &&
8248                         field.valueField &&
8249                         field.displayField &&
8250                         // combos' with local stores can
8251                         // be queried via setValue()
8252                         // to set their value..
8253                         (field.store && !field.store.isLocal)
8254                         ) {
8255                         // it's a combo
8256                         var sd = { };
8257                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8258                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8259                         field.setFromData(sd);
8260
8261                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8262                         
8263                         field.setFromData(values);
8264                         
8265                     } else {
8266                         field.setValue(values[id]);
8267                     }
8268
8269
8270                     if(this.trackResetOnLoad){
8271                         field.originalValue = field.getValue();
8272                     }
8273                 }
8274             }
8275         }
8276
8277         //Roo.each(this.childForms || [], function (f) {
8278         //    f.setValues(values);
8279         //});
8280
8281         return this;
8282     },
8283
8284     /**
8285      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8286      * they are returned as an array.
8287      * @param {Boolean} asString
8288      * @return {Object}
8289      */
8290     getValues : function(asString){
8291         //if (this.childForms) {
8292             // copy values from the child forms
8293         //    Roo.each(this.childForms, function (f) {
8294         //        this.setValues(f.getValues());
8295         //    }, this);
8296         //}
8297
8298
8299
8300         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8301         if(asString === true){
8302             return fs;
8303         }
8304         return Roo.urlDecode(fs);
8305     },
8306
8307     /**
8308      * Returns the fields in this form as an object with key/value pairs.
8309      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8310      * @return {Object}
8311      */
8312     getFieldValues : function(with_hidden)
8313     {
8314         var items = this.getItems();
8315         var ret = {};
8316         items.each(function(f){
8317             
8318             if (!f.getName()) {
8319                 return;
8320             }
8321             
8322             var v = f.getValue();
8323             
8324             if (f.inputType =='radio') {
8325                 if (typeof(ret[f.getName()]) == 'undefined') {
8326                     ret[f.getName()] = ''; // empty..
8327                 }
8328
8329                 if (!f.el.dom.checked) {
8330                     return;
8331
8332                 }
8333                 v = f.el.dom.value;
8334
8335             }
8336             
8337             if(f.xtype == 'MoneyField'){
8338                 ret[f.currencyName] = f.getCurrency();
8339             }
8340
8341             // not sure if this supported any more..
8342             if ((typeof(v) == 'object') && f.getRawValue) {
8343                 v = f.getRawValue() ; // dates..
8344             }
8345             // combo boxes where name != hiddenName...
8346             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8347                 ret[f.name] = f.getRawValue();
8348             }
8349             ret[f.getName()] = v;
8350         });
8351
8352         return ret;
8353     },
8354
8355     /**
8356      * Clears all invalid messages in this form.
8357      * @return {BasicForm} this
8358      */
8359     clearInvalid : function(){
8360         var items = this.getItems();
8361
8362         items.each(function(f){
8363            f.clearInvalid();
8364         });
8365
8366         return this;
8367     },
8368
8369     /**
8370      * Resets this form.
8371      * @return {BasicForm} this
8372      */
8373     reset : function(){
8374         var items = this.getItems();
8375         items.each(function(f){
8376             f.reset();
8377         });
8378
8379         Roo.each(this.childForms || [], function (f) {
8380             f.reset();
8381         });
8382
8383
8384         return this;
8385     },
8386     
8387     getItems : function()
8388     {
8389         var r=new Roo.util.MixedCollection(false, function(o){
8390             return o.id || (o.id = Roo.id());
8391         });
8392         var iter = function(el) {
8393             if (el.inputEl) {
8394                 r.add(el);
8395             }
8396             if (!el.items) {
8397                 return;
8398             }
8399             Roo.each(el.items,function(e) {
8400                 iter(e);
8401             });
8402         };
8403
8404         iter(this);
8405         return r;
8406     },
8407     
8408     hideFields : function(items)
8409     {
8410         Roo.each(items, function(i){
8411             
8412             var f = this.findField(i);
8413             
8414             if(!f){
8415                 return;
8416             }
8417             
8418             f.hide();
8419             
8420         }, this);
8421     },
8422     
8423     showFields : function(items)
8424     {
8425         Roo.each(items, function(i){
8426             
8427             var f = this.findField(i);
8428             
8429             if(!f){
8430                 return;
8431             }
8432             
8433             f.show();
8434             
8435         }, this);
8436     }
8437
8438 });
8439
8440 Roo.apply(Roo.bootstrap.Form, {
8441     
8442     popover : {
8443         
8444         padding : 5,
8445         
8446         isApplied : false,
8447         
8448         isMasked : false,
8449         
8450         form : false,
8451         
8452         target : false,
8453         
8454         toolTip : false,
8455         
8456         intervalID : false,
8457         
8458         maskEl : false,
8459         
8460         apply : function()
8461         {
8462             if(this.isApplied){
8463                 return;
8464             }
8465             
8466             this.maskEl = {
8467                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8468                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8469                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8470                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8471             };
8472             
8473             this.maskEl.top.enableDisplayMode("block");
8474             this.maskEl.left.enableDisplayMode("block");
8475             this.maskEl.bottom.enableDisplayMode("block");
8476             this.maskEl.right.enableDisplayMode("block");
8477             
8478             this.toolTip = new Roo.bootstrap.Tooltip({
8479                 cls : 'roo-form-error-popover',
8480                 alignment : {
8481                     'left' : ['r-l', [-2,0], 'right'],
8482                     'right' : ['l-r', [2,0], 'left'],
8483                     'bottom' : ['tl-bl', [0,2], 'top'],
8484                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8485                 }
8486             });
8487             
8488             this.toolTip.render(Roo.get(document.body));
8489
8490             this.toolTip.el.enableDisplayMode("block");
8491             
8492             Roo.get(document.body).on('click', function(){
8493                 this.unmask();
8494             }, this);
8495             
8496             Roo.get(document.body).on('touchstart', function(){
8497                 this.unmask();
8498             }, this);
8499             
8500             this.isApplied = true
8501         },
8502         
8503         mask : function(form, target)
8504         {
8505             this.form = form;
8506             
8507             this.target = target;
8508             
8509             if(!this.form.errorMask || !target.el){
8510                 return;
8511             }
8512             
8513             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8514             
8515             Roo.log(scrollable);
8516             
8517             var ot = this.target.el.calcOffsetsTo(scrollable);
8518             
8519             var scrollTo = ot[1] - this.form.maskOffset;
8520             
8521             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8522             
8523             scrollable.scrollTo('top', scrollTo);
8524             
8525             var box = this.target.el.getBox();
8526             Roo.log(box);
8527             var zIndex = Roo.bootstrap.Modal.zIndex++;
8528
8529             
8530             this.maskEl.top.setStyle('position', 'absolute');
8531             this.maskEl.top.setStyle('z-index', zIndex);
8532             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8533             this.maskEl.top.setLeft(0);
8534             this.maskEl.top.setTop(0);
8535             this.maskEl.top.show();
8536             
8537             this.maskEl.left.setStyle('position', 'absolute');
8538             this.maskEl.left.setStyle('z-index', zIndex);
8539             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8540             this.maskEl.left.setLeft(0);
8541             this.maskEl.left.setTop(box.y - this.padding);
8542             this.maskEl.left.show();
8543
8544             this.maskEl.bottom.setStyle('position', 'absolute');
8545             this.maskEl.bottom.setStyle('z-index', zIndex);
8546             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8547             this.maskEl.bottom.setLeft(0);
8548             this.maskEl.bottom.setTop(box.bottom + this.padding);
8549             this.maskEl.bottom.show();
8550
8551             this.maskEl.right.setStyle('position', 'absolute');
8552             this.maskEl.right.setStyle('z-index', zIndex);
8553             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8554             this.maskEl.right.setLeft(box.right + this.padding);
8555             this.maskEl.right.setTop(box.y - this.padding);
8556             this.maskEl.right.show();
8557
8558             this.toolTip.bindEl = this.target.el;
8559
8560             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8561
8562             var tip = this.target.blankText;
8563
8564             if(this.target.getValue() !== '' ) {
8565                 
8566                 if (this.target.invalidText.length) {
8567                     tip = this.target.invalidText;
8568                 } else if (this.target.regexText.length){
8569                     tip = this.target.regexText;
8570                 }
8571             }
8572
8573             this.toolTip.show(tip);
8574
8575             this.intervalID = window.setInterval(function() {
8576                 Roo.bootstrap.Form.popover.unmask();
8577             }, 10000);
8578
8579             window.onwheel = function(){ return false;};
8580             
8581             (function(){ this.isMasked = true; }).defer(500, this);
8582             
8583         },
8584         
8585         unmask : function()
8586         {
8587             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8588                 return;
8589             }
8590             
8591             this.maskEl.top.setStyle('position', 'absolute');
8592             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8593             this.maskEl.top.hide();
8594
8595             this.maskEl.left.setStyle('position', 'absolute');
8596             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8597             this.maskEl.left.hide();
8598
8599             this.maskEl.bottom.setStyle('position', 'absolute');
8600             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8601             this.maskEl.bottom.hide();
8602
8603             this.maskEl.right.setStyle('position', 'absolute');
8604             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8605             this.maskEl.right.hide();
8606             
8607             this.toolTip.hide();
8608             
8609             this.toolTip.el.hide();
8610             
8611             window.onwheel = function(){ return true;};
8612             
8613             if(this.intervalID){
8614                 window.clearInterval(this.intervalID);
8615                 this.intervalID = false;
8616             }
8617             
8618             this.isMasked = false;
8619             
8620         }
8621         
8622     }
8623     
8624 });
8625
8626 /*
8627  * Based on:
8628  * Ext JS Library 1.1.1
8629  * Copyright(c) 2006-2007, Ext JS, LLC.
8630  *
8631  * Originally Released Under LGPL - original licence link has changed is not relivant.
8632  *
8633  * Fork - LGPL
8634  * <script type="text/javascript">
8635  */
8636 /**
8637  * @class Roo.form.VTypes
8638  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8639  * @singleton
8640  */
8641 Roo.form.VTypes = function(){
8642     // closure these in so they are only created once.
8643     var alpha = /^[a-zA-Z_]+$/;
8644     var alphanum = /^[a-zA-Z0-9_]+$/;
8645     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8646     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8647
8648     // All these messages and functions are configurable
8649     return {
8650         /**
8651          * The function used to validate email addresses
8652          * @param {String} value The email address
8653          */
8654         'email' : function(v){
8655             return email.test(v);
8656         },
8657         /**
8658          * The error text to display when the email validation function returns false
8659          * @type String
8660          */
8661         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8662         /**
8663          * The keystroke filter mask to be applied on email input
8664          * @type RegExp
8665          */
8666         'emailMask' : /[a-z0-9_\.\-@]/i,
8667
8668         /**
8669          * The function used to validate URLs
8670          * @param {String} value The URL
8671          */
8672         'url' : function(v){
8673             return url.test(v);
8674         },
8675         /**
8676          * The error text to display when the url validation function returns false
8677          * @type String
8678          */
8679         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8680         
8681         /**
8682          * The function used to validate alpha values
8683          * @param {String} value The value
8684          */
8685         'alpha' : function(v){
8686             return alpha.test(v);
8687         },
8688         /**
8689          * The error text to display when the alpha validation function returns false
8690          * @type String
8691          */
8692         'alphaText' : 'This field should only contain letters and _',
8693         /**
8694          * The keystroke filter mask to be applied on alpha input
8695          * @type RegExp
8696          */
8697         'alphaMask' : /[a-z_]/i,
8698
8699         /**
8700          * The function used to validate alphanumeric values
8701          * @param {String} value The value
8702          */
8703         'alphanum' : function(v){
8704             return alphanum.test(v);
8705         },
8706         /**
8707          * The error text to display when the alphanumeric validation function returns false
8708          * @type String
8709          */
8710         'alphanumText' : 'This field should only contain letters, numbers and _',
8711         /**
8712          * The keystroke filter mask to be applied on alphanumeric input
8713          * @type RegExp
8714          */
8715         'alphanumMask' : /[a-z0-9_]/i
8716     };
8717 }();/*
8718  * - LGPL
8719  *
8720  * Input
8721  * 
8722  */
8723
8724 /**
8725  * @class Roo.bootstrap.Input
8726  * @extends Roo.bootstrap.Component
8727  * Bootstrap Input class
8728  * @cfg {Boolean} disabled is it disabled
8729  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8730  * @cfg {String} name name of the input
8731  * @cfg {string} fieldLabel - the label associated
8732  * @cfg {string} placeholder - placeholder to put in text.
8733  * @cfg {string}  before - input group add on before
8734  * @cfg {string} after - input group add on after
8735  * @cfg {string} size - (lg|sm) or leave empty..
8736  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8737  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8738  * @cfg {Number} md colspan out of 12 for computer-sized screens
8739  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8740  * @cfg {string} value default value of the input
8741  * @cfg {Number} labelWidth set the width of label 
8742  * @cfg {Number} labellg set the width of label (1-12)
8743  * @cfg {Number} labelmd set the width of label (1-12)
8744  * @cfg {Number} labelsm set the width of label (1-12)
8745  * @cfg {Number} labelxs set the width of label (1-12)
8746  * @cfg {String} labelAlign (top|left)
8747  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8748  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8749  * @cfg {String} indicatorpos (left|right) default left
8750  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8751  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8752
8753  * @cfg {String} align (left|center|right) Default left
8754  * @cfg {Boolean} forceFeedback (true|false) Default false
8755  * 
8756  * @constructor
8757  * Create a new Input
8758  * @param {Object} config The config object
8759  */
8760
8761 Roo.bootstrap.Input = function(config){
8762     
8763     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8764     
8765     this.addEvents({
8766         /**
8767          * @event focus
8768          * Fires when this field receives input focus.
8769          * @param {Roo.form.Field} this
8770          */
8771         focus : true,
8772         /**
8773          * @event blur
8774          * Fires when this field loses input focus.
8775          * @param {Roo.form.Field} this
8776          */
8777         blur : true,
8778         /**
8779          * @event specialkey
8780          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8781          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8782          * @param {Roo.form.Field} this
8783          * @param {Roo.EventObject} e The event object
8784          */
8785         specialkey : true,
8786         /**
8787          * @event change
8788          * Fires just before the field blurs if the field value has changed.
8789          * @param {Roo.form.Field} this
8790          * @param {Mixed} newValue The new value
8791          * @param {Mixed} oldValue The original value
8792          */
8793         change : true,
8794         /**
8795          * @event invalid
8796          * Fires after the field has been marked as invalid.
8797          * @param {Roo.form.Field} this
8798          * @param {String} msg The validation message
8799          */
8800         invalid : true,
8801         /**
8802          * @event valid
8803          * Fires after the field has been validated with no errors.
8804          * @param {Roo.form.Field} this
8805          */
8806         valid : true,
8807          /**
8808          * @event keyup
8809          * Fires after the key up
8810          * @param {Roo.form.Field} this
8811          * @param {Roo.EventObject}  e The event Object
8812          */
8813         keyup : true
8814     });
8815 };
8816
8817 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8818      /**
8819      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8820       automatic validation (defaults to "keyup").
8821      */
8822     validationEvent : "keyup",
8823      /**
8824      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8825      */
8826     validateOnBlur : true,
8827     /**
8828      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8829      */
8830     validationDelay : 250,
8831      /**
8832      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8833      */
8834     focusClass : "x-form-focus",  // not needed???
8835     
8836        
8837     /**
8838      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8839      */
8840     invalidClass : "has-warning",
8841     
8842     /**
8843      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8844      */
8845     validClass : "has-success",
8846     
8847     /**
8848      * @cfg {Boolean} hasFeedback (true|false) default true
8849      */
8850     hasFeedback : true,
8851     
8852     /**
8853      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8854      */
8855     invalidFeedbackClass : "glyphicon-warning-sign",
8856     
8857     /**
8858      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8859      */
8860     validFeedbackClass : "glyphicon-ok",
8861     
8862     /**
8863      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8864      */
8865     selectOnFocus : false,
8866     
8867      /**
8868      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8869      */
8870     maskRe : null,
8871        /**
8872      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8873      */
8874     vtype : null,
8875     
8876       /**
8877      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8878      */
8879     disableKeyFilter : false,
8880     
8881        /**
8882      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8883      */
8884     disabled : false,
8885      /**
8886      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8887      */
8888     allowBlank : true,
8889     /**
8890      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8891      */
8892     blankText : "Please complete this mandatory field",
8893     
8894      /**
8895      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8896      */
8897     minLength : 0,
8898     /**
8899      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8900      */
8901     maxLength : Number.MAX_VALUE,
8902     /**
8903      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8904      */
8905     minLengthText : "The minimum length for this field is {0}",
8906     /**
8907      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8908      */
8909     maxLengthText : "The maximum length for this field is {0}",
8910   
8911     
8912     /**
8913      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8914      * If available, this function will be called only after the basic validators all return true, and will be passed the
8915      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8916      */
8917     validator : null,
8918     /**
8919      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8920      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8921      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8922      */
8923     regex : null,
8924     /**
8925      * @cfg {String} regexText -- Depricated - use Invalid Text
8926      */
8927     regexText : "",
8928     
8929     /**
8930      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8931      */
8932     invalidText : "",
8933     
8934     
8935     
8936     autocomplete: false,
8937     
8938     
8939     fieldLabel : '',
8940     inputType : 'text',
8941     
8942     name : false,
8943     placeholder: false,
8944     before : false,
8945     after : false,
8946     size : false,
8947     hasFocus : false,
8948     preventMark: false,
8949     isFormField : true,
8950     value : '',
8951     labelWidth : 2,
8952     labelAlign : false,
8953     readOnly : false,
8954     align : false,
8955     formatedValue : false,
8956     forceFeedback : false,
8957     
8958     indicatorpos : 'left',
8959     
8960     labellg : 0,
8961     labelmd : 0,
8962     labelsm : 0,
8963     labelxs : 0,
8964     
8965     capture : '',
8966     accept : '',
8967     
8968     parentLabelAlign : function()
8969     {
8970         var parent = this;
8971         while (parent.parent()) {
8972             parent = parent.parent();
8973             if (typeof(parent.labelAlign) !='undefined') {
8974                 return parent.labelAlign;
8975             }
8976         }
8977         return 'left';
8978         
8979     },
8980     
8981     getAutoCreate : function()
8982     {
8983         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8984         
8985         var id = Roo.id();
8986         
8987         var cfg = {};
8988         
8989         if(this.inputType != 'hidden'){
8990             cfg.cls = 'form-group' //input-group
8991         }
8992         
8993         var input =  {
8994             tag: 'input',
8995             id : id,
8996             type : this.inputType,
8997             value : this.value,
8998             cls : 'form-control',
8999             placeholder : this.placeholder || '',
9000             autocomplete : this.autocomplete || 'new-password'
9001         };
9002         
9003         if(this.capture.length){
9004             input.capture = this.capture;
9005         }
9006         
9007         if(this.accept.length){
9008             input.accept = this.accept + "/*";
9009         }
9010         
9011         if(this.align){
9012             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9013         }
9014         
9015         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9016             input.maxLength = this.maxLength;
9017         }
9018         
9019         if (this.disabled) {
9020             input.disabled=true;
9021         }
9022         
9023         if (this.readOnly) {
9024             input.readonly=true;
9025         }
9026         
9027         if (this.name) {
9028             input.name = this.name;
9029         }
9030         
9031         if (this.size) {
9032             input.cls += ' input-' + this.size;
9033         }
9034         
9035         var settings=this;
9036         ['xs','sm','md','lg'].map(function(size){
9037             if (settings[size]) {
9038                 cfg.cls += ' col-' + size + '-' + settings[size];
9039             }
9040         });
9041         
9042         var inputblock = input;
9043         
9044         var feedback = {
9045             tag: 'span',
9046             cls: 'glyphicon form-control-feedback'
9047         };
9048             
9049         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9050             
9051             inputblock = {
9052                 cls : 'has-feedback',
9053                 cn :  [
9054                     input,
9055                     feedback
9056                 ] 
9057             };  
9058         }
9059         
9060         if (this.before || this.after) {
9061             
9062             inputblock = {
9063                 cls : 'input-group',
9064                 cn :  [] 
9065             };
9066             
9067             if (this.before && typeof(this.before) == 'string') {
9068                 
9069                 inputblock.cn.push({
9070                     tag :'span',
9071                     cls : 'roo-input-before input-group-addon',
9072                     html : this.before
9073                 });
9074             }
9075             if (this.before && typeof(this.before) == 'object') {
9076                 this.before = Roo.factory(this.before);
9077                 
9078                 inputblock.cn.push({
9079                     tag :'span',
9080                     cls : 'roo-input-before input-group-' +
9081                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9082                 });
9083             }
9084             
9085             inputblock.cn.push(input);
9086             
9087             if (this.after && typeof(this.after) == 'string') {
9088                 inputblock.cn.push({
9089                     tag :'span',
9090                     cls : 'roo-input-after input-group-addon',
9091                     html : this.after
9092                 });
9093             }
9094             if (this.after && typeof(this.after) == 'object') {
9095                 this.after = Roo.factory(this.after);
9096                 
9097                 inputblock.cn.push({
9098                     tag :'span',
9099                     cls : 'roo-input-after input-group-' +
9100                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9101                 });
9102             }
9103             
9104             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9105                 inputblock.cls += ' has-feedback';
9106                 inputblock.cn.push(feedback);
9107             }
9108         };
9109         
9110         if (align ==='left' && this.fieldLabel.length) {
9111             
9112             cfg.cls += ' roo-form-group-label-left';
9113             
9114             cfg.cn = [
9115                 {
9116                     tag : 'i',
9117                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9118                     tooltip : 'This field is required'
9119                 },
9120                 {
9121                     tag: 'label',
9122                     'for' :  id,
9123                     cls : 'control-label',
9124                     html : this.fieldLabel
9125
9126                 },
9127                 {
9128                     cls : "", 
9129                     cn: [
9130                         inputblock
9131                     ]
9132                 }
9133             ];
9134             
9135             var labelCfg = cfg.cn[1];
9136             var contentCfg = cfg.cn[2];
9137             
9138             if(this.indicatorpos == 'right'){
9139                 cfg.cn = [
9140                     {
9141                         tag: 'label',
9142                         'for' :  id,
9143                         cls : 'control-label',
9144                         cn : [
9145                             {
9146                                 tag : 'span',
9147                                 html : this.fieldLabel
9148                             },
9149                             {
9150                                 tag : 'i',
9151                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9152                                 tooltip : 'This field is required'
9153                             }
9154                         ]
9155                     },
9156                     {
9157                         cls : "",
9158                         cn: [
9159                             inputblock
9160                         ]
9161                     }
9162
9163                 ];
9164                 
9165                 labelCfg = cfg.cn[0];
9166                 contentCfg = cfg.cn[1];
9167             
9168             }
9169             
9170             if(this.labelWidth > 12){
9171                 labelCfg.style = "width: " + this.labelWidth + 'px';
9172             }
9173             
9174             if(this.labelWidth < 13 && this.labelmd == 0){
9175                 this.labelmd = this.labelWidth;
9176             }
9177             
9178             if(this.labellg > 0){
9179                 labelCfg.cls += ' col-lg-' + this.labellg;
9180                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9181             }
9182             
9183             if(this.labelmd > 0){
9184                 labelCfg.cls += ' col-md-' + this.labelmd;
9185                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9186             }
9187             
9188             if(this.labelsm > 0){
9189                 labelCfg.cls += ' col-sm-' + this.labelsm;
9190                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9191             }
9192             
9193             if(this.labelxs > 0){
9194                 labelCfg.cls += ' col-xs-' + this.labelxs;
9195                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9196             }
9197             
9198             
9199         } else if ( this.fieldLabel.length) {
9200                 
9201             cfg.cn = [
9202                 {
9203                     tag : 'i',
9204                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9205                     tooltip : 'This field is required'
9206                 },
9207                 {
9208                     tag: 'label',
9209                    //cls : 'input-group-addon',
9210                     html : this.fieldLabel
9211
9212                 },
9213
9214                inputblock
9215
9216            ];
9217            
9218            if(this.indicatorpos == 'right'){
9219                 
9220                 cfg.cn = [
9221                     {
9222                         tag: 'label',
9223                        //cls : 'input-group-addon',
9224                         html : this.fieldLabel
9225
9226                     },
9227                     {
9228                         tag : 'i',
9229                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9230                         tooltip : 'This field is required'
9231                     },
9232
9233                    inputblock
9234
9235                ];
9236
9237             }
9238
9239         } else {
9240             
9241             cfg.cn = [
9242
9243                     inputblock
9244
9245             ];
9246                 
9247                 
9248         };
9249         
9250         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9251            cfg.cls += ' navbar-form';
9252         }
9253         
9254         if (this.parentType === 'NavGroup') {
9255            cfg.cls += ' navbar-form';
9256            cfg.tag = 'li';
9257         }
9258         
9259         return cfg;
9260         
9261     },
9262     /**
9263      * return the real input element.
9264      */
9265     inputEl: function ()
9266     {
9267         return this.el.select('input.form-control',true).first();
9268     },
9269     
9270     tooltipEl : function()
9271     {
9272         return this.inputEl();
9273     },
9274     
9275     indicatorEl : function()
9276     {
9277         var indicator = this.el.select('i.roo-required-indicator',true).first();
9278         
9279         if(!indicator){
9280             return false;
9281         }
9282         
9283         return indicator;
9284         
9285     },
9286     
9287     setDisabled : function(v)
9288     {
9289         var i  = this.inputEl().dom;
9290         if (!v) {
9291             i.removeAttribute('disabled');
9292             return;
9293             
9294         }
9295         i.setAttribute('disabled','true');
9296     },
9297     initEvents : function()
9298     {
9299           
9300         this.inputEl().on("keydown" , this.fireKey,  this);
9301         this.inputEl().on("focus", this.onFocus,  this);
9302         this.inputEl().on("blur", this.onBlur,  this);
9303         
9304         this.inputEl().relayEvent('keyup', this);
9305         
9306         this.indicator = this.indicatorEl();
9307         
9308         if(this.indicator){
9309             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9310         }
9311  
9312         // reference to original value for reset
9313         this.originalValue = this.getValue();
9314         //Roo.form.TextField.superclass.initEvents.call(this);
9315         if(this.validationEvent == 'keyup'){
9316             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9317             this.inputEl().on('keyup', this.filterValidation, this);
9318         }
9319         else if(this.validationEvent !== false){
9320             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9321         }
9322         
9323         if(this.selectOnFocus){
9324             this.on("focus", this.preFocus, this);
9325             
9326         }
9327         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9328             this.inputEl().on("keypress", this.filterKeys, this);
9329         } else {
9330             this.inputEl().relayEvent('keypress', this);
9331         }
9332        /* if(this.grow){
9333             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9334             this.el.on("click", this.autoSize,  this);
9335         }
9336         */
9337         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9338             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9339         }
9340         
9341         if (typeof(this.before) == 'object') {
9342             this.before.render(this.el.select('.roo-input-before',true).first());
9343         }
9344         if (typeof(this.after) == 'object') {
9345             this.after.render(this.el.select('.roo-input-after',true).first());
9346         }
9347         
9348         this.inputEl().on('change', this.onChange, this);
9349         
9350     },
9351     filterValidation : function(e){
9352         if(!e.isNavKeyPress()){
9353             this.validationTask.delay(this.validationDelay);
9354         }
9355     },
9356      /**
9357      * Validates the field value
9358      * @return {Boolean} True if the value is valid, else false
9359      */
9360     validate : function(){
9361         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9362         if(this.disabled || this.validateValue(this.getRawValue())){
9363             this.markValid();
9364             return true;
9365         }
9366         
9367         this.markInvalid();
9368         return false;
9369     },
9370     
9371     
9372     /**
9373      * Validates a value according to the field's validation rules and marks the field as invalid
9374      * if the validation fails
9375      * @param {Mixed} value The value to validate
9376      * @return {Boolean} True if the value is valid, else false
9377      */
9378     validateValue : function(value)
9379     {
9380         if(this.getVisibilityEl().hasClass('hidden')){
9381             return true;
9382         }
9383         
9384         if(value.length < 1)  { // if it's blank
9385             if(this.allowBlank){
9386                 return true;
9387             }
9388             return false;
9389         }
9390         
9391         if(value.length < this.minLength){
9392             return false;
9393         }
9394         if(value.length > this.maxLength){
9395             return false;
9396         }
9397         if(this.vtype){
9398             var vt = Roo.form.VTypes;
9399             if(!vt[this.vtype](value, this)){
9400                 return false;
9401             }
9402         }
9403         if(typeof this.validator == "function"){
9404             var msg = this.validator(value);
9405             if(msg !== true){
9406                 return false;
9407             }
9408             if (typeof(msg) == 'string') {
9409                 this.invalidText = msg;
9410             }
9411         }
9412         
9413         if(this.regex && !this.regex.test(value)){
9414             return false;
9415         }
9416         
9417         return true;
9418     },
9419     
9420      // private
9421     fireKey : function(e){
9422         //Roo.log('field ' + e.getKey());
9423         if(e.isNavKeyPress()){
9424             this.fireEvent("specialkey", this, e);
9425         }
9426     },
9427     focus : function (selectText){
9428         if(this.rendered){
9429             this.inputEl().focus();
9430             if(selectText === true){
9431                 this.inputEl().dom.select();
9432             }
9433         }
9434         return this;
9435     } ,
9436     
9437     onFocus : function(){
9438         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9439            // this.el.addClass(this.focusClass);
9440         }
9441         if(!this.hasFocus){
9442             this.hasFocus = true;
9443             this.startValue = this.getValue();
9444             this.fireEvent("focus", this);
9445         }
9446     },
9447     
9448     beforeBlur : Roo.emptyFn,
9449
9450     
9451     // private
9452     onBlur : function(){
9453         this.beforeBlur();
9454         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9455             //this.el.removeClass(this.focusClass);
9456         }
9457         this.hasFocus = false;
9458         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9459             this.validate();
9460         }
9461         var v = this.getValue();
9462         if(String(v) !== String(this.startValue)){
9463             this.fireEvent('change', this, v, this.startValue);
9464         }
9465         this.fireEvent("blur", this);
9466     },
9467     
9468     onChange : function(e)
9469     {
9470         var v = this.getValue();
9471         if(String(v) !== String(this.startValue)){
9472             this.fireEvent('change', this, v, this.startValue);
9473         }
9474         
9475     },
9476     
9477     /**
9478      * Resets the current field value to the originally loaded value and clears any validation messages
9479      */
9480     reset : function(){
9481         this.setValue(this.originalValue);
9482         this.validate();
9483     },
9484      /**
9485      * Returns the name of the field
9486      * @return {Mixed} name The name field
9487      */
9488     getName: function(){
9489         return this.name;
9490     },
9491      /**
9492      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9493      * @return {Mixed} value The field value
9494      */
9495     getValue : function(){
9496         
9497         var v = this.inputEl().getValue();
9498         
9499         return v;
9500     },
9501     /**
9502      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9503      * @return {Mixed} value The field value
9504      */
9505     getRawValue : function(){
9506         var v = this.inputEl().getValue();
9507         
9508         return v;
9509     },
9510     
9511     /**
9512      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9513      * @param {Mixed} value The value to set
9514      */
9515     setRawValue : function(v){
9516         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9517     },
9518     
9519     selectText : function(start, end){
9520         var v = this.getRawValue();
9521         if(v.length > 0){
9522             start = start === undefined ? 0 : start;
9523             end = end === undefined ? v.length : end;
9524             var d = this.inputEl().dom;
9525             if(d.setSelectionRange){
9526                 d.setSelectionRange(start, end);
9527             }else if(d.createTextRange){
9528                 var range = d.createTextRange();
9529                 range.moveStart("character", start);
9530                 range.moveEnd("character", v.length-end);
9531                 range.select();
9532             }
9533         }
9534     },
9535     
9536     /**
9537      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9538      * @param {Mixed} value The value to set
9539      */
9540     setValue : function(v){
9541         this.value = v;
9542         if(this.rendered){
9543             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9544             this.validate();
9545         }
9546     },
9547     
9548     /*
9549     processValue : function(value){
9550         if(this.stripCharsRe){
9551             var newValue = value.replace(this.stripCharsRe, '');
9552             if(newValue !== value){
9553                 this.setRawValue(newValue);
9554                 return newValue;
9555             }
9556         }
9557         return value;
9558     },
9559   */
9560     preFocus : function(){
9561         
9562         if(this.selectOnFocus){
9563             this.inputEl().dom.select();
9564         }
9565     },
9566     filterKeys : function(e){
9567         var k = e.getKey();
9568         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9569             return;
9570         }
9571         var c = e.getCharCode(), cc = String.fromCharCode(c);
9572         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9573             return;
9574         }
9575         if(!this.maskRe.test(cc)){
9576             e.stopEvent();
9577         }
9578     },
9579      /**
9580      * Clear any invalid styles/messages for this field
9581      */
9582     clearInvalid : function(){
9583         
9584         if(!this.el || this.preventMark){ // not rendered
9585             return;
9586         }
9587         
9588      
9589         this.el.removeClass(this.invalidClass);
9590         
9591         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9592             
9593             var feedback = this.el.select('.form-control-feedback', true).first();
9594             
9595             if(feedback){
9596                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9597             }
9598             
9599         }
9600         
9601         if(this.indicator){
9602             this.indicator.removeClass('visible');
9603             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9604         }
9605         
9606         this.fireEvent('valid', this);
9607     },
9608     
9609      /**
9610      * Mark this field as valid
9611      */
9612     markValid : function()
9613     {
9614         if(!this.el  || this.preventMark){ // not rendered...
9615             return;
9616         }
9617         
9618         this.el.removeClass([this.invalidClass, this.validClass]);
9619         
9620         var feedback = this.el.select('.form-control-feedback', true).first();
9621             
9622         if(feedback){
9623             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9624         }
9625         
9626         if(this.indicator){
9627             this.indicator.removeClass('visible');
9628             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9629         }
9630         
9631         if(this.disabled){
9632             return;
9633         }
9634         
9635         if(this.allowBlank && !this.getRawValue().length){
9636             return;
9637         }
9638         
9639         this.el.addClass(this.validClass);
9640         
9641         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9642             
9643             var feedback = this.el.select('.form-control-feedback', true).first();
9644             
9645             if(feedback){
9646                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9647                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9648             }
9649             
9650         }
9651         
9652         this.fireEvent('valid', this);
9653     },
9654     
9655      /**
9656      * Mark this field as invalid
9657      * @param {String} msg The validation message
9658      */
9659     markInvalid : function(msg)
9660     {
9661         if(!this.el  || this.preventMark){ // not rendered
9662             return;
9663         }
9664         
9665         this.el.removeClass([this.invalidClass, this.validClass]);
9666         
9667         var feedback = this.el.select('.form-control-feedback', true).first();
9668             
9669         if(feedback){
9670             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9671         }
9672
9673         if(this.disabled){
9674             return;
9675         }
9676         
9677         if(this.allowBlank && !this.getRawValue().length){
9678             return;
9679         }
9680         
9681         if(this.indicator){
9682             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9683             this.indicator.addClass('visible');
9684         }
9685         
9686         this.el.addClass(this.invalidClass);
9687         
9688         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9689             
9690             var feedback = this.el.select('.form-control-feedback', true).first();
9691             
9692             if(feedback){
9693                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9694                 
9695                 if(this.getValue().length || this.forceFeedback){
9696                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9697                 }
9698                 
9699             }
9700             
9701         }
9702         
9703         this.fireEvent('invalid', this, msg);
9704     },
9705     // private
9706     SafariOnKeyDown : function(event)
9707     {
9708         // this is a workaround for a password hang bug on chrome/ webkit.
9709         if (this.inputEl().dom.type != 'password') {
9710             return;
9711         }
9712         
9713         var isSelectAll = false;
9714         
9715         if(this.inputEl().dom.selectionEnd > 0){
9716             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9717         }
9718         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9719             event.preventDefault();
9720             this.setValue('');
9721             return;
9722         }
9723         
9724         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9725             
9726             event.preventDefault();
9727             // this is very hacky as keydown always get's upper case.
9728             //
9729             var cc = String.fromCharCode(event.getCharCode());
9730             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9731             
9732         }
9733     },
9734     adjustWidth : function(tag, w){
9735         tag = tag.toLowerCase();
9736         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9737             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9738                 if(tag == 'input'){
9739                     return w + 2;
9740                 }
9741                 if(tag == 'textarea'){
9742                     return w-2;
9743                 }
9744             }else if(Roo.isOpera){
9745                 if(tag == 'input'){
9746                     return w + 2;
9747                 }
9748                 if(tag == 'textarea'){
9749                     return w-2;
9750                 }
9751             }
9752         }
9753         return w;
9754     },
9755     
9756     setFieldLabel : function(v)
9757     {
9758         if(!this.rendered){
9759             return;
9760         }
9761         
9762         if(this.indicator){
9763             var ar = this.el.select('label > span',true);
9764             
9765             if (ar.elements.length) {
9766                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9767                 this.fieldLabel = v;
9768                 return;
9769             }
9770             
9771             var br = this.el.select('label',true);
9772             
9773             if(br.elements.length) {
9774                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9775                 this.fieldLabel = v;
9776                 return;
9777             }
9778             
9779             Roo.log('Cannot Found any of label > span || label in input');
9780             return;
9781         }
9782         
9783         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9784         this.fieldLabel = v;
9785         
9786         
9787     }
9788 });
9789
9790  
9791 /*
9792  * - LGPL
9793  *
9794  * Input
9795  * 
9796  */
9797
9798 /**
9799  * @class Roo.bootstrap.TextArea
9800  * @extends Roo.bootstrap.Input
9801  * Bootstrap TextArea class
9802  * @cfg {Number} cols Specifies the visible width of a text area
9803  * @cfg {Number} rows Specifies the visible number of lines in a text area
9804  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9805  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9806  * @cfg {string} html text
9807  * 
9808  * @constructor
9809  * Create a new TextArea
9810  * @param {Object} config The config object
9811  */
9812
9813 Roo.bootstrap.TextArea = function(config){
9814     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9815    
9816 };
9817
9818 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9819      
9820     cols : false,
9821     rows : 5,
9822     readOnly : false,
9823     warp : 'soft',
9824     resize : false,
9825     value: false,
9826     html: false,
9827     
9828     getAutoCreate : function(){
9829         
9830         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9831         
9832         var id = Roo.id();
9833         
9834         var cfg = {};
9835         
9836         if(this.inputType != 'hidden'){
9837             cfg.cls = 'form-group' //input-group
9838         }
9839         
9840         var input =  {
9841             tag: 'textarea',
9842             id : id,
9843             warp : this.warp,
9844             rows : this.rows,
9845             value : this.value || '',
9846             html: this.html || '',
9847             cls : 'form-control',
9848             placeholder : this.placeholder || '' 
9849             
9850         };
9851         
9852         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9853             input.maxLength = this.maxLength;
9854         }
9855         
9856         if(this.resize){
9857             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9858         }
9859         
9860         if(this.cols){
9861             input.cols = this.cols;
9862         }
9863         
9864         if (this.readOnly) {
9865             input.readonly = true;
9866         }
9867         
9868         if (this.name) {
9869             input.name = this.name;
9870         }
9871         
9872         if (this.size) {
9873             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9874         }
9875         
9876         var settings=this;
9877         ['xs','sm','md','lg'].map(function(size){
9878             if (settings[size]) {
9879                 cfg.cls += ' col-' + size + '-' + settings[size];
9880             }
9881         });
9882         
9883         var inputblock = input;
9884         
9885         if(this.hasFeedback && !this.allowBlank){
9886             
9887             var feedback = {
9888                 tag: 'span',
9889                 cls: 'glyphicon form-control-feedback'
9890             };
9891
9892             inputblock = {
9893                 cls : 'has-feedback',
9894                 cn :  [
9895                     input,
9896                     feedback
9897                 ] 
9898             };  
9899         }
9900         
9901         
9902         if (this.before || this.after) {
9903             
9904             inputblock = {
9905                 cls : 'input-group',
9906                 cn :  [] 
9907             };
9908             if (this.before) {
9909                 inputblock.cn.push({
9910                     tag :'span',
9911                     cls : 'input-group-addon',
9912                     html : this.before
9913                 });
9914             }
9915             
9916             inputblock.cn.push(input);
9917             
9918             if(this.hasFeedback && !this.allowBlank){
9919                 inputblock.cls += ' has-feedback';
9920                 inputblock.cn.push(feedback);
9921             }
9922             
9923             if (this.after) {
9924                 inputblock.cn.push({
9925                     tag :'span',
9926                     cls : 'input-group-addon',
9927                     html : this.after
9928                 });
9929             }
9930             
9931         }
9932         
9933         if (align ==='left' && this.fieldLabel.length) {
9934             cfg.cn = [
9935                 {
9936                     tag: 'label',
9937                     'for' :  id,
9938                     cls : 'control-label',
9939                     html : this.fieldLabel
9940                 },
9941                 {
9942                     cls : "",
9943                     cn: [
9944                         inputblock
9945                     ]
9946                 }
9947
9948             ];
9949             
9950             if(this.labelWidth > 12){
9951                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9952             }
9953
9954             if(this.labelWidth < 13 && this.labelmd == 0){
9955                 this.labelmd = this.labelWidth;
9956             }
9957
9958             if(this.labellg > 0){
9959                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9960                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9961             }
9962
9963             if(this.labelmd > 0){
9964                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9965                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9966             }
9967
9968             if(this.labelsm > 0){
9969                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9970                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9971             }
9972
9973             if(this.labelxs > 0){
9974                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9975                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9976             }
9977             
9978         } else if ( this.fieldLabel.length) {
9979             cfg.cn = [
9980
9981                {
9982                    tag: 'label',
9983                    //cls : 'input-group-addon',
9984                    html : this.fieldLabel
9985
9986                },
9987
9988                inputblock
9989
9990            ];
9991
9992         } else {
9993
9994             cfg.cn = [
9995
9996                 inputblock
9997
9998             ];
9999                 
10000         }
10001         
10002         if (this.disabled) {
10003             input.disabled=true;
10004         }
10005         
10006         return cfg;
10007         
10008     },
10009     /**
10010      * return the real textarea element.
10011      */
10012     inputEl: function ()
10013     {
10014         return this.el.select('textarea.form-control',true).first();
10015     },
10016     
10017     /**
10018      * Clear any invalid styles/messages for this field
10019      */
10020     clearInvalid : function()
10021     {
10022         
10023         if(!this.el || this.preventMark){ // not rendered
10024             return;
10025         }
10026         
10027         var label = this.el.select('label', true).first();
10028         var icon = this.el.select('i.fa-star', true).first();
10029         
10030         if(label && icon){
10031             icon.remove();
10032         }
10033         
10034         this.el.removeClass(this.invalidClass);
10035         
10036         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10037             
10038             var feedback = this.el.select('.form-control-feedback', true).first();
10039             
10040             if(feedback){
10041                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10042             }
10043             
10044         }
10045         
10046         this.fireEvent('valid', this);
10047     },
10048     
10049      /**
10050      * Mark this field as valid
10051      */
10052     markValid : function()
10053     {
10054         if(!this.el  || this.preventMark){ // not rendered
10055             return;
10056         }
10057         
10058         this.el.removeClass([this.invalidClass, this.validClass]);
10059         
10060         var feedback = this.el.select('.form-control-feedback', true).first();
10061             
10062         if(feedback){
10063             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10064         }
10065
10066         if(this.disabled || this.allowBlank){
10067             return;
10068         }
10069         
10070         var label = this.el.select('label', true).first();
10071         var icon = this.el.select('i.fa-star', true).first();
10072         
10073         if(label && icon){
10074             icon.remove();
10075         }
10076         
10077         this.el.addClass(this.validClass);
10078         
10079         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10080             
10081             var feedback = this.el.select('.form-control-feedback', true).first();
10082             
10083             if(feedback){
10084                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10085                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10086             }
10087             
10088         }
10089         
10090         this.fireEvent('valid', this);
10091     },
10092     
10093      /**
10094      * Mark this field as invalid
10095      * @param {String} msg The validation message
10096      */
10097     markInvalid : function(msg)
10098     {
10099         if(!this.el  || this.preventMark){ // not rendered
10100             return;
10101         }
10102         
10103         this.el.removeClass([this.invalidClass, this.validClass]);
10104         
10105         var feedback = this.el.select('.form-control-feedback', true).first();
10106             
10107         if(feedback){
10108             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10109         }
10110
10111         if(this.disabled || this.allowBlank){
10112             return;
10113         }
10114         
10115         var label = this.el.select('label', true).first();
10116         var icon = this.el.select('i.fa-star', true).first();
10117         
10118         if(!this.getValue().length && label && !icon){
10119             this.el.createChild({
10120                 tag : 'i',
10121                 cls : 'text-danger fa fa-lg fa-star',
10122                 tooltip : 'This field is required',
10123                 style : 'margin-right:5px;'
10124             }, label, true);
10125         }
10126
10127         this.el.addClass(this.invalidClass);
10128         
10129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10130             
10131             var feedback = this.el.select('.form-control-feedback', true).first();
10132             
10133             if(feedback){
10134                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10135                 
10136                 if(this.getValue().length || this.forceFeedback){
10137                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10138                 }
10139                 
10140             }
10141             
10142         }
10143         
10144         this.fireEvent('invalid', this, msg);
10145     }
10146 });
10147
10148  
10149 /*
10150  * - LGPL
10151  *
10152  * trigger field - base class for combo..
10153  * 
10154  */
10155  
10156 /**
10157  * @class Roo.bootstrap.TriggerField
10158  * @extends Roo.bootstrap.Input
10159  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10160  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10161  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10162  * for which you can provide a custom implementation.  For example:
10163  * <pre><code>
10164 var trigger = new Roo.bootstrap.TriggerField();
10165 trigger.onTriggerClick = myTriggerFn;
10166 trigger.applyTo('my-field');
10167 </code></pre>
10168  *
10169  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10170  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10171  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10172  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10173  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10174
10175  * @constructor
10176  * Create a new TriggerField.
10177  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10178  * to the base TextField)
10179  */
10180 Roo.bootstrap.TriggerField = function(config){
10181     this.mimicing = false;
10182     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10183 };
10184
10185 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10186     /**
10187      * @cfg {String} triggerClass A CSS class to apply to the trigger
10188      */
10189      /**
10190      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10191      */
10192     hideTrigger:false,
10193
10194     /**
10195      * @cfg {Boolean} removable (true|false) special filter default false
10196      */
10197     removable : false,
10198     
10199     /** @cfg {Boolean} grow @hide */
10200     /** @cfg {Number} growMin @hide */
10201     /** @cfg {Number} growMax @hide */
10202
10203     /**
10204      * @hide 
10205      * @method
10206      */
10207     autoSize: Roo.emptyFn,
10208     // private
10209     monitorTab : true,
10210     // private
10211     deferHeight : true,
10212
10213     
10214     actionMode : 'wrap',
10215     
10216     caret : false,
10217     
10218     
10219     getAutoCreate : function(){
10220        
10221         var align = this.labelAlign || this.parentLabelAlign();
10222         
10223         var id = Roo.id();
10224         
10225         var cfg = {
10226             cls: 'form-group' //input-group
10227         };
10228         
10229         
10230         var input =  {
10231             tag: 'input',
10232             id : id,
10233             type : this.inputType,
10234             cls : 'form-control',
10235             autocomplete: 'new-password',
10236             placeholder : this.placeholder || '' 
10237             
10238         };
10239         if (this.name) {
10240             input.name = this.name;
10241         }
10242         if (this.size) {
10243             input.cls += ' input-' + this.size;
10244         }
10245         
10246         if (this.disabled) {
10247             input.disabled=true;
10248         }
10249         
10250         var inputblock = input;
10251         
10252         if(this.hasFeedback && !this.allowBlank){
10253             
10254             var feedback = {
10255                 tag: 'span',
10256                 cls: 'glyphicon form-control-feedback'
10257             };
10258             
10259             if(this.removable && !this.editable && !this.tickable){
10260                 inputblock = {
10261                     cls : 'has-feedback',
10262                     cn :  [
10263                         inputblock,
10264                         {
10265                             tag: 'button',
10266                             html : 'x',
10267                             cls : 'roo-combo-removable-btn close'
10268                         },
10269                         feedback
10270                     ] 
10271                 };
10272             } else {
10273                 inputblock = {
10274                     cls : 'has-feedback',
10275                     cn :  [
10276                         inputblock,
10277                         feedback
10278                     ] 
10279                 };
10280             }
10281
10282         } else {
10283             if(this.removable && !this.editable && !this.tickable){
10284                 inputblock = {
10285                     cls : 'roo-removable',
10286                     cn :  [
10287                         inputblock,
10288                         {
10289                             tag: 'button',
10290                             html : 'x',
10291                             cls : 'roo-combo-removable-btn close'
10292                         }
10293                     ] 
10294                 };
10295             }
10296         }
10297         
10298         if (this.before || this.after) {
10299             
10300             inputblock = {
10301                 cls : 'input-group',
10302                 cn :  [] 
10303             };
10304             if (this.before) {
10305                 inputblock.cn.push({
10306                     tag :'span',
10307                     cls : 'input-group-addon',
10308                     html : this.before
10309                 });
10310             }
10311             
10312             inputblock.cn.push(input);
10313             
10314             if(this.hasFeedback && !this.allowBlank){
10315                 inputblock.cls += ' has-feedback';
10316                 inputblock.cn.push(feedback);
10317             }
10318             
10319             if (this.after) {
10320                 inputblock.cn.push({
10321                     tag :'span',
10322                     cls : 'input-group-addon',
10323                     html : this.after
10324                 });
10325             }
10326             
10327         };
10328         
10329         var box = {
10330             tag: 'div',
10331             cn: [
10332                 {
10333                     tag: 'input',
10334                     type : 'hidden',
10335                     cls: 'form-hidden-field'
10336                 },
10337                 inputblock
10338             ]
10339             
10340         };
10341         
10342         if(this.multiple){
10343             box = {
10344                 tag: 'div',
10345                 cn: [
10346                     {
10347                         tag: 'input',
10348                         type : 'hidden',
10349                         cls: 'form-hidden-field'
10350                     },
10351                     {
10352                         tag: 'ul',
10353                         cls: 'roo-select2-choices',
10354                         cn:[
10355                             {
10356                                 tag: 'li',
10357                                 cls: 'roo-select2-search-field',
10358                                 cn: [
10359
10360                                     inputblock
10361                                 ]
10362                             }
10363                         ]
10364                     }
10365                 ]
10366             }
10367         };
10368         
10369         var combobox = {
10370             cls: 'roo-select2-container input-group',
10371             cn: [
10372                 box
10373 //                {
10374 //                    tag: 'ul',
10375 //                    cls: 'typeahead typeahead-long dropdown-menu',
10376 //                    style: 'display:none'
10377 //                }
10378             ]
10379         };
10380         
10381         if(!this.multiple && this.showToggleBtn){
10382             
10383             var caret = {
10384                         tag: 'span',
10385                         cls: 'caret'
10386              };
10387             if (this.caret != false) {
10388                 caret = {
10389                      tag: 'i',
10390                      cls: 'fa fa-' + this.caret
10391                 };
10392                 
10393             }
10394             
10395             combobox.cn.push({
10396                 tag :'span',
10397                 cls : 'input-group-addon btn dropdown-toggle',
10398                 cn : [
10399                     caret,
10400                     {
10401                         tag: 'span',
10402                         cls: 'combobox-clear',
10403                         cn  : [
10404                             {
10405                                 tag : 'i',
10406                                 cls: 'icon-remove'
10407                             }
10408                         ]
10409                     }
10410                 ]
10411
10412             })
10413         }
10414         
10415         if(this.multiple){
10416             combobox.cls += ' roo-select2-container-multi';
10417         }
10418         
10419         if (align ==='left' && this.fieldLabel.length) {
10420             
10421             cfg.cls += ' roo-form-group-label-left';
10422
10423             cfg.cn = [
10424                 {
10425                     tag : 'i',
10426                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10427                     tooltip : 'This field is required'
10428                 },
10429                 {
10430                     tag: 'label',
10431                     'for' :  id,
10432                     cls : 'control-label',
10433                     html : this.fieldLabel
10434
10435                 },
10436                 {
10437                     cls : "", 
10438                     cn: [
10439                         combobox
10440                     ]
10441                 }
10442
10443             ];
10444             
10445             var labelCfg = cfg.cn[1];
10446             var contentCfg = cfg.cn[2];
10447             
10448             if(this.indicatorpos == 'right'){
10449                 cfg.cn = [
10450                     {
10451                         tag: 'label',
10452                         'for' :  id,
10453                         cls : 'control-label',
10454                         cn : [
10455                             {
10456                                 tag : 'span',
10457                                 html : this.fieldLabel
10458                             },
10459                             {
10460                                 tag : 'i',
10461                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10462                                 tooltip : 'This field is required'
10463                             }
10464                         ]
10465                     },
10466                     {
10467                         cls : "", 
10468                         cn: [
10469                             combobox
10470                         ]
10471                     }
10472
10473                 ];
10474                 
10475                 labelCfg = cfg.cn[0];
10476                 contentCfg = cfg.cn[1];
10477             }
10478             
10479             if(this.labelWidth > 12){
10480                 labelCfg.style = "width: " + this.labelWidth + 'px';
10481             }
10482             
10483             if(this.labelWidth < 13 && this.labelmd == 0){
10484                 this.labelmd = this.labelWidth;
10485             }
10486             
10487             if(this.labellg > 0){
10488                 labelCfg.cls += ' col-lg-' + this.labellg;
10489                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10490             }
10491             
10492             if(this.labelmd > 0){
10493                 labelCfg.cls += ' col-md-' + this.labelmd;
10494                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10495             }
10496             
10497             if(this.labelsm > 0){
10498                 labelCfg.cls += ' col-sm-' + this.labelsm;
10499                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10500             }
10501             
10502             if(this.labelxs > 0){
10503                 labelCfg.cls += ' col-xs-' + this.labelxs;
10504                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10505             }
10506             
10507         } else if ( this.fieldLabel.length) {
10508 //                Roo.log(" label");
10509             cfg.cn = [
10510                 {
10511                    tag : 'i',
10512                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10513                    tooltip : 'This field is required'
10514                },
10515                {
10516                    tag: 'label',
10517                    //cls : 'input-group-addon',
10518                    html : this.fieldLabel
10519
10520                },
10521
10522                combobox
10523
10524             ];
10525             
10526             if(this.indicatorpos == 'right'){
10527                 
10528                 cfg.cn = [
10529                     {
10530                        tag: 'label',
10531                        cn : [
10532                            {
10533                                tag : 'span',
10534                                html : this.fieldLabel
10535                            },
10536                            {
10537                               tag : 'i',
10538                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10539                               tooltip : 'This field is required'
10540                            }
10541                        ]
10542
10543                     },
10544                     combobox
10545
10546                 ];
10547
10548             }
10549
10550         } else {
10551             
10552 //                Roo.log(" no label && no align");
10553                 cfg = combobox
10554                      
10555                 
10556         }
10557         
10558         var settings=this;
10559         ['xs','sm','md','lg'].map(function(size){
10560             if (settings[size]) {
10561                 cfg.cls += ' col-' + size + '-' + settings[size];
10562             }
10563         });
10564         
10565         return cfg;
10566         
10567     },
10568     
10569     
10570     
10571     // private
10572     onResize : function(w, h){
10573 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10574 //        if(typeof w == 'number'){
10575 //            var x = w - this.trigger.getWidth();
10576 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10577 //            this.trigger.setStyle('left', x+'px');
10578 //        }
10579     },
10580
10581     // private
10582     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10583
10584     // private
10585     getResizeEl : function(){
10586         return this.inputEl();
10587     },
10588
10589     // private
10590     getPositionEl : function(){
10591         return this.inputEl();
10592     },
10593
10594     // private
10595     alignErrorIcon : function(){
10596         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10597     },
10598
10599     // private
10600     initEvents : function(){
10601         
10602         this.createList();
10603         
10604         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10605         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10606         if(!this.multiple && this.showToggleBtn){
10607             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10608             if(this.hideTrigger){
10609                 this.trigger.setDisplayed(false);
10610             }
10611             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10612         }
10613         
10614         if(this.multiple){
10615             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10616         }
10617         
10618         if(this.removable && !this.editable && !this.tickable){
10619             var close = this.closeTriggerEl();
10620             
10621             if(close){
10622                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10623                 close.on('click', this.removeBtnClick, this, close);
10624             }
10625         }
10626         
10627         //this.trigger.addClassOnOver('x-form-trigger-over');
10628         //this.trigger.addClassOnClick('x-form-trigger-click');
10629         
10630         //if(!this.width){
10631         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10632         //}
10633     },
10634     
10635     closeTriggerEl : function()
10636     {
10637         var close = this.el.select('.roo-combo-removable-btn', true).first();
10638         return close ? close : false;
10639     },
10640     
10641     removeBtnClick : function(e, h, el)
10642     {
10643         e.preventDefault();
10644         
10645         if(this.fireEvent("remove", this) !== false){
10646             this.reset();
10647             this.fireEvent("afterremove", this)
10648         }
10649     },
10650     
10651     createList : function()
10652     {
10653         this.list = Roo.get(document.body).createChild({
10654             tag: 'ul',
10655             cls: 'typeahead typeahead-long dropdown-menu',
10656             style: 'display:none'
10657         });
10658         
10659         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10660         
10661     },
10662
10663     // private
10664     initTrigger : function(){
10665        
10666     },
10667
10668     // private
10669     onDestroy : function(){
10670         if(this.trigger){
10671             this.trigger.removeAllListeners();
10672           //  this.trigger.remove();
10673         }
10674         //if(this.wrap){
10675         //    this.wrap.remove();
10676         //}
10677         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10678     },
10679
10680     // private
10681     onFocus : function(){
10682         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10683         /*
10684         if(!this.mimicing){
10685             this.wrap.addClass('x-trigger-wrap-focus');
10686             this.mimicing = true;
10687             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10688             if(this.monitorTab){
10689                 this.el.on("keydown", this.checkTab, this);
10690             }
10691         }
10692         */
10693     },
10694
10695     // private
10696     checkTab : function(e){
10697         if(e.getKey() == e.TAB){
10698             this.triggerBlur();
10699         }
10700     },
10701
10702     // private
10703     onBlur : function(){
10704         // do nothing
10705     },
10706
10707     // private
10708     mimicBlur : function(e, t){
10709         /*
10710         if(!this.wrap.contains(t) && this.validateBlur()){
10711             this.triggerBlur();
10712         }
10713         */
10714     },
10715
10716     // private
10717     triggerBlur : function(){
10718         this.mimicing = false;
10719         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10720         if(this.monitorTab){
10721             this.el.un("keydown", this.checkTab, this);
10722         }
10723         //this.wrap.removeClass('x-trigger-wrap-focus');
10724         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10725     },
10726
10727     // private
10728     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10729     validateBlur : function(e, t){
10730         return true;
10731     },
10732
10733     // private
10734     onDisable : function(){
10735         this.inputEl().dom.disabled = true;
10736         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10737         //if(this.wrap){
10738         //    this.wrap.addClass('x-item-disabled');
10739         //}
10740     },
10741
10742     // private
10743     onEnable : function(){
10744         this.inputEl().dom.disabled = false;
10745         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10746         //if(this.wrap){
10747         //    this.el.removeClass('x-item-disabled');
10748         //}
10749     },
10750
10751     // private
10752     onShow : function(){
10753         var ae = this.getActionEl();
10754         
10755         if(ae){
10756             ae.dom.style.display = '';
10757             ae.dom.style.visibility = 'visible';
10758         }
10759     },
10760
10761     // private
10762     
10763     onHide : function(){
10764         var ae = this.getActionEl();
10765         ae.dom.style.display = 'none';
10766     },
10767
10768     /**
10769      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10770      * by an implementing function.
10771      * @method
10772      * @param {EventObject} e
10773      */
10774     onTriggerClick : Roo.emptyFn
10775 });
10776  /*
10777  * Based on:
10778  * Ext JS Library 1.1.1
10779  * Copyright(c) 2006-2007, Ext JS, LLC.
10780  *
10781  * Originally Released Under LGPL - original licence link has changed is not relivant.
10782  *
10783  * Fork - LGPL
10784  * <script type="text/javascript">
10785  */
10786
10787
10788 /**
10789  * @class Roo.data.SortTypes
10790  * @singleton
10791  * Defines the default sorting (casting?) comparison functions used when sorting data.
10792  */
10793 Roo.data.SortTypes = {
10794     /**
10795      * Default sort that does nothing
10796      * @param {Mixed} s The value being converted
10797      * @return {Mixed} The comparison value
10798      */
10799     none : function(s){
10800         return s;
10801     },
10802     
10803     /**
10804      * The regular expression used to strip tags
10805      * @type {RegExp}
10806      * @property
10807      */
10808     stripTagsRE : /<\/?[^>]+>/gi,
10809     
10810     /**
10811      * Strips all HTML tags to sort on text only
10812      * @param {Mixed} s The value being converted
10813      * @return {String} The comparison value
10814      */
10815     asText : function(s){
10816         return String(s).replace(this.stripTagsRE, "");
10817     },
10818     
10819     /**
10820      * Strips all HTML tags to sort on text only - Case insensitive
10821      * @param {Mixed} s The value being converted
10822      * @return {String} The comparison value
10823      */
10824     asUCText : function(s){
10825         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10826     },
10827     
10828     /**
10829      * Case insensitive string
10830      * @param {Mixed} s The value being converted
10831      * @return {String} The comparison value
10832      */
10833     asUCString : function(s) {
10834         return String(s).toUpperCase();
10835     },
10836     
10837     /**
10838      * Date sorting
10839      * @param {Mixed} s The value being converted
10840      * @return {Number} The comparison value
10841      */
10842     asDate : function(s) {
10843         if(!s){
10844             return 0;
10845         }
10846         if(s instanceof Date){
10847             return s.getTime();
10848         }
10849         return Date.parse(String(s));
10850     },
10851     
10852     /**
10853      * Float sorting
10854      * @param {Mixed} s The value being converted
10855      * @return {Float} The comparison value
10856      */
10857     asFloat : function(s) {
10858         var val = parseFloat(String(s).replace(/,/g, ""));
10859         if(isNaN(val)) {
10860             val = 0;
10861         }
10862         return val;
10863     },
10864     
10865     /**
10866      * Integer sorting
10867      * @param {Mixed} s The value being converted
10868      * @return {Number} The comparison value
10869      */
10870     asInt : function(s) {
10871         var val = parseInt(String(s).replace(/,/g, ""));
10872         if(isNaN(val)) {
10873             val = 0;
10874         }
10875         return val;
10876     }
10877 };/*
10878  * Based on:
10879  * Ext JS Library 1.1.1
10880  * Copyright(c) 2006-2007, Ext JS, LLC.
10881  *
10882  * Originally Released Under LGPL - original licence link has changed is not relivant.
10883  *
10884  * Fork - LGPL
10885  * <script type="text/javascript">
10886  */
10887
10888 /**
10889 * @class Roo.data.Record
10890  * Instances of this class encapsulate both record <em>definition</em> information, and record
10891  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10892  * to access Records cached in an {@link Roo.data.Store} object.<br>
10893  * <p>
10894  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10895  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10896  * objects.<br>
10897  * <p>
10898  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10899  * @constructor
10900  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10901  * {@link #create}. The parameters are the same.
10902  * @param {Array} data An associative Array of data values keyed by the field name.
10903  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10904  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10905  * not specified an integer id is generated.
10906  */
10907 Roo.data.Record = function(data, id){
10908     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10909     this.data = data;
10910 };
10911
10912 /**
10913  * Generate a constructor for a specific record layout.
10914  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10915  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10916  * Each field definition object may contain the following properties: <ul>
10917  * <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,
10918  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10919  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10920  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10921  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10922  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10923  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10924  * this may be omitted.</p></li>
10925  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10926  * <ul><li>auto (Default, implies no conversion)</li>
10927  * <li>string</li>
10928  * <li>int</li>
10929  * <li>float</li>
10930  * <li>boolean</li>
10931  * <li>date</li></ul></p></li>
10932  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10933  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10934  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10935  * by the Reader into an object that will be stored in the Record. It is passed the
10936  * following parameters:<ul>
10937  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10938  * </ul></p></li>
10939  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10940  * </ul>
10941  * <br>usage:<br><pre><code>
10942 var TopicRecord = Roo.data.Record.create(
10943     {name: 'title', mapping: 'topic_title'},
10944     {name: 'author', mapping: 'username'},
10945     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10946     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10947     {name: 'lastPoster', mapping: 'user2'},
10948     {name: 'excerpt', mapping: 'post_text'}
10949 );
10950
10951 var myNewRecord = new TopicRecord({
10952     title: 'Do my job please',
10953     author: 'noobie',
10954     totalPosts: 1,
10955     lastPost: new Date(),
10956     lastPoster: 'Animal',
10957     excerpt: 'No way dude!'
10958 });
10959 myStore.add(myNewRecord);
10960 </code></pre>
10961  * @method create
10962  * @static
10963  */
10964 Roo.data.Record.create = function(o){
10965     var f = function(){
10966         f.superclass.constructor.apply(this, arguments);
10967     };
10968     Roo.extend(f, Roo.data.Record);
10969     var p = f.prototype;
10970     p.fields = new Roo.util.MixedCollection(false, function(field){
10971         return field.name;
10972     });
10973     for(var i = 0, len = o.length; i < len; i++){
10974         p.fields.add(new Roo.data.Field(o[i]));
10975     }
10976     f.getField = function(name){
10977         return p.fields.get(name);  
10978     };
10979     return f;
10980 };
10981
10982 Roo.data.Record.AUTO_ID = 1000;
10983 Roo.data.Record.EDIT = 'edit';
10984 Roo.data.Record.REJECT = 'reject';
10985 Roo.data.Record.COMMIT = 'commit';
10986
10987 Roo.data.Record.prototype = {
10988     /**
10989      * Readonly flag - true if this record has been modified.
10990      * @type Boolean
10991      */
10992     dirty : false,
10993     editing : false,
10994     error: null,
10995     modified: null,
10996
10997     // private
10998     join : function(store){
10999         this.store = store;
11000     },
11001
11002     /**
11003      * Set the named field to the specified value.
11004      * @param {String} name The name of the field to set.
11005      * @param {Object} value The value to set the field to.
11006      */
11007     set : function(name, value){
11008         if(this.data[name] == value){
11009             return;
11010         }
11011         this.dirty = true;
11012         if(!this.modified){
11013             this.modified = {};
11014         }
11015         if(typeof this.modified[name] == 'undefined'){
11016             this.modified[name] = this.data[name];
11017         }
11018         this.data[name] = value;
11019         if(!this.editing && this.store){
11020             this.store.afterEdit(this);
11021         }       
11022     },
11023
11024     /**
11025      * Get the value of the named field.
11026      * @param {String} name The name of the field to get the value of.
11027      * @return {Object} The value of the field.
11028      */
11029     get : function(name){
11030         return this.data[name]; 
11031     },
11032
11033     // private
11034     beginEdit : function(){
11035         this.editing = true;
11036         this.modified = {}; 
11037     },
11038
11039     // private
11040     cancelEdit : function(){
11041         this.editing = false;
11042         delete this.modified;
11043     },
11044
11045     // private
11046     endEdit : function(){
11047         this.editing = false;
11048         if(this.dirty && this.store){
11049             this.store.afterEdit(this);
11050         }
11051     },
11052
11053     /**
11054      * Usually called by the {@link Roo.data.Store} which owns the Record.
11055      * Rejects all changes made to the Record since either creation, or the last commit operation.
11056      * Modified fields are reverted to their original values.
11057      * <p>
11058      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11059      * of reject operations.
11060      */
11061     reject : function(){
11062         var m = this.modified;
11063         for(var n in m){
11064             if(typeof m[n] != "function"){
11065                 this.data[n] = m[n];
11066             }
11067         }
11068         this.dirty = false;
11069         delete this.modified;
11070         this.editing = false;
11071         if(this.store){
11072             this.store.afterReject(this);
11073         }
11074     },
11075
11076     /**
11077      * Usually called by the {@link Roo.data.Store} which owns the Record.
11078      * Commits all changes made to the Record since either creation, or the last commit operation.
11079      * <p>
11080      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11081      * of commit operations.
11082      */
11083     commit : function(){
11084         this.dirty = false;
11085         delete this.modified;
11086         this.editing = false;
11087         if(this.store){
11088             this.store.afterCommit(this);
11089         }
11090     },
11091
11092     // private
11093     hasError : function(){
11094         return this.error != null;
11095     },
11096
11097     // private
11098     clearError : function(){
11099         this.error = null;
11100     },
11101
11102     /**
11103      * Creates a copy of this record.
11104      * @param {String} id (optional) A new record id if you don't want to use this record's id
11105      * @return {Record}
11106      */
11107     copy : function(newId) {
11108         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11109     }
11110 };/*
11111  * Based on:
11112  * Ext JS Library 1.1.1
11113  * Copyright(c) 2006-2007, Ext JS, LLC.
11114  *
11115  * Originally Released Under LGPL - original licence link has changed is not relivant.
11116  *
11117  * Fork - LGPL
11118  * <script type="text/javascript">
11119  */
11120
11121
11122
11123 /**
11124  * @class Roo.data.Store
11125  * @extends Roo.util.Observable
11126  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11127  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11128  * <p>
11129  * 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
11130  * has no knowledge of the format of the data returned by the Proxy.<br>
11131  * <p>
11132  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11133  * instances from the data object. These records are cached and made available through accessor functions.
11134  * @constructor
11135  * Creates a new Store.
11136  * @param {Object} config A config object containing the objects needed for the Store to access data,
11137  * and read the data into Records.
11138  */
11139 Roo.data.Store = function(config){
11140     this.data = new Roo.util.MixedCollection(false);
11141     this.data.getKey = function(o){
11142         return o.id;
11143     };
11144     this.baseParams = {};
11145     // private
11146     this.paramNames = {
11147         "start" : "start",
11148         "limit" : "limit",
11149         "sort" : "sort",
11150         "dir" : "dir",
11151         "multisort" : "_multisort"
11152     };
11153
11154     if(config && config.data){
11155         this.inlineData = config.data;
11156         delete config.data;
11157     }
11158
11159     Roo.apply(this, config);
11160     
11161     if(this.reader){ // reader passed
11162         this.reader = Roo.factory(this.reader, Roo.data);
11163         this.reader.xmodule = this.xmodule || false;
11164         if(!this.recordType){
11165             this.recordType = this.reader.recordType;
11166         }
11167         if(this.reader.onMetaChange){
11168             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11169         }
11170     }
11171
11172     if(this.recordType){
11173         this.fields = this.recordType.prototype.fields;
11174     }
11175     this.modified = [];
11176
11177     this.addEvents({
11178         /**
11179          * @event datachanged
11180          * Fires when the data cache has changed, and a widget which is using this Store
11181          * as a Record cache should refresh its view.
11182          * @param {Store} this
11183          */
11184         datachanged : true,
11185         /**
11186          * @event metachange
11187          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11188          * @param {Store} this
11189          * @param {Object} meta The JSON metadata
11190          */
11191         metachange : true,
11192         /**
11193          * @event add
11194          * Fires when Records have been added to the Store
11195          * @param {Store} this
11196          * @param {Roo.data.Record[]} records The array of Records added
11197          * @param {Number} index The index at which the record(s) were added
11198          */
11199         add : true,
11200         /**
11201          * @event remove
11202          * Fires when a Record has been removed from the Store
11203          * @param {Store} this
11204          * @param {Roo.data.Record} record The Record that was removed
11205          * @param {Number} index The index at which the record was removed
11206          */
11207         remove : true,
11208         /**
11209          * @event update
11210          * Fires when a Record has been updated
11211          * @param {Store} this
11212          * @param {Roo.data.Record} record The Record that was updated
11213          * @param {String} operation The update operation being performed.  Value may be one of:
11214          * <pre><code>
11215  Roo.data.Record.EDIT
11216  Roo.data.Record.REJECT
11217  Roo.data.Record.COMMIT
11218          * </code></pre>
11219          */
11220         update : true,
11221         /**
11222          * @event clear
11223          * Fires when the data cache has been cleared.
11224          * @param {Store} this
11225          */
11226         clear : true,
11227         /**
11228          * @event beforeload
11229          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11230          * the load action will be canceled.
11231          * @param {Store} this
11232          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11233          */
11234         beforeload : true,
11235         /**
11236          * @event beforeloadadd
11237          * Fires after a new set of Records has been loaded.
11238          * @param {Store} this
11239          * @param {Roo.data.Record[]} records The Records that were loaded
11240          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11241          */
11242         beforeloadadd : true,
11243         /**
11244          * @event load
11245          * Fires after a new set of Records has been loaded, before they are added to the store.
11246          * @param {Store} this
11247          * @param {Roo.data.Record[]} records The Records that were loaded
11248          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11249          * @params {Object} return from reader
11250          */
11251         load : true,
11252         /**
11253          * @event loadexception
11254          * Fires if an exception occurs in the Proxy during loading.
11255          * Called with the signature of the Proxy's "loadexception" event.
11256          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11257          * 
11258          * @param {Proxy} 
11259          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11260          * @param {Object} load options 
11261          * @param {Object} jsonData from your request (normally this contains the Exception)
11262          */
11263         loadexception : true
11264     });
11265     
11266     if(this.proxy){
11267         this.proxy = Roo.factory(this.proxy, Roo.data);
11268         this.proxy.xmodule = this.xmodule || false;
11269         this.relayEvents(this.proxy,  ["loadexception"]);
11270     }
11271     this.sortToggle = {};
11272     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11273
11274     Roo.data.Store.superclass.constructor.call(this);
11275
11276     if(this.inlineData){
11277         this.loadData(this.inlineData);
11278         delete this.inlineData;
11279     }
11280 };
11281
11282 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11283      /**
11284     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11285     * without a remote query - used by combo/forms at present.
11286     */
11287     
11288     /**
11289     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11290     */
11291     /**
11292     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11293     */
11294     /**
11295     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11296     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11297     */
11298     /**
11299     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11300     * on any HTTP request
11301     */
11302     /**
11303     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11304     */
11305     /**
11306     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11307     */
11308     multiSort: false,
11309     /**
11310     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11311     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11312     */
11313     remoteSort : false,
11314
11315     /**
11316     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11317      * loaded or when a record is removed. (defaults to false).
11318     */
11319     pruneModifiedRecords : false,
11320
11321     // private
11322     lastOptions : null,
11323
11324     /**
11325      * Add Records to the Store and fires the add event.
11326      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11327      */
11328     add : function(records){
11329         records = [].concat(records);
11330         for(var i = 0, len = records.length; i < len; i++){
11331             records[i].join(this);
11332         }
11333         var index = this.data.length;
11334         this.data.addAll(records);
11335         this.fireEvent("add", this, records, index);
11336     },
11337
11338     /**
11339      * Remove a Record from the Store and fires the remove event.
11340      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11341      */
11342     remove : function(record){
11343         var index = this.data.indexOf(record);
11344         this.data.removeAt(index);
11345  
11346         if(this.pruneModifiedRecords){
11347             this.modified.remove(record);
11348         }
11349         this.fireEvent("remove", this, record, index);
11350     },
11351
11352     /**
11353      * Remove all Records from the Store and fires the clear event.
11354      */
11355     removeAll : function(){
11356         this.data.clear();
11357         if(this.pruneModifiedRecords){
11358             this.modified = [];
11359         }
11360         this.fireEvent("clear", this);
11361     },
11362
11363     /**
11364      * Inserts Records to the Store at the given index and fires the add event.
11365      * @param {Number} index The start index at which to insert the passed Records.
11366      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11367      */
11368     insert : function(index, records){
11369         records = [].concat(records);
11370         for(var i = 0, len = records.length; i < len; i++){
11371             this.data.insert(index, records[i]);
11372             records[i].join(this);
11373         }
11374         this.fireEvent("add", this, records, index);
11375     },
11376
11377     /**
11378      * Get the index within the cache of the passed Record.
11379      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11380      * @return {Number} The index of the passed Record. Returns -1 if not found.
11381      */
11382     indexOf : function(record){
11383         return this.data.indexOf(record);
11384     },
11385
11386     /**
11387      * Get the index within the cache of the Record with the passed id.
11388      * @param {String} id The id of the Record to find.
11389      * @return {Number} The index of the Record. Returns -1 if not found.
11390      */
11391     indexOfId : function(id){
11392         return this.data.indexOfKey(id);
11393     },
11394
11395     /**
11396      * Get the Record with the specified id.
11397      * @param {String} id The id of the Record to find.
11398      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11399      */
11400     getById : function(id){
11401         return this.data.key(id);
11402     },
11403
11404     /**
11405      * Get the Record at the specified index.
11406      * @param {Number} index The index of the Record to find.
11407      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11408      */
11409     getAt : function(index){
11410         return this.data.itemAt(index);
11411     },
11412
11413     /**
11414      * Returns a range of Records between specified indices.
11415      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11416      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11417      * @return {Roo.data.Record[]} An array of Records
11418      */
11419     getRange : function(start, end){
11420         return this.data.getRange(start, end);
11421     },
11422
11423     // private
11424     storeOptions : function(o){
11425         o = Roo.apply({}, o);
11426         delete o.callback;
11427         delete o.scope;
11428         this.lastOptions = o;
11429     },
11430
11431     /**
11432      * Loads the Record cache from the configured Proxy using the configured Reader.
11433      * <p>
11434      * If using remote paging, then the first load call must specify the <em>start</em>
11435      * and <em>limit</em> properties in the options.params property to establish the initial
11436      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11437      * <p>
11438      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11439      * and this call will return before the new data has been loaded. Perform any post-processing
11440      * in a callback function, or in a "load" event handler.</strong>
11441      * <p>
11442      * @param {Object} options An object containing properties which control loading options:<ul>
11443      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11444      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11445      * passed the following arguments:<ul>
11446      * <li>r : Roo.data.Record[]</li>
11447      * <li>options: Options object from the load call</li>
11448      * <li>success: Boolean success indicator</li></ul></li>
11449      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11450      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11451      * </ul>
11452      */
11453     load : function(options){
11454         options = options || {};
11455         if(this.fireEvent("beforeload", this, options) !== false){
11456             this.storeOptions(options);
11457             var p = Roo.apply(options.params || {}, this.baseParams);
11458             // if meta was not loaded from remote source.. try requesting it.
11459             if (!this.reader.metaFromRemote) {
11460                 p._requestMeta = 1;
11461             }
11462             if(this.sortInfo && this.remoteSort){
11463                 var pn = this.paramNames;
11464                 p[pn["sort"]] = this.sortInfo.field;
11465                 p[pn["dir"]] = this.sortInfo.direction;
11466             }
11467             if (this.multiSort) {
11468                 var pn = this.paramNames;
11469                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11470             }
11471             
11472             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11473         }
11474     },
11475
11476     /**
11477      * Reloads the Record cache from the configured Proxy using the configured Reader and
11478      * the options from the last load operation performed.
11479      * @param {Object} options (optional) An object containing properties which may override the options
11480      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11481      * the most recently used options are reused).
11482      */
11483     reload : function(options){
11484         this.load(Roo.applyIf(options||{}, this.lastOptions));
11485     },
11486
11487     // private
11488     // Called as a callback by the Reader during a load operation.
11489     loadRecords : function(o, options, success){
11490         if(!o || success === false){
11491             if(success !== false){
11492                 this.fireEvent("load", this, [], options, o);
11493             }
11494             if(options.callback){
11495                 options.callback.call(options.scope || this, [], options, false);
11496             }
11497             return;
11498         }
11499         // if data returned failure - throw an exception.
11500         if (o.success === false) {
11501             // show a message if no listener is registered.
11502             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11503                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11504             }
11505             // loadmask wil be hooked into this..
11506             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11507             return;
11508         }
11509         var r = o.records, t = o.totalRecords || r.length;
11510         
11511         this.fireEvent("beforeloadadd", this, r, options, o);
11512         
11513         if(!options || options.add !== true){
11514             if(this.pruneModifiedRecords){
11515                 this.modified = [];
11516             }
11517             for(var i = 0, len = r.length; i < len; i++){
11518                 r[i].join(this);
11519             }
11520             if(this.snapshot){
11521                 this.data = this.snapshot;
11522                 delete this.snapshot;
11523             }
11524             this.data.clear();
11525             this.data.addAll(r);
11526             this.totalLength = t;
11527             this.applySort();
11528             this.fireEvent("datachanged", this);
11529         }else{
11530             this.totalLength = Math.max(t, this.data.length+r.length);
11531             this.add(r);
11532         }
11533         
11534         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11535                 
11536             var e = new Roo.data.Record({});
11537
11538             e.set(this.parent.displayField, this.parent.emptyTitle);
11539             e.set(this.parent.valueField, '');
11540
11541             this.insert(0, e);
11542         }
11543             
11544         this.fireEvent("load", this, r, options, o);
11545         if(options.callback){
11546             options.callback.call(options.scope || this, r, options, true);
11547         }
11548     },
11549
11550
11551     /**
11552      * Loads data from a passed data block. A Reader which understands the format of the data
11553      * must have been configured in the constructor.
11554      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11555      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11556      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11557      */
11558     loadData : function(o, append){
11559         var r = this.reader.readRecords(o);
11560         this.loadRecords(r, {add: append}, true);
11561     },
11562
11563     /**
11564      * Gets the number of cached records.
11565      * <p>
11566      * <em>If using paging, this may not be the total size of the dataset. If the data object
11567      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11568      * the data set size</em>
11569      */
11570     getCount : function(){
11571         return this.data.length || 0;
11572     },
11573
11574     /**
11575      * Gets the total number of records in the dataset as returned by the server.
11576      * <p>
11577      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11578      * the dataset size</em>
11579      */
11580     getTotalCount : function(){
11581         return this.totalLength || 0;
11582     },
11583
11584     /**
11585      * Returns the sort state of the Store as an object with two properties:
11586      * <pre><code>
11587  field {String} The name of the field by which the Records are sorted
11588  direction {String} The sort order, "ASC" or "DESC"
11589      * </code></pre>
11590      */
11591     getSortState : function(){
11592         return this.sortInfo;
11593     },
11594
11595     // private
11596     applySort : function(){
11597         if(this.sortInfo && !this.remoteSort){
11598             var s = this.sortInfo, f = s.field;
11599             var st = this.fields.get(f).sortType;
11600             var fn = function(r1, r2){
11601                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11602                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11603             };
11604             this.data.sort(s.direction, fn);
11605             if(this.snapshot && this.snapshot != this.data){
11606                 this.snapshot.sort(s.direction, fn);
11607             }
11608         }
11609     },
11610
11611     /**
11612      * Sets the default sort column and order to be used by the next load operation.
11613      * @param {String} fieldName The name of the field to sort by.
11614      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11615      */
11616     setDefaultSort : function(field, dir){
11617         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11618     },
11619
11620     /**
11621      * Sort the Records.
11622      * If remote sorting is used, the sort is performed on the server, and the cache is
11623      * reloaded. If local sorting is used, the cache is sorted internally.
11624      * @param {String} fieldName The name of the field to sort by.
11625      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11626      */
11627     sort : function(fieldName, dir){
11628         var f = this.fields.get(fieldName);
11629         if(!dir){
11630             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11631             
11632             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11633                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11634             }else{
11635                 dir = f.sortDir;
11636             }
11637         }
11638         this.sortToggle[f.name] = dir;
11639         this.sortInfo = {field: f.name, direction: dir};
11640         if(!this.remoteSort){
11641             this.applySort();
11642             this.fireEvent("datachanged", this);
11643         }else{
11644             this.load(this.lastOptions);
11645         }
11646     },
11647
11648     /**
11649      * Calls the specified function for each of the Records in the cache.
11650      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11651      * Returning <em>false</em> aborts and exits the iteration.
11652      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11653      */
11654     each : function(fn, scope){
11655         this.data.each(fn, scope);
11656     },
11657
11658     /**
11659      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11660      * (e.g., during paging).
11661      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11662      */
11663     getModifiedRecords : function(){
11664         return this.modified;
11665     },
11666
11667     // private
11668     createFilterFn : function(property, value, anyMatch){
11669         if(!value.exec){ // not a regex
11670             value = String(value);
11671             if(value.length == 0){
11672                 return false;
11673             }
11674             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11675         }
11676         return function(r){
11677             return value.test(r.data[property]);
11678         };
11679     },
11680
11681     /**
11682      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11683      * @param {String} property A field on your records
11684      * @param {Number} start The record index to start at (defaults to 0)
11685      * @param {Number} end The last record index to include (defaults to length - 1)
11686      * @return {Number} The sum
11687      */
11688     sum : function(property, start, end){
11689         var rs = this.data.items, v = 0;
11690         start = start || 0;
11691         end = (end || end === 0) ? end : rs.length-1;
11692
11693         for(var i = start; i <= end; i++){
11694             v += (rs[i].data[property] || 0);
11695         }
11696         return v;
11697     },
11698
11699     /**
11700      * Filter the records by a specified property.
11701      * @param {String} field A field on your records
11702      * @param {String/RegExp} value Either a string that the field
11703      * should start with or a RegExp to test against the field
11704      * @param {Boolean} anyMatch True to match any part not just the beginning
11705      */
11706     filter : function(property, value, anyMatch){
11707         var fn = this.createFilterFn(property, value, anyMatch);
11708         return fn ? this.filterBy(fn) : this.clearFilter();
11709     },
11710
11711     /**
11712      * Filter by a function. The specified function will be called with each
11713      * record in this data source. If the function returns true the record is included,
11714      * otherwise it is filtered.
11715      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11716      * @param {Object} scope (optional) The scope of the function (defaults to this)
11717      */
11718     filterBy : function(fn, scope){
11719         this.snapshot = this.snapshot || this.data;
11720         this.data = this.queryBy(fn, scope||this);
11721         this.fireEvent("datachanged", this);
11722     },
11723
11724     /**
11725      * Query the records by a specified property.
11726      * @param {String} field A field on your records
11727      * @param {String/RegExp} value Either a string that the field
11728      * should start with or a RegExp to test against the field
11729      * @param {Boolean} anyMatch True to match any part not just the beginning
11730      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11731      */
11732     query : function(property, value, anyMatch){
11733         var fn = this.createFilterFn(property, value, anyMatch);
11734         return fn ? this.queryBy(fn) : this.data.clone();
11735     },
11736
11737     /**
11738      * Query by a function. The specified function will be called with each
11739      * record in this data source. If the function returns true the record is included
11740      * in the results.
11741      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11742      * @param {Object} scope (optional) The scope of the function (defaults to this)
11743       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11744      **/
11745     queryBy : function(fn, scope){
11746         var data = this.snapshot || this.data;
11747         return data.filterBy(fn, scope||this);
11748     },
11749
11750     /**
11751      * Collects unique values for a particular dataIndex from this store.
11752      * @param {String} dataIndex The property to collect
11753      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11754      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11755      * @return {Array} An array of the unique values
11756      **/
11757     collect : function(dataIndex, allowNull, bypassFilter){
11758         var d = (bypassFilter === true && this.snapshot) ?
11759                 this.snapshot.items : this.data.items;
11760         var v, sv, r = [], l = {};
11761         for(var i = 0, len = d.length; i < len; i++){
11762             v = d[i].data[dataIndex];
11763             sv = String(v);
11764             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11765                 l[sv] = true;
11766                 r[r.length] = v;
11767             }
11768         }
11769         return r;
11770     },
11771
11772     /**
11773      * Revert to a view of the Record cache with no filtering applied.
11774      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11775      */
11776     clearFilter : function(suppressEvent){
11777         if(this.snapshot && this.snapshot != this.data){
11778             this.data = this.snapshot;
11779             delete this.snapshot;
11780             if(suppressEvent !== true){
11781                 this.fireEvent("datachanged", this);
11782             }
11783         }
11784     },
11785
11786     // private
11787     afterEdit : function(record){
11788         if(this.modified.indexOf(record) == -1){
11789             this.modified.push(record);
11790         }
11791         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11792     },
11793     
11794     // private
11795     afterReject : function(record){
11796         this.modified.remove(record);
11797         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11798     },
11799
11800     // private
11801     afterCommit : function(record){
11802         this.modified.remove(record);
11803         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11804     },
11805
11806     /**
11807      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11808      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11809      */
11810     commitChanges : function(){
11811         var m = this.modified.slice(0);
11812         this.modified = [];
11813         for(var i = 0, len = m.length; i < len; i++){
11814             m[i].commit();
11815         }
11816     },
11817
11818     /**
11819      * Cancel outstanding changes on all changed records.
11820      */
11821     rejectChanges : function(){
11822         var m = this.modified.slice(0);
11823         this.modified = [];
11824         for(var i = 0, len = m.length; i < len; i++){
11825             m[i].reject();
11826         }
11827     },
11828
11829     onMetaChange : function(meta, rtype, o){
11830         this.recordType = rtype;
11831         this.fields = rtype.prototype.fields;
11832         delete this.snapshot;
11833         this.sortInfo = meta.sortInfo || this.sortInfo;
11834         this.modified = [];
11835         this.fireEvent('metachange', this, this.reader.meta);
11836     },
11837     
11838     moveIndex : function(data, type)
11839     {
11840         var index = this.indexOf(data);
11841         
11842         var newIndex = index + type;
11843         
11844         this.remove(data);
11845         
11846         this.insert(newIndex, data);
11847         
11848     }
11849 });/*
11850  * Based on:
11851  * Ext JS Library 1.1.1
11852  * Copyright(c) 2006-2007, Ext JS, LLC.
11853  *
11854  * Originally Released Under LGPL - original licence link has changed is not relivant.
11855  *
11856  * Fork - LGPL
11857  * <script type="text/javascript">
11858  */
11859
11860 /**
11861  * @class Roo.data.SimpleStore
11862  * @extends Roo.data.Store
11863  * Small helper class to make creating Stores from Array data easier.
11864  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11865  * @cfg {Array} fields An array of field definition objects, or field name strings.
11866  * @cfg {Array} data The multi-dimensional array of data
11867  * @constructor
11868  * @param {Object} config
11869  */
11870 Roo.data.SimpleStore = function(config){
11871     Roo.data.SimpleStore.superclass.constructor.call(this, {
11872         isLocal : true,
11873         reader: new Roo.data.ArrayReader({
11874                 id: config.id
11875             },
11876             Roo.data.Record.create(config.fields)
11877         ),
11878         proxy : new Roo.data.MemoryProxy(config.data)
11879     });
11880     this.load();
11881 };
11882 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11883  * Based on:
11884  * Ext JS Library 1.1.1
11885  * Copyright(c) 2006-2007, Ext JS, LLC.
11886  *
11887  * Originally Released Under LGPL - original licence link has changed is not relivant.
11888  *
11889  * Fork - LGPL
11890  * <script type="text/javascript">
11891  */
11892
11893 /**
11894 /**
11895  * @extends Roo.data.Store
11896  * @class Roo.data.JsonStore
11897  * Small helper class to make creating Stores for JSON data easier. <br/>
11898 <pre><code>
11899 var store = new Roo.data.JsonStore({
11900     url: 'get-images.php',
11901     root: 'images',
11902     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11903 });
11904 </code></pre>
11905  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11906  * JsonReader and HttpProxy (unless inline data is provided).</b>
11907  * @cfg {Array} fields An array of field definition objects, or field name strings.
11908  * @constructor
11909  * @param {Object} config
11910  */
11911 Roo.data.JsonStore = function(c){
11912     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11913         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11914         reader: new Roo.data.JsonReader(c, c.fields)
11915     }));
11916 };
11917 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11918  * Based on:
11919  * Ext JS Library 1.1.1
11920  * Copyright(c) 2006-2007, Ext JS, LLC.
11921  *
11922  * Originally Released Under LGPL - original licence link has changed is not relivant.
11923  *
11924  * Fork - LGPL
11925  * <script type="text/javascript">
11926  */
11927
11928  
11929 Roo.data.Field = function(config){
11930     if(typeof config == "string"){
11931         config = {name: config};
11932     }
11933     Roo.apply(this, config);
11934     
11935     if(!this.type){
11936         this.type = "auto";
11937     }
11938     
11939     var st = Roo.data.SortTypes;
11940     // named sortTypes are supported, here we look them up
11941     if(typeof this.sortType == "string"){
11942         this.sortType = st[this.sortType];
11943     }
11944     
11945     // set default sortType for strings and dates
11946     if(!this.sortType){
11947         switch(this.type){
11948             case "string":
11949                 this.sortType = st.asUCString;
11950                 break;
11951             case "date":
11952                 this.sortType = st.asDate;
11953                 break;
11954             default:
11955                 this.sortType = st.none;
11956         }
11957     }
11958
11959     // define once
11960     var stripRe = /[\$,%]/g;
11961
11962     // prebuilt conversion function for this field, instead of
11963     // switching every time we're reading a value
11964     if(!this.convert){
11965         var cv, dateFormat = this.dateFormat;
11966         switch(this.type){
11967             case "":
11968             case "auto":
11969             case undefined:
11970                 cv = function(v){ return v; };
11971                 break;
11972             case "string":
11973                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11974                 break;
11975             case "int":
11976                 cv = function(v){
11977                     return v !== undefined && v !== null && v !== '' ?
11978                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11979                     };
11980                 break;
11981             case "float":
11982                 cv = function(v){
11983                     return v !== undefined && v !== null && v !== '' ?
11984                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11985                     };
11986                 break;
11987             case "bool":
11988             case "boolean":
11989                 cv = function(v){ return v === true || v === "true" || v == 1; };
11990                 break;
11991             case "date":
11992                 cv = function(v){
11993                     if(!v){
11994                         return '';
11995                     }
11996                     if(v instanceof Date){
11997                         return v;
11998                     }
11999                     if(dateFormat){
12000                         if(dateFormat == "timestamp"){
12001                             return new Date(v*1000);
12002                         }
12003                         return Date.parseDate(v, dateFormat);
12004                     }
12005                     var parsed = Date.parse(v);
12006                     return parsed ? new Date(parsed) : null;
12007                 };
12008              break;
12009             
12010         }
12011         this.convert = cv;
12012     }
12013 };
12014
12015 Roo.data.Field.prototype = {
12016     dateFormat: null,
12017     defaultValue: "",
12018     mapping: null,
12019     sortType : null,
12020     sortDir : "ASC"
12021 };/*
12022  * Based on:
12023  * Ext JS Library 1.1.1
12024  * Copyright(c) 2006-2007, Ext JS, LLC.
12025  *
12026  * Originally Released Under LGPL - original licence link has changed is not relivant.
12027  *
12028  * Fork - LGPL
12029  * <script type="text/javascript">
12030  */
12031  
12032 // Base class for reading structured data from a data source.  This class is intended to be
12033 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12034
12035 /**
12036  * @class Roo.data.DataReader
12037  * Base class for reading structured data from a data source.  This class is intended to be
12038  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12039  */
12040
12041 Roo.data.DataReader = function(meta, recordType){
12042     
12043     this.meta = meta;
12044     
12045     this.recordType = recordType instanceof Array ? 
12046         Roo.data.Record.create(recordType) : recordType;
12047 };
12048
12049 Roo.data.DataReader.prototype = {
12050      /**
12051      * Create an empty record
12052      * @param {Object} data (optional) - overlay some values
12053      * @return {Roo.data.Record} record created.
12054      */
12055     newRow :  function(d) {
12056         var da =  {};
12057         this.recordType.prototype.fields.each(function(c) {
12058             switch( c.type) {
12059                 case 'int' : da[c.name] = 0; break;
12060                 case 'date' : da[c.name] = new Date(); break;
12061                 case 'float' : da[c.name] = 0.0; break;
12062                 case 'boolean' : da[c.name] = false; break;
12063                 default : da[c.name] = ""; break;
12064             }
12065             
12066         });
12067         return new this.recordType(Roo.apply(da, d));
12068     }
12069     
12070 };/*
12071  * Based on:
12072  * Ext JS Library 1.1.1
12073  * Copyright(c) 2006-2007, Ext JS, LLC.
12074  *
12075  * Originally Released Under LGPL - original licence link has changed is not relivant.
12076  *
12077  * Fork - LGPL
12078  * <script type="text/javascript">
12079  */
12080
12081 /**
12082  * @class Roo.data.DataProxy
12083  * @extends Roo.data.Observable
12084  * This class is an abstract base class for implementations which provide retrieval of
12085  * unformatted data objects.<br>
12086  * <p>
12087  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12088  * (of the appropriate type which knows how to parse the data object) to provide a block of
12089  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12090  * <p>
12091  * Custom implementations must implement the load method as described in
12092  * {@link Roo.data.HttpProxy#load}.
12093  */
12094 Roo.data.DataProxy = function(){
12095     this.addEvents({
12096         /**
12097          * @event beforeload
12098          * Fires before a network request is made to retrieve a data object.
12099          * @param {Object} This DataProxy object.
12100          * @param {Object} params The params parameter to the load function.
12101          */
12102         beforeload : true,
12103         /**
12104          * @event load
12105          * Fires before the load method's callback is called.
12106          * @param {Object} This DataProxy object.
12107          * @param {Object} o The data object.
12108          * @param {Object} arg The callback argument object passed to the load function.
12109          */
12110         load : true,
12111         /**
12112          * @event loadexception
12113          * Fires if an Exception occurs during data retrieval.
12114          * @param {Object} This DataProxy object.
12115          * @param {Object} o The data object.
12116          * @param {Object} arg The callback argument object passed to the load function.
12117          * @param {Object} e The Exception.
12118          */
12119         loadexception : true
12120     });
12121     Roo.data.DataProxy.superclass.constructor.call(this);
12122 };
12123
12124 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12125
12126     /**
12127      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12128      */
12129 /*
12130  * Based on:
12131  * Ext JS Library 1.1.1
12132  * Copyright(c) 2006-2007, Ext JS, LLC.
12133  *
12134  * Originally Released Under LGPL - original licence link has changed is not relivant.
12135  *
12136  * Fork - LGPL
12137  * <script type="text/javascript">
12138  */
12139 /**
12140  * @class Roo.data.MemoryProxy
12141  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12142  * to the Reader when its load method is called.
12143  * @constructor
12144  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12145  */
12146 Roo.data.MemoryProxy = function(data){
12147     if (data.data) {
12148         data = data.data;
12149     }
12150     Roo.data.MemoryProxy.superclass.constructor.call(this);
12151     this.data = data;
12152 };
12153
12154 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12155     
12156     /**
12157      * Load data from the requested source (in this case an in-memory
12158      * data object passed to the constructor), read the data object into
12159      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12160      * process that block using the passed callback.
12161      * @param {Object} params This parameter is not used by the MemoryProxy class.
12162      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12163      * object into a block of Roo.data.Records.
12164      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12165      * The function must be passed <ul>
12166      * <li>The Record block object</li>
12167      * <li>The "arg" argument from the load function</li>
12168      * <li>A boolean success indicator</li>
12169      * </ul>
12170      * @param {Object} scope The scope in which to call the callback
12171      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12172      */
12173     load : function(params, reader, callback, scope, arg){
12174         params = params || {};
12175         var result;
12176         try {
12177             result = reader.readRecords(this.data);
12178         }catch(e){
12179             this.fireEvent("loadexception", this, arg, null, e);
12180             callback.call(scope, null, arg, false);
12181             return;
12182         }
12183         callback.call(scope, result, arg, true);
12184     },
12185     
12186     // private
12187     update : function(params, records){
12188         
12189     }
12190 });/*
12191  * Based on:
12192  * Ext JS Library 1.1.1
12193  * Copyright(c) 2006-2007, Ext JS, LLC.
12194  *
12195  * Originally Released Under LGPL - original licence link has changed is not relivant.
12196  *
12197  * Fork - LGPL
12198  * <script type="text/javascript">
12199  */
12200 /**
12201  * @class Roo.data.HttpProxy
12202  * @extends Roo.data.DataProxy
12203  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12204  * configured to reference a certain URL.<br><br>
12205  * <p>
12206  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12207  * from which the running page was served.<br><br>
12208  * <p>
12209  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12210  * <p>
12211  * Be aware that to enable the browser to parse an XML document, the server must set
12212  * the Content-Type header in the HTTP response to "text/xml".
12213  * @constructor
12214  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12215  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12216  * will be used to make the request.
12217  */
12218 Roo.data.HttpProxy = function(conn){
12219     Roo.data.HttpProxy.superclass.constructor.call(this);
12220     // is conn a conn config or a real conn?
12221     this.conn = conn;
12222     this.useAjax = !conn || !conn.events;
12223   
12224 };
12225
12226 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12227     // thse are take from connection...
12228     
12229     /**
12230      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12231      */
12232     /**
12233      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12234      * extra parameters to each request made by this object. (defaults to undefined)
12235      */
12236     /**
12237      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12238      *  to each request made by this object. (defaults to undefined)
12239      */
12240     /**
12241      * @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)
12242      */
12243     /**
12244      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12245      */
12246      /**
12247      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12248      * @type Boolean
12249      */
12250   
12251
12252     /**
12253      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12254      * @type Boolean
12255      */
12256     /**
12257      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12258      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12259      * a finer-grained basis than the DataProxy events.
12260      */
12261     getConnection : function(){
12262         return this.useAjax ? Roo.Ajax : this.conn;
12263     },
12264
12265     /**
12266      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12267      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12268      * process that block using the passed callback.
12269      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12270      * for the request to the remote server.
12271      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12272      * object into a block of Roo.data.Records.
12273      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12274      * The function must be passed <ul>
12275      * <li>The Record block object</li>
12276      * <li>The "arg" argument from the load function</li>
12277      * <li>A boolean success indicator</li>
12278      * </ul>
12279      * @param {Object} scope The scope in which to call the callback
12280      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12281      */
12282     load : function(params, reader, callback, scope, arg){
12283         if(this.fireEvent("beforeload", this, params) !== false){
12284             var  o = {
12285                 params : params || {},
12286                 request: {
12287                     callback : callback,
12288                     scope : scope,
12289                     arg : arg
12290                 },
12291                 reader: reader,
12292                 callback : this.loadResponse,
12293                 scope: this
12294             };
12295             if(this.useAjax){
12296                 Roo.applyIf(o, this.conn);
12297                 if(this.activeRequest){
12298                     Roo.Ajax.abort(this.activeRequest);
12299                 }
12300                 this.activeRequest = Roo.Ajax.request(o);
12301             }else{
12302                 this.conn.request(o);
12303             }
12304         }else{
12305             callback.call(scope||this, null, arg, false);
12306         }
12307     },
12308
12309     // private
12310     loadResponse : function(o, success, response){
12311         delete this.activeRequest;
12312         if(!success){
12313             this.fireEvent("loadexception", this, o, response);
12314             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12315             return;
12316         }
12317         var result;
12318         try {
12319             result = o.reader.read(response);
12320         }catch(e){
12321             this.fireEvent("loadexception", this, o, response, e);
12322             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12323             return;
12324         }
12325         
12326         this.fireEvent("load", this, o, o.request.arg);
12327         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12328     },
12329
12330     // private
12331     update : function(dataSet){
12332
12333     },
12334
12335     // private
12336     updateResponse : function(dataSet){
12337
12338     }
12339 });/*
12340  * Based on:
12341  * Ext JS Library 1.1.1
12342  * Copyright(c) 2006-2007, Ext JS, LLC.
12343  *
12344  * Originally Released Under LGPL - original licence link has changed is not relivant.
12345  *
12346  * Fork - LGPL
12347  * <script type="text/javascript">
12348  */
12349
12350 /**
12351  * @class Roo.data.ScriptTagProxy
12352  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12353  * other than the originating domain of the running page.<br><br>
12354  * <p>
12355  * <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
12356  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12357  * <p>
12358  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12359  * source code that is used as the source inside a &lt;script> tag.<br><br>
12360  * <p>
12361  * In order for the browser to process the returned data, the server must wrap the data object
12362  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12363  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12364  * depending on whether the callback name was passed:
12365  * <p>
12366  * <pre><code>
12367 boolean scriptTag = false;
12368 String cb = request.getParameter("callback");
12369 if (cb != null) {
12370     scriptTag = true;
12371     response.setContentType("text/javascript");
12372 } else {
12373     response.setContentType("application/x-json");
12374 }
12375 Writer out = response.getWriter();
12376 if (scriptTag) {
12377     out.write(cb + "(");
12378 }
12379 out.print(dataBlock.toJsonString());
12380 if (scriptTag) {
12381     out.write(");");
12382 }
12383 </pre></code>
12384  *
12385  * @constructor
12386  * @param {Object} config A configuration object.
12387  */
12388 Roo.data.ScriptTagProxy = function(config){
12389     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12390     Roo.apply(this, config);
12391     this.head = document.getElementsByTagName("head")[0];
12392 };
12393
12394 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12395
12396 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12397     /**
12398      * @cfg {String} url The URL from which to request the data object.
12399      */
12400     /**
12401      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12402      */
12403     timeout : 30000,
12404     /**
12405      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12406      * the server the name of the callback function set up by the load call to process the returned data object.
12407      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12408      * javascript output which calls this named function passing the data object as its only parameter.
12409      */
12410     callbackParam : "callback",
12411     /**
12412      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12413      * name to the request.
12414      */
12415     nocache : true,
12416
12417     /**
12418      * Load data from the configured URL, read the data object into
12419      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12420      * process that block using the passed callback.
12421      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12422      * for the request to the remote server.
12423      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12424      * object into a block of Roo.data.Records.
12425      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12426      * The function must be passed <ul>
12427      * <li>The Record block object</li>
12428      * <li>The "arg" argument from the load function</li>
12429      * <li>A boolean success indicator</li>
12430      * </ul>
12431      * @param {Object} scope The scope in which to call the callback
12432      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12433      */
12434     load : function(params, reader, callback, scope, arg){
12435         if(this.fireEvent("beforeload", this, params) !== false){
12436
12437             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12438
12439             var url = this.url;
12440             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12441             if(this.nocache){
12442                 url += "&_dc=" + (new Date().getTime());
12443             }
12444             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12445             var trans = {
12446                 id : transId,
12447                 cb : "stcCallback"+transId,
12448                 scriptId : "stcScript"+transId,
12449                 params : params,
12450                 arg : arg,
12451                 url : url,
12452                 callback : callback,
12453                 scope : scope,
12454                 reader : reader
12455             };
12456             var conn = this;
12457
12458             window[trans.cb] = function(o){
12459                 conn.handleResponse(o, trans);
12460             };
12461
12462             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12463
12464             if(this.autoAbort !== false){
12465                 this.abort();
12466             }
12467
12468             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12469
12470             var script = document.createElement("script");
12471             script.setAttribute("src", url);
12472             script.setAttribute("type", "text/javascript");
12473             script.setAttribute("id", trans.scriptId);
12474             this.head.appendChild(script);
12475
12476             this.trans = trans;
12477         }else{
12478             callback.call(scope||this, null, arg, false);
12479         }
12480     },
12481
12482     // private
12483     isLoading : function(){
12484         return this.trans ? true : false;
12485     },
12486
12487     /**
12488      * Abort the current server request.
12489      */
12490     abort : function(){
12491         if(this.isLoading()){
12492             this.destroyTrans(this.trans);
12493         }
12494     },
12495
12496     // private
12497     destroyTrans : function(trans, isLoaded){
12498         this.head.removeChild(document.getElementById(trans.scriptId));
12499         clearTimeout(trans.timeoutId);
12500         if(isLoaded){
12501             window[trans.cb] = undefined;
12502             try{
12503                 delete window[trans.cb];
12504             }catch(e){}
12505         }else{
12506             // if hasn't been loaded, wait for load to remove it to prevent script error
12507             window[trans.cb] = function(){
12508                 window[trans.cb] = undefined;
12509                 try{
12510                     delete window[trans.cb];
12511                 }catch(e){}
12512             };
12513         }
12514     },
12515
12516     // private
12517     handleResponse : function(o, trans){
12518         this.trans = false;
12519         this.destroyTrans(trans, true);
12520         var result;
12521         try {
12522             result = trans.reader.readRecords(o);
12523         }catch(e){
12524             this.fireEvent("loadexception", this, o, trans.arg, e);
12525             trans.callback.call(trans.scope||window, null, trans.arg, false);
12526             return;
12527         }
12528         this.fireEvent("load", this, o, trans.arg);
12529         trans.callback.call(trans.scope||window, result, trans.arg, true);
12530     },
12531
12532     // private
12533     handleFailure : function(trans){
12534         this.trans = false;
12535         this.destroyTrans(trans, false);
12536         this.fireEvent("loadexception", this, null, trans.arg);
12537         trans.callback.call(trans.scope||window, null, trans.arg, false);
12538     }
12539 });/*
12540  * Based on:
12541  * Ext JS Library 1.1.1
12542  * Copyright(c) 2006-2007, Ext JS, LLC.
12543  *
12544  * Originally Released Under LGPL - original licence link has changed is not relivant.
12545  *
12546  * Fork - LGPL
12547  * <script type="text/javascript">
12548  */
12549
12550 /**
12551  * @class Roo.data.JsonReader
12552  * @extends Roo.data.DataReader
12553  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12554  * based on mappings in a provided Roo.data.Record constructor.
12555  * 
12556  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12557  * in the reply previously. 
12558  * 
12559  * <p>
12560  * Example code:
12561  * <pre><code>
12562 var RecordDef = Roo.data.Record.create([
12563     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12564     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12565 ]);
12566 var myReader = new Roo.data.JsonReader({
12567     totalProperty: "results",    // The property which contains the total dataset size (optional)
12568     root: "rows",                // The property which contains an Array of row objects
12569     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12570 }, RecordDef);
12571 </code></pre>
12572  * <p>
12573  * This would consume a JSON file like this:
12574  * <pre><code>
12575 { 'results': 2, 'rows': [
12576     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12577     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12578 }
12579 </code></pre>
12580  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12581  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12582  * paged from the remote server.
12583  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12584  * @cfg {String} root name of the property which contains the Array of row objects.
12585  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12586  * @cfg {Array} fields Array of field definition objects
12587  * @constructor
12588  * Create a new JsonReader
12589  * @param {Object} meta Metadata configuration options
12590  * @param {Object} recordType Either an Array of field definition objects,
12591  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12592  */
12593 Roo.data.JsonReader = function(meta, recordType){
12594     
12595     meta = meta || {};
12596     // set some defaults:
12597     Roo.applyIf(meta, {
12598         totalProperty: 'total',
12599         successProperty : 'success',
12600         root : 'data',
12601         id : 'id'
12602     });
12603     
12604     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12605 };
12606 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12607     
12608     /**
12609      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12610      * Used by Store query builder to append _requestMeta to params.
12611      * 
12612      */
12613     metaFromRemote : false,
12614     /**
12615      * This method is only used by a DataProxy which has retrieved data from a remote server.
12616      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12617      * @return {Object} data A data block which is used by an Roo.data.Store object as
12618      * a cache of Roo.data.Records.
12619      */
12620     read : function(response){
12621         var json = response.responseText;
12622        
12623         var o = /* eval:var:o */ eval("("+json+")");
12624         if(!o) {
12625             throw {message: "JsonReader.read: Json object not found"};
12626         }
12627         
12628         if(o.metaData){
12629             
12630             delete this.ef;
12631             this.metaFromRemote = true;
12632             this.meta = o.metaData;
12633             this.recordType = Roo.data.Record.create(o.metaData.fields);
12634             this.onMetaChange(this.meta, this.recordType, o);
12635         }
12636         return this.readRecords(o);
12637     },
12638
12639     // private function a store will implement
12640     onMetaChange : function(meta, recordType, o){
12641
12642     },
12643
12644     /**
12645          * @ignore
12646          */
12647     simpleAccess: function(obj, subsc) {
12648         return obj[subsc];
12649     },
12650
12651         /**
12652          * @ignore
12653          */
12654     getJsonAccessor: function(){
12655         var re = /[\[\.]/;
12656         return function(expr) {
12657             try {
12658                 return(re.test(expr))
12659                     ? new Function("obj", "return obj." + expr)
12660                     : function(obj){
12661                         return obj[expr];
12662                     };
12663             } catch(e){}
12664             return Roo.emptyFn;
12665         };
12666     }(),
12667
12668     /**
12669      * Create a data block containing Roo.data.Records from an XML document.
12670      * @param {Object} o An object which contains an Array of row objects in the property specified
12671      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12672      * which contains the total size of the dataset.
12673      * @return {Object} data A data block which is used by an Roo.data.Store object as
12674      * a cache of Roo.data.Records.
12675      */
12676     readRecords : function(o){
12677         /**
12678          * After any data loads, the raw JSON data is available for further custom processing.
12679          * @type Object
12680          */
12681         this.o = o;
12682         var s = this.meta, Record = this.recordType,
12683             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12684
12685 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12686         if (!this.ef) {
12687             if(s.totalProperty) {
12688                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12689                 }
12690                 if(s.successProperty) {
12691                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12692                 }
12693                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12694                 if (s.id) {
12695                         var g = this.getJsonAccessor(s.id);
12696                         this.getId = function(rec) {
12697                                 var r = g(rec);  
12698                                 return (r === undefined || r === "") ? null : r;
12699                         };
12700                 } else {
12701                         this.getId = function(){return null;};
12702                 }
12703             this.ef = [];
12704             for(var jj = 0; jj < fl; jj++){
12705                 f = fi[jj];
12706                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12707                 this.ef[jj] = this.getJsonAccessor(map);
12708             }
12709         }
12710
12711         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12712         if(s.totalProperty){
12713             var vt = parseInt(this.getTotal(o), 10);
12714             if(!isNaN(vt)){
12715                 totalRecords = vt;
12716             }
12717         }
12718         if(s.successProperty){
12719             var vs = this.getSuccess(o);
12720             if(vs === false || vs === 'false'){
12721                 success = false;
12722             }
12723         }
12724         var records = [];
12725         for(var i = 0; i < c; i++){
12726                 var n = root[i];
12727             var values = {};
12728             var id = this.getId(n);
12729             for(var j = 0; j < fl; j++){
12730                 f = fi[j];
12731             var v = this.ef[j](n);
12732             if (!f.convert) {
12733                 Roo.log('missing convert for ' + f.name);
12734                 Roo.log(f);
12735                 continue;
12736             }
12737             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12738             }
12739             var record = new Record(values, id);
12740             record.json = n;
12741             records[i] = record;
12742         }
12743         return {
12744             raw : o,
12745             success : success,
12746             records : records,
12747             totalRecords : totalRecords
12748         };
12749     }
12750 });/*
12751  * Based on:
12752  * Ext JS Library 1.1.1
12753  * Copyright(c) 2006-2007, Ext JS, LLC.
12754  *
12755  * Originally Released Under LGPL - original licence link has changed is not relivant.
12756  *
12757  * Fork - LGPL
12758  * <script type="text/javascript">
12759  */
12760
12761 /**
12762  * @class Roo.data.ArrayReader
12763  * @extends Roo.data.DataReader
12764  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12765  * Each element of that Array represents a row of data fields. The
12766  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12767  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12768  * <p>
12769  * Example code:.
12770  * <pre><code>
12771 var RecordDef = Roo.data.Record.create([
12772     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12773     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12774 ]);
12775 var myReader = new Roo.data.ArrayReader({
12776     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12777 }, RecordDef);
12778 </code></pre>
12779  * <p>
12780  * This would consume an Array like this:
12781  * <pre><code>
12782 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12783   </code></pre>
12784  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12785  * @constructor
12786  * Create a new JsonReader
12787  * @param {Object} meta Metadata configuration options.
12788  * @param {Object} recordType Either an Array of field definition objects
12789  * as specified to {@link Roo.data.Record#create},
12790  * or an {@link Roo.data.Record} object
12791  * created using {@link Roo.data.Record#create}.
12792  */
12793 Roo.data.ArrayReader = function(meta, recordType){
12794     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12795 };
12796
12797 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12798     /**
12799      * Create a data block containing Roo.data.Records from an XML document.
12800      * @param {Object} o An Array of row objects which represents the dataset.
12801      * @return {Object} data A data block which is used by an Roo.data.Store object as
12802      * a cache of Roo.data.Records.
12803      */
12804     readRecords : function(o){
12805         var sid = this.meta ? this.meta.id : null;
12806         var recordType = this.recordType, fields = recordType.prototype.fields;
12807         var records = [];
12808         var root = o;
12809             for(var i = 0; i < root.length; i++){
12810                     var n = root[i];
12811                 var values = {};
12812                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12813                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12814                 var f = fields.items[j];
12815                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12816                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12817                 v = f.convert(v);
12818                 values[f.name] = v;
12819             }
12820                 var record = new recordType(values, id);
12821                 record.json = n;
12822                 records[records.length] = record;
12823             }
12824             return {
12825                 records : records,
12826                 totalRecords : records.length
12827             };
12828     }
12829 });/*
12830  * - LGPL
12831  * * 
12832  */
12833
12834 /**
12835  * @class Roo.bootstrap.ComboBox
12836  * @extends Roo.bootstrap.TriggerField
12837  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12838  * @cfg {Boolean} append (true|false) default false
12839  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12840  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12841  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12842  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12843  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12844  * @cfg {Boolean} animate default true
12845  * @cfg {Boolean} emptyResultText only for touch device
12846  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12847  * @cfg {String} emptyTitle default ''
12848  * @constructor
12849  * Create a new ComboBox.
12850  * @param {Object} config Configuration options
12851  */
12852 Roo.bootstrap.ComboBox = function(config){
12853     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12854     this.addEvents({
12855         /**
12856          * @event expand
12857          * Fires when the dropdown list is expanded
12858         * @param {Roo.bootstrap.ComboBox} combo This combo box
12859         */
12860         'expand' : true,
12861         /**
12862          * @event collapse
12863          * Fires when the dropdown list is collapsed
12864         * @param {Roo.bootstrap.ComboBox} combo This combo box
12865         */
12866         'collapse' : true,
12867         /**
12868          * @event beforeselect
12869          * Fires before a list item is selected. Return false to cancel the selection.
12870         * @param {Roo.bootstrap.ComboBox} combo This combo box
12871         * @param {Roo.data.Record} record The data record returned from the underlying store
12872         * @param {Number} index The index of the selected item in the dropdown list
12873         */
12874         'beforeselect' : true,
12875         /**
12876          * @event select
12877          * Fires when a list item is selected
12878         * @param {Roo.bootstrap.ComboBox} combo This combo box
12879         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12880         * @param {Number} index The index of the selected item in the dropdown list
12881         */
12882         'select' : true,
12883         /**
12884          * @event beforequery
12885          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12886          * The event object passed has these properties:
12887         * @param {Roo.bootstrap.ComboBox} combo This combo box
12888         * @param {String} query The query
12889         * @param {Boolean} forceAll true to force "all" query
12890         * @param {Boolean} cancel true to cancel the query
12891         * @param {Object} e The query event object
12892         */
12893         'beforequery': true,
12894          /**
12895          * @event add
12896          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12897         * @param {Roo.bootstrap.ComboBox} combo This combo box
12898         */
12899         'add' : true,
12900         /**
12901          * @event edit
12902          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12903         * @param {Roo.bootstrap.ComboBox} combo This combo box
12904         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12905         */
12906         'edit' : true,
12907         /**
12908          * @event remove
12909          * Fires when the remove value from the combobox array
12910         * @param {Roo.bootstrap.ComboBox} combo This combo box
12911         */
12912         'remove' : true,
12913         /**
12914          * @event afterremove
12915          * Fires when the remove value from the combobox array
12916         * @param {Roo.bootstrap.ComboBox} combo This combo box
12917         */
12918         'afterremove' : true,
12919         /**
12920          * @event specialfilter
12921          * Fires when specialfilter
12922             * @param {Roo.bootstrap.ComboBox} combo This combo box
12923             */
12924         'specialfilter' : true,
12925         /**
12926          * @event tick
12927          * Fires when tick the element
12928             * @param {Roo.bootstrap.ComboBox} combo This combo box
12929             */
12930         'tick' : true,
12931         /**
12932          * @event touchviewdisplay
12933          * Fires when touch view require special display (default is using displayField)
12934             * @param {Roo.bootstrap.ComboBox} combo This combo box
12935             * @param {Object} cfg set html .
12936             */
12937         'touchviewdisplay' : true
12938         
12939     });
12940     
12941     this.item = [];
12942     this.tickItems = [];
12943     
12944     this.selectedIndex = -1;
12945     if(this.mode == 'local'){
12946         if(config.queryDelay === undefined){
12947             this.queryDelay = 10;
12948         }
12949         if(config.minChars === undefined){
12950             this.minChars = 0;
12951         }
12952     }
12953 };
12954
12955 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12956      
12957     /**
12958      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12959      * rendering into an Roo.Editor, defaults to false)
12960      */
12961     /**
12962      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12963      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12964      */
12965     /**
12966      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12967      */
12968     /**
12969      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12970      * the dropdown list (defaults to undefined, with no header element)
12971      */
12972
12973      /**
12974      * @cfg {String/Roo.Template} tpl The template to use to render the output
12975      */
12976      
12977      /**
12978      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12979      */
12980     listWidth: undefined,
12981     /**
12982      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12983      * mode = 'remote' or 'text' if mode = 'local')
12984      */
12985     displayField: undefined,
12986     
12987     /**
12988      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12989      * mode = 'remote' or 'value' if mode = 'local'). 
12990      * Note: use of a valueField requires the user make a selection
12991      * in order for a value to be mapped.
12992      */
12993     valueField: undefined,
12994     /**
12995      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12996      */
12997     modalTitle : '',
12998     
12999     /**
13000      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13001      * field's data value (defaults to the underlying DOM element's name)
13002      */
13003     hiddenName: undefined,
13004     /**
13005      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13006      */
13007     listClass: '',
13008     /**
13009      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13010      */
13011     selectedClass: 'active',
13012     
13013     /**
13014      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13015      */
13016     shadow:'sides',
13017     /**
13018      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13019      * anchor positions (defaults to 'tl-bl')
13020      */
13021     listAlign: 'tl-bl?',
13022     /**
13023      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13024      */
13025     maxHeight: 300,
13026     /**
13027      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13028      * query specified by the allQuery config option (defaults to 'query')
13029      */
13030     triggerAction: 'query',
13031     /**
13032      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13033      * (defaults to 4, does not apply if editable = false)
13034      */
13035     minChars : 4,
13036     /**
13037      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13038      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13039      */
13040     typeAhead: false,
13041     /**
13042      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13043      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13044      */
13045     queryDelay: 500,
13046     /**
13047      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13048      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13049      */
13050     pageSize: 0,
13051     /**
13052      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13053      * when editable = true (defaults to false)
13054      */
13055     selectOnFocus:false,
13056     /**
13057      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13058      */
13059     queryParam: 'query',
13060     /**
13061      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13062      * when mode = 'remote' (defaults to 'Loading...')
13063      */
13064     loadingText: 'Loading...',
13065     /**
13066      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13067      */
13068     resizable: false,
13069     /**
13070      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13071      */
13072     handleHeight : 8,
13073     /**
13074      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13075      * traditional select (defaults to true)
13076      */
13077     editable: true,
13078     /**
13079      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13080      */
13081     allQuery: '',
13082     /**
13083      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13084      */
13085     mode: 'remote',
13086     /**
13087      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13088      * listWidth has a higher value)
13089      */
13090     minListWidth : 70,
13091     /**
13092      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13093      * allow the user to set arbitrary text into the field (defaults to false)
13094      */
13095     forceSelection:false,
13096     /**
13097      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13098      * if typeAhead = true (defaults to 250)
13099      */
13100     typeAheadDelay : 250,
13101     /**
13102      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13103      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13104      */
13105     valueNotFoundText : undefined,
13106     /**
13107      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13108      */
13109     blockFocus : false,
13110     
13111     /**
13112      * @cfg {Boolean} disableClear Disable showing of clear button.
13113      */
13114     disableClear : false,
13115     /**
13116      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13117      */
13118     alwaysQuery : false,
13119     
13120     /**
13121      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13122      */
13123     multiple : false,
13124     
13125     /**
13126      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13127      */
13128     invalidClass : "has-warning",
13129     
13130     /**
13131      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13132      */
13133     validClass : "has-success",
13134     
13135     /**
13136      * @cfg {Boolean} specialFilter (true|false) special filter default false
13137      */
13138     specialFilter : false,
13139     
13140     /**
13141      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13142      */
13143     mobileTouchView : true,
13144     
13145     /**
13146      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13147      */
13148     useNativeIOS : false,
13149     
13150     /**
13151      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13152      */
13153     mobile_restrict_height : false,
13154     
13155     ios_options : false,
13156     
13157     //private
13158     addicon : false,
13159     editicon: false,
13160     
13161     page: 0,
13162     hasQuery: false,
13163     append: false,
13164     loadNext: false,
13165     autoFocus : true,
13166     tickable : false,
13167     btnPosition : 'right',
13168     triggerList : true,
13169     showToggleBtn : true,
13170     animate : true,
13171     emptyResultText: 'Empty',
13172     triggerText : 'Select',
13173     emptyTitle : '',
13174     
13175     // element that contains real text value.. (when hidden is used..)
13176     
13177     getAutoCreate : function()
13178     {   
13179         var cfg = false;
13180         //render
13181         /*
13182          * Render classic select for iso
13183          */
13184         
13185         if(Roo.isIOS && this.useNativeIOS){
13186             cfg = this.getAutoCreateNativeIOS();
13187             return cfg;
13188         }
13189         
13190         /*
13191          * Touch Devices
13192          */
13193         
13194         if(Roo.isTouch && this.mobileTouchView){
13195             cfg = this.getAutoCreateTouchView();
13196             return cfg;;
13197         }
13198         
13199         /*
13200          *  Normal ComboBox
13201          */
13202         if(!this.tickable){
13203             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13204             return cfg;
13205         }
13206         
13207         /*
13208          *  ComboBox with tickable selections
13209          */
13210              
13211         var align = this.labelAlign || this.parentLabelAlign();
13212         
13213         cfg = {
13214             cls : 'form-group roo-combobox-tickable' //input-group
13215         };
13216         
13217         var btn_text_select = '';
13218         var btn_text_done = '';
13219         var btn_text_cancel = '';
13220         
13221         if (this.btn_text_show) {
13222             btn_text_select = 'Select';
13223             btn_text_done = 'Done';
13224             btn_text_cancel = 'Cancel'; 
13225         }
13226         
13227         var buttons = {
13228             tag : 'div',
13229             cls : 'tickable-buttons',
13230             cn : [
13231                 {
13232                     tag : 'button',
13233                     type : 'button',
13234                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13235                     //html : this.triggerText
13236                     html: btn_text_select
13237                 },
13238                 {
13239                     tag : 'button',
13240                     type : 'button',
13241                     name : 'ok',
13242                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13243                     //html : 'Done'
13244                     html: btn_text_done
13245                 },
13246                 {
13247                     tag : 'button',
13248                     type : 'button',
13249                     name : 'cancel',
13250                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13251                     //html : 'Cancel'
13252                     html: btn_text_cancel
13253                 }
13254             ]
13255         };
13256         
13257         if(this.editable){
13258             buttons.cn.unshift({
13259                 tag: 'input',
13260                 cls: 'roo-select2-search-field-input'
13261             });
13262         }
13263         
13264         var _this = this;
13265         
13266         Roo.each(buttons.cn, function(c){
13267             if (_this.size) {
13268                 c.cls += ' btn-' + _this.size;
13269             }
13270
13271             if (_this.disabled) {
13272                 c.disabled = true;
13273             }
13274         });
13275         
13276         var box = {
13277             tag: 'div',
13278             cn: [
13279                 {
13280                     tag: 'input',
13281                     type : 'hidden',
13282                     cls: 'form-hidden-field'
13283                 },
13284                 {
13285                     tag: 'ul',
13286                     cls: 'roo-select2-choices',
13287                     cn:[
13288                         {
13289                             tag: 'li',
13290                             cls: 'roo-select2-search-field',
13291                             cn: [
13292                                 buttons
13293                             ]
13294                         }
13295                     ]
13296                 }
13297             ]
13298         };
13299         
13300         var combobox = {
13301             cls: 'roo-select2-container input-group roo-select2-container-multi',
13302             cn: [
13303                 box
13304 //                {
13305 //                    tag: 'ul',
13306 //                    cls: 'typeahead typeahead-long dropdown-menu',
13307 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13308 //                }
13309             ]
13310         };
13311         
13312         if(this.hasFeedback && !this.allowBlank){
13313             
13314             var feedback = {
13315                 tag: 'span',
13316                 cls: 'glyphicon form-control-feedback'
13317             };
13318
13319             combobox.cn.push(feedback);
13320         }
13321         
13322         
13323         if (align ==='left' && this.fieldLabel.length) {
13324             
13325             cfg.cls += ' roo-form-group-label-left';
13326             
13327             cfg.cn = [
13328                 {
13329                     tag : 'i',
13330                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13331                     tooltip : 'This field is required'
13332                 },
13333                 {
13334                     tag: 'label',
13335                     'for' :  id,
13336                     cls : 'control-label',
13337                     html : this.fieldLabel
13338
13339                 },
13340                 {
13341                     cls : "", 
13342                     cn: [
13343                         combobox
13344                     ]
13345                 }
13346
13347             ];
13348             
13349             var labelCfg = cfg.cn[1];
13350             var contentCfg = cfg.cn[2];
13351             
13352
13353             if(this.indicatorpos == 'right'){
13354                 
13355                 cfg.cn = [
13356                     {
13357                         tag: 'label',
13358                         'for' :  id,
13359                         cls : 'control-label',
13360                         cn : [
13361                             {
13362                                 tag : 'span',
13363                                 html : this.fieldLabel
13364                             },
13365                             {
13366                                 tag : 'i',
13367                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13368                                 tooltip : 'This field is required'
13369                             }
13370                         ]
13371                     },
13372                     {
13373                         cls : "",
13374                         cn: [
13375                             combobox
13376                         ]
13377                     }
13378
13379                 ];
13380                 
13381                 
13382                 
13383                 labelCfg = cfg.cn[0];
13384                 contentCfg = cfg.cn[1];
13385             
13386             }
13387             
13388             if(this.labelWidth > 12){
13389                 labelCfg.style = "width: " + this.labelWidth + 'px';
13390             }
13391             
13392             if(this.labelWidth < 13 && this.labelmd == 0){
13393                 this.labelmd = this.labelWidth;
13394             }
13395             
13396             if(this.labellg > 0){
13397                 labelCfg.cls += ' col-lg-' + this.labellg;
13398                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13399             }
13400             
13401             if(this.labelmd > 0){
13402                 labelCfg.cls += ' col-md-' + this.labelmd;
13403                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13404             }
13405             
13406             if(this.labelsm > 0){
13407                 labelCfg.cls += ' col-sm-' + this.labelsm;
13408                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13409             }
13410             
13411             if(this.labelxs > 0){
13412                 labelCfg.cls += ' col-xs-' + this.labelxs;
13413                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13414             }
13415                 
13416                 
13417         } else if ( this.fieldLabel.length) {
13418 //                Roo.log(" label");
13419                  cfg.cn = [
13420                     {
13421                         tag : 'i',
13422                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13423                         tooltip : 'This field is required'
13424                     },
13425                     {
13426                         tag: 'label',
13427                         //cls : 'input-group-addon',
13428                         html : this.fieldLabel
13429                     },
13430                     combobox
13431                 ];
13432                 
13433                 if(this.indicatorpos == 'right'){
13434                     cfg.cn = [
13435                         {
13436                             tag: 'label',
13437                             //cls : 'input-group-addon',
13438                             html : this.fieldLabel
13439                         },
13440                         {
13441                             tag : 'i',
13442                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13443                             tooltip : 'This field is required'
13444                         },
13445                         combobox
13446                     ];
13447                     
13448                 }
13449
13450         } else {
13451             
13452 //                Roo.log(" no label && no align");
13453                 cfg = combobox
13454                      
13455                 
13456         }
13457          
13458         var settings=this;
13459         ['xs','sm','md','lg'].map(function(size){
13460             if (settings[size]) {
13461                 cfg.cls += ' col-' + size + '-' + settings[size];
13462             }
13463         });
13464         
13465         return cfg;
13466         
13467     },
13468     
13469     _initEventsCalled : false,
13470     
13471     // private
13472     initEvents: function()
13473     {   
13474         if (this._initEventsCalled) { // as we call render... prevent looping...
13475             return;
13476         }
13477         this._initEventsCalled = true;
13478         
13479         if (!this.store) {
13480             throw "can not find store for combo";
13481         }
13482         
13483         this.indicator = this.indicatorEl();
13484         
13485         this.store = Roo.factory(this.store, Roo.data);
13486         this.store.parent = this;
13487         
13488         // if we are building from html. then this element is so complex, that we can not really
13489         // use the rendered HTML.
13490         // so we have to trash and replace the previous code.
13491         if (Roo.XComponent.build_from_html) {
13492             // remove this element....
13493             var e = this.el.dom, k=0;
13494             while (e ) { e = e.previousSibling;  ++k;}
13495
13496             this.el.remove();
13497             
13498             this.el=false;
13499             this.rendered = false;
13500             
13501             this.render(this.parent().getChildContainer(true), k);
13502         }
13503         
13504         if(Roo.isIOS && this.useNativeIOS){
13505             this.initIOSView();
13506             return;
13507         }
13508         
13509         /*
13510          * Touch Devices
13511          */
13512         
13513         if(Roo.isTouch && this.mobileTouchView){
13514             this.initTouchView();
13515             return;
13516         }
13517         
13518         if(this.tickable){
13519             this.initTickableEvents();
13520             return;
13521         }
13522         
13523         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13524         
13525         if(this.hiddenName){
13526             
13527             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13528             
13529             this.hiddenField.dom.value =
13530                 this.hiddenValue !== undefined ? this.hiddenValue :
13531                 this.value !== undefined ? this.value : '';
13532
13533             // prevent input submission
13534             this.el.dom.removeAttribute('name');
13535             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13536              
13537              
13538         }
13539         //if(Roo.isGecko){
13540         //    this.el.dom.setAttribute('autocomplete', 'off');
13541         //}
13542         
13543         var cls = 'x-combo-list';
13544         
13545         //this.list = new Roo.Layer({
13546         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13547         //});
13548         
13549         var _this = this;
13550         
13551         (function(){
13552             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13553             _this.list.setWidth(lw);
13554         }).defer(100);
13555         
13556         this.list.on('mouseover', this.onViewOver, this);
13557         this.list.on('mousemove', this.onViewMove, this);
13558         this.list.on('scroll', this.onViewScroll, this);
13559         
13560         /*
13561         this.list.swallowEvent('mousewheel');
13562         this.assetHeight = 0;
13563
13564         if(this.title){
13565             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13566             this.assetHeight += this.header.getHeight();
13567         }
13568
13569         this.innerList = this.list.createChild({cls:cls+'-inner'});
13570         this.innerList.on('mouseover', this.onViewOver, this);
13571         this.innerList.on('mousemove', this.onViewMove, this);
13572         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13573         
13574         if(this.allowBlank && !this.pageSize && !this.disableClear){
13575             this.footer = this.list.createChild({cls:cls+'-ft'});
13576             this.pageTb = new Roo.Toolbar(this.footer);
13577            
13578         }
13579         if(this.pageSize){
13580             this.footer = this.list.createChild({cls:cls+'-ft'});
13581             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13582                     {pageSize: this.pageSize});
13583             
13584         }
13585         
13586         if (this.pageTb && this.allowBlank && !this.disableClear) {
13587             var _this = this;
13588             this.pageTb.add(new Roo.Toolbar.Fill(), {
13589                 cls: 'x-btn-icon x-btn-clear',
13590                 text: '&#160;',
13591                 handler: function()
13592                 {
13593                     _this.collapse();
13594                     _this.clearValue();
13595                     _this.onSelect(false, -1);
13596                 }
13597             });
13598         }
13599         if (this.footer) {
13600             this.assetHeight += this.footer.getHeight();
13601         }
13602         */
13603             
13604         if(!this.tpl){
13605             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13606         }
13607
13608         this.view = new Roo.View(this.list, this.tpl, {
13609             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13610         });
13611         //this.view.wrapEl.setDisplayed(false);
13612         this.view.on('click', this.onViewClick, this);
13613         
13614         
13615         this.store.on('beforeload', this.onBeforeLoad, this);
13616         this.store.on('load', this.onLoad, this);
13617         this.store.on('loadexception', this.onLoadException, this);
13618         /*
13619         if(this.resizable){
13620             this.resizer = new Roo.Resizable(this.list,  {
13621                pinned:true, handles:'se'
13622             });
13623             this.resizer.on('resize', function(r, w, h){
13624                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13625                 this.listWidth = w;
13626                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13627                 this.restrictHeight();
13628             }, this);
13629             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13630         }
13631         */
13632         if(!this.editable){
13633             this.editable = true;
13634             this.setEditable(false);
13635         }
13636         
13637         /*
13638         
13639         if (typeof(this.events.add.listeners) != 'undefined') {
13640             
13641             this.addicon = this.wrap.createChild(
13642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13643        
13644             this.addicon.on('click', function(e) {
13645                 this.fireEvent('add', this);
13646             }, this);
13647         }
13648         if (typeof(this.events.edit.listeners) != 'undefined') {
13649             
13650             this.editicon = this.wrap.createChild(
13651                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13652             if (this.addicon) {
13653                 this.editicon.setStyle('margin-left', '40px');
13654             }
13655             this.editicon.on('click', function(e) {
13656                 
13657                 // we fire even  if inothing is selected..
13658                 this.fireEvent('edit', this, this.lastData );
13659                 
13660             }, this);
13661         }
13662         */
13663         
13664         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13665             "up" : function(e){
13666                 this.inKeyMode = true;
13667                 this.selectPrev();
13668             },
13669
13670             "down" : function(e){
13671                 if(!this.isExpanded()){
13672                     this.onTriggerClick();
13673                 }else{
13674                     this.inKeyMode = true;
13675                     this.selectNext();
13676                 }
13677             },
13678
13679             "enter" : function(e){
13680 //                this.onViewClick();
13681                 //return true;
13682                 this.collapse();
13683                 
13684                 if(this.fireEvent("specialkey", this, e)){
13685                     this.onViewClick(false);
13686                 }
13687                 
13688                 return true;
13689             },
13690
13691             "esc" : function(e){
13692                 this.collapse();
13693             },
13694
13695             "tab" : function(e){
13696                 this.collapse();
13697                 
13698                 if(this.fireEvent("specialkey", this, e)){
13699                     this.onViewClick(false);
13700                 }
13701                 
13702                 return true;
13703             },
13704
13705             scope : this,
13706
13707             doRelay : function(foo, bar, hname){
13708                 if(hname == 'down' || this.scope.isExpanded()){
13709                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13710                 }
13711                 return true;
13712             },
13713
13714             forceKeyDown: true
13715         });
13716         
13717         
13718         this.queryDelay = Math.max(this.queryDelay || 10,
13719                 this.mode == 'local' ? 10 : 250);
13720         
13721         
13722         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13723         
13724         if(this.typeAhead){
13725             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13726         }
13727         if(this.editable !== false){
13728             this.inputEl().on("keyup", this.onKeyUp, this);
13729         }
13730         if(this.forceSelection){
13731             this.inputEl().on('blur', this.doForce, this);
13732         }
13733         
13734         if(this.multiple){
13735             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13736             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13737         }
13738     },
13739     
13740     initTickableEvents: function()
13741     {   
13742         this.createList();
13743         
13744         if(this.hiddenName){
13745             
13746             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13747             
13748             this.hiddenField.dom.value =
13749                 this.hiddenValue !== undefined ? this.hiddenValue :
13750                 this.value !== undefined ? this.value : '';
13751
13752             // prevent input submission
13753             this.el.dom.removeAttribute('name');
13754             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13755              
13756              
13757         }
13758         
13759 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13760         
13761         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13762         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13763         if(this.triggerList){
13764             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13765         }
13766          
13767         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13768         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13769         
13770         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13771         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13772         
13773         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13774         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13775         
13776         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13777         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13778         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13779         
13780         this.okBtn.hide();
13781         this.cancelBtn.hide();
13782         
13783         var _this = this;
13784         
13785         (function(){
13786             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13787             _this.list.setWidth(lw);
13788         }).defer(100);
13789         
13790         this.list.on('mouseover', this.onViewOver, this);
13791         this.list.on('mousemove', this.onViewMove, this);
13792         
13793         this.list.on('scroll', this.onViewScroll, this);
13794         
13795         if(!this.tpl){
13796             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13797                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13798         }
13799
13800         this.view = new Roo.View(this.list, this.tpl, {
13801             singleSelect:true,
13802             tickable:true,
13803             parent:this,
13804             store: this.store,
13805             selectedClass: this.selectedClass
13806         });
13807         
13808         //this.view.wrapEl.setDisplayed(false);
13809         this.view.on('click', this.onViewClick, this);
13810         
13811         
13812         
13813         this.store.on('beforeload', this.onBeforeLoad, this);
13814         this.store.on('load', this.onLoad, this);
13815         this.store.on('loadexception', this.onLoadException, this);
13816         
13817         if(this.editable){
13818             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13819                 "up" : function(e){
13820                     this.inKeyMode = true;
13821                     this.selectPrev();
13822                 },
13823
13824                 "down" : function(e){
13825                     this.inKeyMode = true;
13826                     this.selectNext();
13827                 },
13828
13829                 "enter" : function(e){
13830                     if(this.fireEvent("specialkey", this, e)){
13831                         this.onViewClick(false);
13832                     }
13833                     
13834                     return true;
13835                 },
13836
13837                 "esc" : function(e){
13838                     this.onTickableFooterButtonClick(e, false, false);
13839                 },
13840
13841                 "tab" : function(e){
13842                     this.fireEvent("specialkey", this, e);
13843                     
13844                     this.onTickableFooterButtonClick(e, false, false);
13845                     
13846                     return true;
13847                 },
13848
13849                 scope : this,
13850
13851                 doRelay : function(e, fn, key){
13852                     if(this.scope.isExpanded()){
13853                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13854                     }
13855                     return true;
13856                 },
13857
13858                 forceKeyDown: true
13859             });
13860         }
13861         
13862         this.queryDelay = Math.max(this.queryDelay || 10,
13863                 this.mode == 'local' ? 10 : 250);
13864         
13865         
13866         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13867         
13868         if(this.typeAhead){
13869             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13870         }
13871         
13872         if(this.editable !== false){
13873             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13874         }
13875         
13876         this.indicator = this.indicatorEl();
13877         
13878         if(this.indicator){
13879             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13880             this.indicator.hide();
13881         }
13882         
13883     },
13884
13885     onDestroy : function(){
13886         if(this.view){
13887             this.view.setStore(null);
13888             this.view.el.removeAllListeners();
13889             this.view.el.remove();
13890             this.view.purgeListeners();
13891         }
13892         if(this.list){
13893             this.list.dom.innerHTML  = '';
13894         }
13895         
13896         if(this.store){
13897             this.store.un('beforeload', this.onBeforeLoad, this);
13898             this.store.un('load', this.onLoad, this);
13899             this.store.un('loadexception', this.onLoadException, this);
13900         }
13901         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13902     },
13903
13904     // private
13905     fireKey : function(e){
13906         if(e.isNavKeyPress() && !this.list.isVisible()){
13907             this.fireEvent("specialkey", this, e);
13908         }
13909     },
13910
13911     // private
13912     onResize: function(w, h){
13913 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13914 //        
13915 //        if(typeof w != 'number'){
13916 //            // we do not handle it!?!?
13917 //            return;
13918 //        }
13919 //        var tw = this.trigger.getWidth();
13920 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13921 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13922 //        var x = w - tw;
13923 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13924 //            
13925 //        //this.trigger.setStyle('left', x+'px');
13926 //        
13927 //        if(this.list && this.listWidth === undefined){
13928 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13929 //            this.list.setWidth(lw);
13930 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13931 //        }
13932         
13933     
13934         
13935     },
13936
13937     /**
13938      * Allow or prevent the user from directly editing the field text.  If false is passed,
13939      * the user will only be able to select from the items defined in the dropdown list.  This method
13940      * is the runtime equivalent of setting the 'editable' config option at config time.
13941      * @param {Boolean} value True to allow the user to directly edit the field text
13942      */
13943     setEditable : function(value){
13944         if(value == this.editable){
13945             return;
13946         }
13947         this.editable = value;
13948         if(!value){
13949             this.inputEl().dom.setAttribute('readOnly', true);
13950             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13951             this.inputEl().addClass('x-combo-noedit');
13952         }else{
13953             this.inputEl().dom.setAttribute('readOnly', false);
13954             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13955             this.inputEl().removeClass('x-combo-noedit');
13956         }
13957     },
13958
13959     // private
13960     
13961     onBeforeLoad : function(combo,opts){
13962         if(!this.hasFocus){
13963             return;
13964         }
13965          if (!opts.add) {
13966             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13967          }
13968         this.restrictHeight();
13969         this.selectedIndex = -1;
13970     },
13971
13972     // private
13973     onLoad : function(){
13974         
13975         this.hasQuery = false;
13976         
13977         if(!this.hasFocus){
13978             return;
13979         }
13980         
13981         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13982             this.loading.hide();
13983         }
13984         
13985         if(this.store.getCount() > 0){
13986             
13987             this.expand();
13988             this.restrictHeight();
13989             if(this.lastQuery == this.allQuery){
13990                 if(this.editable && !this.tickable){
13991                     this.inputEl().dom.select();
13992                 }
13993                 
13994                 if(
13995                     !this.selectByValue(this.value, true) &&
13996                     this.autoFocus && 
13997                     (
13998                         !this.store.lastOptions ||
13999                         typeof(this.store.lastOptions.add) == 'undefined' || 
14000                         this.store.lastOptions.add != true
14001                     )
14002                 ){
14003                     this.select(0, true);
14004                 }
14005             }else{
14006                 if(this.autoFocus){
14007                     this.selectNext();
14008                 }
14009                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14010                     this.taTask.delay(this.typeAheadDelay);
14011                 }
14012             }
14013         }else{
14014             this.onEmptyResults();
14015         }
14016         
14017         //this.el.focus();
14018     },
14019     // private
14020     onLoadException : function()
14021     {
14022         this.hasQuery = false;
14023         
14024         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14025             this.loading.hide();
14026         }
14027         
14028         if(this.tickable && this.editable){
14029             return;
14030         }
14031         
14032         this.collapse();
14033         // only causes errors at present
14034         //Roo.log(this.store.reader.jsonData);
14035         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14036             // fixme
14037             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14038         //}
14039         
14040         
14041     },
14042     // private
14043     onTypeAhead : function(){
14044         if(this.store.getCount() > 0){
14045             var r = this.store.getAt(0);
14046             var newValue = r.data[this.displayField];
14047             var len = newValue.length;
14048             var selStart = this.getRawValue().length;
14049             
14050             if(selStart != len){
14051                 this.setRawValue(newValue);
14052                 this.selectText(selStart, newValue.length);
14053             }
14054         }
14055     },
14056
14057     // private
14058     onSelect : function(record, index){
14059         
14060         if(this.fireEvent('beforeselect', this, record, index) !== false){
14061         
14062             this.setFromData(index > -1 ? record.data : false);
14063             
14064             this.collapse();
14065             this.fireEvent('select', this, record, index);
14066         }
14067     },
14068
14069     /**
14070      * Returns the currently selected field value or empty string if no value is set.
14071      * @return {String} value The selected value
14072      */
14073     getValue : function()
14074     {
14075         if(Roo.isIOS && this.useNativeIOS){
14076             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14077         }
14078         
14079         if(this.multiple){
14080             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14081         }
14082         
14083         if(this.valueField){
14084             return typeof this.value != 'undefined' ? this.value : '';
14085         }else{
14086             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14087         }
14088     },
14089     
14090     getRawValue : function()
14091     {
14092         if(Roo.isIOS && this.useNativeIOS){
14093             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14094         }
14095         
14096         var v = this.inputEl().getValue();
14097         
14098         return v;
14099     },
14100
14101     /**
14102      * Clears any text/value currently set in the field
14103      */
14104     clearValue : function(){
14105         
14106         if(this.hiddenField){
14107             this.hiddenField.dom.value = '';
14108         }
14109         this.value = '';
14110         this.setRawValue('');
14111         this.lastSelectionText = '';
14112         this.lastData = false;
14113         
14114         var close = this.closeTriggerEl();
14115         
14116         if(close){
14117             close.hide();
14118         }
14119         
14120         this.validate();
14121         
14122     },
14123
14124     /**
14125      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14126      * will be displayed in the field.  If the value does not match the data value of an existing item,
14127      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14128      * Otherwise the field will be blank (although the value will still be set).
14129      * @param {String} value The value to match
14130      */
14131     setValue : function(v)
14132     {
14133         if(Roo.isIOS && this.useNativeIOS){
14134             this.setIOSValue(v);
14135             return;
14136         }
14137         
14138         if(this.multiple){
14139             this.syncValue();
14140             return;
14141         }
14142         
14143         var text = v;
14144         if(this.valueField){
14145             var r = this.findRecord(this.valueField, v);
14146             if(r){
14147                 text = r.data[this.displayField];
14148             }else if(this.valueNotFoundText !== undefined){
14149                 text = this.valueNotFoundText;
14150             }
14151         }
14152         this.lastSelectionText = text;
14153         if(this.hiddenField){
14154             this.hiddenField.dom.value = v;
14155         }
14156         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14157         this.value = v;
14158         
14159         var close = this.closeTriggerEl();
14160         
14161         if(close){
14162             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14163         }
14164         
14165         this.validate();
14166     },
14167     /**
14168      * @property {Object} the last set data for the element
14169      */
14170     
14171     lastData : false,
14172     /**
14173      * Sets the value of the field based on a object which is related to the record format for the store.
14174      * @param {Object} value the value to set as. or false on reset?
14175      */
14176     setFromData : function(o){
14177         
14178         if(this.multiple){
14179             this.addItem(o);
14180             return;
14181         }
14182             
14183         var dv = ''; // display value
14184         var vv = ''; // value value..
14185         this.lastData = o;
14186         if (this.displayField) {
14187             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14188         } else {
14189             // this is an error condition!!!
14190             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14191         }
14192         
14193         if(this.valueField){
14194             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14195         }
14196         
14197         var close = this.closeTriggerEl();
14198         
14199         if(close){
14200             if(dv.length || vv * 1 > 0){
14201                 close.show() ;
14202                 this.blockFocus=true;
14203             } else {
14204                 close.hide();
14205             }             
14206         }
14207         
14208         if(this.hiddenField){
14209             this.hiddenField.dom.value = vv;
14210             
14211             this.lastSelectionText = dv;
14212             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14213             this.value = vv;
14214             return;
14215         }
14216         // no hidden field.. - we store the value in 'value', but still display
14217         // display field!!!!
14218         this.lastSelectionText = dv;
14219         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14220         this.value = vv;
14221         
14222         
14223         
14224     },
14225     // private
14226     reset : function(){
14227         // overridden so that last data is reset..
14228         
14229         if(this.multiple){
14230             this.clearItem();
14231             return;
14232         }
14233         
14234         this.setValue(this.originalValue);
14235         //this.clearInvalid();
14236         this.lastData = false;
14237         if (this.view) {
14238             this.view.clearSelections();
14239         }
14240         
14241         this.validate();
14242     },
14243     // private
14244     findRecord : function(prop, value){
14245         var record;
14246         if(this.store.getCount() > 0){
14247             this.store.each(function(r){
14248                 if(r.data[prop] == value){
14249                     record = r;
14250                     return false;
14251                 }
14252                 return true;
14253             });
14254         }
14255         return record;
14256     },
14257     
14258     getName: function()
14259     {
14260         // returns hidden if it's set..
14261         if (!this.rendered) {return ''};
14262         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14263         
14264     },
14265     // private
14266     onViewMove : function(e, t){
14267         this.inKeyMode = false;
14268     },
14269
14270     // private
14271     onViewOver : function(e, t){
14272         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14273             return;
14274         }
14275         var item = this.view.findItemFromChild(t);
14276         
14277         if(item){
14278             var index = this.view.indexOf(item);
14279             this.select(index, false);
14280         }
14281     },
14282
14283     // private
14284     onViewClick : function(view, doFocus, el, e)
14285     {
14286         var index = this.view.getSelectedIndexes()[0];
14287         
14288         var r = this.store.getAt(index);
14289         
14290         if(this.tickable){
14291             
14292             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14293                 return;
14294             }
14295             
14296             var rm = false;
14297             var _this = this;
14298             
14299             Roo.each(this.tickItems, function(v,k){
14300                 
14301                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14302                     Roo.log(v);
14303                     _this.tickItems.splice(k, 1);
14304                     
14305                     if(typeof(e) == 'undefined' && view == false){
14306                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14307                     }
14308                     
14309                     rm = true;
14310                     return;
14311                 }
14312             });
14313             
14314             if(rm){
14315                 return;
14316             }
14317             
14318             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14319                 this.tickItems.push(r.data);
14320             }
14321             
14322             if(typeof(e) == 'undefined' && view == false){
14323                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14324             }
14325                     
14326             return;
14327         }
14328         
14329         if(r){
14330             this.onSelect(r, index);
14331         }
14332         if(doFocus !== false && !this.blockFocus){
14333             this.inputEl().focus();
14334         }
14335     },
14336
14337     // private
14338     restrictHeight : function(){
14339         //this.innerList.dom.style.height = '';
14340         //var inner = this.innerList.dom;
14341         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14342         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14343         //this.list.beginUpdate();
14344         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14345         this.list.alignTo(this.inputEl(), this.listAlign);
14346         this.list.alignTo(this.inputEl(), this.listAlign);
14347         //this.list.endUpdate();
14348     },
14349
14350     // private
14351     onEmptyResults : function(){
14352         
14353         if(this.tickable && this.editable){
14354             this.hasFocus = false;
14355             this.restrictHeight();
14356             return;
14357         }
14358         
14359         this.collapse();
14360     },
14361
14362     /**
14363      * Returns true if the dropdown list is expanded, else false.
14364      */
14365     isExpanded : function(){
14366         return this.list.isVisible();
14367     },
14368
14369     /**
14370      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14371      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14372      * @param {String} value The data value of the item to select
14373      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14374      * selected item if it is not currently in view (defaults to true)
14375      * @return {Boolean} True if the value matched an item in the list, else false
14376      */
14377     selectByValue : function(v, scrollIntoView){
14378         if(v !== undefined && v !== null){
14379             var r = this.findRecord(this.valueField || this.displayField, v);
14380             if(r){
14381                 this.select(this.store.indexOf(r), scrollIntoView);
14382                 return true;
14383             }
14384         }
14385         return false;
14386     },
14387
14388     /**
14389      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14390      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14391      * @param {Number} index The zero-based index of the list item to select
14392      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14393      * selected item if it is not currently in view (defaults to true)
14394      */
14395     select : function(index, scrollIntoView){
14396         this.selectedIndex = index;
14397         this.view.select(index);
14398         if(scrollIntoView !== false){
14399             var el = this.view.getNode(index);
14400             /*
14401              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14402              */
14403             if(el){
14404                 this.list.scrollChildIntoView(el, false);
14405             }
14406         }
14407     },
14408
14409     // private
14410     selectNext : function(){
14411         var ct = this.store.getCount();
14412         if(ct > 0){
14413             if(this.selectedIndex == -1){
14414                 this.select(0);
14415             }else if(this.selectedIndex < ct-1){
14416                 this.select(this.selectedIndex+1);
14417             }
14418         }
14419     },
14420
14421     // private
14422     selectPrev : function(){
14423         var ct = this.store.getCount();
14424         if(ct > 0){
14425             if(this.selectedIndex == -1){
14426                 this.select(0);
14427             }else if(this.selectedIndex != 0){
14428                 this.select(this.selectedIndex-1);
14429             }
14430         }
14431     },
14432
14433     // private
14434     onKeyUp : function(e){
14435         if(this.editable !== false && !e.isSpecialKey()){
14436             this.lastKey = e.getKey();
14437             this.dqTask.delay(this.queryDelay);
14438         }
14439     },
14440
14441     // private
14442     validateBlur : function(){
14443         return !this.list || !this.list.isVisible();   
14444     },
14445
14446     // private
14447     initQuery : function(){
14448         
14449         var v = this.getRawValue();
14450         
14451         if(this.tickable && this.editable){
14452             v = this.tickableInputEl().getValue();
14453         }
14454         
14455         this.doQuery(v);
14456     },
14457
14458     // private
14459     doForce : function(){
14460         if(this.inputEl().dom.value.length > 0){
14461             this.inputEl().dom.value =
14462                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14463              
14464         }
14465     },
14466
14467     /**
14468      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14469      * query allowing the query action to be canceled if needed.
14470      * @param {String} query The SQL query to execute
14471      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14472      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14473      * saved in the current store (defaults to false)
14474      */
14475     doQuery : function(q, forceAll){
14476         
14477         if(q === undefined || q === null){
14478             q = '';
14479         }
14480         var qe = {
14481             query: q,
14482             forceAll: forceAll,
14483             combo: this,
14484             cancel:false
14485         };
14486         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14487             return false;
14488         }
14489         q = qe.query;
14490         
14491         forceAll = qe.forceAll;
14492         if(forceAll === true || (q.length >= this.minChars)){
14493             
14494             this.hasQuery = true;
14495             
14496             if(this.lastQuery != q || this.alwaysQuery){
14497                 this.lastQuery = q;
14498                 if(this.mode == 'local'){
14499                     this.selectedIndex = -1;
14500                     if(forceAll){
14501                         this.store.clearFilter();
14502                     }else{
14503                         
14504                         if(this.specialFilter){
14505                             this.fireEvent('specialfilter', this);
14506                             this.onLoad();
14507                             return;
14508                         }
14509                         
14510                         this.store.filter(this.displayField, q);
14511                     }
14512                     
14513                     this.store.fireEvent("datachanged", this.store);
14514                     
14515                     this.onLoad();
14516                     
14517                     
14518                 }else{
14519                     
14520                     this.store.baseParams[this.queryParam] = q;
14521                     
14522                     var options = {params : this.getParams(q)};
14523                     
14524                     if(this.loadNext){
14525                         options.add = true;
14526                         options.params.start = this.page * this.pageSize;
14527                     }
14528                     
14529                     this.store.load(options);
14530                     
14531                     /*
14532                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14533                      *  we should expand the list on onLoad
14534                      *  so command out it
14535                      */
14536 //                    this.expand();
14537                 }
14538             }else{
14539                 this.selectedIndex = -1;
14540                 this.onLoad();   
14541             }
14542         }
14543         
14544         this.loadNext = false;
14545     },
14546     
14547     // private
14548     getParams : function(q){
14549         var p = {};
14550         //p[this.queryParam] = q;
14551         
14552         if(this.pageSize){
14553             p.start = 0;
14554             p.limit = this.pageSize;
14555         }
14556         return p;
14557     },
14558
14559     /**
14560      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14561      */
14562     collapse : function(){
14563         if(!this.isExpanded()){
14564             return;
14565         }
14566         
14567         this.list.hide();
14568         
14569         this.hasFocus = false;
14570         
14571         if(this.tickable){
14572             this.okBtn.hide();
14573             this.cancelBtn.hide();
14574             this.trigger.show();
14575             
14576             if(this.editable){
14577                 this.tickableInputEl().dom.value = '';
14578                 this.tickableInputEl().blur();
14579             }
14580             
14581         }
14582         
14583         Roo.get(document).un('mousedown', this.collapseIf, this);
14584         Roo.get(document).un('mousewheel', this.collapseIf, this);
14585         if (!this.editable) {
14586             Roo.get(document).un('keydown', this.listKeyPress, this);
14587         }
14588         this.fireEvent('collapse', this);
14589         
14590         this.validate();
14591     },
14592
14593     // private
14594     collapseIf : function(e){
14595         var in_combo  = e.within(this.el);
14596         var in_list =  e.within(this.list);
14597         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14598         
14599         if (in_combo || in_list || is_list) {
14600             //e.stopPropagation();
14601             return;
14602         }
14603         
14604         if(this.tickable){
14605             this.onTickableFooterButtonClick(e, false, false);
14606         }
14607
14608         this.collapse();
14609         
14610     },
14611
14612     /**
14613      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14614      */
14615     expand : function(){
14616        
14617         if(this.isExpanded() || !this.hasFocus){
14618             return;
14619         }
14620         
14621         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14622         this.list.setWidth(lw);
14623         
14624         Roo.log('expand');
14625         
14626         this.list.show();
14627         
14628         this.restrictHeight();
14629         
14630         if(this.tickable){
14631             
14632             this.tickItems = Roo.apply([], this.item);
14633             
14634             this.okBtn.show();
14635             this.cancelBtn.show();
14636             this.trigger.hide();
14637             
14638             if(this.editable){
14639                 this.tickableInputEl().focus();
14640             }
14641             
14642         }
14643         
14644         Roo.get(document).on('mousedown', this.collapseIf, this);
14645         Roo.get(document).on('mousewheel', this.collapseIf, this);
14646         if (!this.editable) {
14647             Roo.get(document).on('keydown', this.listKeyPress, this);
14648         }
14649         
14650         this.fireEvent('expand', this);
14651     },
14652
14653     // private
14654     // Implements the default empty TriggerField.onTriggerClick function
14655     onTriggerClick : function(e)
14656     {
14657         Roo.log('trigger click');
14658         
14659         if(this.disabled || !this.triggerList){
14660             return;
14661         }
14662         
14663         this.page = 0;
14664         this.loadNext = false;
14665         
14666         if(this.isExpanded()){
14667             this.collapse();
14668             if (!this.blockFocus) {
14669                 this.inputEl().focus();
14670             }
14671             
14672         }else {
14673             this.hasFocus = true;
14674             if(this.triggerAction == 'all') {
14675                 this.doQuery(this.allQuery, true);
14676             } else {
14677                 this.doQuery(this.getRawValue());
14678             }
14679             if (!this.blockFocus) {
14680                 this.inputEl().focus();
14681             }
14682         }
14683     },
14684     
14685     onTickableTriggerClick : function(e)
14686     {
14687         if(this.disabled){
14688             return;
14689         }
14690         
14691         this.page = 0;
14692         this.loadNext = false;
14693         this.hasFocus = true;
14694         
14695         if(this.triggerAction == 'all') {
14696             this.doQuery(this.allQuery, true);
14697         } else {
14698             this.doQuery(this.getRawValue());
14699         }
14700     },
14701     
14702     onSearchFieldClick : function(e)
14703     {
14704         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14705             this.onTickableFooterButtonClick(e, false, false);
14706             return;
14707         }
14708         
14709         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14710             return;
14711         }
14712         
14713         this.page = 0;
14714         this.loadNext = false;
14715         this.hasFocus = true;
14716         
14717         if(this.triggerAction == 'all') {
14718             this.doQuery(this.allQuery, true);
14719         } else {
14720             this.doQuery(this.getRawValue());
14721         }
14722     },
14723     
14724     listKeyPress : function(e)
14725     {
14726         //Roo.log('listkeypress');
14727         // scroll to first matching element based on key pres..
14728         if (e.isSpecialKey()) {
14729             return false;
14730         }
14731         var k = String.fromCharCode(e.getKey()).toUpperCase();
14732         //Roo.log(k);
14733         var match  = false;
14734         var csel = this.view.getSelectedNodes();
14735         var cselitem = false;
14736         if (csel.length) {
14737             var ix = this.view.indexOf(csel[0]);
14738             cselitem  = this.store.getAt(ix);
14739             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14740                 cselitem = false;
14741             }
14742             
14743         }
14744         
14745         this.store.each(function(v) { 
14746             if (cselitem) {
14747                 // start at existing selection.
14748                 if (cselitem.id == v.id) {
14749                     cselitem = false;
14750                 }
14751                 return true;
14752             }
14753                 
14754             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14755                 match = this.store.indexOf(v);
14756                 return false;
14757             }
14758             return true;
14759         }, this);
14760         
14761         if (match === false) {
14762             return true; // no more action?
14763         }
14764         // scroll to?
14765         this.view.select(match);
14766         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14767         sn.scrollIntoView(sn.dom.parentNode, false);
14768     },
14769     
14770     onViewScroll : function(e, t){
14771         
14772         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){
14773             return;
14774         }
14775         
14776         this.hasQuery = true;
14777         
14778         this.loading = this.list.select('.loading', true).first();
14779         
14780         if(this.loading === null){
14781             this.list.createChild({
14782                 tag: 'div',
14783                 cls: 'loading roo-select2-more-results roo-select2-active',
14784                 html: 'Loading more results...'
14785             });
14786             
14787             this.loading = this.list.select('.loading', true).first();
14788             
14789             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14790             
14791             this.loading.hide();
14792         }
14793         
14794         this.loading.show();
14795         
14796         var _combo = this;
14797         
14798         this.page++;
14799         this.loadNext = true;
14800         
14801         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14802         
14803         return;
14804     },
14805     
14806     addItem : function(o)
14807     {   
14808         var dv = ''; // display value
14809         
14810         if (this.displayField) {
14811             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14812         } else {
14813             // this is an error condition!!!
14814             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14815         }
14816         
14817         if(!dv.length){
14818             return;
14819         }
14820         
14821         var choice = this.choices.createChild({
14822             tag: 'li',
14823             cls: 'roo-select2-search-choice',
14824             cn: [
14825                 {
14826                     tag: 'div',
14827                     html: dv
14828                 },
14829                 {
14830                     tag: 'a',
14831                     href: '#',
14832                     cls: 'roo-select2-search-choice-close fa fa-times',
14833                     tabindex: '-1'
14834                 }
14835             ]
14836             
14837         }, this.searchField);
14838         
14839         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14840         
14841         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14842         
14843         this.item.push(o);
14844         
14845         this.lastData = o;
14846         
14847         this.syncValue();
14848         
14849         this.inputEl().dom.value = '';
14850         
14851         this.validate();
14852     },
14853     
14854     onRemoveItem : function(e, _self, o)
14855     {
14856         e.preventDefault();
14857         
14858         this.lastItem = Roo.apply([], this.item);
14859         
14860         var index = this.item.indexOf(o.data) * 1;
14861         
14862         if( index < 0){
14863             Roo.log('not this item?!');
14864             return;
14865         }
14866         
14867         this.item.splice(index, 1);
14868         o.item.remove();
14869         
14870         this.syncValue();
14871         
14872         this.fireEvent('remove', this, e);
14873         
14874         this.validate();
14875         
14876     },
14877     
14878     syncValue : function()
14879     {
14880         if(!this.item.length){
14881             this.clearValue();
14882             return;
14883         }
14884             
14885         var value = [];
14886         var _this = this;
14887         Roo.each(this.item, function(i){
14888             if(_this.valueField){
14889                 value.push(i[_this.valueField]);
14890                 return;
14891             }
14892
14893             value.push(i);
14894         });
14895
14896         this.value = value.join(',');
14897
14898         if(this.hiddenField){
14899             this.hiddenField.dom.value = this.value;
14900         }
14901         
14902         this.store.fireEvent("datachanged", this.store);
14903         
14904         this.validate();
14905     },
14906     
14907     clearItem : function()
14908     {
14909         if(!this.multiple){
14910             return;
14911         }
14912         
14913         this.item = [];
14914         
14915         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14916            c.remove();
14917         });
14918         
14919         this.syncValue();
14920         
14921         this.validate();
14922         
14923         if(this.tickable && !Roo.isTouch){
14924             this.view.refresh();
14925         }
14926     },
14927     
14928     inputEl: function ()
14929     {
14930         if(Roo.isIOS && this.useNativeIOS){
14931             return this.el.select('select.roo-ios-select', true).first();
14932         }
14933         
14934         if(Roo.isTouch && this.mobileTouchView){
14935             return this.el.select('input.form-control',true).first();
14936         }
14937         
14938         if(this.tickable){
14939             return this.searchField;
14940         }
14941         
14942         return this.el.select('input.form-control',true).first();
14943     },
14944     
14945     onTickableFooterButtonClick : function(e, btn, el)
14946     {
14947         e.preventDefault();
14948         
14949         this.lastItem = Roo.apply([], this.item);
14950         
14951         if(btn && btn.name == 'cancel'){
14952             this.tickItems = Roo.apply([], this.item);
14953             this.collapse();
14954             return;
14955         }
14956         
14957         this.clearItem();
14958         
14959         var _this = this;
14960         
14961         Roo.each(this.tickItems, function(o){
14962             _this.addItem(o);
14963         });
14964         
14965         this.collapse();
14966         
14967     },
14968     
14969     validate : function()
14970     {
14971         if(this.getVisibilityEl().hasClass('hidden')){
14972             return true;
14973         }
14974         
14975         var v = this.getRawValue();
14976         
14977         if(this.multiple){
14978             v = this.getValue();
14979         }
14980         
14981         if(this.disabled || this.allowBlank || v.length){
14982             this.markValid();
14983             return true;
14984         }
14985         
14986         this.markInvalid();
14987         return false;
14988     },
14989     
14990     tickableInputEl : function()
14991     {
14992         if(!this.tickable || !this.editable){
14993             return this.inputEl();
14994         }
14995         
14996         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14997     },
14998     
14999     
15000     getAutoCreateTouchView : function()
15001     {
15002         var id = Roo.id();
15003         
15004         var cfg = {
15005             cls: 'form-group' //input-group
15006         };
15007         
15008         var input =  {
15009             tag: 'input',
15010             id : id,
15011             type : this.inputType,
15012             cls : 'form-control x-combo-noedit',
15013             autocomplete: 'new-password',
15014             placeholder : this.placeholder || '',
15015             readonly : true
15016         };
15017         
15018         if (this.name) {
15019             input.name = this.name;
15020         }
15021         
15022         if (this.size) {
15023             input.cls += ' input-' + this.size;
15024         }
15025         
15026         if (this.disabled) {
15027             input.disabled = true;
15028         }
15029         
15030         var inputblock = {
15031             cls : '',
15032             cn : [
15033                 input
15034             ]
15035         };
15036         
15037         if(this.before){
15038             inputblock.cls += ' input-group';
15039             
15040             inputblock.cn.unshift({
15041                 tag :'span',
15042                 cls : 'input-group-addon',
15043                 html : this.before
15044             });
15045         }
15046         
15047         if(this.removable && !this.multiple){
15048             inputblock.cls += ' roo-removable';
15049             
15050             inputblock.cn.push({
15051                 tag: 'button',
15052                 html : 'x',
15053                 cls : 'roo-combo-removable-btn close'
15054             });
15055         }
15056
15057         if(this.hasFeedback && !this.allowBlank){
15058             
15059             inputblock.cls += ' has-feedback';
15060             
15061             inputblock.cn.push({
15062                 tag: 'span',
15063                 cls: 'glyphicon form-control-feedback'
15064             });
15065             
15066         }
15067         
15068         if (this.after) {
15069             
15070             inputblock.cls += (this.before) ? '' : ' input-group';
15071             
15072             inputblock.cn.push({
15073                 tag :'span',
15074                 cls : 'input-group-addon',
15075                 html : this.after
15076             });
15077         }
15078
15079         var box = {
15080             tag: 'div',
15081             cn: [
15082                 {
15083                     tag: 'input',
15084                     type : 'hidden',
15085                     cls: 'form-hidden-field'
15086                 },
15087                 inputblock
15088             ]
15089             
15090         };
15091         
15092         if(this.multiple){
15093             box = {
15094                 tag: 'div',
15095                 cn: [
15096                     {
15097                         tag: 'input',
15098                         type : 'hidden',
15099                         cls: 'form-hidden-field'
15100                     },
15101                     {
15102                         tag: 'ul',
15103                         cls: 'roo-select2-choices',
15104                         cn:[
15105                             {
15106                                 tag: 'li',
15107                                 cls: 'roo-select2-search-field',
15108                                 cn: [
15109
15110                                     inputblock
15111                                 ]
15112                             }
15113                         ]
15114                     }
15115                 ]
15116             }
15117         };
15118         
15119         var combobox = {
15120             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15121             cn: [
15122                 box
15123             ]
15124         };
15125         
15126         if(!this.multiple && this.showToggleBtn){
15127             
15128             var caret = {
15129                         tag: 'span',
15130                         cls: 'caret'
15131             };
15132             
15133             if (this.caret != false) {
15134                 caret = {
15135                      tag: 'i',
15136                      cls: 'fa fa-' + this.caret
15137                 };
15138                 
15139             }
15140             
15141             combobox.cn.push({
15142                 tag :'span',
15143                 cls : 'input-group-addon btn dropdown-toggle',
15144                 cn : [
15145                     caret,
15146                     {
15147                         tag: 'span',
15148                         cls: 'combobox-clear',
15149                         cn  : [
15150                             {
15151                                 tag : 'i',
15152                                 cls: 'icon-remove'
15153                             }
15154                         ]
15155                     }
15156                 ]
15157
15158             })
15159         }
15160         
15161         if(this.multiple){
15162             combobox.cls += ' roo-select2-container-multi';
15163         }
15164         
15165         var align = this.labelAlign || this.parentLabelAlign();
15166         
15167         if (align ==='left' && this.fieldLabel.length) {
15168
15169             cfg.cn = [
15170                 {
15171                    tag : 'i',
15172                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15173                    tooltip : 'This field is required'
15174                 },
15175                 {
15176                     tag: 'label',
15177                     cls : 'control-label',
15178                     html : this.fieldLabel
15179
15180                 },
15181                 {
15182                     cls : '', 
15183                     cn: [
15184                         combobox
15185                     ]
15186                 }
15187             ];
15188             
15189             var labelCfg = cfg.cn[1];
15190             var contentCfg = cfg.cn[2];
15191             
15192
15193             if(this.indicatorpos == 'right'){
15194                 cfg.cn = [
15195                     {
15196                         tag: 'label',
15197                         'for' :  id,
15198                         cls : 'control-label',
15199                         cn : [
15200                             {
15201                                 tag : 'span',
15202                                 html : this.fieldLabel
15203                             },
15204                             {
15205                                 tag : 'i',
15206                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15207                                 tooltip : 'This field is required'
15208                             }
15209                         ]
15210                     },
15211                     {
15212                         cls : "",
15213                         cn: [
15214                             combobox
15215                         ]
15216                     }
15217
15218                 ];
15219                 
15220                 labelCfg = cfg.cn[0];
15221                 contentCfg = cfg.cn[1];
15222             }
15223             
15224            
15225             
15226             if(this.labelWidth > 12){
15227                 labelCfg.style = "width: " + this.labelWidth + 'px';
15228             }
15229             
15230             if(this.labelWidth < 13 && this.labelmd == 0){
15231                 this.labelmd = this.labelWidth;
15232             }
15233             
15234             if(this.labellg > 0){
15235                 labelCfg.cls += ' col-lg-' + this.labellg;
15236                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15237             }
15238             
15239             if(this.labelmd > 0){
15240                 labelCfg.cls += ' col-md-' + this.labelmd;
15241                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15242             }
15243             
15244             if(this.labelsm > 0){
15245                 labelCfg.cls += ' col-sm-' + this.labelsm;
15246                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15247             }
15248             
15249             if(this.labelxs > 0){
15250                 labelCfg.cls += ' col-xs-' + this.labelxs;
15251                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15252             }
15253                 
15254                 
15255         } else if ( this.fieldLabel.length) {
15256             cfg.cn = [
15257                 {
15258                    tag : 'i',
15259                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15260                    tooltip : 'This field is required'
15261                 },
15262                 {
15263                     tag: 'label',
15264                     cls : 'control-label',
15265                     html : this.fieldLabel
15266
15267                 },
15268                 {
15269                     cls : '', 
15270                     cn: [
15271                         combobox
15272                     ]
15273                 }
15274             ];
15275             
15276             if(this.indicatorpos == 'right'){
15277                 cfg.cn = [
15278                     {
15279                         tag: 'label',
15280                         cls : 'control-label',
15281                         html : this.fieldLabel,
15282                         cn : [
15283                             {
15284                                tag : 'i',
15285                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15286                                tooltip : 'This field is required'
15287                             }
15288                         ]
15289                     },
15290                     {
15291                         cls : '', 
15292                         cn: [
15293                             combobox
15294                         ]
15295                     }
15296                 ];
15297             }
15298         } else {
15299             cfg.cn = combobox;    
15300         }
15301         
15302         
15303         var settings = this;
15304         
15305         ['xs','sm','md','lg'].map(function(size){
15306             if (settings[size]) {
15307                 cfg.cls += ' col-' + size + '-' + settings[size];
15308             }
15309         });
15310         
15311         return cfg;
15312     },
15313     
15314     initTouchView : function()
15315     {
15316         this.renderTouchView();
15317         
15318         this.touchViewEl.on('scroll', function(){
15319             this.el.dom.scrollTop = 0;
15320         }, this);
15321         
15322         this.originalValue = this.getValue();
15323         
15324         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15325         
15326         this.inputEl().on("click", this.showTouchView, this);
15327         if (this.triggerEl) {
15328             this.triggerEl.on("click", this.showTouchView, this);
15329         }
15330         
15331         
15332         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15333         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15334         
15335         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15336         
15337         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15338         this.store.on('load', this.onTouchViewLoad, this);
15339         this.store.on('loadexception', this.onTouchViewLoadException, this);
15340         
15341         if(this.hiddenName){
15342             
15343             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15344             
15345             this.hiddenField.dom.value =
15346                 this.hiddenValue !== undefined ? this.hiddenValue :
15347                 this.value !== undefined ? this.value : '';
15348         
15349             this.el.dom.removeAttribute('name');
15350             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15351         }
15352         
15353         if(this.multiple){
15354             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15355             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15356         }
15357         
15358         if(this.removable && !this.multiple){
15359             var close = this.closeTriggerEl();
15360             if(close){
15361                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15362                 close.on('click', this.removeBtnClick, this, close);
15363             }
15364         }
15365         /*
15366          * fix the bug in Safari iOS8
15367          */
15368         this.inputEl().on("focus", function(e){
15369             document.activeElement.blur();
15370         }, this);
15371         
15372         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15373         
15374         return;
15375         
15376         
15377     },
15378     
15379     renderTouchView : function()
15380     {
15381         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15382         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15383         
15384         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15385         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15386         
15387         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15388         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15389         this.touchViewBodyEl.setStyle('overflow', 'auto');
15390         
15391         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15392         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15393         
15394         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15395         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15396         
15397     },
15398     
15399     showTouchView : function()
15400     {
15401         if(this.disabled){
15402             return;
15403         }
15404         
15405         this.touchViewHeaderEl.hide();
15406
15407         if(this.modalTitle.length){
15408             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15409             this.touchViewHeaderEl.show();
15410         }
15411
15412         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15413         this.touchViewEl.show();
15414
15415         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15416         
15417         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15418         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15419
15420         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15421
15422         if(this.modalTitle.length){
15423             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15424         }
15425         
15426         this.touchViewBodyEl.setHeight(bodyHeight);
15427
15428         if(this.animate){
15429             var _this = this;
15430             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15431         }else{
15432             this.touchViewEl.addClass('in');
15433         }
15434         
15435         if(this._touchViewMask){
15436             Roo.get(document.body).addClass("x-body-masked");
15437             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15438             this._touchViewMask.setStyle('z-index', 10000);
15439             this._touchViewMask.addClass('show');
15440         }
15441         
15442         this.doTouchViewQuery();
15443         
15444     },
15445     
15446     hideTouchView : function()
15447     {
15448         this.touchViewEl.removeClass('in');
15449
15450         if(this.animate){
15451             var _this = this;
15452             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15453         }else{
15454             this.touchViewEl.setStyle('display', 'none');
15455         }
15456         
15457         if(this._touchViewMask){
15458             this._touchViewMask.removeClass('show');
15459             Roo.get(document.body).removeClass("x-body-masked");
15460         }
15461     },
15462     
15463     setTouchViewValue : function()
15464     {
15465         if(this.multiple){
15466             this.clearItem();
15467         
15468             var _this = this;
15469
15470             Roo.each(this.tickItems, function(o){
15471                 this.addItem(o);
15472             }, this);
15473         }
15474         
15475         this.hideTouchView();
15476     },
15477     
15478     doTouchViewQuery : function()
15479     {
15480         var qe = {
15481             query: '',
15482             forceAll: true,
15483             combo: this,
15484             cancel:false
15485         };
15486         
15487         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15488             return false;
15489         }
15490         
15491         if(!this.alwaysQuery || this.mode == 'local'){
15492             this.onTouchViewLoad();
15493             return;
15494         }
15495         
15496         this.store.load();
15497     },
15498     
15499     onTouchViewBeforeLoad : function(combo,opts)
15500     {
15501         return;
15502     },
15503
15504     // private
15505     onTouchViewLoad : function()
15506     {
15507         if(this.store.getCount() < 1){
15508             this.onTouchViewEmptyResults();
15509             return;
15510         }
15511         
15512         this.clearTouchView();
15513         
15514         var rawValue = this.getRawValue();
15515         
15516         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15517         
15518         this.tickItems = [];
15519         
15520         this.store.data.each(function(d, rowIndex){
15521             var row = this.touchViewListGroup.createChild(template);
15522             
15523             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15524                 row.addClass(d.data.cls);
15525             }
15526             
15527             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15528                 var cfg = {
15529                     data : d.data,
15530                     html : d.data[this.displayField]
15531                 };
15532                 
15533                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15534                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15535                 }
15536             }
15537             row.removeClass('selected');
15538             if(!this.multiple && this.valueField &&
15539                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15540             {
15541                 // radio buttons..
15542                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15543                 row.addClass('selected');
15544             }
15545             
15546             if(this.multiple && this.valueField &&
15547                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15548             {
15549                 
15550                 // checkboxes...
15551                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15552                 this.tickItems.push(d.data);
15553             }
15554             
15555             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15556             
15557         }, this);
15558         
15559         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15560         
15561         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15562
15563         if(this.modalTitle.length){
15564             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15565         }
15566
15567         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15568         
15569         if(this.mobile_restrict_height && listHeight < bodyHeight){
15570             this.touchViewBodyEl.setHeight(listHeight);
15571         }
15572         
15573         var _this = this;
15574         
15575         if(firstChecked && listHeight > bodyHeight){
15576             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15577         }
15578         
15579     },
15580     
15581     onTouchViewLoadException : function()
15582     {
15583         this.hideTouchView();
15584     },
15585     
15586     onTouchViewEmptyResults : function()
15587     {
15588         this.clearTouchView();
15589         
15590         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15591         
15592         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15593         
15594     },
15595     
15596     clearTouchView : function()
15597     {
15598         this.touchViewListGroup.dom.innerHTML = '';
15599     },
15600     
15601     onTouchViewClick : function(e, el, o)
15602     {
15603         e.preventDefault();
15604         
15605         var row = o.row;
15606         var rowIndex = o.rowIndex;
15607         
15608         var r = this.store.getAt(rowIndex);
15609         
15610         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15611             
15612             if(!this.multiple){
15613                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15614                     c.dom.removeAttribute('checked');
15615                 }, this);
15616
15617                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15618
15619                 this.setFromData(r.data);
15620
15621                 var close = this.closeTriggerEl();
15622
15623                 if(close){
15624                     close.show();
15625                 }
15626
15627                 this.hideTouchView();
15628
15629                 this.fireEvent('select', this, r, rowIndex);
15630
15631                 return;
15632             }
15633
15634             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15635                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15636                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15637                 return;
15638             }
15639
15640             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15641             this.addItem(r.data);
15642             this.tickItems.push(r.data);
15643         }
15644     },
15645     
15646     getAutoCreateNativeIOS : function()
15647     {
15648         var cfg = {
15649             cls: 'form-group' //input-group,
15650         };
15651         
15652         var combobox =  {
15653             tag: 'select',
15654             cls : 'roo-ios-select'
15655         };
15656         
15657         if (this.name) {
15658             combobox.name = this.name;
15659         }
15660         
15661         if (this.disabled) {
15662             combobox.disabled = true;
15663         }
15664         
15665         var settings = this;
15666         
15667         ['xs','sm','md','lg'].map(function(size){
15668             if (settings[size]) {
15669                 cfg.cls += ' col-' + size + '-' + settings[size];
15670             }
15671         });
15672         
15673         cfg.cn = combobox;
15674         
15675         return cfg;
15676         
15677     },
15678     
15679     initIOSView : function()
15680     {
15681         this.store.on('load', this.onIOSViewLoad, this);
15682         
15683         return;
15684     },
15685     
15686     onIOSViewLoad : function()
15687     {
15688         if(this.store.getCount() < 1){
15689             return;
15690         }
15691         
15692         this.clearIOSView();
15693         
15694         if(this.allowBlank) {
15695             
15696             var default_text = '-- SELECT --';
15697             
15698             if(this.placeholder.length){
15699                 default_text = this.placeholder;
15700             }
15701             
15702             if(this.emptyTitle.length){
15703                 default_text += ' - ' + this.emptyTitle + ' -';
15704             }
15705             
15706             var opt = this.inputEl().createChild({
15707                 tag: 'option',
15708                 value : 0,
15709                 html : default_text
15710             });
15711             
15712             var o = {};
15713             o[this.valueField] = 0;
15714             o[this.displayField] = default_text;
15715             
15716             this.ios_options.push({
15717                 data : o,
15718                 el : opt
15719             });
15720             
15721         }
15722         
15723         this.store.data.each(function(d, rowIndex){
15724             
15725             var html = '';
15726             
15727             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15728                 html = d.data[this.displayField];
15729             }
15730             
15731             var value = '';
15732             
15733             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15734                 value = d.data[this.valueField];
15735             }
15736             
15737             var option = {
15738                 tag: 'option',
15739                 value : value,
15740                 html : html
15741             };
15742             
15743             if(this.value == d.data[this.valueField]){
15744                 option['selected'] = true;
15745             }
15746             
15747             var opt = this.inputEl().createChild(option);
15748             
15749             this.ios_options.push({
15750                 data : d.data,
15751                 el : opt
15752             });
15753             
15754         }, this);
15755         
15756         this.inputEl().on('change', function(){
15757            this.fireEvent('select', this);
15758         }, this);
15759         
15760     },
15761     
15762     clearIOSView: function()
15763     {
15764         this.inputEl().dom.innerHTML = '';
15765         
15766         this.ios_options = [];
15767     },
15768     
15769     setIOSValue: function(v)
15770     {
15771         this.value = v;
15772         
15773         if(!this.ios_options){
15774             return;
15775         }
15776         
15777         Roo.each(this.ios_options, function(opts){
15778            
15779            opts.el.dom.removeAttribute('selected');
15780            
15781            if(opts.data[this.valueField] != v){
15782                return;
15783            }
15784            
15785            opts.el.dom.setAttribute('selected', true);
15786            
15787         }, this);
15788     }
15789
15790     /** 
15791     * @cfg {Boolean} grow 
15792     * @hide 
15793     */
15794     /** 
15795     * @cfg {Number} growMin 
15796     * @hide 
15797     */
15798     /** 
15799     * @cfg {Number} growMax 
15800     * @hide 
15801     */
15802     /**
15803      * @hide
15804      * @method autoSize
15805      */
15806 });
15807
15808 Roo.apply(Roo.bootstrap.ComboBox,  {
15809     
15810     header : {
15811         tag: 'div',
15812         cls: 'modal-header',
15813         cn: [
15814             {
15815                 tag: 'h4',
15816                 cls: 'modal-title'
15817             }
15818         ]
15819     },
15820     
15821     body : {
15822         tag: 'div',
15823         cls: 'modal-body',
15824         cn: [
15825             {
15826                 tag: 'ul',
15827                 cls: 'list-group'
15828             }
15829         ]
15830     },
15831     
15832     listItemRadio : {
15833         tag: 'li',
15834         cls: 'list-group-item',
15835         cn: [
15836             {
15837                 tag: 'span',
15838                 cls: 'roo-combobox-list-group-item-value'
15839             },
15840             {
15841                 tag: 'div',
15842                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15843                 cn: [
15844                     {
15845                         tag: 'input',
15846                         type: 'radio'
15847                     },
15848                     {
15849                         tag: 'label'
15850                     }
15851                 ]
15852             }
15853         ]
15854     },
15855     
15856     listItemCheckbox : {
15857         tag: 'li',
15858         cls: 'list-group-item',
15859         cn: [
15860             {
15861                 tag: 'span',
15862                 cls: 'roo-combobox-list-group-item-value'
15863             },
15864             {
15865                 tag: 'div',
15866                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15867                 cn: [
15868                     {
15869                         tag: 'input',
15870                         type: 'checkbox'
15871                     },
15872                     {
15873                         tag: 'label'
15874                     }
15875                 ]
15876             }
15877         ]
15878     },
15879     
15880     emptyResult : {
15881         tag: 'div',
15882         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15883     },
15884     
15885     footer : {
15886         tag: 'div',
15887         cls: 'modal-footer',
15888         cn: [
15889             {
15890                 tag: 'div',
15891                 cls: 'row',
15892                 cn: [
15893                     {
15894                         tag: 'div',
15895                         cls: 'col-xs-6 text-left',
15896                         cn: {
15897                             tag: 'button',
15898                             cls: 'btn btn-danger roo-touch-view-cancel',
15899                             html: 'Cancel'
15900                         }
15901                     },
15902                     {
15903                         tag: 'div',
15904                         cls: 'col-xs-6 text-right',
15905                         cn: {
15906                             tag: 'button',
15907                             cls: 'btn btn-success roo-touch-view-ok',
15908                             html: 'OK'
15909                         }
15910                     }
15911                 ]
15912             }
15913         ]
15914         
15915     }
15916 });
15917
15918 Roo.apply(Roo.bootstrap.ComboBox,  {
15919     
15920     touchViewTemplate : {
15921         tag: 'div',
15922         cls: 'modal fade roo-combobox-touch-view',
15923         cn: [
15924             {
15925                 tag: 'div',
15926                 cls: 'modal-dialog',
15927                 style : 'position:fixed', // we have to fix position....
15928                 cn: [
15929                     {
15930                         tag: 'div',
15931                         cls: 'modal-content',
15932                         cn: [
15933                             Roo.bootstrap.ComboBox.header,
15934                             Roo.bootstrap.ComboBox.body,
15935                             Roo.bootstrap.ComboBox.footer
15936                         ]
15937                     }
15938                 ]
15939             }
15940         ]
15941     }
15942 });/*
15943  * Based on:
15944  * Ext JS Library 1.1.1
15945  * Copyright(c) 2006-2007, Ext JS, LLC.
15946  *
15947  * Originally Released Under LGPL - original licence link has changed is not relivant.
15948  *
15949  * Fork - LGPL
15950  * <script type="text/javascript">
15951  */
15952
15953 /**
15954  * @class Roo.View
15955  * @extends Roo.util.Observable
15956  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15957  * This class also supports single and multi selection modes. <br>
15958  * Create a data model bound view:
15959  <pre><code>
15960  var store = new Roo.data.Store(...);
15961
15962  var view = new Roo.View({
15963     el : "my-element",
15964     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15965  
15966     singleSelect: true,
15967     selectedClass: "ydataview-selected",
15968     store: store
15969  });
15970
15971  // listen for node click?
15972  view.on("click", function(vw, index, node, e){
15973  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15974  });
15975
15976  // load XML data
15977  dataModel.load("foobar.xml");
15978  </code></pre>
15979  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15980  * <br><br>
15981  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15982  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15983  * 
15984  * Note: old style constructor is still suported (container, template, config)
15985  * 
15986  * @constructor
15987  * Create a new View
15988  * @param {Object} config The config object
15989  * 
15990  */
15991 Roo.View = function(config, depreciated_tpl, depreciated_config){
15992     
15993     this.parent = false;
15994     
15995     if (typeof(depreciated_tpl) == 'undefined') {
15996         // new way.. - universal constructor.
15997         Roo.apply(this, config);
15998         this.el  = Roo.get(this.el);
15999     } else {
16000         // old format..
16001         this.el  = Roo.get(config);
16002         this.tpl = depreciated_tpl;
16003         Roo.apply(this, depreciated_config);
16004     }
16005     this.wrapEl  = this.el.wrap().wrap();
16006     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16007     
16008     
16009     if(typeof(this.tpl) == "string"){
16010         this.tpl = new Roo.Template(this.tpl);
16011     } else {
16012         // support xtype ctors..
16013         this.tpl = new Roo.factory(this.tpl, Roo);
16014     }
16015     
16016     
16017     this.tpl.compile();
16018     
16019     /** @private */
16020     this.addEvents({
16021         /**
16022          * @event beforeclick
16023          * Fires before a click is processed. Returns false to cancel the default action.
16024          * @param {Roo.View} this
16025          * @param {Number} index The index of the target node
16026          * @param {HTMLElement} node The target node
16027          * @param {Roo.EventObject} e The raw event object
16028          */
16029             "beforeclick" : true,
16030         /**
16031          * @event click
16032          * Fires when a template node is clicked.
16033          * @param {Roo.View} this
16034          * @param {Number} index The index of the target node
16035          * @param {HTMLElement} node The target node
16036          * @param {Roo.EventObject} e The raw event object
16037          */
16038             "click" : true,
16039         /**
16040          * @event dblclick
16041          * Fires when a template node is double clicked.
16042          * @param {Roo.View} this
16043          * @param {Number} index The index of the target node
16044          * @param {HTMLElement} node The target node
16045          * @param {Roo.EventObject} e The raw event object
16046          */
16047             "dblclick" : true,
16048         /**
16049          * @event contextmenu
16050          * Fires when a template node is right clicked.
16051          * @param {Roo.View} this
16052          * @param {Number} index The index of the target node
16053          * @param {HTMLElement} node The target node
16054          * @param {Roo.EventObject} e The raw event object
16055          */
16056             "contextmenu" : true,
16057         /**
16058          * @event selectionchange
16059          * Fires when the selected nodes change.
16060          * @param {Roo.View} this
16061          * @param {Array} selections Array of the selected nodes
16062          */
16063             "selectionchange" : true,
16064     
16065         /**
16066          * @event beforeselect
16067          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16068          * @param {Roo.View} this
16069          * @param {HTMLElement} node The node to be selected
16070          * @param {Array} selections Array of currently selected nodes
16071          */
16072             "beforeselect" : true,
16073         /**
16074          * @event preparedata
16075          * Fires on every row to render, to allow you to change the data.
16076          * @param {Roo.View} this
16077          * @param {Object} data to be rendered (change this)
16078          */
16079           "preparedata" : true
16080           
16081           
16082         });
16083
16084
16085
16086     this.el.on({
16087         "click": this.onClick,
16088         "dblclick": this.onDblClick,
16089         "contextmenu": this.onContextMenu,
16090         scope:this
16091     });
16092
16093     this.selections = [];
16094     this.nodes = [];
16095     this.cmp = new Roo.CompositeElementLite([]);
16096     if(this.store){
16097         this.store = Roo.factory(this.store, Roo.data);
16098         this.setStore(this.store, true);
16099     }
16100     
16101     if ( this.footer && this.footer.xtype) {
16102            
16103          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16104         
16105         this.footer.dataSource = this.store;
16106         this.footer.container = fctr;
16107         this.footer = Roo.factory(this.footer, Roo);
16108         fctr.insertFirst(this.el);
16109         
16110         // this is a bit insane - as the paging toolbar seems to detach the el..
16111 //        dom.parentNode.parentNode.parentNode
16112          // they get detached?
16113     }
16114     
16115     
16116     Roo.View.superclass.constructor.call(this);
16117     
16118     
16119 };
16120
16121 Roo.extend(Roo.View, Roo.util.Observable, {
16122     
16123      /**
16124      * @cfg {Roo.data.Store} store Data store to load data from.
16125      */
16126     store : false,
16127     
16128     /**
16129      * @cfg {String|Roo.Element} el The container element.
16130      */
16131     el : '',
16132     
16133     /**
16134      * @cfg {String|Roo.Template} tpl The template used by this View 
16135      */
16136     tpl : false,
16137     /**
16138      * @cfg {String} dataName the named area of the template to use as the data area
16139      *                          Works with domtemplates roo-name="name"
16140      */
16141     dataName: false,
16142     /**
16143      * @cfg {String} selectedClass The css class to add to selected nodes
16144      */
16145     selectedClass : "x-view-selected",
16146      /**
16147      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16148      */
16149     emptyText : "",
16150     
16151     /**
16152      * @cfg {String} text to display on mask (default Loading)
16153      */
16154     mask : false,
16155     /**
16156      * @cfg {Boolean} multiSelect Allow multiple selection
16157      */
16158     multiSelect : false,
16159     /**
16160      * @cfg {Boolean} singleSelect Allow single selection
16161      */
16162     singleSelect:  false,
16163     
16164     /**
16165      * @cfg {Boolean} toggleSelect - selecting 
16166      */
16167     toggleSelect : false,
16168     
16169     /**
16170      * @cfg {Boolean} tickable - selecting 
16171      */
16172     tickable : false,
16173     
16174     /**
16175      * Returns the element this view is bound to.
16176      * @return {Roo.Element}
16177      */
16178     getEl : function(){
16179         return this.wrapEl;
16180     },
16181     
16182     
16183
16184     /**
16185      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16186      */
16187     refresh : function(){
16188         //Roo.log('refresh');
16189         var t = this.tpl;
16190         
16191         // if we are using something like 'domtemplate', then
16192         // the what gets used is:
16193         // t.applySubtemplate(NAME, data, wrapping data..)
16194         // the outer template then get' applied with
16195         //     the store 'extra data'
16196         // and the body get's added to the
16197         //      roo-name="data" node?
16198         //      <span class='roo-tpl-{name}'></span> ?????
16199         
16200         
16201         
16202         this.clearSelections();
16203         this.el.update("");
16204         var html = [];
16205         var records = this.store.getRange();
16206         if(records.length < 1) {
16207             
16208             // is this valid??  = should it render a template??
16209             
16210             this.el.update(this.emptyText);
16211             return;
16212         }
16213         var el = this.el;
16214         if (this.dataName) {
16215             this.el.update(t.apply(this.store.meta)); //????
16216             el = this.el.child('.roo-tpl-' + this.dataName);
16217         }
16218         
16219         for(var i = 0, len = records.length; i < len; i++){
16220             var data = this.prepareData(records[i].data, i, records[i]);
16221             this.fireEvent("preparedata", this, data, i, records[i]);
16222             
16223             var d = Roo.apply({}, data);
16224             
16225             if(this.tickable){
16226                 Roo.apply(d, {'roo-id' : Roo.id()});
16227                 
16228                 var _this = this;
16229             
16230                 Roo.each(this.parent.item, function(item){
16231                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16232                         return;
16233                     }
16234                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16235                 });
16236             }
16237             
16238             html[html.length] = Roo.util.Format.trim(
16239                 this.dataName ?
16240                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16241                     t.apply(d)
16242             );
16243         }
16244         
16245         
16246         
16247         el.update(html.join(""));
16248         this.nodes = el.dom.childNodes;
16249         this.updateIndexes(0);
16250     },
16251     
16252
16253     /**
16254      * Function to override to reformat the data that is sent to
16255      * the template for each node.
16256      * DEPRICATED - use the preparedata event handler.
16257      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16258      * a JSON object for an UpdateManager bound view).
16259      */
16260     prepareData : function(data, index, record)
16261     {
16262         this.fireEvent("preparedata", this, data, index, record);
16263         return data;
16264     },
16265
16266     onUpdate : function(ds, record){
16267         // Roo.log('on update');   
16268         this.clearSelections();
16269         var index = this.store.indexOf(record);
16270         var n = this.nodes[index];
16271         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16272         n.parentNode.removeChild(n);
16273         this.updateIndexes(index, index);
16274     },
16275
16276     
16277     
16278 // --------- FIXME     
16279     onAdd : function(ds, records, index)
16280     {
16281         //Roo.log(['on Add', ds, records, index] );        
16282         this.clearSelections();
16283         if(this.nodes.length == 0){
16284             this.refresh();
16285             return;
16286         }
16287         var n = this.nodes[index];
16288         for(var i = 0, len = records.length; i < len; i++){
16289             var d = this.prepareData(records[i].data, i, records[i]);
16290             if(n){
16291                 this.tpl.insertBefore(n, d);
16292             }else{
16293                 
16294                 this.tpl.append(this.el, d);
16295             }
16296         }
16297         this.updateIndexes(index);
16298     },
16299
16300     onRemove : function(ds, record, index){
16301        // Roo.log('onRemove');
16302         this.clearSelections();
16303         var el = this.dataName  ?
16304             this.el.child('.roo-tpl-' + this.dataName) :
16305             this.el; 
16306         
16307         el.dom.removeChild(this.nodes[index]);
16308         this.updateIndexes(index);
16309     },
16310
16311     /**
16312      * Refresh an individual node.
16313      * @param {Number} index
16314      */
16315     refreshNode : function(index){
16316         this.onUpdate(this.store, this.store.getAt(index));
16317     },
16318
16319     updateIndexes : function(startIndex, endIndex){
16320         var ns = this.nodes;
16321         startIndex = startIndex || 0;
16322         endIndex = endIndex || ns.length - 1;
16323         for(var i = startIndex; i <= endIndex; i++){
16324             ns[i].nodeIndex = i;
16325         }
16326     },
16327
16328     /**
16329      * Changes the data store this view uses and refresh the view.
16330      * @param {Store} store
16331      */
16332     setStore : function(store, initial){
16333         if(!initial && this.store){
16334             this.store.un("datachanged", this.refresh);
16335             this.store.un("add", this.onAdd);
16336             this.store.un("remove", this.onRemove);
16337             this.store.un("update", this.onUpdate);
16338             this.store.un("clear", this.refresh);
16339             this.store.un("beforeload", this.onBeforeLoad);
16340             this.store.un("load", this.onLoad);
16341             this.store.un("loadexception", this.onLoad);
16342         }
16343         if(store){
16344           
16345             store.on("datachanged", this.refresh, this);
16346             store.on("add", this.onAdd, this);
16347             store.on("remove", this.onRemove, this);
16348             store.on("update", this.onUpdate, this);
16349             store.on("clear", this.refresh, this);
16350             store.on("beforeload", this.onBeforeLoad, this);
16351             store.on("load", this.onLoad, this);
16352             store.on("loadexception", this.onLoad, this);
16353         }
16354         
16355         if(store){
16356             this.refresh();
16357         }
16358     },
16359     /**
16360      * onbeforeLoad - masks the loading area.
16361      *
16362      */
16363     onBeforeLoad : function(store,opts)
16364     {
16365          //Roo.log('onBeforeLoad');   
16366         if (!opts.add) {
16367             this.el.update("");
16368         }
16369         this.el.mask(this.mask ? this.mask : "Loading" ); 
16370     },
16371     onLoad : function ()
16372     {
16373         this.el.unmask();
16374     },
16375     
16376
16377     /**
16378      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16379      * @param {HTMLElement} node
16380      * @return {HTMLElement} The template node
16381      */
16382     findItemFromChild : function(node){
16383         var el = this.dataName  ?
16384             this.el.child('.roo-tpl-' + this.dataName,true) :
16385             this.el.dom; 
16386         
16387         if(!node || node.parentNode == el){
16388                     return node;
16389             }
16390             var p = node.parentNode;
16391             while(p && p != el){
16392             if(p.parentNode == el){
16393                 return p;
16394             }
16395             p = p.parentNode;
16396         }
16397             return null;
16398     },
16399
16400     /** @ignore */
16401     onClick : function(e){
16402         var item = this.findItemFromChild(e.getTarget());
16403         if(item){
16404             var index = this.indexOf(item);
16405             if(this.onItemClick(item, index, e) !== false){
16406                 this.fireEvent("click", this, index, item, e);
16407             }
16408         }else{
16409             this.clearSelections();
16410         }
16411     },
16412
16413     /** @ignore */
16414     onContextMenu : function(e){
16415         var item = this.findItemFromChild(e.getTarget());
16416         if(item){
16417             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16418         }
16419     },
16420
16421     /** @ignore */
16422     onDblClick : function(e){
16423         var item = this.findItemFromChild(e.getTarget());
16424         if(item){
16425             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16426         }
16427     },
16428
16429     onItemClick : function(item, index, e)
16430     {
16431         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16432             return false;
16433         }
16434         if (this.toggleSelect) {
16435             var m = this.isSelected(item) ? 'unselect' : 'select';
16436             //Roo.log(m);
16437             var _t = this;
16438             _t[m](item, true, false);
16439             return true;
16440         }
16441         if(this.multiSelect || this.singleSelect){
16442             if(this.multiSelect && e.shiftKey && this.lastSelection){
16443                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16444             }else{
16445                 this.select(item, this.multiSelect && e.ctrlKey);
16446                 this.lastSelection = item;
16447             }
16448             
16449             if(!this.tickable){
16450                 e.preventDefault();
16451             }
16452             
16453         }
16454         return true;
16455     },
16456
16457     /**
16458      * Get the number of selected nodes.
16459      * @return {Number}
16460      */
16461     getSelectionCount : function(){
16462         return this.selections.length;
16463     },
16464
16465     /**
16466      * Get the currently selected nodes.
16467      * @return {Array} An array of HTMLElements
16468      */
16469     getSelectedNodes : function(){
16470         return this.selections;
16471     },
16472
16473     /**
16474      * Get the indexes of the selected nodes.
16475      * @return {Array}
16476      */
16477     getSelectedIndexes : function(){
16478         var indexes = [], s = this.selections;
16479         for(var i = 0, len = s.length; i < len; i++){
16480             indexes.push(s[i].nodeIndex);
16481         }
16482         return indexes;
16483     },
16484
16485     /**
16486      * Clear all selections
16487      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16488      */
16489     clearSelections : function(suppressEvent){
16490         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16491             this.cmp.elements = this.selections;
16492             this.cmp.removeClass(this.selectedClass);
16493             this.selections = [];
16494             if(!suppressEvent){
16495                 this.fireEvent("selectionchange", this, this.selections);
16496             }
16497         }
16498     },
16499
16500     /**
16501      * Returns true if the passed node is selected
16502      * @param {HTMLElement/Number} node The node or node index
16503      * @return {Boolean}
16504      */
16505     isSelected : function(node){
16506         var s = this.selections;
16507         if(s.length < 1){
16508             return false;
16509         }
16510         node = this.getNode(node);
16511         return s.indexOf(node) !== -1;
16512     },
16513
16514     /**
16515      * Selects nodes.
16516      * @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
16517      * @param {Boolean} keepExisting (optional) true to keep existing selections
16518      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16519      */
16520     select : function(nodeInfo, keepExisting, suppressEvent){
16521         if(nodeInfo instanceof Array){
16522             if(!keepExisting){
16523                 this.clearSelections(true);
16524             }
16525             for(var i = 0, len = nodeInfo.length; i < len; i++){
16526                 this.select(nodeInfo[i], true, true);
16527             }
16528             return;
16529         } 
16530         var node = this.getNode(nodeInfo);
16531         if(!node || this.isSelected(node)){
16532             return; // already selected.
16533         }
16534         if(!keepExisting){
16535             this.clearSelections(true);
16536         }
16537         
16538         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16539             Roo.fly(node).addClass(this.selectedClass);
16540             this.selections.push(node);
16541             if(!suppressEvent){
16542                 this.fireEvent("selectionchange", this, this.selections);
16543             }
16544         }
16545         
16546         
16547     },
16548       /**
16549      * Unselects nodes.
16550      * @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
16551      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16553      */
16554     unselect : function(nodeInfo, keepExisting, suppressEvent)
16555     {
16556         if(nodeInfo instanceof Array){
16557             Roo.each(this.selections, function(s) {
16558                 this.unselect(s, nodeInfo);
16559             }, this);
16560             return;
16561         }
16562         var node = this.getNode(nodeInfo);
16563         if(!node || !this.isSelected(node)){
16564             //Roo.log("not selected");
16565             return; // not selected.
16566         }
16567         // fireevent???
16568         var ns = [];
16569         Roo.each(this.selections, function(s) {
16570             if (s == node ) {
16571                 Roo.fly(node).removeClass(this.selectedClass);
16572
16573                 return;
16574             }
16575             ns.push(s);
16576         },this);
16577         
16578         this.selections= ns;
16579         this.fireEvent("selectionchange", this, this.selections);
16580     },
16581
16582     /**
16583      * Gets a template node.
16584      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16585      * @return {HTMLElement} The node or null if it wasn't found
16586      */
16587     getNode : function(nodeInfo){
16588         if(typeof nodeInfo == "string"){
16589             return document.getElementById(nodeInfo);
16590         }else if(typeof nodeInfo == "number"){
16591             return this.nodes[nodeInfo];
16592         }
16593         return nodeInfo;
16594     },
16595
16596     /**
16597      * Gets a range template nodes.
16598      * @param {Number} startIndex
16599      * @param {Number} endIndex
16600      * @return {Array} An array of nodes
16601      */
16602     getNodes : function(start, end){
16603         var ns = this.nodes;
16604         start = start || 0;
16605         end = typeof end == "undefined" ? ns.length - 1 : end;
16606         var nodes = [];
16607         if(start <= end){
16608             for(var i = start; i <= end; i++){
16609                 nodes.push(ns[i]);
16610             }
16611         } else{
16612             for(var i = start; i >= end; i--){
16613                 nodes.push(ns[i]);
16614             }
16615         }
16616         return nodes;
16617     },
16618
16619     /**
16620      * Finds the index of the passed node
16621      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16622      * @return {Number} The index of the node or -1
16623      */
16624     indexOf : function(node){
16625         node = this.getNode(node);
16626         if(typeof node.nodeIndex == "number"){
16627             return node.nodeIndex;
16628         }
16629         var ns = this.nodes;
16630         for(var i = 0, len = ns.length; i < len; i++){
16631             if(ns[i] == node){
16632                 return i;
16633             }
16634         }
16635         return -1;
16636     }
16637 });
16638 /*
16639  * - LGPL
16640  *
16641  * based on jquery fullcalendar
16642  * 
16643  */
16644
16645 Roo.bootstrap = Roo.bootstrap || {};
16646 /**
16647  * @class Roo.bootstrap.Calendar
16648  * @extends Roo.bootstrap.Component
16649  * Bootstrap Calendar class
16650  * @cfg {Boolean} loadMask (true|false) default false
16651  * @cfg {Object} header generate the user specific header of the calendar, default false
16652
16653  * @constructor
16654  * Create a new Container
16655  * @param {Object} config The config object
16656  */
16657
16658
16659
16660 Roo.bootstrap.Calendar = function(config){
16661     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16662      this.addEvents({
16663         /**
16664              * @event select
16665              * Fires when a date is selected
16666              * @param {DatePicker} this
16667              * @param {Date} date The selected date
16668              */
16669         'select': true,
16670         /**
16671              * @event monthchange
16672              * Fires when the displayed month changes 
16673              * @param {DatePicker} this
16674              * @param {Date} date The selected month
16675              */
16676         'monthchange': true,
16677         /**
16678              * @event evententer
16679              * Fires when mouse over an event
16680              * @param {Calendar} this
16681              * @param {event} Event
16682              */
16683         'evententer': true,
16684         /**
16685              * @event eventleave
16686              * Fires when the mouse leaves an
16687              * @param {Calendar} this
16688              * @param {event}
16689              */
16690         'eventleave': true,
16691         /**
16692              * @event eventclick
16693              * Fires when the mouse click an
16694              * @param {Calendar} this
16695              * @param {event}
16696              */
16697         'eventclick': true
16698         
16699     });
16700
16701 };
16702
16703 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16704     
16705      /**
16706      * @cfg {Number} startDay
16707      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16708      */
16709     startDay : 0,
16710     
16711     loadMask : false,
16712     
16713     header : false,
16714       
16715     getAutoCreate : function(){
16716         
16717         
16718         var fc_button = function(name, corner, style, content ) {
16719             return Roo.apply({},{
16720                 tag : 'span',
16721                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16722                          (corner.length ?
16723                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16724                             ''
16725                         ),
16726                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16727                 unselectable: 'on'
16728             });
16729         };
16730         
16731         var header = {};
16732         
16733         if(!this.header){
16734             header = {
16735                 tag : 'table',
16736                 cls : 'fc-header',
16737                 style : 'width:100%',
16738                 cn : [
16739                     {
16740                         tag: 'tr',
16741                         cn : [
16742                             {
16743                                 tag : 'td',
16744                                 cls : 'fc-header-left',
16745                                 cn : [
16746                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16747                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16748                                     { tag: 'span', cls: 'fc-header-space' },
16749                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16750
16751
16752                                 ]
16753                             },
16754
16755                             {
16756                                 tag : 'td',
16757                                 cls : 'fc-header-center',
16758                                 cn : [
16759                                     {
16760                                         tag: 'span',
16761                                         cls: 'fc-header-title',
16762                                         cn : {
16763                                             tag: 'H2',
16764                                             html : 'month / year'
16765                                         }
16766                                     }
16767
16768                                 ]
16769                             },
16770                             {
16771                                 tag : 'td',
16772                                 cls : 'fc-header-right',
16773                                 cn : [
16774                               /*      fc_button('month', 'left', '', 'month' ),
16775                                     fc_button('week', '', '', 'week' ),
16776                                     fc_button('day', 'right', '', 'day' )
16777                                 */    
16778
16779                                 ]
16780                             }
16781
16782                         ]
16783                     }
16784                 ]
16785             };
16786         }
16787         
16788         header = this.header;
16789         
16790        
16791         var cal_heads = function() {
16792             var ret = [];
16793             // fixme - handle this.
16794             
16795             for (var i =0; i < Date.dayNames.length; i++) {
16796                 var d = Date.dayNames[i];
16797                 ret.push({
16798                     tag: 'th',
16799                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16800                     html : d.substring(0,3)
16801                 });
16802                 
16803             }
16804             ret[0].cls += ' fc-first';
16805             ret[6].cls += ' fc-last';
16806             return ret;
16807         };
16808         var cal_cell = function(n) {
16809             return  {
16810                 tag: 'td',
16811                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16812                 cn : [
16813                     {
16814                         cn : [
16815                             {
16816                                 cls: 'fc-day-number',
16817                                 html: 'D'
16818                             },
16819                             {
16820                                 cls: 'fc-day-content',
16821                              
16822                                 cn : [
16823                                      {
16824                                         style: 'position: relative;' // height: 17px;
16825                                     }
16826                                 ]
16827                             }
16828                             
16829                             
16830                         ]
16831                     }
16832                 ]
16833                 
16834             }
16835         };
16836         var cal_rows = function() {
16837             
16838             var ret = [];
16839             for (var r = 0; r < 6; r++) {
16840                 var row= {
16841                     tag : 'tr',
16842                     cls : 'fc-week',
16843                     cn : []
16844                 };
16845                 
16846                 for (var i =0; i < Date.dayNames.length; i++) {
16847                     var d = Date.dayNames[i];
16848                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16849
16850                 }
16851                 row.cn[0].cls+=' fc-first';
16852                 row.cn[0].cn[0].style = 'min-height:90px';
16853                 row.cn[6].cls+=' fc-last';
16854                 ret.push(row);
16855                 
16856             }
16857             ret[0].cls += ' fc-first';
16858             ret[4].cls += ' fc-prev-last';
16859             ret[5].cls += ' fc-last';
16860             return ret;
16861             
16862         };
16863         
16864         var cal_table = {
16865             tag: 'table',
16866             cls: 'fc-border-separate',
16867             style : 'width:100%',
16868             cellspacing  : 0,
16869             cn : [
16870                 { 
16871                     tag: 'thead',
16872                     cn : [
16873                         { 
16874                             tag: 'tr',
16875                             cls : 'fc-first fc-last',
16876                             cn : cal_heads()
16877                         }
16878                     ]
16879                 },
16880                 { 
16881                     tag: 'tbody',
16882                     cn : cal_rows()
16883                 }
16884                   
16885             ]
16886         };
16887          
16888          var cfg = {
16889             cls : 'fc fc-ltr',
16890             cn : [
16891                 header,
16892                 {
16893                     cls : 'fc-content',
16894                     style : "position: relative;",
16895                     cn : [
16896                         {
16897                             cls : 'fc-view fc-view-month fc-grid',
16898                             style : 'position: relative',
16899                             unselectable : 'on',
16900                             cn : [
16901                                 {
16902                                     cls : 'fc-event-container',
16903                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16904                                 },
16905                                 cal_table
16906                             ]
16907                         }
16908                     ]
16909     
16910                 }
16911            ] 
16912             
16913         };
16914         
16915          
16916         
16917         return cfg;
16918     },
16919     
16920     
16921     initEvents : function()
16922     {
16923         if(!this.store){
16924             throw "can not find store for calendar";
16925         }
16926         
16927         var mark = {
16928             tag: "div",
16929             cls:"x-dlg-mask",
16930             style: "text-align:center",
16931             cn: [
16932                 {
16933                     tag: "div",
16934                     style: "background-color:white;width:50%;margin:250 auto",
16935                     cn: [
16936                         {
16937                             tag: "img",
16938                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16939                         },
16940                         {
16941                             tag: "span",
16942                             html: "Loading"
16943                         }
16944                         
16945                     ]
16946                 }
16947             ]
16948         };
16949         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16950         
16951         var size = this.el.select('.fc-content', true).first().getSize();
16952         this.maskEl.setSize(size.width, size.height);
16953         this.maskEl.enableDisplayMode("block");
16954         if(!this.loadMask){
16955             this.maskEl.hide();
16956         }
16957         
16958         this.store = Roo.factory(this.store, Roo.data);
16959         this.store.on('load', this.onLoad, this);
16960         this.store.on('beforeload', this.onBeforeLoad, this);
16961         
16962         this.resize();
16963         
16964         this.cells = this.el.select('.fc-day',true);
16965         //Roo.log(this.cells);
16966         this.textNodes = this.el.query('.fc-day-number');
16967         this.cells.addClassOnOver('fc-state-hover');
16968         
16969         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16970         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16971         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16972         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16973         
16974         this.on('monthchange', this.onMonthChange, this);
16975         
16976         this.update(new Date().clearTime());
16977     },
16978     
16979     resize : function() {
16980         var sz  = this.el.getSize();
16981         
16982         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16983         this.el.select('.fc-day-content div',true).setHeight(34);
16984     },
16985     
16986     
16987     // private
16988     showPrevMonth : function(e){
16989         this.update(this.activeDate.add("mo", -1));
16990     },
16991     showToday : function(e){
16992         this.update(new Date().clearTime());
16993     },
16994     // private
16995     showNextMonth : function(e){
16996         this.update(this.activeDate.add("mo", 1));
16997     },
16998
16999     // private
17000     showPrevYear : function(){
17001         this.update(this.activeDate.add("y", -1));
17002     },
17003
17004     // private
17005     showNextYear : function(){
17006         this.update(this.activeDate.add("y", 1));
17007     },
17008
17009     
17010    // private
17011     update : function(date)
17012     {
17013         var vd = this.activeDate;
17014         this.activeDate = date;
17015 //        if(vd && this.el){
17016 //            var t = date.getTime();
17017 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17018 //                Roo.log('using add remove');
17019 //                
17020 //                this.fireEvent('monthchange', this, date);
17021 //                
17022 //                this.cells.removeClass("fc-state-highlight");
17023 //                this.cells.each(function(c){
17024 //                   if(c.dateValue == t){
17025 //                       c.addClass("fc-state-highlight");
17026 //                       setTimeout(function(){
17027 //                            try{c.dom.firstChild.focus();}catch(e){}
17028 //                       }, 50);
17029 //                       return false;
17030 //                   }
17031 //                   return true;
17032 //                });
17033 //                return;
17034 //            }
17035 //        }
17036         
17037         var days = date.getDaysInMonth();
17038         
17039         var firstOfMonth = date.getFirstDateOfMonth();
17040         var startingPos = firstOfMonth.getDay()-this.startDay;
17041         
17042         if(startingPos < this.startDay){
17043             startingPos += 7;
17044         }
17045         
17046         var pm = date.add(Date.MONTH, -1);
17047         var prevStart = pm.getDaysInMonth()-startingPos;
17048 //        
17049         this.cells = this.el.select('.fc-day',true);
17050         this.textNodes = this.el.query('.fc-day-number');
17051         this.cells.addClassOnOver('fc-state-hover');
17052         
17053         var cells = this.cells.elements;
17054         var textEls = this.textNodes;
17055         
17056         Roo.each(cells, function(cell){
17057             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17058         });
17059         
17060         days += startingPos;
17061
17062         // convert everything to numbers so it's fast
17063         var day = 86400000;
17064         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17065         //Roo.log(d);
17066         //Roo.log(pm);
17067         //Roo.log(prevStart);
17068         
17069         var today = new Date().clearTime().getTime();
17070         var sel = date.clearTime().getTime();
17071         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17072         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17073         var ddMatch = this.disabledDatesRE;
17074         var ddText = this.disabledDatesText;
17075         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17076         var ddaysText = this.disabledDaysText;
17077         var format = this.format;
17078         
17079         var setCellClass = function(cal, cell){
17080             cell.row = 0;
17081             cell.events = [];
17082             cell.more = [];
17083             //Roo.log('set Cell Class');
17084             cell.title = "";
17085             var t = d.getTime();
17086             
17087             //Roo.log(d);
17088             
17089             cell.dateValue = t;
17090             if(t == today){
17091                 cell.className += " fc-today";
17092                 cell.className += " fc-state-highlight";
17093                 cell.title = cal.todayText;
17094             }
17095             if(t == sel){
17096                 // disable highlight in other month..
17097                 //cell.className += " fc-state-highlight";
17098                 
17099             }
17100             // disabling
17101             if(t < min) {
17102                 cell.className = " fc-state-disabled";
17103                 cell.title = cal.minText;
17104                 return;
17105             }
17106             if(t > max) {
17107                 cell.className = " fc-state-disabled";
17108                 cell.title = cal.maxText;
17109                 return;
17110             }
17111             if(ddays){
17112                 if(ddays.indexOf(d.getDay()) != -1){
17113                     cell.title = ddaysText;
17114                     cell.className = " fc-state-disabled";
17115                 }
17116             }
17117             if(ddMatch && format){
17118                 var fvalue = d.dateFormat(format);
17119                 if(ddMatch.test(fvalue)){
17120                     cell.title = ddText.replace("%0", fvalue);
17121                     cell.className = " fc-state-disabled";
17122                 }
17123             }
17124             
17125             if (!cell.initialClassName) {
17126                 cell.initialClassName = cell.dom.className;
17127             }
17128             
17129             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17130         };
17131
17132         var i = 0;
17133         
17134         for(; i < startingPos; i++) {
17135             textEls[i].innerHTML = (++prevStart);
17136             d.setDate(d.getDate()+1);
17137             
17138             cells[i].className = "fc-past fc-other-month";
17139             setCellClass(this, cells[i]);
17140         }
17141         
17142         var intDay = 0;
17143         
17144         for(; i < days; i++){
17145             intDay = i - startingPos + 1;
17146             textEls[i].innerHTML = (intDay);
17147             d.setDate(d.getDate()+1);
17148             
17149             cells[i].className = ''; // "x-date-active";
17150             setCellClass(this, cells[i]);
17151         }
17152         var extraDays = 0;
17153         
17154         for(; i < 42; i++) {
17155             textEls[i].innerHTML = (++extraDays);
17156             d.setDate(d.getDate()+1);
17157             
17158             cells[i].className = "fc-future fc-other-month";
17159             setCellClass(this, cells[i]);
17160         }
17161         
17162         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17163         
17164         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17165         
17166         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17167         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17168         
17169         if(totalRows != 6){
17170             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17171             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17172         }
17173         
17174         this.fireEvent('monthchange', this, date);
17175         
17176         
17177         /*
17178         if(!this.internalRender){
17179             var main = this.el.dom.firstChild;
17180             var w = main.offsetWidth;
17181             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17182             Roo.fly(main).setWidth(w);
17183             this.internalRender = true;
17184             // opera does not respect the auto grow header center column
17185             // then, after it gets a width opera refuses to recalculate
17186             // without a second pass
17187             if(Roo.isOpera && !this.secondPass){
17188                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17189                 this.secondPass = true;
17190                 this.update.defer(10, this, [date]);
17191             }
17192         }
17193         */
17194         
17195     },
17196     
17197     findCell : function(dt) {
17198         dt = dt.clearTime().getTime();
17199         var ret = false;
17200         this.cells.each(function(c){
17201             //Roo.log("check " +c.dateValue + '?=' + dt);
17202             if(c.dateValue == dt){
17203                 ret = c;
17204                 return false;
17205             }
17206             return true;
17207         });
17208         
17209         return ret;
17210     },
17211     
17212     findCells : function(ev) {
17213         var s = ev.start.clone().clearTime().getTime();
17214        // Roo.log(s);
17215         var e= ev.end.clone().clearTime().getTime();
17216        // Roo.log(e);
17217         var ret = [];
17218         this.cells.each(function(c){
17219              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17220             
17221             if(c.dateValue > e){
17222                 return ;
17223             }
17224             if(c.dateValue < s){
17225                 return ;
17226             }
17227             ret.push(c);
17228         });
17229         
17230         return ret;    
17231     },
17232     
17233 //    findBestRow: function(cells)
17234 //    {
17235 //        var ret = 0;
17236 //        
17237 //        for (var i =0 ; i < cells.length;i++) {
17238 //            ret  = Math.max(cells[i].rows || 0,ret);
17239 //        }
17240 //        return ret;
17241 //        
17242 //    },
17243     
17244     
17245     addItem : function(ev)
17246     {
17247         // look for vertical location slot in
17248         var cells = this.findCells(ev);
17249         
17250 //        ev.row = this.findBestRow(cells);
17251         
17252         // work out the location.
17253         
17254         var crow = false;
17255         var rows = [];
17256         for(var i =0; i < cells.length; i++) {
17257             
17258             cells[i].row = cells[0].row;
17259             
17260             if(i == 0){
17261                 cells[i].row = cells[i].row + 1;
17262             }
17263             
17264             if (!crow) {
17265                 crow = {
17266                     start : cells[i],
17267                     end :  cells[i]
17268                 };
17269                 continue;
17270             }
17271             if (crow.start.getY() == cells[i].getY()) {
17272                 // on same row.
17273                 crow.end = cells[i];
17274                 continue;
17275             }
17276             // different row.
17277             rows.push(crow);
17278             crow = {
17279                 start: cells[i],
17280                 end : cells[i]
17281             };
17282             
17283         }
17284         
17285         rows.push(crow);
17286         ev.els = [];
17287         ev.rows = rows;
17288         ev.cells = cells;
17289         
17290         cells[0].events.push(ev);
17291         
17292         this.calevents.push(ev);
17293     },
17294     
17295     clearEvents: function() {
17296         
17297         if(!this.calevents){
17298             return;
17299         }
17300         
17301         Roo.each(this.cells.elements, function(c){
17302             c.row = 0;
17303             c.events = [];
17304             c.more = [];
17305         });
17306         
17307         Roo.each(this.calevents, function(e) {
17308             Roo.each(e.els, function(el) {
17309                 el.un('mouseenter' ,this.onEventEnter, this);
17310                 el.un('mouseleave' ,this.onEventLeave, this);
17311                 el.remove();
17312             },this);
17313         },this);
17314         
17315         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17316             e.remove();
17317         });
17318         
17319     },
17320     
17321     renderEvents: function()
17322     {   
17323         var _this = this;
17324         
17325         this.cells.each(function(c) {
17326             
17327             if(c.row < 5){
17328                 return;
17329             }
17330             
17331             var ev = c.events;
17332             
17333             var r = 4;
17334             if(c.row != c.events.length){
17335                 r = 4 - (4 - (c.row - c.events.length));
17336             }
17337             
17338             c.events = ev.slice(0, r);
17339             c.more = ev.slice(r);
17340             
17341             if(c.more.length && c.more.length == 1){
17342                 c.events.push(c.more.pop());
17343             }
17344             
17345             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17346             
17347         });
17348             
17349         this.cells.each(function(c) {
17350             
17351             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17352             
17353             
17354             for (var e = 0; e < c.events.length; e++){
17355                 var ev = c.events[e];
17356                 var rows = ev.rows;
17357                 
17358                 for(var i = 0; i < rows.length; i++) {
17359                 
17360                     // how many rows should it span..
17361
17362                     var  cfg = {
17363                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17364                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17365
17366                         unselectable : "on",
17367                         cn : [
17368                             {
17369                                 cls: 'fc-event-inner',
17370                                 cn : [
17371     //                                {
17372     //                                  tag:'span',
17373     //                                  cls: 'fc-event-time',
17374     //                                  html : cells.length > 1 ? '' : ev.time
17375     //                                },
17376                                     {
17377                                       tag:'span',
17378                                       cls: 'fc-event-title',
17379                                       html : String.format('{0}', ev.title)
17380                                     }
17381
17382
17383                                 ]
17384                             },
17385                             {
17386                                 cls: 'ui-resizable-handle ui-resizable-e',
17387                                 html : '&nbsp;&nbsp;&nbsp'
17388                             }
17389
17390                         ]
17391                     };
17392
17393                     if (i == 0) {
17394                         cfg.cls += ' fc-event-start';
17395                     }
17396                     if ((i+1) == rows.length) {
17397                         cfg.cls += ' fc-event-end';
17398                     }
17399
17400                     var ctr = _this.el.select('.fc-event-container',true).first();
17401                     var cg = ctr.createChild(cfg);
17402
17403                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17404                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17405
17406                     var r = (c.more.length) ? 1 : 0;
17407                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17408                     cg.setWidth(ebox.right - sbox.x -2);
17409
17410                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17411                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17412                     cg.on('click', _this.onEventClick, _this, ev);
17413
17414                     ev.els.push(cg);
17415                     
17416                 }
17417                 
17418             }
17419             
17420             
17421             if(c.more.length){
17422                 var  cfg = {
17423                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17424                     style : 'position: absolute',
17425                     unselectable : "on",
17426                     cn : [
17427                         {
17428                             cls: 'fc-event-inner',
17429                             cn : [
17430                                 {
17431                                   tag:'span',
17432                                   cls: 'fc-event-title',
17433                                   html : 'More'
17434                                 }
17435
17436
17437                             ]
17438                         },
17439                         {
17440                             cls: 'ui-resizable-handle ui-resizable-e',
17441                             html : '&nbsp;&nbsp;&nbsp'
17442                         }
17443
17444                     ]
17445                 };
17446
17447                 var ctr = _this.el.select('.fc-event-container',true).first();
17448                 var cg = ctr.createChild(cfg);
17449
17450                 var sbox = c.select('.fc-day-content',true).first().getBox();
17451                 var ebox = c.select('.fc-day-content',true).first().getBox();
17452                 //Roo.log(cg);
17453                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17454                 cg.setWidth(ebox.right - sbox.x -2);
17455
17456                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17457                 
17458             }
17459             
17460         });
17461         
17462         
17463         
17464     },
17465     
17466     onEventEnter: function (e, el,event,d) {
17467         this.fireEvent('evententer', this, el, event);
17468     },
17469     
17470     onEventLeave: function (e, el,event,d) {
17471         this.fireEvent('eventleave', this, el, event);
17472     },
17473     
17474     onEventClick: function (e, el,event,d) {
17475         this.fireEvent('eventclick', this, el, event);
17476     },
17477     
17478     onMonthChange: function () {
17479         this.store.load();
17480     },
17481     
17482     onMoreEventClick: function(e, el, more)
17483     {
17484         var _this = this;
17485         
17486         this.calpopover.placement = 'right';
17487         this.calpopover.setTitle('More');
17488         
17489         this.calpopover.setContent('');
17490         
17491         var ctr = this.calpopover.el.select('.popover-content', true).first();
17492         
17493         Roo.each(more, function(m){
17494             var cfg = {
17495                 cls : 'fc-event-hori fc-event-draggable',
17496                 html : m.title
17497             };
17498             var cg = ctr.createChild(cfg);
17499             
17500             cg.on('click', _this.onEventClick, _this, m);
17501         });
17502         
17503         this.calpopover.show(el);
17504         
17505         
17506     },
17507     
17508     onLoad: function () 
17509     {   
17510         this.calevents = [];
17511         var cal = this;
17512         
17513         if(this.store.getCount() > 0){
17514             this.store.data.each(function(d){
17515                cal.addItem({
17516                     id : d.data.id,
17517                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17518                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17519                     time : d.data.start_time,
17520                     title : d.data.title,
17521                     description : d.data.description,
17522                     venue : d.data.venue
17523                 });
17524             });
17525         }
17526         
17527         this.renderEvents();
17528         
17529         if(this.calevents.length && this.loadMask){
17530             this.maskEl.hide();
17531         }
17532     },
17533     
17534     onBeforeLoad: function()
17535     {
17536         this.clearEvents();
17537         if(this.loadMask){
17538             this.maskEl.show();
17539         }
17540     }
17541 });
17542
17543  
17544  /*
17545  * - LGPL
17546  *
17547  * element
17548  * 
17549  */
17550
17551 /**
17552  * @class Roo.bootstrap.Popover
17553  * @extends Roo.bootstrap.Component
17554  * Bootstrap Popover class
17555  * @cfg {String} html contents of the popover   (or false to use children..)
17556  * @cfg {String} title of popover (or false to hide)
17557  * @cfg {String} placement how it is placed
17558  * @cfg {String} trigger click || hover (or false to trigger manually)
17559  * @cfg {String} over what (parent or false to trigger manually.)
17560  * @cfg {Number} delay - delay before showing
17561  
17562  * @constructor
17563  * Create a new Popover
17564  * @param {Object} config The config object
17565  */
17566
17567 Roo.bootstrap.Popover = function(config){
17568     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17569     
17570     this.addEvents({
17571         // raw events
17572          /**
17573          * @event show
17574          * After the popover show
17575          * 
17576          * @param {Roo.bootstrap.Popover} this
17577          */
17578         "show" : true,
17579         /**
17580          * @event hide
17581          * After the popover hide
17582          * 
17583          * @param {Roo.bootstrap.Popover} this
17584          */
17585         "hide" : true
17586     });
17587 };
17588
17589 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17590     
17591     title: 'Fill in a title',
17592     html: false,
17593     
17594     placement : 'right',
17595     trigger : 'hover', // hover
17596     
17597     delay : 0,
17598     
17599     over: 'parent',
17600     
17601     can_build_overlaid : false,
17602     
17603     getChildContainer : function()
17604     {
17605         return this.el.select('.popover-content',true).first();
17606     },
17607     
17608     getAutoCreate : function(){
17609          
17610         var cfg = {
17611            cls : 'popover roo-dynamic',
17612            style: 'display:block',
17613            cn : [
17614                 {
17615                     cls : 'arrow'
17616                 },
17617                 {
17618                     cls : 'popover-inner',
17619                     cn : [
17620                         {
17621                             tag: 'h3',
17622                             cls: 'popover-title',
17623                             html : this.title
17624                         },
17625                         {
17626                             cls : 'popover-content',
17627                             html : this.html
17628                         }
17629                     ]
17630                     
17631                 }
17632            ]
17633         };
17634         
17635         return cfg;
17636     },
17637     setTitle: function(str)
17638     {
17639         this.title = str;
17640         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17641     },
17642     setContent: function(str)
17643     {
17644         this.html = str;
17645         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17646     },
17647     // as it get's added to the bottom of the page.
17648     onRender : function(ct, position)
17649     {
17650         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17651         if(!this.el){
17652             var cfg = Roo.apply({},  this.getAutoCreate());
17653             cfg.id = Roo.id();
17654             
17655             if (this.cls) {
17656                 cfg.cls += ' ' + this.cls;
17657             }
17658             if (this.style) {
17659                 cfg.style = this.style;
17660             }
17661             //Roo.log("adding to ");
17662             this.el = Roo.get(document.body).createChild(cfg, position);
17663 //            Roo.log(this.el);
17664         }
17665         this.initEvents();
17666     },
17667     
17668     initEvents : function()
17669     {
17670         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17671         this.el.enableDisplayMode('block');
17672         this.el.hide();
17673         if (this.over === false) {
17674             return; 
17675         }
17676         if (this.triggers === false) {
17677             return;
17678         }
17679         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17680         var triggers = this.trigger ? this.trigger.split(' ') : [];
17681         Roo.each(triggers, function(trigger) {
17682         
17683             if (trigger == 'click') {
17684                 on_el.on('click', this.toggle, this);
17685             } else if (trigger != 'manual') {
17686                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17687                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17688       
17689                 on_el.on(eventIn  ,this.enter, this);
17690                 on_el.on(eventOut, this.leave, this);
17691             }
17692         }, this);
17693         
17694     },
17695     
17696     
17697     // private
17698     timeout : null,
17699     hoverState : null,
17700     
17701     toggle : function () {
17702         this.hoverState == 'in' ? this.leave() : this.enter();
17703     },
17704     
17705     enter : function () {
17706         
17707         clearTimeout(this.timeout);
17708     
17709         this.hoverState = 'in';
17710     
17711         if (!this.delay || !this.delay.show) {
17712             this.show();
17713             return;
17714         }
17715         var _t = this;
17716         this.timeout = setTimeout(function () {
17717             if (_t.hoverState == 'in') {
17718                 _t.show();
17719             }
17720         }, this.delay.show)
17721     },
17722     
17723     leave : function() {
17724         clearTimeout(this.timeout);
17725     
17726         this.hoverState = 'out';
17727     
17728         if (!this.delay || !this.delay.hide) {
17729             this.hide();
17730             return;
17731         }
17732         var _t = this;
17733         this.timeout = setTimeout(function () {
17734             if (_t.hoverState == 'out') {
17735                 _t.hide();
17736             }
17737         }, this.delay.hide)
17738     },
17739     
17740     show : function (on_el)
17741     {
17742         if (!on_el) {
17743             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17744         }
17745         
17746         // set content.
17747         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17748         if (this.html !== false) {
17749             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17750         }
17751         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17752         if (!this.title.length) {
17753             this.el.select('.popover-title',true).hide();
17754         }
17755         
17756         var placement = typeof this.placement == 'function' ?
17757             this.placement.call(this, this.el, on_el) :
17758             this.placement;
17759             
17760         var autoToken = /\s?auto?\s?/i;
17761         var autoPlace = autoToken.test(placement);
17762         if (autoPlace) {
17763             placement = placement.replace(autoToken, '') || 'top';
17764         }
17765         
17766         //this.el.detach()
17767         //this.el.setXY([0,0]);
17768         this.el.show();
17769         this.el.dom.style.display='block';
17770         this.el.addClass(placement);
17771         
17772         //this.el.appendTo(on_el);
17773         
17774         var p = this.getPosition();
17775         var box = this.el.getBox();
17776         
17777         if (autoPlace) {
17778             // fixme..
17779         }
17780         var align = Roo.bootstrap.Popover.alignment[placement];
17781         
17782 //        Roo.log(align);
17783         this.el.alignTo(on_el, align[0],align[1]);
17784         //var arrow = this.el.select('.arrow',true).first();
17785         //arrow.set(align[2], 
17786         
17787         this.el.addClass('in');
17788         
17789         
17790         if (this.el.hasClass('fade')) {
17791             // fade it?
17792         }
17793         
17794         this.hoverState = 'in';
17795         
17796         this.fireEvent('show', this);
17797         
17798     },
17799     hide : function()
17800     {
17801         this.el.setXY([0,0]);
17802         this.el.removeClass('in');
17803         this.el.hide();
17804         this.hoverState = null;
17805         
17806         this.fireEvent('hide', this);
17807     }
17808     
17809 });
17810
17811 Roo.bootstrap.Popover.alignment = {
17812     'left' : ['r-l', [-10,0], 'right'],
17813     'right' : ['l-r', [10,0], 'left'],
17814     'bottom' : ['t-b', [0,10], 'top'],
17815     'top' : [ 'b-t', [0,-10], 'bottom']
17816 };
17817
17818  /*
17819  * - LGPL
17820  *
17821  * Progress
17822  * 
17823  */
17824
17825 /**
17826  * @class Roo.bootstrap.Progress
17827  * @extends Roo.bootstrap.Component
17828  * Bootstrap Progress class
17829  * @cfg {Boolean} striped striped of the progress bar
17830  * @cfg {Boolean} active animated of the progress bar
17831  * 
17832  * 
17833  * @constructor
17834  * Create a new Progress
17835  * @param {Object} config The config object
17836  */
17837
17838 Roo.bootstrap.Progress = function(config){
17839     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17840 };
17841
17842 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17843     
17844     striped : false,
17845     active: false,
17846     
17847     getAutoCreate : function(){
17848         var cfg = {
17849             tag: 'div',
17850             cls: 'progress'
17851         };
17852         
17853         
17854         if(this.striped){
17855             cfg.cls += ' progress-striped';
17856         }
17857       
17858         if(this.active){
17859             cfg.cls += ' active';
17860         }
17861         
17862         
17863         return cfg;
17864     }
17865    
17866 });
17867
17868  
17869
17870  /*
17871  * - LGPL
17872  *
17873  * ProgressBar
17874  * 
17875  */
17876
17877 /**
17878  * @class Roo.bootstrap.ProgressBar
17879  * @extends Roo.bootstrap.Component
17880  * Bootstrap ProgressBar class
17881  * @cfg {Number} aria_valuenow aria-value now
17882  * @cfg {Number} aria_valuemin aria-value min
17883  * @cfg {Number} aria_valuemax aria-value max
17884  * @cfg {String} label label for the progress bar
17885  * @cfg {String} panel (success | info | warning | danger )
17886  * @cfg {String} role role of the progress bar
17887  * @cfg {String} sr_only text
17888  * 
17889  * 
17890  * @constructor
17891  * Create a new ProgressBar
17892  * @param {Object} config The config object
17893  */
17894
17895 Roo.bootstrap.ProgressBar = function(config){
17896     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17897 };
17898
17899 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17900     
17901     aria_valuenow : 0,
17902     aria_valuemin : 0,
17903     aria_valuemax : 100,
17904     label : false,
17905     panel : false,
17906     role : false,
17907     sr_only: false,
17908     
17909     getAutoCreate : function()
17910     {
17911         
17912         var cfg = {
17913             tag: 'div',
17914             cls: 'progress-bar',
17915             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17916         };
17917         
17918         if(this.sr_only){
17919             cfg.cn = {
17920                 tag: 'span',
17921                 cls: 'sr-only',
17922                 html: this.sr_only
17923             }
17924         }
17925         
17926         if(this.role){
17927             cfg.role = this.role;
17928         }
17929         
17930         if(this.aria_valuenow){
17931             cfg['aria-valuenow'] = this.aria_valuenow;
17932         }
17933         
17934         if(this.aria_valuemin){
17935             cfg['aria-valuemin'] = this.aria_valuemin;
17936         }
17937         
17938         if(this.aria_valuemax){
17939             cfg['aria-valuemax'] = this.aria_valuemax;
17940         }
17941         
17942         if(this.label && !this.sr_only){
17943             cfg.html = this.label;
17944         }
17945         
17946         if(this.panel){
17947             cfg.cls += ' progress-bar-' + this.panel;
17948         }
17949         
17950         return cfg;
17951     },
17952     
17953     update : function(aria_valuenow)
17954     {
17955         this.aria_valuenow = aria_valuenow;
17956         
17957         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17958     }
17959    
17960 });
17961
17962  
17963
17964  /*
17965  * - LGPL
17966  *
17967  * column
17968  * 
17969  */
17970
17971 /**
17972  * @class Roo.bootstrap.TabGroup
17973  * @extends Roo.bootstrap.Column
17974  * Bootstrap Column class
17975  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17976  * @cfg {Boolean} carousel true to make the group behave like a carousel
17977  * @cfg {Boolean} bullets show bullets for the panels
17978  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17979  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17980  * @cfg {Boolean} showarrow (true|false) show arrow default true
17981  * 
17982  * @constructor
17983  * Create a new TabGroup
17984  * @param {Object} config The config object
17985  */
17986
17987 Roo.bootstrap.TabGroup = function(config){
17988     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17989     if (!this.navId) {
17990         this.navId = Roo.id();
17991     }
17992     this.tabs = [];
17993     Roo.bootstrap.TabGroup.register(this);
17994     
17995 };
17996
17997 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17998     
17999     carousel : false,
18000     transition : false,
18001     bullets : 0,
18002     timer : 0,
18003     autoslide : false,
18004     slideFn : false,
18005     slideOnTouch : false,
18006     showarrow : true,
18007     
18008     getAutoCreate : function()
18009     {
18010         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18011         
18012         cfg.cls += ' tab-content';
18013         
18014         if (this.carousel) {
18015             cfg.cls += ' carousel slide';
18016             
18017             cfg.cn = [{
18018                cls : 'carousel-inner',
18019                cn : []
18020             }];
18021         
18022             if(this.bullets  && !Roo.isTouch){
18023                 
18024                 var bullets = {
18025                     cls : 'carousel-bullets',
18026                     cn : []
18027                 };
18028                
18029                 if(this.bullets_cls){
18030                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18031                 }
18032                 
18033                 bullets.cn.push({
18034                     cls : 'clear'
18035                 });
18036                 
18037                 cfg.cn[0].cn.push(bullets);
18038             }
18039             
18040             if(this.showarrow){
18041                 cfg.cn[0].cn.push({
18042                     tag : 'div',
18043                     class : 'carousel-arrow',
18044                     cn : [
18045                         {
18046                             tag : 'div',
18047                             class : 'carousel-prev',
18048                             cn : [
18049                                 {
18050                                     tag : 'i',
18051                                     class : 'fa fa-chevron-left'
18052                                 }
18053                             ]
18054                         },
18055                         {
18056                             tag : 'div',
18057                             class : 'carousel-next',
18058                             cn : [
18059                                 {
18060                                     tag : 'i',
18061                                     class : 'fa fa-chevron-right'
18062                                 }
18063                             ]
18064                         }
18065                     ]
18066                 });
18067             }
18068             
18069         }
18070         
18071         return cfg;
18072     },
18073     
18074     initEvents:  function()
18075     {
18076 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18077 //            this.el.on("touchstart", this.onTouchStart, this);
18078 //        }
18079         
18080         if(this.autoslide){
18081             var _this = this;
18082             
18083             this.slideFn = window.setInterval(function() {
18084                 _this.showPanelNext();
18085             }, this.timer);
18086         }
18087         
18088         if(this.showarrow){
18089             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18090             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18091         }
18092         
18093         
18094     },
18095     
18096 //    onTouchStart : function(e, el, o)
18097 //    {
18098 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18099 //            return;
18100 //        }
18101 //        
18102 //        this.showPanelNext();
18103 //    },
18104     
18105     
18106     getChildContainer : function()
18107     {
18108         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18109     },
18110     
18111     /**
18112     * register a Navigation item
18113     * @param {Roo.bootstrap.NavItem} the navitem to add
18114     */
18115     register : function(item)
18116     {
18117         this.tabs.push( item);
18118         item.navId = this.navId; // not really needed..
18119         this.addBullet();
18120     
18121     },
18122     
18123     getActivePanel : function()
18124     {
18125         var r = false;
18126         Roo.each(this.tabs, function(t) {
18127             if (t.active) {
18128                 r = t;
18129                 return false;
18130             }
18131             return null;
18132         });
18133         return r;
18134         
18135     },
18136     getPanelByName : function(n)
18137     {
18138         var r = false;
18139         Roo.each(this.tabs, function(t) {
18140             if (t.tabId == n) {
18141                 r = t;
18142                 return false;
18143             }
18144             return null;
18145         });
18146         return r;
18147     },
18148     indexOfPanel : function(p)
18149     {
18150         var r = false;
18151         Roo.each(this.tabs, function(t,i) {
18152             if (t.tabId == p.tabId) {
18153                 r = i;
18154                 return false;
18155             }
18156             return null;
18157         });
18158         return r;
18159     },
18160     /**
18161      * show a specific panel
18162      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18163      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18164      */
18165     showPanel : function (pan)
18166     {
18167         if(this.transition || typeof(pan) == 'undefined'){
18168             Roo.log("waiting for the transitionend");
18169             return;
18170         }
18171         
18172         if (typeof(pan) == 'number') {
18173             pan = this.tabs[pan];
18174         }
18175         
18176         if (typeof(pan) == 'string') {
18177             pan = this.getPanelByName(pan);
18178         }
18179         
18180         var cur = this.getActivePanel();
18181         
18182         if(!pan || !cur){
18183             Roo.log('pan or acitve pan is undefined');
18184             return false;
18185         }
18186         
18187         if (pan.tabId == this.getActivePanel().tabId) {
18188             return true;
18189         }
18190         
18191         if (false === cur.fireEvent('beforedeactivate')) {
18192             return false;
18193         }
18194         
18195         if(this.bullets > 0 && !Roo.isTouch){
18196             this.setActiveBullet(this.indexOfPanel(pan));
18197         }
18198         
18199         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18200             
18201             this.transition = true;
18202             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18203             var lr = dir == 'next' ? 'left' : 'right';
18204             pan.el.addClass(dir); // or prev
18205             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18206             cur.el.addClass(lr); // or right
18207             pan.el.addClass(lr);
18208             
18209             var _this = this;
18210             cur.el.on('transitionend', function() {
18211                 Roo.log("trans end?");
18212                 
18213                 pan.el.removeClass([lr,dir]);
18214                 pan.setActive(true);
18215                 
18216                 cur.el.removeClass([lr]);
18217                 cur.setActive(false);
18218                 
18219                 _this.transition = false;
18220                 
18221             }, this, { single:  true } );
18222             
18223             return true;
18224         }
18225         
18226         cur.setActive(false);
18227         pan.setActive(true);
18228         
18229         return true;
18230         
18231     },
18232     showPanelNext : function()
18233     {
18234         var i = this.indexOfPanel(this.getActivePanel());
18235         
18236         if (i >= this.tabs.length - 1 && !this.autoslide) {
18237             return;
18238         }
18239         
18240         if (i >= this.tabs.length - 1 && this.autoslide) {
18241             i = -1;
18242         }
18243         
18244         this.showPanel(this.tabs[i+1]);
18245     },
18246     
18247     showPanelPrev : function()
18248     {
18249         var i = this.indexOfPanel(this.getActivePanel());
18250         
18251         if (i  < 1 && !this.autoslide) {
18252             return;
18253         }
18254         
18255         if (i < 1 && this.autoslide) {
18256             i = this.tabs.length;
18257         }
18258         
18259         this.showPanel(this.tabs[i-1]);
18260     },
18261     
18262     
18263     addBullet: function()
18264     {
18265         if(!this.bullets || Roo.isTouch){
18266             return;
18267         }
18268         var ctr = this.el.select('.carousel-bullets',true).first();
18269         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18270         var bullet = ctr.createChild({
18271             cls : 'bullet bullet-' + i
18272         },ctr.dom.lastChild);
18273         
18274         
18275         var _this = this;
18276         
18277         bullet.on('click', (function(e, el, o, ii, t){
18278
18279             e.preventDefault();
18280
18281             this.showPanel(ii);
18282
18283             if(this.autoslide && this.slideFn){
18284                 clearInterval(this.slideFn);
18285                 this.slideFn = window.setInterval(function() {
18286                     _this.showPanelNext();
18287                 }, this.timer);
18288             }
18289
18290         }).createDelegate(this, [i, bullet], true));
18291                 
18292         
18293     },
18294      
18295     setActiveBullet : function(i)
18296     {
18297         if(Roo.isTouch){
18298             return;
18299         }
18300         
18301         Roo.each(this.el.select('.bullet', true).elements, function(el){
18302             el.removeClass('selected');
18303         });
18304
18305         var bullet = this.el.select('.bullet-' + i, true).first();
18306         
18307         if(!bullet){
18308             return;
18309         }
18310         
18311         bullet.addClass('selected');
18312     }
18313     
18314     
18315   
18316 });
18317
18318  
18319
18320  
18321  
18322 Roo.apply(Roo.bootstrap.TabGroup, {
18323     
18324     groups: {},
18325      /**
18326     * register a Navigation Group
18327     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18328     */
18329     register : function(navgrp)
18330     {
18331         this.groups[navgrp.navId] = navgrp;
18332         
18333     },
18334     /**
18335     * fetch a Navigation Group based on the navigation ID
18336     * if one does not exist , it will get created.
18337     * @param {string} the navgroup to add
18338     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18339     */
18340     get: function(navId) {
18341         if (typeof(this.groups[navId]) == 'undefined') {
18342             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18343         }
18344         return this.groups[navId] ;
18345     }
18346     
18347     
18348     
18349 });
18350
18351  /*
18352  * - LGPL
18353  *
18354  * TabPanel
18355  * 
18356  */
18357
18358 /**
18359  * @class Roo.bootstrap.TabPanel
18360  * @extends Roo.bootstrap.Component
18361  * Bootstrap TabPanel class
18362  * @cfg {Boolean} active panel active
18363  * @cfg {String} html panel content
18364  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18365  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18366  * @cfg {String} href click to link..
18367  * 
18368  * 
18369  * @constructor
18370  * Create a new TabPanel
18371  * @param {Object} config The config object
18372  */
18373
18374 Roo.bootstrap.TabPanel = function(config){
18375     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18376     this.addEvents({
18377         /**
18378              * @event changed
18379              * Fires when the active status changes
18380              * @param {Roo.bootstrap.TabPanel} this
18381              * @param {Boolean} state the new state
18382             
18383          */
18384         'changed': true,
18385         /**
18386              * @event beforedeactivate
18387              * Fires before a tab is de-activated - can be used to do validation on a form.
18388              * @param {Roo.bootstrap.TabPanel} this
18389              * @return {Boolean} false if there is an error
18390             
18391          */
18392         'beforedeactivate': true
18393      });
18394     
18395     this.tabId = this.tabId || Roo.id();
18396   
18397 };
18398
18399 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18400     
18401     active: false,
18402     html: false,
18403     tabId: false,
18404     navId : false,
18405     href : '',
18406     
18407     getAutoCreate : function(){
18408         var cfg = {
18409             tag: 'div',
18410             // item is needed for carousel - not sure if it has any effect otherwise
18411             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18412             html: this.html || ''
18413         };
18414         
18415         if(this.active){
18416             cfg.cls += ' active';
18417         }
18418         
18419         if(this.tabId){
18420             cfg.tabId = this.tabId;
18421         }
18422         
18423         
18424         return cfg;
18425     },
18426     
18427     initEvents:  function()
18428     {
18429         var p = this.parent();
18430         
18431         this.navId = this.navId || p.navId;
18432         
18433         if (typeof(this.navId) != 'undefined') {
18434             // not really needed.. but just in case.. parent should be a NavGroup.
18435             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18436             
18437             tg.register(this);
18438             
18439             var i = tg.tabs.length - 1;
18440             
18441             if(this.active && tg.bullets > 0 && i < tg.bullets){
18442                 tg.setActiveBullet(i);
18443             }
18444         }
18445         
18446         this.el.on('click', this.onClick, this);
18447         
18448         if(Roo.isTouch){
18449             this.el.on("touchstart", this.onTouchStart, this);
18450             this.el.on("touchmove", this.onTouchMove, this);
18451             this.el.on("touchend", this.onTouchEnd, this);
18452         }
18453         
18454     },
18455     
18456     onRender : function(ct, position)
18457     {
18458         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18459     },
18460     
18461     setActive : function(state)
18462     {
18463         Roo.log("panel - set active " + this.tabId + "=" + state);
18464         
18465         this.active = state;
18466         if (!state) {
18467             this.el.removeClass('active');
18468             
18469         } else  if (!this.el.hasClass('active')) {
18470             this.el.addClass('active');
18471         }
18472         
18473         this.fireEvent('changed', this, state);
18474     },
18475     
18476     onClick : function(e)
18477     {
18478         e.preventDefault();
18479         
18480         if(!this.href.length){
18481             return;
18482         }
18483         
18484         window.location.href = this.href;
18485     },
18486     
18487     startX : 0,
18488     startY : 0,
18489     endX : 0,
18490     endY : 0,
18491     swiping : false,
18492     
18493     onTouchStart : function(e)
18494     {
18495         this.swiping = false;
18496         
18497         this.startX = e.browserEvent.touches[0].clientX;
18498         this.startY = e.browserEvent.touches[0].clientY;
18499     },
18500     
18501     onTouchMove : function(e)
18502     {
18503         this.swiping = true;
18504         
18505         this.endX = e.browserEvent.touches[0].clientX;
18506         this.endY = e.browserEvent.touches[0].clientY;
18507     },
18508     
18509     onTouchEnd : function(e)
18510     {
18511         if(!this.swiping){
18512             this.onClick(e);
18513             return;
18514         }
18515         
18516         var tabGroup = this.parent();
18517         
18518         if(this.endX > this.startX){ // swiping right
18519             tabGroup.showPanelPrev();
18520             return;
18521         }
18522         
18523         if(this.startX > this.endX){ // swiping left
18524             tabGroup.showPanelNext();
18525             return;
18526         }
18527     }
18528     
18529     
18530 });
18531  
18532
18533  
18534
18535  /*
18536  * - LGPL
18537  *
18538  * DateField
18539  * 
18540  */
18541
18542 /**
18543  * @class Roo.bootstrap.DateField
18544  * @extends Roo.bootstrap.Input
18545  * Bootstrap DateField class
18546  * @cfg {Number} weekStart default 0
18547  * @cfg {String} viewMode default empty, (months|years)
18548  * @cfg {String} minViewMode default empty, (months|years)
18549  * @cfg {Number} startDate default -Infinity
18550  * @cfg {Number} endDate default Infinity
18551  * @cfg {Boolean} todayHighlight default false
18552  * @cfg {Boolean} todayBtn default false
18553  * @cfg {Boolean} calendarWeeks default false
18554  * @cfg {Object} daysOfWeekDisabled default empty
18555  * @cfg {Boolean} singleMode default false (true | false)
18556  * 
18557  * @cfg {Boolean} keyboardNavigation default true
18558  * @cfg {String} language default en
18559  * 
18560  * @constructor
18561  * Create a new DateField
18562  * @param {Object} config The config object
18563  */
18564
18565 Roo.bootstrap.DateField = function(config){
18566     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18567      this.addEvents({
18568             /**
18569              * @event show
18570              * Fires when this field show.
18571              * @param {Roo.bootstrap.DateField} this
18572              * @param {Mixed} date The date value
18573              */
18574             show : true,
18575             /**
18576              * @event show
18577              * Fires when this field hide.
18578              * @param {Roo.bootstrap.DateField} this
18579              * @param {Mixed} date The date value
18580              */
18581             hide : true,
18582             /**
18583              * @event select
18584              * Fires when select a date.
18585              * @param {Roo.bootstrap.DateField} this
18586              * @param {Mixed} date The date value
18587              */
18588             select : true,
18589             /**
18590              * @event beforeselect
18591              * Fires when before select a date.
18592              * @param {Roo.bootstrap.DateField} this
18593              * @param {Mixed} date The date value
18594              */
18595             beforeselect : true
18596         });
18597 };
18598
18599 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18600     
18601     /**
18602      * @cfg {String} format
18603      * The default date format string which can be overriden for localization support.  The format must be
18604      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18605      */
18606     format : "m/d/y",
18607     /**
18608      * @cfg {String} altFormats
18609      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18610      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18611      */
18612     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18613     
18614     weekStart : 0,
18615     
18616     viewMode : '',
18617     
18618     minViewMode : '',
18619     
18620     todayHighlight : false,
18621     
18622     todayBtn: false,
18623     
18624     language: 'en',
18625     
18626     keyboardNavigation: true,
18627     
18628     calendarWeeks: false,
18629     
18630     startDate: -Infinity,
18631     
18632     endDate: Infinity,
18633     
18634     daysOfWeekDisabled: [],
18635     
18636     _events: [],
18637     
18638     singleMode : false,
18639     
18640     UTCDate: function()
18641     {
18642         return new Date(Date.UTC.apply(Date, arguments));
18643     },
18644     
18645     UTCToday: function()
18646     {
18647         var today = new Date();
18648         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18649     },
18650     
18651     getDate: function() {
18652             var d = this.getUTCDate();
18653             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18654     },
18655     
18656     getUTCDate: function() {
18657             return this.date;
18658     },
18659     
18660     setDate: function(d) {
18661             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18662     },
18663     
18664     setUTCDate: function(d) {
18665             this.date = d;
18666             this.setValue(this.formatDate(this.date));
18667     },
18668         
18669     onRender: function(ct, position)
18670     {
18671         
18672         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18673         
18674         this.language = this.language || 'en';
18675         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18676         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18677         
18678         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18679         this.format = this.format || 'm/d/y';
18680         this.isInline = false;
18681         this.isInput = true;
18682         this.component = this.el.select('.add-on', true).first() || false;
18683         this.component = (this.component && this.component.length === 0) ? false : this.component;
18684         this.hasInput = this.component && this.inputEl().length;
18685         
18686         if (typeof(this.minViewMode === 'string')) {
18687             switch (this.minViewMode) {
18688                 case 'months':
18689                     this.minViewMode = 1;
18690                     break;
18691                 case 'years':
18692                     this.minViewMode = 2;
18693                     break;
18694                 default:
18695                     this.minViewMode = 0;
18696                     break;
18697             }
18698         }
18699         
18700         if (typeof(this.viewMode === 'string')) {
18701             switch (this.viewMode) {
18702                 case 'months':
18703                     this.viewMode = 1;
18704                     break;
18705                 case 'years':
18706                     this.viewMode = 2;
18707                     break;
18708                 default:
18709                     this.viewMode = 0;
18710                     break;
18711             }
18712         }
18713                 
18714         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18715         
18716 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18717         
18718         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18719         
18720         this.picker().on('mousedown', this.onMousedown, this);
18721         this.picker().on('click', this.onClick, this);
18722         
18723         this.picker().addClass('datepicker-dropdown');
18724         
18725         this.startViewMode = this.viewMode;
18726         
18727         if(this.singleMode){
18728             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18729                 v.setVisibilityMode(Roo.Element.DISPLAY);
18730                 v.hide();
18731             });
18732             
18733             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18734                 v.setStyle('width', '189px');
18735             });
18736         }
18737         
18738         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18739             if(!this.calendarWeeks){
18740                 v.remove();
18741                 return;
18742             }
18743             
18744             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18745             v.attr('colspan', function(i, val){
18746                 return parseInt(val) + 1;
18747             });
18748         });
18749                         
18750         
18751         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18752         
18753         this.setStartDate(this.startDate);
18754         this.setEndDate(this.endDate);
18755         
18756         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18757         
18758         this.fillDow();
18759         this.fillMonths();
18760         this.update();
18761         this.showMode();
18762         
18763         if(this.isInline) {
18764             this.showPopup();
18765         }
18766     },
18767     
18768     picker : function()
18769     {
18770         return this.pickerEl;
18771 //        return this.el.select('.datepicker', true).first();
18772     },
18773     
18774     fillDow: function()
18775     {
18776         var dowCnt = this.weekStart;
18777         
18778         var dow = {
18779             tag: 'tr',
18780             cn: [
18781                 
18782             ]
18783         };
18784         
18785         if(this.calendarWeeks){
18786             dow.cn.push({
18787                 tag: 'th',
18788                 cls: 'cw',
18789                 html: '&nbsp;'
18790             })
18791         }
18792         
18793         while (dowCnt < this.weekStart + 7) {
18794             dow.cn.push({
18795                 tag: 'th',
18796                 cls: 'dow',
18797                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18798             });
18799         }
18800         
18801         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18802     },
18803     
18804     fillMonths: function()
18805     {    
18806         var i = 0;
18807         var months = this.picker().select('>.datepicker-months td', true).first();
18808         
18809         months.dom.innerHTML = '';
18810         
18811         while (i < 12) {
18812             var month = {
18813                 tag: 'span',
18814                 cls: 'month',
18815                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18816             };
18817             
18818             months.createChild(month);
18819         }
18820         
18821     },
18822     
18823     update: function()
18824     {
18825         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;
18826         
18827         if (this.date < this.startDate) {
18828             this.viewDate = new Date(this.startDate);
18829         } else if (this.date > this.endDate) {
18830             this.viewDate = new Date(this.endDate);
18831         } else {
18832             this.viewDate = new Date(this.date);
18833         }
18834         
18835         this.fill();
18836     },
18837     
18838     fill: function() 
18839     {
18840         var d = new Date(this.viewDate),
18841                 year = d.getUTCFullYear(),
18842                 month = d.getUTCMonth(),
18843                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18844                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18845                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18846                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18847                 currentDate = this.date && this.date.valueOf(),
18848                 today = this.UTCToday();
18849         
18850         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18851         
18852 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18853         
18854 //        this.picker.select('>tfoot th.today').
18855 //                                              .text(dates[this.language].today)
18856 //                                              .toggle(this.todayBtn !== false);
18857     
18858         this.updateNavArrows();
18859         this.fillMonths();
18860                                                 
18861         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18862         
18863         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18864          
18865         prevMonth.setUTCDate(day);
18866         
18867         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18868         
18869         var nextMonth = new Date(prevMonth);
18870         
18871         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18872         
18873         nextMonth = nextMonth.valueOf();
18874         
18875         var fillMonths = false;
18876         
18877         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18878         
18879         while(prevMonth.valueOf() <= nextMonth) {
18880             var clsName = '';
18881             
18882             if (prevMonth.getUTCDay() === this.weekStart) {
18883                 if(fillMonths){
18884                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18885                 }
18886                     
18887                 fillMonths = {
18888                     tag: 'tr',
18889                     cn: []
18890                 };
18891                 
18892                 if(this.calendarWeeks){
18893                     // ISO 8601: First week contains first thursday.
18894                     // ISO also states week starts on Monday, but we can be more abstract here.
18895                     var
18896                     // Start of current week: based on weekstart/current date
18897                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18898                     // Thursday of this week
18899                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18900                     // First Thursday of year, year from thursday
18901                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18902                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18903                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18904                     
18905                     fillMonths.cn.push({
18906                         tag: 'td',
18907                         cls: 'cw',
18908                         html: calWeek
18909                     });
18910                 }
18911             }
18912             
18913             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18914                 clsName += ' old';
18915             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18916                 clsName += ' new';
18917             }
18918             if (this.todayHighlight &&
18919                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18920                 prevMonth.getUTCMonth() == today.getMonth() &&
18921                 prevMonth.getUTCDate() == today.getDate()) {
18922                 clsName += ' today';
18923             }
18924             
18925             if (currentDate && prevMonth.valueOf() === currentDate) {
18926                 clsName += ' active';
18927             }
18928             
18929             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18930                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18931                     clsName += ' disabled';
18932             }
18933             
18934             fillMonths.cn.push({
18935                 tag: 'td',
18936                 cls: 'day ' + clsName,
18937                 html: prevMonth.getDate()
18938             });
18939             
18940             prevMonth.setDate(prevMonth.getDate()+1);
18941         }
18942           
18943         var currentYear = this.date && this.date.getUTCFullYear();
18944         var currentMonth = this.date && this.date.getUTCMonth();
18945         
18946         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18947         
18948         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18949             v.removeClass('active');
18950             
18951             if(currentYear === year && k === currentMonth){
18952                 v.addClass('active');
18953             }
18954             
18955             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18956                 v.addClass('disabled');
18957             }
18958             
18959         });
18960         
18961         
18962         year = parseInt(year/10, 10) * 10;
18963         
18964         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18965         
18966         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18967         
18968         year -= 1;
18969         for (var i = -1; i < 11; i++) {
18970             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18971                 tag: 'span',
18972                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18973                 html: year
18974             });
18975             
18976             year += 1;
18977         }
18978     },
18979     
18980     showMode: function(dir) 
18981     {
18982         if (dir) {
18983             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18984         }
18985         
18986         Roo.each(this.picker().select('>div',true).elements, function(v){
18987             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18988             v.hide();
18989         });
18990         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18991     },
18992     
18993     place: function()
18994     {
18995         if(this.isInline) {
18996             return;
18997         }
18998         
18999         this.picker().removeClass(['bottom', 'top']);
19000         
19001         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19002             /*
19003              * place to the top of element!
19004              *
19005              */
19006             
19007             this.picker().addClass('top');
19008             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19009             
19010             return;
19011         }
19012         
19013         this.picker().addClass('bottom');
19014         
19015         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19016     },
19017     
19018     parseDate : function(value)
19019     {
19020         if(!value || value instanceof Date){
19021             return value;
19022         }
19023         var v = Date.parseDate(value, this.format);
19024         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19025             v = Date.parseDate(value, 'Y-m-d');
19026         }
19027         if(!v && this.altFormats){
19028             if(!this.altFormatsArray){
19029                 this.altFormatsArray = this.altFormats.split("|");
19030             }
19031             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19032                 v = Date.parseDate(value, this.altFormatsArray[i]);
19033             }
19034         }
19035         return v;
19036     },
19037     
19038     formatDate : function(date, fmt)
19039     {   
19040         return (!date || !(date instanceof Date)) ?
19041         date : date.dateFormat(fmt || this.format);
19042     },
19043     
19044     onFocus : function()
19045     {
19046         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19047         this.showPopup();
19048     },
19049     
19050     onBlur : function()
19051     {
19052         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19053         
19054         var d = this.inputEl().getValue();
19055         
19056         this.setValue(d);
19057                 
19058         this.hidePopup();
19059     },
19060     
19061     showPopup : function()
19062     {
19063         this.picker().show();
19064         this.update();
19065         this.place();
19066         
19067         this.fireEvent('showpopup', this, this.date);
19068     },
19069     
19070     hidePopup : function()
19071     {
19072         if(this.isInline) {
19073             return;
19074         }
19075         this.picker().hide();
19076         this.viewMode = this.startViewMode;
19077         this.showMode();
19078         
19079         this.fireEvent('hidepopup', this, this.date);
19080         
19081     },
19082     
19083     onMousedown: function(e)
19084     {
19085         e.stopPropagation();
19086         e.preventDefault();
19087     },
19088     
19089     keyup: function(e)
19090     {
19091         Roo.bootstrap.DateField.superclass.keyup.call(this);
19092         this.update();
19093     },
19094
19095     setValue: function(v)
19096     {
19097         if(this.fireEvent('beforeselect', this, v) !== false){
19098             var d = new Date(this.parseDate(v) ).clearTime();
19099         
19100             if(isNaN(d.getTime())){
19101                 this.date = this.viewDate = '';
19102                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19103                 return;
19104             }
19105
19106             v = this.formatDate(d);
19107
19108             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19109
19110             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19111
19112             this.update();
19113
19114             this.fireEvent('select', this, this.date);
19115         }
19116     },
19117     
19118     getValue: function()
19119     {
19120         return this.formatDate(this.date);
19121     },
19122     
19123     fireKey: function(e)
19124     {
19125         if (!this.picker().isVisible()){
19126             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19127                 this.showPopup();
19128             }
19129             return;
19130         }
19131         
19132         var dateChanged = false,
19133         dir, day, month,
19134         newDate, newViewDate;
19135         
19136         switch(e.keyCode){
19137             case 27: // escape
19138                 this.hidePopup();
19139                 e.preventDefault();
19140                 break;
19141             case 37: // left
19142             case 39: // right
19143                 if (!this.keyboardNavigation) {
19144                     break;
19145                 }
19146                 dir = e.keyCode == 37 ? -1 : 1;
19147                 
19148                 if (e.ctrlKey){
19149                     newDate = this.moveYear(this.date, dir);
19150                     newViewDate = this.moveYear(this.viewDate, dir);
19151                 } else if (e.shiftKey){
19152                     newDate = this.moveMonth(this.date, dir);
19153                     newViewDate = this.moveMonth(this.viewDate, dir);
19154                 } else {
19155                     newDate = new Date(this.date);
19156                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19157                     newViewDate = new Date(this.viewDate);
19158                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19159                 }
19160                 if (this.dateWithinRange(newDate)){
19161                     this.date = newDate;
19162                     this.viewDate = newViewDate;
19163                     this.setValue(this.formatDate(this.date));
19164 //                    this.update();
19165                     e.preventDefault();
19166                     dateChanged = true;
19167                 }
19168                 break;
19169             case 38: // up
19170             case 40: // down
19171                 if (!this.keyboardNavigation) {
19172                     break;
19173                 }
19174                 dir = e.keyCode == 38 ? -1 : 1;
19175                 if (e.ctrlKey){
19176                     newDate = this.moveYear(this.date, dir);
19177                     newViewDate = this.moveYear(this.viewDate, dir);
19178                 } else if (e.shiftKey){
19179                     newDate = this.moveMonth(this.date, dir);
19180                     newViewDate = this.moveMonth(this.viewDate, dir);
19181                 } else {
19182                     newDate = new Date(this.date);
19183                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19184                     newViewDate = new Date(this.viewDate);
19185                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19186                 }
19187                 if (this.dateWithinRange(newDate)){
19188                     this.date = newDate;
19189                     this.viewDate = newViewDate;
19190                     this.setValue(this.formatDate(this.date));
19191 //                    this.update();
19192                     e.preventDefault();
19193                     dateChanged = true;
19194                 }
19195                 break;
19196             case 13: // enter
19197                 this.setValue(this.formatDate(this.date));
19198                 this.hidePopup();
19199                 e.preventDefault();
19200                 break;
19201             case 9: // tab
19202                 this.setValue(this.formatDate(this.date));
19203                 this.hidePopup();
19204                 break;
19205             case 16: // shift
19206             case 17: // ctrl
19207             case 18: // alt
19208                 break;
19209             default :
19210                 this.hidePopup();
19211                 
19212         }
19213     },
19214     
19215     
19216     onClick: function(e) 
19217     {
19218         e.stopPropagation();
19219         e.preventDefault();
19220         
19221         var target = e.getTarget();
19222         
19223         if(target.nodeName.toLowerCase() === 'i'){
19224             target = Roo.get(target).dom.parentNode;
19225         }
19226         
19227         var nodeName = target.nodeName;
19228         var className = target.className;
19229         var html = target.innerHTML;
19230         //Roo.log(nodeName);
19231         
19232         switch(nodeName.toLowerCase()) {
19233             case 'th':
19234                 switch(className) {
19235                     case 'switch':
19236                         this.showMode(1);
19237                         break;
19238                     case 'prev':
19239                     case 'next':
19240                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19241                         switch(this.viewMode){
19242                                 case 0:
19243                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19244                                         break;
19245                                 case 1:
19246                                 case 2:
19247                                         this.viewDate = this.moveYear(this.viewDate, dir);
19248                                         break;
19249                         }
19250                         this.fill();
19251                         break;
19252                     case 'today':
19253                         var date = new Date();
19254                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19255 //                        this.fill()
19256                         this.setValue(this.formatDate(this.date));
19257                         
19258                         this.hidePopup();
19259                         break;
19260                 }
19261                 break;
19262             case 'span':
19263                 if (className.indexOf('disabled') < 0) {
19264                     this.viewDate.setUTCDate(1);
19265                     if (className.indexOf('month') > -1) {
19266                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19267                     } else {
19268                         var year = parseInt(html, 10) || 0;
19269                         this.viewDate.setUTCFullYear(year);
19270                         
19271                     }
19272                     
19273                     if(this.singleMode){
19274                         this.setValue(this.formatDate(this.viewDate));
19275                         this.hidePopup();
19276                         return;
19277                     }
19278                     
19279                     this.showMode(-1);
19280                     this.fill();
19281                 }
19282                 break;
19283                 
19284             case 'td':
19285                 //Roo.log(className);
19286                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19287                     var day = parseInt(html, 10) || 1;
19288                     var year = this.viewDate.getUTCFullYear(),
19289                         month = this.viewDate.getUTCMonth();
19290
19291                     if (className.indexOf('old') > -1) {
19292                         if(month === 0 ){
19293                             month = 11;
19294                             year -= 1;
19295                         }else{
19296                             month -= 1;
19297                         }
19298                     } else if (className.indexOf('new') > -1) {
19299                         if (month == 11) {
19300                             month = 0;
19301                             year += 1;
19302                         } else {
19303                             month += 1;
19304                         }
19305                     }
19306                     //Roo.log([year,month,day]);
19307                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19308                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19309 //                    this.fill();
19310                     //Roo.log(this.formatDate(this.date));
19311                     this.setValue(this.formatDate(this.date));
19312                     this.hidePopup();
19313                 }
19314                 break;
19315         }
19316     },
19317     
19318     setStartDate: function(startDate)
19319     {
19320         this.startDate = startDate || -Infinity;
19321         if (this.startDate !== -Infinity) {
19322             this.startDate = this.parseDate(this.startDate);
19323         }
19324         this.update();
19325         this.updateNavArrows();
19326     },
19327
19328     setEndDate: function(endDate)
19329     {
19330         this.endDate = endDate || Infinity;
19331         if (this.endDate !== Infinity) {
19332             this.endDate = this.parseDate(this.endDate);
19333         }
19334         this.update();
19335         this.updateNavArrows();
19336     },
19337     
19338     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19339     {
19340         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19341         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19342             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19343         }
19344         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19345             return parseInt(d, 10);
19346         });
19347         this.update();
19348         this.updateNavArrows();
19349     },
19350     
19351     updateNavArrows: function() 
19352     {
19353         if(this.singleMode){
19354             return;
19355         }
19356         
19357         var d = new Date(this.viewDate),
19358         year = d.getUTCFullYear(),
19359         month = d.getUTCMonth();
19360         
19361         Roo.each(this.picker().select('.prev', true).elements, function(v){
19362             v.show();
19363             switch (this.viewMode) {
19364                 case 0:
19365
19366                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19367                         v.hide();
19368                     }
19369                     break;
19370                 case 1:
19371                 case 2:
19372                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19373                         v.hide();
19374                     }
19375                     break;
19376             }
19377         });
19378         
19379         Roo.each(this.picker().select('.next', true).elements, function(v){
19380             v.show();
19381             switch (this.viewMode) {
19382                 case 0:
19383
19384                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19385                         v.hide();
19386                     }
19387                     break;
19388                 case 1:
19389                 case 2:
19390                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19391                         v.hide();
19392                     }
19393                     break;
19394             }
19395         })
19396     },
19397     
19398     moveMonth: function(date, dir)
19399     {
19400         if (!dir) {
19401             return date;
19402         }
19403         var new_date = new Date(date.valueOf()),
19404         day = new_date.getUTCDate(),
19405         month = new_date.getUTCMonth(),
19406         mag = Math.abs(dir),
19407         new_month, test;
19408         dir = dir > 0 ? 1 : -1;
19409         if (mag == 1){
19410             test = dir == -1
19411             // If going back one month, make sure month is not current month
19412             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19413             ? function(){
19414                 return new_date.getUTCMonth() == month;
19415             }
19416             // If going forward one month, make sure month is as expected
19417             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19418             : function(){
19419                 return new_date.getUTCMonth() != new_month;
19420             };
19421             new_month = month + dir;
19422             new_date.setUTCMonth(new_month);
19423             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19424             if (new_month < 0 || new_month > 11) {
19425                 new_month = (new_month + 12) % 12;
19426             }
19427         } else {
19428             // For magnitudes >1, move one month at a time...
19429             for (var i=0; i<mag; i++) {
19430                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19431                 new_date = this.moveMonth(new_date, dir);
19432             }
19433             // ...then reset the day, keeping it in the new month
19434             new_month = new_date.getUTCMonth();
19435             new_date.setUTCDate(day);
19436             test = function(){
19437                 return new_month != new_date.getUTCMonth();
19438             };
19439         }
19440         // Common date-resetting loop -- if date is beyond end of month, make it
19441         // end of month
19442         while (test()){
19443             new_date.setUTCDate(--day);
19444             new_date.setUTCMonth(new_month);
19445         }
19446         return new_date;
19447     },
19448
19449     moveYear: function(date, dir)
19450     {
19451         return this.moveMonth(date, dir*12);
19452     },
19453
19454     dateWithinRange: function(date)
19455     {
19456         return date >= this.startDate && date <= this.endDate;
19457     },
19458
19459     
19460     remove: function() 
19461     {
19462         this.picker().remove();
19463     },
19464     
19465     validateValue : function(value)
19466     {
19467         if(this.getVisibilityEl().hasClass('hidden')){
19468             return true;
19469         }
19470         
19471         if(value.length < 1)  {
19472             if(this.allowBlank){
19473                 return true;
19474             }
19475             return false;
19476         }
19477         
19478         if(value.length < this.minLength){
19479             return false;
19480         }
19481         if(value.length > this.maxLength){
19482             return false;
19483         }
19484         if(this.vtype){
19485             var vt = Roo.form.VTypes;
19486             if(!vt[this.vtype](value, this)){
19487                 return false;
19488             }
19489         }
19490         if(typeof this.validator == "function"){
19491             var msg = this.validator(value);
19492             if(msg !== true){
19493                 return false;
19494             }
19495         }
19496         
19497         if(this.regex && !this.regex.test(value)){
19498             return false;
19499         }
19500         
19501         if(typeof(this.parseDate(value)) == 'undefined'){
19502             return false;
19503         }
19504         
19505         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19506             return false;
19507         }      
19508         
19509         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19510             return false;
19511         } 
19512         
19513         
19514         return true;
19515     },
19516     
19517     reset : function()
19518     {
19519         this.date = this.viewDate = '';
19520         
19521         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19522     }
19523    
19524 });
19525
19526 Roo.apply(Roo.bootstrap.DateField,  {
19527     
19528     head : {
19529         tag: 'thead',
19530         cn: [
19531         {
19532             tag: 'tr',
19533             cn: [
19534             {
19535                 tag: 'th',
19536                 cls: 'prev',
19537                 html: '<i class="fa fa-arrow-left"/>'
19538             },
19539             {
19540                 tag: 'th',
19541                 cls: 'switch',
19542                 colspan: '5'
19543             },
19544             {
19545                 tag: 'th',
19546                 cls: 'next',
19547                 html: '<i class="fa fa-arrow-right"/>'
19548             }
19549
19550             ]
19551         }
19552         ]
19553     },
19554     
19555     content : {
19556         tag: 'tbody',
19557         cn: [
19558         {
19559             tag: 'tr',
19560             cn: [
19561             {
19562                 tag: 'td',
19563                 colspan: '7'
19564             }
19565             ]
19566         }
19567         ]
19568     },
19569     
19570     footer : {
19571         tag: 'tfoot',
19572         cn: [
19573         {
19574             tag: 'tr',
19575             cn: [
19576             {
19577                 tag: 'th',
19578                 colspan: '7',
19579                 cls: 'today'
19580             }
19581                     
19582             ]
19583         }
19584         ]
19585     },
19586     
19587     dates:{
19588         en: {
19589             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19590             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19591             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19592             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19593             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19594             today: "Today"
19595         }
19596     },
19597     
19598     modes: [
19599     {
19600         clsName: 'days',
19601         navFnc: 'Month',
19602         navStep: 1
19603     },
19604     {
19605         clsName: 'months',
19606         navFnc: 'FullYear',
19607         navStep: 1
19608     },
19609     {
19610         clsName: 'years',
19611         navFnc: 'FullYear',
19612         navStep: 10
19613     }]
19614 });
19615
19616 Roo.apply(Roo.bootstrap.DateField,  {
19617   
19618     template : {
19619         tag: 'div',
19620         cls: 'datepicker dropdown-menu roo-dynamic',
19621         cn: [
19622         {
19623             tag: 'div',
19624             cls: 'datepicker-days',
19625             cn: [
19626             {
19627                 tag: 'table',
19628                 cls: 'table-condensed',
19629                 cn:[
19630                 Roo.bootstrap.DateField.head,
19631                 {
19632                     tag: 'tbody'
19633                 },
19634                 Roo.bootstrap.DateField.footer
19635                 ]
19636             }
19637             ]
19638         },
19639         {
19640             tag: 'div',
19641             cls: 'datepicker-months',
19642             cn: [
19643             {
19644                 tag: 'table',
19645                 cls: 'table-condensed',
19646                 cn:[
19647                 Roo.bootstrap.DateField.head,
19648                 Roo.bootstrap.DateField.content,
19649                 Roo.bootstrap.DateField.footer
19650                 ]
19651             }
19652             ]
19653         },
19654         {
19655             tag: 'div',
19656             cls: 'datepicker-years',
19657             cn: [
19658             {
19659                 tag: 'table',
19660                 cls: 'table-condensed',
19661                 cn:[
19662                 Roo.bootstrap.DateField.head,
19663                 Roo.bootstrap.DateField.content,
19664                 Roo.bootstrap.DateField.footer
19665                 ]
19666             }
19667             ]
19668         }
19669         ]
19670     }
19671 });
19672
19673  
19674
19675  /*
19676  * - LGPL
19677  *
19678  * TimeField
19679  * 
19680  */
19681
19682 /**
19683  * @class Roo.bootstrap.TimeField
19684  * @extends Roo.bootstrap.Input
19685  * Bootstrap DateField class
19686  * 
19687  * 
19688  * @constructor
19689  * Create a new TimeField
19690  * @param {Object} config The config object
19691  */
19692
19693 Roo.bootstrap.TimeField = function(config){
19694     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19695     this.addEvents({
19696             /**
19697              * @event show
19698              * Fires when this field show.
19699              * @param {Roo.bootstrap.DateField} thisthis
19700              * @param {Mixed} date The date value
19701              */
19702             show : true,
19703             /**
19704              * @event show
19705              * Fires when this field hide.
19706              * @param {Roo.bootstrap.DateField} this
19707              * @param {Mixed} date The date value
19708              */
19709             hide : true,
19710             /**
19711              * @event select
19712              * Fires when select a date.
19713              * @param {Roo.bootstrap.DateField} this
19714              * @param {Mixed} date The date value
19715              */
19716             select : true
19717         });
19718 };
19719
19720 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19721     
19722     /**
19723      * @cfg {String} format
19724      * The default time format string which can be overriden for localization support.  The format must be
19725      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19726      */
19727     format : "H:i",
19728        
19729     onRender: function(ct, position)
19730     {
19731         
19732         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19733                 
19734         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19735         
19736         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19737         
19738         this.pop = this.picker().select('>.datepicker-time',true).first();
19739         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19740         
19741         this.picker().on('mousedown', this.onMousedown, this);
19742         this.picker().on('click', this.onClick, this);
19743         
19744         this.picker().addClass('datepicker-dropdown');
19745     
19746         this.fillTime();
19747         this.update();
19748             
19749         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19750         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19751         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19752         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19753         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19754         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19755
19756     },
19757     
19758     fireKey: function(e){
19759         if (!this.picker().isVisible()){
19760             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19761                 this.show();
19762             }
19763             return;
19764         }
19765
19766         e.preventDefault();
19767         
19768         switch(e.keyCode){
19769             case 27: // escape
19770                 this.hide();
19771                 break;
19772             case 37: // left
19773             case 39: // right
19774                 this.onTogglePeriod();
19775                 break;
19776             case 38: // up
19777                 this.onIncrementMinutes();
19778                 break;
19779             case 40: // down
19780                 this.onDecrementMinutes();
19781                 break;
19782             case 13: // enter
19783             case 9: // tab
19784                 this.setTime();
19785                 break;
19786         }
19787     },
19788     
19789     onClick: function(e) {
19790         e.stopPropagation();
19791         e.preventDefault();
19792     },
19793     
19794     picker : function()
19795     {
19796         return this.el.select('.datepicker', true).first();
19797     },
19798     
19799     fillTime: function()
19800     {    
19801         var time = this.pop.select('tbody', true).first();
19802         
19803         time.dom.innerHTML = '';
19804         
19805         time.createChild({
19806             tag: 'tr',
19807             cn: [
19808                 {
19809                     tag: 'td',
19810                     cn: [
19811                         {
19812                             tag: 'a',
19813                             href: '#',
19814                             cls: 'btn',
19815                             cn: [
19816                                 {
19817                                     tag: 'span',
19818                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19819                                 }
19820                             ]
19821                         } 
19822                     ]
19823                 },
19824                 {
19825                     tag: 'td',
19826                     cls: 'separator'
19827                 },
19828                 {
19829                     tag: 'td',
19830                     cn: [
19831                         {
19832                             tag: 'a',
19833                             href: '#',
19834                             cls: 'btn',
19835                             cn: [
19836                                 {
19837                                     tag: 'span',
19838                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19839                                 }
19840                             ]
19841                         }
19842                     ]
19843                 },
19844                 {
19845                     tag: 'td',
19846                     cls: 'separator'
19847                 }
19848             ]
19849         });
19850         
19851         time.createChild({
19852             tag: 'tr',
19853             cn: [
19854                 {
19855                     tag: 'td',
19856                     cn: [
19857                         {
19858                             tag: 'span',
19859                             cls: 'timepicker-hour',
19860                             html: '00'
19861                         }  
19862                     ]
19863                 },
19864                 {
19865                     tag: 'td',
19866                     cls: 'separator',
19867                     html: ':'
19868                 },
19869                 {
19870                     tag: 'td',
19871                     cn: [
19872                         {
19873                             tag: 'span',
19874                             cls: 'timepicker-minute',
19875                             html: '00'
19876                         }  
19877                     ]
19878                 },
19879                 {
19880                     tag: 'td',
19881                     cls: 'separator'
19882                 },
19883                 {
19884                     tag: 'td',
19885                     cn: [
19886                         {
19887                             tag: 'button',
19888                             type: 'button',
19889                             cls: 'btn btn-primary period',
19890                             html: 'AM'
19891                             
19892                         }
19893                     ]
19894                 }
19895             ]
19896         });
19897         
19898         time.createChild({
19899             tag: 'tr',
19900             cn: [
19901                 {
19902                     tag: 'td',
19903                     cn: [
19904                         {
19905                             tag: 'a',
19906                             href: '#',
19907                             cls: 'btn',
19908                             cn: [
19909                                 {
19910                                     tag: 'span',
19911                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19912                                 }
19913                             ]
19914                         }
19915                     ]
19916                 },
19917                 {
19918                     tag: 'td',
19919                     cls: 'separator'
19920                 },
19921                 {
19922                     tag: 'td',
19923                     cn: [
19924                         {
19925                             tag: 'a',
19926                             href: '#',
19927                             cls: 'btn',
19928                             cn: [
19929                                 {
19930                                     tag: 'span',
19931                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19932                                 }
19933                             ]
19934                         }
19935                     ]
19936                 },
19937                 {
19938                     tag: 'td',
19939                     cls: 'separator'
19940                 }
19941             ]
19942         });
19943         
19944     },
19945     
19946     update: function()
19947     {
19948         
19949         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19950         
19951         this.fill();
19952     },
19953     
19954     fill: function() 
19955     {
19956         var hours = this.time.getHours();
19957         var minutes = this.time.getMinutes();
19958         var period = 'AM';
19959         
19960         if(hours > 11){
19961             period = 'PM';
19962         }
19963         
19964         if(hours == 0){
19965             hours = 12;
19966         }
19967         
19968         
19969         if(hours > 12){
19970             hours = hours - 12;
19971         }
19972         
19973         if(hours < 10){
19974             hours = '0' + hours;
19975         }
19976         
19977         if(minutes < 10){
19978             minutes = '0' + minutes;
19979         }
19980         
19981         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19982         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19983         this.pop.select('button', true).first().dom.innerHTML = period;
19984         
19985     },
19986     
19987     place: function()
19988     {   
19989         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19990         
19991         var cls = ['bottom'];
19992         
19993         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19994             cls.pop();
19995             cls.push('top');
19996         }
19997         
19998         cls.push('right');
19999         
20000         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20001             cls.pop();
20002             cls.push('left');
20003         }
20004         
20005         this.picker().addClass(cls.join('-'));
20006         
20007         var _this = this;
20008         
20009         Roo.each(cls, function(c){
20010             if(c == 'bottom'){
20011                 _this.picker().setTop(_this.inputEl().getHeight());
20012                 return;
20013             }
20014             if(c == 'top'){
20015                 _this.picker().setTop(0 - _this.picker().getHeight());
20016                 return;
20017             }
20018             
20019             if(c == 'left'){
20020                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20021                 return;
20022             }
20023             if(c == 'right'){
20024                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20025                 return;
20026             }
20027         });
20028         
20029     },
20030   
20031     onFocus : function()
20032     {
20033         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20034         this.show();
20035     },
20036     
20037     onBlur : function()
20038     {
20039         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20040         this.hide();
20041     },
20042     
20043     show : function()
20044     {
20045         this.picker().show();
20046         this.pop.show();
20047         this.update();
20048         this.place();
20049         
20050         this.fireEvent('show', this, this.date);
20051     },
20052     
20053     hide : function()
20054     {
20055         this.picker().hide();
20056         this.pop.hide();
20057         
20058         this.fireEvent('hide', this, this.date);
20059     },
20060     
20061     setTime : function()
20062     {
20063         this.hide();
20064         this.setValue(this.time.format(this.format));
20065         
20066         this.fireEvent('select', this, this.date);
20067         
20068         
20069     },
20070     
20071     onMousedown: function(e){
20072         e.stopPropagation();
20073         e.preventDefault();
20074     },
20075     
20076     onIncrementHours: function()
20077     {
20078         Roo.log('onIncrementHours');
20079         this.time = this.time.add(Date.HOUR, 1);
20080         this.update();
20081         
20082     },
20083     
20084     onDecrementHours: function()
20085     {
20086         Roo.log('onDecrementHours');
20087         this.time = this.time.add(Date.HOUR, -1);
20088         this.update();
20089     },
20090     
20091     onIncrementMinutes: function()
20092     {
20093         Roo.log('onIncrementMinutes');
20094         this.time = this.time.add(Date.MINUTE, 1);
20095         this.update();
20096     },
20097     
20098     onDecrementMinutes: function()
20099     {
20100         Roo.log('onDecrementMinutes');
20101         this.time = this.time.add(Date.MINUTE, -1);
20102         this.update();
20103     },
20104     
20105     onTogglePeriod: function()
20106     {
20107         Roo.log('onTogglePeriod');
20108         this.time = this.time.add(Date.HOUR, 12);
20109         this.update();
20110     }
20111     
20112    
20113 });
20114
20115 Roo.apply(Roo.bootstrap.TimeField,  {
20116     
20117     content : {
20118         tag: 'tbody',
20119         cn: [
20120             {
20121                 tag: 'tr',
20122                 cn: [
20123                 {
20124                     tag: 'td',
20125                     colspan: '7'
20126                 }
20127                 ]
20128             }
20129         ]
20130     },
20131     
20132     footer : {
20133         tag: 'tfoot',
20134         cn: [
20135             {
20136                 tag: 'tr',
20137                 cn: [
20138                 {
20139                     tag: 'th',
20140                     colspan: '7',
20141                     cls: '',
20142                     cn: [
20143                         {
20144                             tag: 'button',
20145                             cls: 'btn btn-info ok',
20146                             html: 'OK'
20147                         }
20148                     ]
20149                 }
20150
20151                 ]
20152             }
20153         ]
20154     }
20155 });
20156
20157 Roo.apply(Roo.bootstrap.TimeField,  {
20158   
20159     template : {
20160         tag: 'div',
20161         cls: 'datepicker dropdown-menu',
20162         cn: [
20163             {
20164                 tag: 'div',
20165                 cls: 'datepicker-time',
20166                 cn: [
20167                 {
20168                     tag: 'table',
20169                     cls: 'table-condensed',
20170                     cn:[
20171                     Roo.bootstrap.TimeField.content,
20172                     Roo.bootstrap.TimeField.footer
20173                     ]
20174                 }
20175                 ]
20176             }
20177         ]
20178     }
20179 });
20180
20181  
20182
20183  /*
20184  * - LGPL
20185  *
20186  * MonthField
20187  * 
20188  */
20189
20190 /**
20191  * @class Roo.bootstrap.MonthField
20192  * @extends Roo.bootstrap.Input
20193  * Bootstrap MonthField class
20194  * 
20195  * @cfg {String} language default en
20196  * 
20197  * @constructor
20198  * Create a new MonthField
20199  * @param {Object} config The config object
20200  */
20201
20202 Roo.bootstrap.MonthField = function(config){
20203     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20204     
20205     this.addEvents({
20206         /**
20207          * @event show
20208          * Fires when this field show.
20209          * @param {Roo.bootstrap.MonthField} this
20210          * @param {Mixed} date The date value
20211          */
20212         show : true,
20213         /**
20214          * @event show
20215          * Fires when this field hide.
20216          * @param {Roo.bootstrap.MonthField} this
20217          * @param {Mixed} date The date value
20218          */
20219         hide : true,
20220         /**
20221          * @event select
20222          * Fires when select a date.
20223          * @param {Roo.bootstrap.MonthField} this
20224          * @param {String} oldvalue The old value
20225          * @param {String} newvalue The new value
20226          */
20227         select : true
20228     });
20229 };
20230
20231 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20232     
20233     onRender: function(ct, position)
20234     {
20235         
20236         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20237         
20238         this.language = this.language || 'en';
20239         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20240         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20241         
20242         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20243         this.isInline = false;
20244         this.isInput = true;
20245         this.component = this.el.select('.add-on', true).first() || false;
20246         this.component = (this.component && this.component.length === 0) ? false : this.component;
20247         this.hasInput = this.component && this.inputEL().length;
20248         
20249         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20250         
20251         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20252         
20253         this.picker().on('mousedown', this.onMousedown, this);
20254         this.picker().on('click', this.onClick, this);
20255         
20256         this.picker().addClass('datepicker-dropdown');
20257         
20258         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20259             v.setStyle('width', '189px');
20260         });
20261         
20262         this.fillMonths();
20263         
20264         this.update();
20265         
20266         if(this.isInline) {
20267             this.show();
20268         }
20269         
20270     },
20271     
20272     setValue: function(v, suppressEvent)
20273     {   
20274         var o = this.getValue();
20275         
20276         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20277         
20278         this.update();
20279
20280         if(suppressEvent !== true){
20281             this.fireEvent('select', this, o, v);
20282         }
20283         
20284     },
20285     
20286     getValue: function()
20287     {
20288         return this.value;
20289     },
20290     
20291     onClick: function(e) 
20292     {
20293         e.stopPropagation();
20294         e.preventDefault();
20295         
20296         var target = e.getTarget();
20297         
20298         if(target.nodeName.toLowerCase() === 'i'){
20299             target = Roo.get(target).dom.parentNode;
20300         }
20301         
20302         var nodeName = target.nodeName;
20303         var className = target.className;
20304         var html = target.innerHTML;
20305         
20306         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20307             return;
20308         }
20309         
20310         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20311         
20312         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20313         
20314         this.hide();
20315                         
20316     },
20317     
20318     picker : function()
20319     {
20320         return this.pickerEl;
20321     },
20322     
20323     fillMonths: function()
20324     {    
20325         var i = 0;
20326         var months = this.picker().select('>.datepicker-months td', true).first();
20327         
20328         months.dom.innerHTML = '';
20329         
20330         while (i < 12) {
20331             var month = {
20332                 tag: 'span',
20333                 cls: 'month',
20334                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20335             };
20336             
20337             months.createChild(month);
20338         }
20339         
20340     },
20341     
20342     update: function()
20343     {
20344         var _this = this;
20345         
20346         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20347             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20348         }
20349         
20350         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20351             e.removeClass('active');
20352             
20353             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20354                 e.addClass('active');
20355             }
20356         })
20357     },
20358     
20359     place: function()
20360     {
20361         if(this.isInline) {
20362             return;
20363         }
20364         
20365         this.picker().removeClass(['bottom', 'top']);
20366         
20367         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20368             /*
20369              * place to the top of element!
20370              *
20371              */
20372             
20373             this.picker().addClass('top');
20374             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20375             
20376             return;
20377         }
20378         
20379         this.picker().addClass('bottom');
20380         
20381         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20382     },
20383     
20384     onFocus : function()
20385     {
20386         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20387         this.show();
20388     },
20389     
20390     onBlur : function()
20391     {
20392         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20393         
20394         var d = this.inputEl().getValue();
20395         
20396         this.setValue(d);
20397                 
20398         this.hide();
20399     },
20400     
20401     show : function()
20402     {
20403         this.picker().show();
20404         this.picker().select('>.datepicker-months', true).first().show();
20405         this.update();
20406         this.place();
20407         
20408         this.fireEvent('show', this, this.date);
20409     },
20410     
20411     hide : function()
20412     {
20413         if(this.isInline) {
20414             return;
20415         }
20416         this.picker().hide();
20417         this.fireEvent('hide', this, this.date);
20418         
20419     },
20420     
20421     onMousedown: function(e)
20422     {
20423         e.stopPropagation();
20424         e.preventDefault();
20425     },
20426     
20427     keyup: function(e)
20428     {
20429         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20430         this.update();
20431     },
20432
20433     fireKey: function(e)
20434     {
20435         if (!this.picker().isVisible()){
20436             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20437                 this.show();
20438             }
20439             return;
20440         }
20441         
20442         var dir;
20443         
20444         switch(e.keyCode){
20445             case 27: // escape
20446                 this.hide();
20447                 e.preventDefault();
20448                 break;
20449             case 37: // left
20450             case 39: // right
20451                 dir = e.keyCode == 37 ? -1 : 1;
20452                 
20453                 this.vIndex = this.vIndex + dir;
20454                 
20455                 if(this.vIndex < 0){
20456                     this.vIndex = 0;
20457                 }
20458                 
20459                 if(this.vIndex > 11){
20460                     this.vIndex = 11;
20461                 }
20462                 
20463                 if(isNaN(this.vIndex)){
20464                     this.vIndex = 0;
20465                 }
20466                 
20467                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20468                 
20469                 break;
20470             case 38: // up
20471             case 40: // down
20472                 
20473                 dir = e.keyCode == 38 ? -1 : 1;
20474                 
20475                 this.vIndex = this.vIndex + dir * 4;
20476                 
20477                 if(this.vIndex < 0){
20478                     this.vIndex = 0;
20479                 }
20480                 
20481                 if(this.vIndex > 11){
20482                     this.vIndex = 11;
20483                 }
20484                 
20485                 if(isNaN(this.vIndex)){
20486                     this.vIndex = 0;
20487                 }
20488                 
20489                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20490                 break;
20491                 
20492             case 13: // enter
20493                 
20494                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20495                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20496                 }
20497                 
20498                 this.hide();
20499                 e.preventDefault();
20500                 break;
20501             case 9: // tab
20502                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20503                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20504                 }
20505                 this.hide();
20506                 break;
20507             case 16: // shift
20508             case 17: // ctrl
20509             case 18: // alt
20510                 break;
20511             default :
20512                 this.hide();
20513                 
20514         }
20515     },
20516     
20517     remove: function() 
20518     {
20519         this.picker().remove();
20520     }
20521    
20522 });
20523
20524 Roo.apply(Roo.bootstrap.MonthField,  {
20525     
20526     content : {
20527         tag: 'tbody',
20528         cn: [
20529         {
20530             tag: 'tr',
20531             cn: [
20532             {
20533                 tag: 'td',
20534                 colspan: '7'
20535             }
20536             ]
20537         }
20538         ]
20539     },
20540     
20541     dates:{
20542         en: {
20543             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20544             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20545         }
20546     }
20547 });
20548
20549 Roo.apply(Roo.bootstrap.MonthField,  {
20550   
20551     template : {
20552         tag: 'div',
20553         cls: 'datepicker dropdown-menu roo-dynamic',
20554         cn: [
20555             {
20556                 tag: 'div',
20557                 cls: 'datepicker-months',
20558                 cn: [
20559                 {
20560                     tag: 'table',
20561                     cls: 'table-condensed',
20562                     cn:[
20563                         Roo.bootstrap.DateField.content
20564                     ]
20565                 }
20566                 ]
20567             }
20568         ]
20569     }
20570 });
20571
20572  
20573
20574  
20575  /*
20576  * - LGPL
20577  *
20578  * CheckBox
20579  * 
20580  */
20581
20582 /**
20583  * @class Roo.bootstrap.CheckBox
20584  * @extends Roo.bootstrap.Input
20585  * Bootstrap CheckBox class
20586  * 
20587  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20588  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20589  * @cfg {String} boxLabel The text that appears beside the checkbox
20590  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20591  * @cfg {Boolean} checked initnal the element
20592  * @cfg {Boolean} inline inline the element (default false)
20593  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20594  * @cfg {String} tooltip label tooltip
20595  * 
20596  * @constructor
20597  * Create a new CheckBox
20598  * @param {Object} config The config object
20599  */
20600
20601 Roo.bootstrap.CheckBox = function(config){
20602     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20603    
20604     this.addEvents({
20605         /**
20606         * @event check
20607         * Fires when the element is checked or unchecked.
20608         * @param {Roo.bootstrap.CheckBox} this This input
20609         * @param {Boolean} checked The new checked value
20610         */
20611        check : true,
20612        /**
20613         * @event click
20614         * Fires when the element is click.
20615         * @param {Roo.bootstrap.CheckBox} this This input
20616         */
20617        click : true
20618     });
20619     
20620 };
20621
20622 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20623   
20624     inputType: 'checkbox',
20625     inputValue: 1,
20626     valueOff: 0,
20627     boxLabel: false,
20628     checked: false,
20629     weight : false,
20630     inline: false,
20631     tooltip : '',
20632     
20633     getAutoCreate : function()
20634     {
20635         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20636         
20637         var id = Roo.id();
20638         
20639         var cfg = {};
20640         
20641         cfg.cls = 'form-group ' + this.inputType; //input-group
20642         
20643         if(this.inline){
20644             cfg.cls += ' ' + this.inputType + '-inline';
20645         }
20646         
20647         var input =  {
20648             tag: 'input',
20649             id : id,
20650             type : this.inputType,
20651             value : this.inputValue,
20652             cls : 'roo-' + this.inputType, //'form-box',
20653             placeholder : this.placeholder || ''
20654             
20655         };
20656         
20657         if(this.inputType != 'radio'){
20658             var hidden =  {
20659                 tag: 'input',
20660                 type : 'hidden',
20661                 cls : 'roo-hidden-value',
20662                 value : this.checked ? this.inputValue : this.valueOff
20663             };
20664         }
20665         
20666             
20667         if (this.weight) { // Validity check?
20668             cfg.cls += " " + this.inputType + "-" + this.weight;
20669         }
20670         
20671         if (this.disabled) {
20672             input.disabled=true;
20673         }
20674         
20675         if(this.checked){
20676             input.checked = this.checked;
20677         }
20678         
20679         if (this.name) {
20680             
20681             input.name = this.name;
20682             
20683             if(this.inputType != 'radio'){
20684                 hidden.name = this.name;
20685                 input.name = '_hidden_' + this.name;
20686             }
20687         }
20688         
20689         if (this.size) {
20690             input.cls += ' input-' + this.size;
20691         }
20692         
20693         var settings=this;
20694         
20695         ['xs','sm','md','lg'].map(function(size){
20696             if (settings[size]) {
20697                 cfg.cls += ' col-' + size + '-' + settings[size];
20698             }
20699         });
20700         
20701         var inputblock = input;
20702          
20703         if (this.before || this.after) {
20704             
20705             inputblock = {
20706                 cls : 'input-group',
20707                 cn :  [] 
20708             };
20709             
20710             if (this.before) {
20711                 inputblock.cn.push({
20712                     tag :'span',
20713                     cls : 'input-group-addon',
20714                     html : this.before
20715                 });
20716             }
20717             
20718             inputblock.cn.push(input);
20719             
20720             if(this.inputType != 'radio'){
20721                 inputblock.cn.push(hidden);
20722             }
20723             
20724             if (this.after) {
20725                 inputblock.cn.push({
20726                     tag :'span',
20727                     cls : 'input-group-addon',
20728                     html : this.after
20729                 });
20730             }
20731             
20732         }
20733         
20734         if (align ==='left' && this.fieldLabel.length) {
20735 //                Roo.log("left and has label");
20736             cfg.cn = [
20737                 {
20738                     tag: 'label',
20739                     'for' :  id,
20740                     cls : 'control-label',
20741                     html : this.fieldLabel
20742                 },
20743                 {
20744                     cls : "", 
20745                     cn: [
20746                         inputblock
20747                     ]
20748                 }
20749             ];
20750             
20751             if(this.labelWidth > 12){
20752                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20753             }
20754             
20755             if(this.labelWidth < 13 && this.labelmd == 0){
20756                 this.labelmd = this.labelWidth;
20757             }
20758             
20759             if(this.labellg > 0){
20760                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20761                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20762             }
20763             
20764             if(this.labelmd > 0){
20765                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20766                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20767             }
20768             
20769             if(this.labelsm > 0){
20770                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20771                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20772             }
20773             
20774             if(this.labelxs > 0){
20775                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20776                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20777             }
20778             
20779         } else if ( this.fieldLabel.length) {
20780 //                Roo.log(" label");
20781                 cfg.cn = [
20782                    
20783                     {
20784                         tag: this.boxLabel ? 'span' : 'label',
20785                         'for': id,
20786                         cls: 'control-label box-input-label',
20787                         //cls : 'input-group-addon',
20788                         html : this.fieldLabel
20789                     },
20790                     
20791                     inputblock
20792                     
20793                 ];
20794
20795         } else {
20796             
20797 //                Roo.log(" no label && no align");
20798                 cfg.cn = [  inputblock ] ;
20799                 
20800                 
20801         }
20802         
20803         if(this.boxLabel){
20804              var boxLabelCfg = {
20805                 tag: 'label',
20806                 //'for': id, // box label is handled by onclick - so no for...
20807                 cls: 'box-label',
20808                 html: this.boxLabel
20809             };
20810             
20811             if(this.tooltip){
20812                 boxLabelCfg.tooltip = this.tooltip;
20813             }
20814              
20815             cfg.cn.push(boxLabelCfg);
20816         }
20817         
20818         if(this.inputType != 'radio'){
20819             cfg.cn.push(hidden);
20820         }
20821         
20822         return cfg;
20823         
20824     },
20825     
20826     /**
20827      * return the real input element.
20828      */
20829     inputEl: function ()
20830     {
20831         return this.el.select('input.roo-' + this.inputType,true).first();
20832     },
20833     hiddenEl: function ()
20834     {
20835         return this.el.select('input.roo-hidden-value',true).first();
20836     },
20837     
20838     labelEl: function()
20839     {
20840         return this.el.select('label.control-label',true).first();
20841     },
20842     /* depricated... */
20843     
20844     label: function()
20845     {
20846         return this.labelEl();
20847     },
20848     
20849     boxLabelEl: function()
20850     {
20851         return this.el.select('label.box-label',true).first();
20852     },
20853     
20854     initEvents : function()
20855     {
20856 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20857         
20858         this.inputEl().on('click', this.onClick,  this);
20859         
20860         if (this.boxLabel) { 
20861             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20862         }
20863         
20864         this.startValue = this.getValue();
20865         
20866         if(this.groupId){
20867             Roo.bootstrap.CheckBox.register(this);
20868         }
20869     },
20870     
20871     onClick : function(e)
20872     {   
20873         if(this.fireEvent('click', this, e) !== false){
20874             this.setChecked(!this.checked);
20875         }
20876         
20877     },
20878     
20879     setChecked : function(state,suppressEvent)
20880     {
20881         this.startValue = this.getValue();
20882
20883         if(this.inputType == 'radio'){
20884             
20885             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20886                 e.dom.checked = false;
20887             });
20888             
20889             this.inputEl().dom.checked = true;
20890             
20891             this.inputEl().dom.value = this.inputValue;
20892             
20893             if(suppressEvent !== true){
20894                 this.fireEvent('check', this, true);
20895             }
20896             
20897             this.validate();
20898             
20899             return;
20900         }
20901         
20902         this.checked = state;
20903         
20904         this.inputEl().dom.checked = state;
20905         
20906         
20907         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20908         
20909         if(suppressEvent !== true){
20910             this.fireEvent('check', this, state);
20911         }
20912         
20913         this.validate();
20914     },
20915     
20916     getValue : function()
20917     {
20918         if(this.inputType == 'radio'){
20919             return this.getGroupValue();
20920         }
20921         
20922         return this.hiddenEl().dom.value;
20923         
20924     },
20925     
20926     getGroupValue : function()
20927     {
20928         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20929             return '';
20930         }
20931         
20932         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20933     },
20934     
20935     setValue : function(v,suppressEvent)
20936     {
20937         if(this.inputType == 'radio'){
20938             this.setGroupValue(v, suppressEvent);
20939             return;
20940         }
20941         
20942         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20943         
20944         this.validate();
20945     },
20946     
20947     setGroupValue : function(v, suppressEvent)
20948     {
20949         this.startValue = this.getValue();
20950         
20951         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20952             e.dom.checked = false;
20953             
20954             if(e.dom.value == v){
20955                 e.dom.checked = true;
20956             }
20957         });
20958         
20959         if(suppressEvent !== true){
20960             this.fireEvent('check', this, true);
20961         }
20962
20963         this.validate();
20964         
20965         return;
20966     },
20967     
20968     validate : function()
20969     {
20970         if(this.getVisibilityEl().hasClass('hidden')){
20971             return true;
20972         }
20973         
20974         if(
20975                 this.disabled || 
20976                 (this.inputType == 'radio' && this.validateRadio()) ||
20977                 (this.inputType == 'checkbox' && this.validateCheckbox())
20978         ){
20979             this.markValid();
20980             return true;
20981         }
20982         
20983         this.markInvalid();
20984         return false;
20985     },
20986     
20987     validateRadio : function()
20988     {
20989         if(this.getVisibilityEl().hasClass('hidden')){
20990             return true;
20991         }
20992         
20993         if(this.allowBlank){
20994             return true;
20995         }
20996         
20997         var valid = false;
20998         
20999         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21000             if(!e.dom.checked){
21001                 return;
21002             }
21003             
21004             valid = true;
21005             
21006             return false;
21007         });
21008         
21009         return valid;
21010     },
21011     
21012     validateCheckbox : function()
21013     {
21014         if(!this.groupId){
21015             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21016             //return (this.getValue() == this.inputValue) ? true : false;
21017         }
21018         
21019         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21020         
21021         if(!group){
21022             return false;
21023         }
21024         
21025         var r = false;
21026         
21027         for(var i in group){
21028             if(group[i].el.isVisible(true)){
21029                 r = false;
21030                 break;
21031             }
21032             
21033             r = true;
21034         }
21035         
21036         for(var i in group){
21037             if(r){
21038                 break;
21039             }
21040             
21041             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21042         }
21043         
21044         return r;
21045     },
21046     
21047     /**
21048      * Mark this field as valid
21049      */
21050     markValid : function()
21051     {
21052         var _this = this;
21053         
21054         this.fireEvent('valid', this);
21055         
21056         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21057         
21058         if(this.groupId){
21059             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21060         }
21061         
21062         if(label){
21063             label.markValid();
21064         }
21065
21066         if(this.inputType == 'radio'){
21067             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21068                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21069                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21070             });
21071             
21072             return;
21073         }
21074
21075         if(!this.groupId){
21076             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21077             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21078             return;
21079         }
21080         
21081         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21082         
21083         if(!group){
21084             return;
21085         }
21086         
21087         for(var i in group){
21088             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21089             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21090         }
21091     },
21092     
21093      /**
21094      * Mark this field as invalid
21095      * @param {String} msg The validation message
21096      */
21097     markInvalid : function(msg)
21098     {
21099         if(this.allowBlank){
21100             return;
21101         }
21102         
21103         var _this = this;
21104         
21105         this.fireEvent('invalid', this, msg);
21106         
21107         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21108         
21109         if(this.groupId){
21110             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21111         }
21112         
21113         if(label){
21114             label.markInvalid();
21115         }
21116             
21117         if(this.inputType == 'radio'){
21118             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21119                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21120                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21121             });
21122             
21123             return;
21124         }
21125         
21126         if(!this.groupId){
21127             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21128             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21129             return;
21130         }
21131         
21132         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21133         
21134         if(!group){
21135             return;
21136         }
21137         
21138         for(var i in group){
21139             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21140             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21141         }
21142         
21143     },
21144     
21145     clearInvalid : function()
21146     {
21147         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21148         
21149         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21150         
21151         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21152         
21153         if (label && label.iconEl) {
21154             label.iconEl.removeClass(label.validClass);
21155             label.iconEl.removeClass(label.invalidClass);
21156         }
21157     },
21158     
21159     disable : function()
21160     {
21161         if(this.inputType != 'radio'){
21162             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21163             return;
21164         }
21165         
21166         var _this = this;
21167         
21168         if(this.rendered){
21169             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21170                 _this.getActionEl().addClass(this.disabledClass);
21171                 e.dom.disabled = true;
21172             });
21173         }
21174         
21175         this.disabled = true;
21176         this.fireEvent("disable", this);
21177         return this;
21178     },
21179
21180     enable : function()
21181     {
21182         if(this.inputType != 'radio'){
21183             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21184             return;
21185         }
21186         
21187         var _this = this;
21188         
21189         if(this.rendered){
21190             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21191                 _this.getActionEl().removeClass(this.disabledClass);
21192                 e.dom.disabled = false;
21193             });
21194         }
21195         
21196         this.disabled = false;
21197         this.fireEvent("enable", this);
21198         return this;
21199     },
21200     
21201     setBoxLabel : function(v)
21202     {
21203         this.boxLabel = v;
21204         
21205         if(this.rendered){
21206             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21207         }
21208     }
21209
21210 });
21211
21212 Roo.apply(Roo.bootstrap.CheckBox, {
21213     
21214     groups: {},
21215     
21216      /**
21217     * register a CheckBox Group
21218     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21219     */
21220     register : function(checkbox)
21221     {
21222         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21223             this.groups[checkbox.groupId] = {};
21224         }
21225         
21226         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21227             return;
21228         }
21229         
21230         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21231         
21232     },
21233     /**
21234     * fetch a CheckBox Group based on the group ID
21235     * @param {string} the group ID
21236     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21237     */
21238     get: function(groupId) {
21239         if (typeof(this.groups[groupId]) == 'undefined') {
21240             return false;
21241         }
21242         
21243         return this.groups[groupId] ;
21244     }
21245     
21246     
21247 });
21248 /*
21249  * - LGPL
21250  *
21251  * RadioItem
21252  * 
21253  */
21254
21255 /**
21256  * @class Roo.bootstrap.Radio
21257  * @extends Roo.bootstrap.Component
21258  * Bootstrap Radio class
21259  * @cfg {String} boxLabel - the label associated
21260  * @cfg {String} value - the value of radio
21261  * 
21262  * @constructor
21263  * Create a new Radio
21264  * @param {Object} config The config object
21265  */
21266 Roo.bootstrap.Radio = function(config){
21267     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21268     
21269 };
21270
21271 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21272     
21273     boxLabel : '',
21274     
21275     value : '',
21276     
21277     getAutoCreate : function()
21278     {
21279         var cfg = {
21280             tag : 'div',
21281             cls : 'form-group radio',
21282             cn : [
21283                 {
21284                     tag : 'label',
21285                     cls : 'box-label',
21286                     html : this.boxLabel
21287                 }
21288             ]
21289         };
21290         
21291         return cfg;
21292     },
21293     
21294     initEvents : function() 
21295     {
21296         this.parent().register(this);
21297         
21298         this.el.on('click', this.onClick, this);
21299         
21300     },
21301     
21302     onClick : function(e)
21303     {
21304         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21305             this.setChecked(true);
21306         }
21307     },
21308     
21309     setChecked : function(state, suppressEvent)
21310     {
21311         this.parent().setValue(this.value, suppressEvent);
21312         
21313     },
21314     
21315     setBoxLabel : function(v)
21316     {
21317         this.boxLabel = v;
21318         
21319         if(this.rendered){
21320             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21321         }
21322     }
21323     
21324 });
21325  
21326
21327  /*
21328  * - LGPL
21329  *
21330  * Input
21331  * 
21332  */
21333
21334 /**
21335  * @class Roo.bootstrap.SecurePass
21336  * @extends Roo.bootstrap.Input
21337  * Bootstrap SecurePass class
21338  *
21339  * 
21340  * @constructor
21341  * Create a new SecurePass
21342  * @param {Object} config The config object
21343  */
21344  
21345 Roo.bootstrap.SecurePass = function (config) {
21346     // these go here, so the translation tool can replace them..
21347     this.errors = {
21348         PwdEmpty: "Please type a password, and then retype it to confirm.",
21349         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21350         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21351         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21352         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21353         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21354         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21355         TooWeak: "Your password is Too Weak."
21356     },
21357     this.meterLabel = "Password strength:";
21358     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21359     this.meterClass = [
21360         "roo-password-meter-tooweak", 
21361         "roo-password-meter-weak", 
21362         "roo-password-meter-medium", 
21363         "roo-password-meter-strong", 
21364         "roo-password-meter-grey"
21365     ];
21366     
21367     this.errors = {};
21368     
21369     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21370 }
21371
21372 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21373     /**
21374      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21375      * {
21376      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21377      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21378      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21379      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21380      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21381      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21382      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21383      * })
21384      */
21385     // private
21386     
21387     meterWidth: 300,
21388     errorMsg :'',    
21389     errors: false,
21390     imageRoot: '/',
21391     /**
21392      * @cfg {String/Object} Label for the strength meter (defaults to
21393      * 'Password strength:')
21394      */
21395     // private
21396     meterLabel: '',
21397     /**
21398      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21399      * ['Weak', 'Medium', 'Strong'])
21400      */
21401     // private    
21402     pwdStrengths: false,    
21403     // private
21404     strength: 0,
21405     // private
21406     _lastPwd: null,
21407     // private
21408     kCapitalLetter: 0,
21409     kSmallLetter: 1,
21410     kDigit: 2,
21411     kPunctuation: 3,
21412     
21413     insecure: false,
21414     // private
21415     initEvents: function ()
21416     {
21417         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21418
21419         if (this.el.is('input[type=password]') && Roo.isSafari) {
21420             this.el.on('keydown', this.SafariOnKeyDown, this);
21421         }
21422
21423         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21424     },
21425     // private
21426     onRender: function (ct, position)
21427     {
21428         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21429         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21430         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21431
21432         this.trigger.createChild({
21433                    cn: [
21434                     {
21435                     //id: 'PwdMeter',
21436                     tag: 'div',
21437                     cls: 'roo-password-meter-grey col-xs-12',
21438                     style: {
21439                         //width: 0,
21440                         //width: this.meterWidth + 'px'                                                
21441                         }
21442                     },
21443                     {                            
21444                          cls: 'roo-password-meter-text'                          
21445                     }
21446                 ]            
21447         });
21448
21449          
21450         if (this.hideTrigger) {
21451             this.trigger.setDisplayed(false);
21452         }
21453         this.setSize(this.width || '', this.height || '');
21454     },
21455     // private
21456     onDestroy: function ()
21457     {
21458         if (this.trigger) {
21459             this.trigger.removeAllListeners();
21460             this.trigger.remove();
21461         }
21462         if (this.wrap) {
21463             this.wrap.remove();
21464         }
21465         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21466     },
21467     // private
21468     checkStrength: function ()
21469     {
21470         var pwd = this.inputEl().getValue();
21471         if (pwd == this._lastPwd) {
21472             return;
21473         }
21474
21475         var strength;
21476         if (this.ClientSideStrongPassword(pwd)) {
21477             strength = 3;
21478         } else if (this.ClientSideMediumPassword(pwd)) {
21479             strength = 2;
21480         } else if (this.ClientSideWeakPassword(pwd)) {
21481             strength = 1;
21482         } else {
21483             strength = 0;
21484         }
21485         
21486         Roo.log('strength1: ' + strength);
21487         
21488         //var pm = this.trigger.child('div/div/div').dom;
21489         var pm = this.trigger.child('div/div');
21490         pm.removeClass(this.meterClass);
21491         pm.addClass(this.meterClass[strength]);
21492                 
21493         
21494         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21495                 
21496         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21497         
21498         this._lastPwd = pwd;
21499     },
21500     reset: function ()
21501     {
21502         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21503         
21504         this._lastPwd = '';
21505         
21506         var pm = this.trigger.child('div/div');
21507         pm.removeClass(this.meterClass);
21508         pm.addClass('roo-password-meter-grey');        
21509         
21510         
21511         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21512         
21513         pt.innerHTML = '';
21514         this.inputEl().dom.type='password';
21515     },
21516     // private
21517     validateValue: function (value)
21518     {
21519         
21520         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21521             return false;
21522         }
21523         if (value.length == 0) {
21524             if (this.allowBlank) {
21525                 this.clearInvalid();
21526                 return true;
21527             }
21528
21529             this.markInvalid(this.errors.PwdEmpty);
21530             this.errorMsg = this.errors.PwdEmpty;
21531             return false;
21532         }
21533         
21534         if(this.insecure){
21535             return true;
21536         }
21537         
21538         if ('[\x21-\x7e]*'.match(value)) {
21539             this.markInvalid(this.errors.PwdBadChar);
21540             this.errorMsg = this.errors.PwdBadChar;
21541             return false;
21542         }
21543         if (value.length < 6) {
21544             this.markInvalid(this.errors.PwdShort);
21545             this.errorMsg = this.errors.PwdShort;
21546             return false;
21547         }
21548         if (value.length > 16) {
21549             this.markInvalid(this.errors.PwdLong);
21550             this.errorMsg = this.errors.PwdLong;
21551             return false;
21552         }
21553         var strength;
21554         if (this.ClientSideStrongPassword(value)) {
21555             strength = 3;
21556         } else if (this.ClientSideMediumPassword(value)) {
21557             strength = 2;
21558         } else if (this.ClientSideWeakPassword(value)) {
21559             strength = 1;
21560         } else {
21561             strength = 0;
21562         }
21563
21564         
21565         if (strength < 2) {
21566             //this.markInvalid(this.errors.TooWeak);
21567             this.errorMsg = this.errors.TooWeak;
21568             //return false;
21569         }
21570         
21571         
21572         console.log('strength2: ' + strength);
21573         
21574         //var pm = this.trigger.child('div/div/div').dom;
21575         
21576         var pm = this.trigger.child('div/div');
21577         pm.removeClass(this.meterClass);
21578         pm.addClass(this.meterClass[strength]);
21579                 
21580         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21581                 
21582         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21583         
21584         this.errorMsg = ''; 
21585         return true;
21586     },
21587     // private
21588     CharacterSetChecks: function (type)
21589     {
21590         this.type = type;
21591         this.fResult = false;
21592     },
21593     // private
21594     isctype: function (character, type)
21595     {
21596         switch (type) {  
21597             case this.kCapitalLetter:
21598                 if (character >= 'A' && character <= 'Z') {
21599                     return true;
21600                 }
21601                 break;
21602             
21603             case this.kSmallLetter:
21604                 if (character >= 'a' && character <= 'z') {
21605                     return true;
21606                 }
21607                 break;
21608             
21609             case this.kDigit:
21610                 if (character >= '0' && character <= '9') {
21611                     return true;
21612                 }
21613                 break;
21614             
21615             case this.kPunctuation:
21616                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21617                     return true;
21618                 }
21619                 break;
21620             
21621             default:
21622                 return false;
21623         }
21624
21625     },
21626     // private
21627     IsLongEnough: function (pwd, size)
21628     {
21629         return !(pwd == null || isNaN(size) || pwd.length < size);
21630     },
21631     // private
21632     SpansEnoughCharacterSets: function (word, nb)
21633     {
21634         if (!this.IsLongEnough(word, nb))
21635         {
21636             return false;
21637         }
21638
21639         var characterSetChecks = new Array(
21640             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21641             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21642         );
21643         
21644         for (var index = 0; index < word.length; ++index) {
21645             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21646                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21647                     characterSetChecks[nCharSet].fResult = true;
21648                     break;
21649                 }
21650             }
21651         }
21652
21653         var nCharSets = 0;
21654         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21655             if (characterSetChecks[nCharSet].fResult) {
21656                 ++nCharSets;
21657             }
21658         }
21659
21660         if (nCharSets < nb) {
21661             return false;
21662         }
21663         return true;
21664     },
21665     // private
21666     ClientSideStrongPassword: function (pwd)
21667     {
21668         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21669     },
21670     // private
21671     ClientSideMediumPassword: function (pwd)
21672     {
21673         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21674     },
21675     // private
21676     ClientSideWeakPassword: function (pwd)
21677     {
21678         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21679     }
21680           
21681 })//<script type="text/javascript">
21682
21683 /*
21684  * Based  Ext JS Library 1.1.1
21685  * Copyright(c) 2006-2007, Ext JS, LLC.
21686  * LGPL
21687  *
21688  */
21689  
21690 /**
21691  * @class Roo.HtmlEditorCore
21692  * @extends Roo.Component
21693  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21694  *
21695  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21696  */
21697
21698 Roo.HtmlEditorCore = function(config){
21699     
21700     
21701     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21702     
21703     
21704     this.addEvents({
21705         /**
21706          * @event initialize
21707          * Fires when the editor is fully initialized (including the iframe)
21708          * @param {Roo.HtmlEditorCore} this
21709          */
21710         initialize: true,
21711         /**
21712          * @event activate
21713          * Fires when the editor is first receives the focus. Any insertion must wait
21714          * until after this event.
21715          * @param {Roo.HtmlEditorCore} this
21716          */
21717         activate: true,
21718          /**
21719          * @event beforesync
21720          * Fires before the textarea is updated with content from the editor iframe. Return false
21721          * to cancel the sync.
21722          * @param {Roo.HtmlEditorCore} this
21723          * @param {String} html
21724          */
21725         beforesync: true,
21726          /**
21727          * @event beforepush
21728          * Fires before the iframe editor is updated with content from the textarea. Return false
21729          * to cancel the push.
21730          * @param {Roo.HtmlEditorCore} this
21731          * @param {String} html
21732          */
21733         beforepush: true,
21734          /**
21735          * @event sync
21736          * Fires when the textarea is updated with content from the editor iframe.
21737          * @param {Roo.HtmlEditorCore} this
21738          * @param {String} html
21739          */
21740         sync: true,
21741          /**
21742          * @event push
21743          * Fires when the iframe editor is updated with content from the textarea.
21744          * @param {Roo.HtmlEditorCore} this
21745          * @param {String} html
21746          */
21747         push: true,
21748         
21749         /**
21750          * @event editorevent
21751          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21752          * @param {Roo.HtmlEditorCore} this
21753          */
21754         editorevent: true
21755         
21756     });
21757     
21758     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21759     
21760     // defaults : white / black...
21761     this.applyBlacklists();
21762     
21763     
21764     
21765 };
21766
21767
21768 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21769
21770
21771      /**
21772      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21773      */
21774     
21775     owner : false,
21776     
21777      /**
21778      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21779      *                        Roo.resizable.
21780      */
21781     resizable : false,
21782      /**
21783      * @cfg {Number} height (in pixels)
21784      */   
21785     height: 300,
21786    /**
21787      * @cfg {Number} width (in pixels)
21788      */   
21789     width: 500,
21790     
21791     /**
21792      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21793      * 
21794      */
21795     stylesheets: false,
21796     
21797     // id of frame..
21798     frameId: false,
21799     
21800     // private properties
21801     validationEvent : false,
21802     deferHeight: true,
21803     initialized : false,
21804     activated : false,
21805     sourceEditMode : false,
21806     onFocus : Roo.emptyFn,
21807     iframePad:3,
21808     hideMode:'offsets',
21809     
21810     clearUp: true,
21811     
21812     // blacklist + whitelisted elements..
21813     black: false,
21814     white: false,
21815      
21816     bodyCls : '',
21817
21818     /**
21819      * Protected method that will not generally be called directly. It
21820      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21821      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21822      */
21823     getDocMarkup : function(){
21824         // body styles..
21825         var st = '';
21826         
21827         // inherit styels from page...?? 
21828         if (this.stylesheets === false) {
21829             
21830             Roo.get(document.head).select('style').each(function(node) {
21831                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21832             });
21833             
21834             Roo.get(document.head).select('link').each(function(node) { 
21835                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21836             });
21837             
21838         } else if (!this.stylesheets.length) {
21839                 // simple..
21840                 st = '<style type="text/css">' +
21841                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21842                    '</style>';
21843         } else { 
21844             st = '<style type="text/css">' +
21845                     this.stylesheets +
21846                 '</style>';
21847         }
21848         
21849         st +=  '<style type="text/css">' +
21850             'IMG { cursor: pointer } ' +
21851         '</style>';
21852
21853         var cls = 'roo-htmleditor-body';
21854         
21855         if(this.bodyCls.length){
21856             cls += ' ' + this.bodyCls;
21857         }
21858         
21859         return '<html><head>' + st  +
21860             //<style type="text/css">' +
21861             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21862             //'</style>' +
21863             ' </head><body class="' +  cls + '"></body></html>';
21864     },
21865
21866     // private
21867     onRender : function(ct, position)
21868     {
21869         var _t = this;
21870         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21871         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21872         
21873         
21874         this.el.dom.style.border = '0 none';
21875         this.el.dom.setAttribute('tabIndex', -1);
21876         this.el.addClass('x-hidden hide');
21877         
21878         
21879         
21880         if(Roo.isIE){ // fix IE 1px bogus margin
21881             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21882         }
21883        
21884         
21885         this.frameId = Roo.id();
21886         
21887          
21888         
21889         var iframe = this.owner.wrap.createChild({
21890             tag: 'iframe',
21891             cls: 'form-control', // bootstrap..
21892             id: this.frameId,
21893             name: this.frameId,
21894             frameBorder : 'no',
21895             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21896         }, this.el
21897         );
21898         
21899         
21900         this.iframe = iframe.dom;
21901
21902          this.assignDocWin();
21903         
21904         this.doc.designMode = 'on';
21905        
21906         this.doc.open();
21907         this.doc.write(this.getDocMarkup());
21908         this.doc.close();
21909
21910         
21911         var task = { // must defer to wait for browser to be ready
21912             run : function(){
21913                 //console.log("run task?" + this.doc.readyState);
21914                 this.assignDocWin();
21915                 if(this.doc.body || this.doc.readyState == 'complete'){
21916                     try {
21917                         this.doc.designMode="on";
21918                     } catch (e) {
21919                         return;
21920                     }
21921                     Roo.TaskMgr.stop(task);
21922                     this.initEditor.defer(10, this);
21923                 }
21924             },
21925             interval : 10,
21926             duration: 10000,
21927             scope: this
21928         };
21929         Roo.TaskMgr.start(task);
21930
21931     },
21932
21933     // private
21934     onResize : function(w, h)
21935     {
21936          Roo.log('resize: ' +w + ',' + h );
21937         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21938         if(!this.iframe){
21939             return;
21940         }
21941         if(typeof w == 'number'){
21942             
21943             this.iframe.style.width = w + 'px';
21944         }
21945         if(typeof h == 'number'){
21946             
21947             this.iframe.style.height = h + 'px';
21948             if(this.doc){
21949                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21950             }
21951         }
21952         
21953     },
21954
21955     /**
21956      * Toggles the editor between standard and source edit mode.
21957      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21958      */
21959     toggleSourceEdit : function(sourceEditMode){
21960         
21961         this.sourceEditMode = sourceEditMode === true;
21962         
21963         if(this.sourceEditMode){
21964  
21965             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21966             
21967         }else{
21968             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21969             //this.iframe.className = '';
21970             this.deferFocus();
21971         }
21972         //this.setSize(this.owner.wrap.getSize());
21973         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21974     },
21975
21976     
21977   
21978
21979     /**
21980      * Protected method that will not generally be called directly. If you need/want
21981      * custom HTML cleanup, this is the method you should override.
21982      * @param {String} html The HTML to be cleaned
21983      * return {String} The cleaned HTML
21984      */
21985     cleanHtml : function(html){
21986         html = String(html);
21987         if(html.length > 5){
21988             if(Roo.isSafari){ // strip safari nonsense
21989                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21990             }
21991         }
21992         if(html == '&nbsp;'){
21993             html = '';
21994         }
21995         return html;
21996     },
21997
21998     /**
21999      * HTML Editor -> Textarea
22000      * Protected method that will not generally be called directly. Syncs the contents
22001      * of the editor iframe with the textarea.
22002      */
22003     syncValue : function(){
22004         if(this.initialized){
22005             var bd = (this.doc.body || this.doc.documentElement);
22006             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22007             var html = bd.innerHTML;
22008             if(Roo.isSafari){
22009                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22010                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22011                 if(m && m[1]){
22012                     html = '<div style="'+m[0]+'">' + html + '</div>';
22013                 }
22014             }
22015             html = this.cleanHtml(html);
22016             // fix up the special chars.. normaly like back quotes in word...
22017             // however we do not want to do this with chinese..
22018             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22019                 var cc = b.charCodeAt();
22020                 if (
22021                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22022                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22023                     (cc >= 0xf900 && cc < 0xfb00 )
22024                 ) {
22025                         return b;
22026                 }
22027                 return "&#"+cc+";" 
22028             });
22029             if(this.owner.fireEvent('beforesync', this, html) !== false){
22030                 this.el.dom.value = html;
22031                 this.owner.fireEvent('sync', this, html);
22032             }
22033         }
22034     },
22035
22036     /**
22037      * Protected method that will not generally be called directly. Pushes the value of the textarea
22038      * into the iframe editor.
22039      */
22040     pushValue : function(){
22041         if(this.initialized){
22042             var v = this.el.dom.value.trim();
22043             
22044 //            if(v.length < 1){
22045 //                v = '&#160;';
22046 //            }
22047             
22048             if(this.owner.fireEvent('beforepush', this, v) !== false){
22049                 var d = (this.doc.body || this.doc.documentElement);
22050                 d.innerHTML = v;
22051                 this.cleanUpPaste();
22052                 this.el.dom.value = d.innerHTML;
22053                 this.owner.fireEvent('push', this, v);
22054             }
22055         }
22056     },
22057
22058     // private
22059     deferFocus : function(){
22060         this.focus.defer(10, this);
22061     },
22062
22063     // doc'ed in Field
22064     focus : function(){
22065         if(this.win && !this.sourceEditMode){
22066             this.win.focus();
22067         }else{
22068             this.el.focus();
22069         }
22070     },
22071     
22072     assignDocWin: function()
22073     {
22074         var iframe = this.iframe;
22075         
22076          if(Roo.isIE){
22077             this.doc = iframe.contentWindow.document;
22078             this.win = iframe.contentWindow;
22079         } else {
22080 //            if (!Roo.get(this.frameId)) {
22081 //                return;
22082 //            }
22083 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22084 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22085             
22086             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22087                 return;
22088             }
22089             
22090             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22091             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22092         }
22093     },
22094     
22095     // private
22096     initEditor : function(){
22097         //console.log("INIT EDITOR");
22098         this.assignDocWin();
22099         
22100         
22101         
22102         this.doc.designMode="on";
22103         this.doc.open();
22104         this.doc.write(this.getDocMarkup());
22105         this.doc.close();
22106         
22107         var dbody = (this.doc.body || this.doc.documentElement);
22108         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22109         // this copies styles from the containing element into thsi one..
22110         // not sure why we need all of this..
22111         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22112         
22113         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22114         //ss['background-attachment'] = 'fixed'; // w3c
22115         dbody.bgProperties = 'fixed'; // ie
22116         //Roo.DomHelper.applyStyles(dbody, ss);
22117         Roo.EventManager.on(this.doc, {
22118             //'mousedown': this.onEditorEvent,
22119             'mouseup': this.onEditorEvent,
22120             'dblclick': this.onEditorEvent,
22121             'click': this.onEditorEvent,
22122             'keyup': this.onEditorEvent,
22123             buffer:100,
22124             scope: this
22125         });
22126         if(Roo.isGecko){
22127             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22128         }
22129         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22130             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22131         }
22132         this.initialized = true;
22133
22134         this.owner.fireEvent('initialize', this);
22135         this.pushValue();
22136     },
22137
22138     // private
22139     onDestroy : function(){
22140         
22141         
22142         
22143         if(this.rendered){
22144             
22145             //for (var i =0; i < this.toolbars.length;i++) {
22146             //    // fixme - ask toolbars for heights?
22147             //    this.toolbars[i].onDestroy();
22148            // }
22149             
22150             //this.wrap.dom.innerHTML = '';
22151             //this.wrap.remove();
22152         }
22153     },
22154
22155     // private
22156     onFirstFocus : function(){
22157         
22158         this.assignDocWin();
22159         
22160         
22161         this.activated = true;
22162          
22163     
22164         if(Roo.isGecko){ // prevent silly gecko errors
22165             this.win.focus();
22166             var s = this.win.getSelection();
22167             if(!s.focusNode || s.focusNode.nodeType != 3){
22168                 var r = s.getRangeAt(0);
22169                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22170                 r.collapse(true);
22171                 this.deferFocus();
22172             }
22173             try{
22174                 this.execCmd('useCSS', true);
22175                 this.execCmd('styleWithCSS', false);
22176             }catch(e){}
22177         }
22178         this.owner.fireEvent('activate', this);
22179     },
22180
22181     // private
22182     adjustFont: function(btn){
22183         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22184         //if(Roo.isSafari){ // safari
22185         //    adjust *= 2;
22186        // }
22187         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22188         if(Roo.isSafari){ // safari
22189             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22190             v =  (v < 10) ? 10 : v;
22191             v =  (v > 48) ? 48 : v;
22192             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22193             
22194         }
22195         
22196         
22197         v = Math.max(1, v+adjust);
22198         
22199         this.execCmd('FontSize', v  );
22200     },
22201
22202     onEditorEvent : function(e)
22203     {
22204         this.owner.fireEvent('editorevent', this, e);
22205       //  this.updateToolbar();
22206         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22207     },
22208
22209     insertTag : function(tg)
22210     {
22211         // could be a bit smarter... -> wrap the current selected tRoo..
22212         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22213             
22214             range = this.createRange(this.getSelection());
22215             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22216             wrappingNode.appendChild(range.extractContents());
22217             range.insertNode(wrappingNode);
22218
22219             return;
22220             
22221             
22222             
22223         }
22224         this.execCmd("formatblock",   tg);
22225         
22226     },
22227     
22228     insertText : function(txt)
22229     {
22230         
22231         
22232         var range = this.createRange();
22233         range.deleteContents();
22234                //alert(Sender.getAttribute('label'));
22235                
22236         range.insertNode(this.doc.createTextNode(txt));
22237     } ,
22238     
22239      
22240
22241     /**
22242      * Executes a Midas editor command on the editor document and performs necessary focus and
22243      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22244      * @param {String} cmd The Midas command
22245      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22246      */
22247     relayCmd : function(cmd, value){
22248         this.win.focus();
22249         this.execCmd(cmd, value);
22250         this.owner.fireEvent('editorevent', this);
22251         //this.updateToolbar();
22252         this.owner.deferFocus();
22253     },
22254
22255     /**
22256      * Executes a Midas editor command directly on the editor document.
22257      * For visual commands, you should use {@link #relayCmd} instead.
22258      * <b>This should only be called after the editor is initialized.</b>
22259      * @param {String} cmd The Midas command
22260      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22261      */
22262     execCmd : function(cmd, value){
22263         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22264         this.syncValue();
22265     },
22266  
22267  
22268    
22269     /**
22270      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22271      * to insert tRoo.
22272      * @param {String} text | dom node.. 
22273      */
22274     insertAtCursor : function(text)
22275     {
22276         
22277         if(!this.activated){
22278             return;
22279         }
22280         /*
22281         if(Roo.isIE){
22282             this.win.focus();
22283             var r = this.doc.selection.createRange();
22284             if(r){
22285                 r.collapse(true);
22286                 r.pasteHTML(text);
22287                 this.syncValue();
22288                 this.deferFocus();
22289             
22290             }
22291             return;
22292         }
22293         */
22294         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22295             this.win.focus();
22296             
22297             
22298             // from jquery ui (MIT licenced)
22299             var range, node;
22300             var win = this.win;
22301             
22302             if (win.getSelection && win.getSelection().getRangeAt) {
22303                 range = win.getSelection().getRangeAt(0);
22304                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22305                 range.insertNode(node);
22306             } else if (win.document.selection && win.document.selection.createRange) {
22307                 // no firefox support
22308                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22309                 win.document.selection.createRange().pasteHTML(txt);
22310             } else {
22311                 // no firefox support
22312                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22313                 this.execCmd('InsertHTML', txt);
22314             } 
22315             
22316             this.syncValue();
22317             
22318             this.deferFocus();
22319         }
22320     },
22321  // private
22322     mozKeyPress : function(e){
22323         if(e.ctrlKey){
22324             var c = e.getCharCode(), cmd;
22325           
22326             if(c > 0){
22327                 c = String.fromCharCode(c).toLowerCase();
22328                 switch(c){
22329                     case 'b':
22330                         cmd = 'bold';
22331                         break;
22332                     case 'i':
22333                         cmd = 'italic';
22334                         break;
22335                     
22336                     case 'u':
22337                         cmd = 'underline';
22338                         break;
22339                     
22340                     case 'v':
22341                         this.cleanUpPaste.defer(100, this);
22342                         return;
22343                         
22344                 }
22345                 if(cmd){
22346                     this.win.focus();
22347                     this.execCmd(cmd);
22348                     this.deferFocus();
22349                     e.preventDefault();
22350                 }
22351                 
22352             }
22353         }
22354     },
22355
22356     // private
22357     fixKeys : function(){ // load time branching for fastest keydown performance
22358         if(Roo.isIE){
22359             return function(e){
22360                 var k = e.getKey(), r;
22361                 if(k == e.TAB){
22362                     e.stopEvent();
22363                     r = this.doc.selection.createRange();
22364                     if(r){
22365                         r.collapse(true);
22366                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22367                         this.deferFocus();
22368                     }
22369                     return;
22370                 }
22371                 
22372                 if(k == e.ENTER){
22373                     r = this.doc.selection.createRange();
22374                     if(r){
22375                         var target = r.parentElement();
22376                         if(!target || target.tagName.toLowerCase() != 'li'){
22377                             e.stopEvent();
22378                             r.pasteHTML('<br />');
22379                             r.collapse(false);
22380                             r.select();
22381                         }
22382                     }
22383                 }
22384                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22385                     this.cleanUpPaste.defer(100, this);
22386                     return;
22387                 }
22388                 
22389                 
22390             };
22391         }else if(Roo.isOpera){
22392             return function(e){
22393                 var k = e.getKey();
22394                 if(k == e.TAB){
22395                     e.stopEvent();
22396                     this.win.focus();
22397                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22398                     this.deferFocus();
22399                 }
22400                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22401                     this.cleanUpPaste.defer(100, this);
22402                     return;
22403                 }
22404                 
22405             };
22406         }else if(Roo.isSafari){
22407             return function(e){
22408                 var k = e.getKey();
22409                 
22410                 if(k == e.TAB){
22411                     e.stopEvent();
22412                     this.execCmd('InsertText','\t');
22413                     this.deferFocus();
22414                     return;
22415                 }
22416                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22417                     this.cleanUpPaste.defer(100, this);
22418                     return;
22419                 }
22420                 
22421              };
22422         }
22423     }(),
22424     
22425     getAllAncestors: function()
22426     {
22427         var p = this.getSelectedNode();
22428         var a = [];
22429         if (!p) {
22430             a.push(p); // push blank onto stack..
22431             p = this.getParentElement();
22432         }
22433         
22434         
22435         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22436             a.push(p);
22437             p = p.parentNode;
22438         }
22439         a.push(this.doc.body);
22440         return a;
22441     },
22442     lastSel : false,
22443     lastSelNode : false,
22444     
22445     
22446     getSelection : function() 
22447     {
22448         this.assignDocWin();
22449         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22450     },
22451     
22452     getSelectedNode: function() 
22453     {
22454         // this may only work on Gecko!!!
22455         
22456         // should we cache this!!!!
22457         
22458         
22459         
22460          
22461         var range = this.createRange(this.getSelection()).cloneRange();
22462         
22463         if (Roo.isIE) {
22464             var parent = range.parentElement();
22465             while (true) {
22466                 var testRange = range.duplicate();
22467                 testRange.moveToElementText(parent);
22468                 if (testRange.inRange(range)) {
22469                     break;
22470                 }
22471                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22472                     break;
22473                 }
22474                 parent = parent.parentElement;
22475             }
22476             return parent;
22477         }
22478         
22479         // is ancestor a text element.
22480         var ac =  range.commonAncestorContainer;
22481         if (ac.nodeType == 3) {
22482             ac = ac.parentNode;
22483         }
22484         
22485         var ar = ac.childNodes;
22486          
22487         var nodes = [];
22488         var other_nodes = [];
22489         var has_other_nodes = false;
22490         for (var i=0;i<ar.length;i++) {
22491             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22492                 continue;
22493             }
22494             // fullly contained node.
22495             
22496             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22497                 nodes.push(ar[i]);
22498                 continue;
22499             }
22500             
22501             // probably selected..
22502             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22503                 other_nodes.push(ar[i]);
22504                 continue;
22505             }
22506             // outer..
22507             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22508                 continue;
22509             }
22510             
22511             
22512             has_other_nodes = true;
22513         }
22514         if (!nodes.length && other_nodes.length) {
22515             nodes= other_nodes;
22516         }
22517         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22518             return false;
22519         }
22520         
22521         return nodes[0];
22522     },
22523     createRange: function(sel)
22524     {
22525         // this has strange effects when using with 
22526         // top toolbar - not sure if it's a great idea.
22527         //this.editor.contentWindow.focus();
22528         if (typeof sel != "undefined") {
22529             try {
22530                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22531             } catch(e) {
22532                 return this.doc.createRange();
22533             }
22534         } else {
22535             return this.doc.createRange();
22536         }
22537     },
22538     getParentElement: function()
22539     {
22540         
22541         this.assignDocWin();
22542         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22543         
22544         var range = this.createRange(sel);
22545          
22546         try {
22547             var p = range.commonAncestorContainer;
22548             while (p.nodeType == 3) { // text node
22549                 p = p.parentNode;
22550             }
22551             return p;
22552         } catch (e) {
22553             return null;
22554         }
22555     
22556     },
22557     /***
22558      *
22559      * Range intersection.. the hard stuff...
22560      *  '-1' = before
22561      *  '0' = hits..
22562      *  '1' = after.
22563      *         [ -- selected range --- ]
22564      *   [fail]                        [fail]
22565      *
22566      *    basically..
22567      *      if end is before start or  hits it. fail.
22568      *      if start is after end or hits it fail.
22569      *
22570      *   if either hits (but other is outside. - then it's not 
22571      *   
22572      *    
22573      **/
22574     
22575     
22576     // @see http://www.thismuchiknow.co.uk/?p=64.
22577     rangeIntersectsNode : function(range, node)
22578     {
22579         var nodeRange = node.ownerDocument.createRange();
22580         try {
22581             nodeRange.selectNode(node);
22582         } catch (e) {
22583             nodeRange.selectNodeContents(node);
22584         }
22585     
22586         var rangeStartRange = range.cloneRange();
22587         rangeStartRange.collapse(true);
22588     
22589         var rangeEndRange = range.cloneRange();
22590         rangeEndRange.collapse(false);
22591     
22592         var nodeStartRange = nodeRange.cloneRange();
22593         nodeStartRange.collapse(true);
22594     
22595         var nodeEndRange = nodeRange.cloneRange();
22596         nodeEndRange.collapse(false);
22597     
22598         return rangeStartRange.compareBoundaryPoints(
22599                  Range.START_TO_START, nodeEndRange) == -1 &&
22600                rangeEndRange.compareBoundaryPoints(
22601                  Range.START_TO_START, nodeStartRange) == 1;
22602         
22603          
22604     },
22605     rangeCompareNode : function(range, node)
22606     {
22607         var nodeRange = node.ownerDocument.createRange();
22608         try {
22609             nodeRange.selectNode(node);
22610         } catch (e) {
22611             nodeRange.selectNodeContents(node);
22612         }
22613         
22614         
22615         range.collapse(true);
22616     
22617         nodeRange.collapse(true);
22618      
22619         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22620         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22621          
22622         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22623         
22624         var nodeIsBefore   =  ss == 1;
22625         var nodeIsAfter    = ee == -1;
22626         
22627         if (nodeIsBefore && nodeIsAfter) {
22628             return 0; // outer
22629         }
22630         if (!nodeIsBefore && nodeIsAfter) {
22631             return 1; //right trailed.
22632         }
22633         
22634         if (nodeIsBefore && !nodeIsAfter) {
22635             return 2;  // left trailed.
22636         }
22637         // fully contined.
22638         return 3;
22639     },
22640
22641     // private? - in a new class?
22642     cleanUpPaste :  function()
22643     {
22644         // cleans up the whole document..
22645         Roo.log('cleanuppaste');
22646         
22647         this.cleanUpChildren(this.doc.body);
22648         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22649         if (clean != this.doc.body.innerHTML) {
22650             this.doc.body.innerHTML = clean;
22651         }
22652         
22653     },
22654     
22655     cleanWordChars : function(input) {// change the chars to hex code
22656         var he = Roo.HtmlEditorCore;
22657         
22658         var output = input;
22659         Roo.each(he.swapCodes, function(sw) { 
22660             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22661             
22662             output = output.replace(swapper, sw[1]);
22663         });
22664         
22665         return output;
22666     },
22667     
22668     
22669     cleanUpChildren : function (n)
22670     {
22671         if (!n.childNodes.length) {
22672             return;
22673         }
22674         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22675            this.cleanUpChild(n.childNodes[i]);
22676         }
22677     },
22678     
22679     
22680         
22681     
22682     cleanUpChild : function (node)
22683     {
22684         var ed = this;
22685         //console.log(node);
22686         if (node.nodeName == "#text") {
22687             // clean up silly Windows -- stuff?
22688             return; 
22689         }
22690         if (node.nodeName == "#comment") {
22691             node.parentNode.removeChild(node);
22692             // clean up silly Windows -- stuff?
22693             return; 
22694         }
22695         var lcname = node.tagName.toLowerCase();
22696         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22697         // whitelist of tags..
22698         
22699         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22700             // remove node.
22701             node.parentNode.removeChild(node);
22702             return;
22703             
22704         }
22705         
22706         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22707         
22708         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22709         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22710         
22711         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22712         //    remove_keep_children = true;
22713         //}
22714         
22715         if (remove_keep_children) {
22716             this.cleanUpChildren(node);
22717             // inserts everything just before this node...
22718             while (node.childNodes.length) {
22719                 var cn = node.childNodes[0];
22720                 node.removeChild(cn);
22721                 node.parentNode.insertBefore(cn, node);
22722             }
22723             node.parentNode.removeChild(node);
22724             return;
22725         }
22726         
22727         if (!node.attributes || !node.attributes.length) {
22728             this.cleanUpChildren(node);
22729             return;
22730         }
22731         
22732         function cleanAttr(n,v)
22733         {
22734             
22735             if (v.match(/^\./) || v.match(/^\//)) {
22736                 return;
22737             }
22738             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22739                 return;
22740             }
22741             if (v.match(/^#/)) {
22742                 return;
22743             }
22744 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22745             node.removeAttribute(n);
22746             
22747         }
22748         
22749         var cwhite = this.cwhite;
22750         var cblack = this.cblack;
22751             
22752         function cleanStyle(n,v)
22753         {
22754             if (v.match(/expression/)) { //XSS?? should we even bother..
22755                 node.removeAttribute(n);
22756                 return;
22757             }
22758             
22759             var parts = v.split(/;/);
22760             var clean = [];
22761             
22762             Roo.each(parts, function(p) {
22763                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22764                 if (!p.length) {
22765                     return true;
22766                 }
22767                 var l = p.split(':').shift().replace(/\s+/g,'');
22768                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22769                 
22770                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22771 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22772                     //node.removeAttribute(n);
22773                     return true;
22774                 }
22775                 //Roo.log()
22776                 // only allow 'c whitelisted system attributes'
22777                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22778 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22779                     //node.removeAttribute(n);
22780                     return true;
22781                 }
22782                 
22783                 
22784                  
22785                 
22786                 clean.push(p);
22787                 return true;
22788             });
22789             if (clean.length) { 
22790                 node.setAttribute(n, clean.join(';'));
22791             } else {
22792                 node.removeAttribute(n);
22793             }
22794             
22795         }
22796         
22797         
22798         for (var i = node.attributes.length-1; i > -1 ; i--) {
22799             var a = node.attributes[i];
22800             //console.log(a);
22801             
22802             if (a.name.toLowerCase().substr(0,2)=='on')  {
22803                 node.removeAttribute(a.name);
22804                 continue;
22805             }
22806             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22807                 node.removeAttribute(a.name);
22808                 continue;
22809             }
22810             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22811                 cleanAttr(a.name,a.value); // fixme..
22812                 continue;
22813             }
22814             if (a.name == 'style') {
22815                 cleanStyle(a.name,a.value);
22816                 continue;
22817             }
22818             /// clean up MS crap..
22819             // tecnically this should be a list of valid class'es..
22820             
22821             
22822             if (a.name == 'class') {
22823                 if (a.value.match(/^Mso/)) {
22824                     node.className = '';
22825                 }
22826                 
22827                 if (a.value.match(/^body$/)) {
22828                     node.className = '';
22829                 }
22830                 continue;
22831             }
22832             
22833             // style cleanup!?
22834             // class cleanup?
22835             
22836         }
22837         
22838         
22839         this.cleanUpChildren(node);
22840         
22841         
22842     },
22843     
22844     /**
22845      * Clean up MS wordisms...
22846      */
22847     cleanWord : function(node)
22848     {
22849         
22850         
22851         if (!node) {
22852             this.cleanWord(this.doc.body);
22853             return;
22854         }
22855         if (node.nodeName == "#text") {
22856             // clean up silly Windows -- stuff?
22857             return; 
22858         }
22859         if (node.nodeName == "#comment") {
22860             node.parentNode.removeChild(node);
22861             // clean up silly Windows -- stuff?
22862             return; 
22863         }
22864         
22865         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22866             node.parentNode.removeChild(node);
22867             return;
22868         }
22869         
22870         // remove - but keep children..
22871         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22872             while (node.childNodes.length) {
22873                 var cn = node.childNodes[0];
22874                 node.removeChild(cn);
22875                 node.parentNode.insertBefore(cn, node);
22876             }
22877             node.parentNode.removeChild(node);
22878             this.iterateChildren(node, this.cleanWord);
22879             return;
22880         }
22881         // clean styles
22882         if (node.className.length) {
22883             
22884             var cn = node.className.split(/\W+/);
22885             var cna = [];
22886             Roo.each(cn, function(cls) {
22887                 if (cls.match(/Mso[a-zA-Z]+/)) {
22888                     return;
22889                 }
22890                 cna.push(cls);
22891             });
22892             node.className = cna.length ? cna.join(' ') : '';
22893             if (!cna.length) {
22894                 node.removeAttribute("class");
22895             }
22896         }
22897         
22898         if (node.hasAttribute("lang")) {
22899             node.removeAttribute("lang");
22900         }
22901         
22902         if (node.hasAttribute("style")) {
22903             
22904             var styles = node.getAttribute("style").split(";");
22905             var nstyle = [];
22906             Roo.each(styles, function(s) {
22907                 if (!s.match(/:/)) {
22908                     return;
22909                 }
22910                 var kv = s.split(":");
22911                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22912                     return;
22913                 }
22914                 // what ever is left... we allow.
22915                 nstyle.push(s);
22916             });
22917             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22918             if (!nstyle.length) {
22919                 node.removeAttribute('style');
22920             }
22921         }
22922         this.iterateChildren(node, this.cleanWord);
22923         
22924         
22925         
22926     },
22927     /**
22928      * iterateChildren of a Node, calling fn each time, using this as the scole..
22929      * @param {DomNode} node node to iterate children of.
22930      * @param {Function} fn method of this class to call on each item.
22931      */
22932     iterateChildren : function(node, fn)
22933     {
22934         if (!node.childNodes.length) {
22935                 return;
22936         }
22937         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22938            fn.call(this, node.childNodes[i])
22939         }
22940     },
22941     
22942     
22943     /**
22944      * cleanTableWidths.
22945      *
22946      * Quite often pasting from word etc.. results in tables with column and widths.
22947      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22948      *
22949      */
22950     cleanTableWidths : function(node)
22951     {
22952          
22953          
22954         if (!node) {
22955             this.cleanTableWidths(this.doc.body);
22956             return;
22957         }
22958         
22959         // ignore list...
22960         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22961             return; 
22962         }
22963         Roo.log(node.tagName);
22964         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22965             this.iterateChildren(node, this.cleanTableWidths);
22966             return;
22967         }
22968         if (node.hasAttribute('width')) {
22969             node.removeAttribute('width');
22970         }
22971         
22972          
22973         if (node.hasAttribute("style")) {
22974             // pretty basic...
22975             
22976             var styles = node.getAttribute("style").split(";");
22977             var nstyle = [];
22978             Roo.each(styles, function(s) {
22979                 if (!s.match(/:/)) {
22980                     return;
22981                 }
22982                 var kv = s.split(":");
22983                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22984                     return;
22985                 }
22986                 // what ever is left... we allow.
22987                 nstyle.push(s);
22988             });
22989             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22990             if (!nstyle.length) {
22991                 node.removeAttribute('style');
22992             }
22993         }
22994         
22995         this.iterateChildren(node, this.cleanTableWidths);
22996         
22997         
22998     },
22999     
23000     
23001     
23002     
23003     domToHTML : function(currentElement, depth, nopadtext) {
23004         
23005         depth = depth || 0;
23006         nopadtext = nopadtext || false;
23007     
23008         if (!currentElement) {
23009             return this.domToHTML(this.doc.body);
23010         }
23011         
23012         //Roo.log(currentElement);
23013         var j;
23014         var allText = false;
23015         var nodeName = currentElement.nodeName;
23016         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23017         
23018         if  (nodeName == '#text') {
23019             
23020             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23021         }
23022         
23023         
23024         var ret = '';
23025         if (nodeName != 'BODY') {
23026              
23027             var i = 0;
23028             // Prints the node tagName, such as <A>, <IMG>, etc
23029             if (tagName) {
23030                 var attr = [];
23031                 for(i = 0; i < currentElement.attributes.length;i++) {
23032                     // quoting?
23033                     var aname = currentElement.attributes.item(i).name;
23034                     if (!currentElement.attributes.item(i).value.length) {
23035                         continue;
23036                     }
23037                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23038                 }
23039                 
23040                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23041             } 
23042             else {
23043                 
23044                 // eack
23045             }
23046         } else {
23047             tagName = false;
23048         }
23049         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23050             return ret;
23051         }
23052         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23053             nopadtext = true;
23054         }
23055         
23056         
23057         // Traverse the tree
23058         i = 0;
23059         var currentElementChild = currentElement.childNodes.item(i);
23060         var allText = true;
23061         var innerHTML  = '';
23062         lastnode = '';
23063         while (currentElementChild) {
23064             // Formatting code (indent the tree so it looks nice on the screen)
23065             var nopad = nopadtext;
23066             if (lastnode == 'SPAN') {
23067                 nopad  = true;
23068             }
23069             // text
23070             if  (currentElementChild.nodeName == '#text') {
23071                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23072                 toadd = nopadtext ? toadd : toadd.trim();
23073                 if (!nopad && toadd.length > 80) {
23074                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23075                 }
23076                 innerHTML  += toadd;
23077                 
23078                 i++;
23079                 currentElementChild = currentElement.childNodes.item(i);
23080                 lastNode = '';
23081                 continue;
23082             }
23083             allText = false;
23084             
23085             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23086                 
23087             // Recursively traverse the tree structure of the child node
23088             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23089             lastnode = currentElementChild.nodeName;
23090             i++;
23091             currentElementChild=currentElement.childNodes.item(i);
23092         }
23093         
23094         ret += innerHTML;
23095         
23096         if (!allText) {
23097                 // The remaining code is mostly for formatting the tree
23098             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23099         }
23100         
23101         
23102         if (tagName) {
23103             ret+= "</"+tagName+">";
23104         }
23105         return ret;
23106         
23107     },
23108         
23109     applyBlacklists : function()
23110     {
23111         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23112         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23113         
23114         this.white = [];
23115         this.black = [];
23116         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23117             if (b.indexOf(tag) > -1) {
23118                 return;
23119             }
23120             this.white.push(tag);
23121             
23122         }, this);
23123         
23124         Roo.each(w, function(tag) {
23125             if (b.indexOf(tag) > -1) {
23126                 return;
23127             }
23128             if (this.white.indexOf(tag) > -1) {
23129                 return;
23130             }
23131             this.white.push(tag);
23132             
23133         }, this);
23134         
23135         
23136         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23137             if (w.indexOf(tag) > -1) {
23138                 return;
23139             }
23140             this.black.push(tag);
23141             
23142         }, this);
23143         
23144         Roo.each(b, function(tag) {
23145             if (w.indexOf(tag) > -1) {
23146                 return;
23147             }
23148             if (this.black.indexOf(tag) > -1) {
23149                 return;
23150             }
23151             this.black.push(tag);
23152             
23153         }, this);
23154         
23155         
23156         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23157         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23158         
23159         this.cwhite = [];
23160         this.cblack = [];
23161         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23162             if (b.indexOf(tag) > -1) {
23163                 return;
23164             }
23165             this.cwhite.push(tag);
23166             
23167         }, this);
23168         
23169         Roo.each(w, function(tag) {
23170             if (b.indexOf(tag) > -1) {
23171                 return;
23172             }
23173             if (this.cwhite.indexOf(tag) > -1) {
23174                 return;
23175             }
23176             this.cwhite.push(tag);
23177             
23178         }, this);
23179         
23180         
23181         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23182             if (w.indexOf(tag) > -1) {
23183                 return;
23184             }
23185             this.cblack.push(tag);
23186             
23187         }, this);
23188         
23189         Roo.each(b, function(tag) {
23190             if (w.indexOf(tag) > -1) {
23191                 return;
23192             }
23193             if (this.cblack.indexOf(tag) > -1) {
23194                 return;
23195             }
23196             this.cblack.push(tag);
23197             
23198         }, this);
23199     },
23200     
23201     setStylesheets : function(stylesheets)
23202     {
23203         if(typeof(stylesheets) == 'string'){
23204             Roo.get(this.iframe.contentDocument.head).createChild({
23205                 tag : 'link',
23206                 rel : 'stylesheet',
23207                 type : 'text/css',
23208                 href : stylesheets
23209             });
23210             
23211             return;
23212         }
23213         var _this = this;
23214      
23215         Roo.each(stylesheets, function(s) {
23216             if(!s.length){
23217                 return;
23218             }
23219             
23220             Roo.get(_this.iframe.contentDocument.head).createChild({
23221                 tag : 'link',
23222                 rel : 'stylesheet',
23223                 type : 'text/css',
23224                 href : s
23225             });
23226         });
23227
23228         
23229     },
23230     
23231     removeStylesheets : function()
23232     {
23233         var _this = this;
23234         
23235         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23236             s.remove();
23237         });
23238     },
23239     
23240     setStyle : function(style)
23241     {
23242         Roo.get(this.iframe.contentDocument.head).createChild({
23243             tag : 'style',
23244             type : 'text/css',
23245             html : style
23246         });
23247
23248         return;
23249     }
23250     
23251     // hide stuff that is not compatible
23252     /**
23253      * @event blur
23254      * @hide
23255      */
23256     /**
23257      * @event change
23258      * @hide
23259      */
23260     /**
23261      * @event focus
23262      * @hide
23263      */
23264     /**
23265      * @event specialkey
23266      * @hide
23267      */
23268     /**
23269      * @cfg {String} fieldClass @hide
23270      */
23271     /**
23272      * @cfg {String} focusClass @hide
23273      */
23274     /**
23275      * @cfg {String} autoCreate @hide
23276      */
23277     /**
23278      * @cfg {String} inputType @hide
23279      */
23280     /**
23281      * @cfg {String} invalidClass @hide
23282      */
23283     /**
23284      * @cfg {String} invalidText @hide
23285      */
23286     /**
23287      * @cfg {String} msgFx @hide
23288      */
23289     /**
23290      * @cfg {String} validateOnBlur @hide
23291      */
23292 });
23293
23294 Roo.HtmlEditorCore.white = [
23295         'area', 'br', 'img', 'input', 'hr', 'wbr',
23296         
23297        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23298        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23299        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23300        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23301        'table',   'ul',         'xmp', 
23302        
23303        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23304       'thead',   'tr', 
23305      
23306       'dir', 'menu', 'ol', 'ul', 'dl',
23307        
23308       'embed',  'object'
23309 ];
23310
23311
23312 Roo.HtmlEditorCore.black = [
23313     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23314         'applet', // 
23315         'base',   'basefont', 'bgsound', 'blink',  'body', 
23316         'frame',  'frameset', 'head',    'html',   'ilayer', 
23317         'iframe', 'layer',  'link',     'meta',    'object',   
23318         'script', 'style' ,'title',  'xml' // clean later..
23319 ];
23320 Roo.HtmlEditorCore.clean = [
23321     'script', 'style', 'title', 'xml'
23322 ];
23323 Roo.HtmlEditorCore.remove = [
23324     'font'
23325 ];
23326 // attributes..
23327
23328 Roo.HtmlEditorCore.ablack = [
23329     'on'
23330 ];
23331     
23332 Roo.HtmlEditorCore.aclean = [ 
23333     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23334 ];
23335
23336 // protocols..
23337 Roo.HtmlEditorCore.pwhite= [
23338         'http',  'https',  'mailto'
23339 ];
23340
23341 // white listed style attributes.
23342 Roo.HtmlEditorCore.cwhite= [
23343       //  'text-align', /// default is to allow most things..
23344       
23345          
23346 //        'font-size'//??
23347 ];
23348
23349 // black listed style attributes.
23350 Roo.HtmlEditorCore.cblack= [
23351       //  'font-size' -- this can be set by the project 
23352 ];
23353
23354
23355 Roo.HtmlEditorCore.swapCodes   =[ 
23356     [    8211, "--" ], 
23357     [    8212, "--" ], 
23358     [    8216,  "'" ],  
23359     [    8217, "'" ],  
23360     [    8220, '"' ],  
23361     [    8221, '"' ],  
23362     [    8226, "*" ],  
23363     [    8230, "..." ]
23364 ]; 
23365
23366     /*
23367  * - LGPL
23368  *
23369  * HtmlEditor
23370  * 
23371  */
23372
23373 /**
23374  * @class Roo.bootstrap.HtmlEditor
23375  * @extends Roo.bootstrap.TextArea
23376  * Bootstrap HtmlEditor class
23377
23378  * @constructor
23379  * Create a new HtmlEditor
23380  * @param {Object} config The config object
23381  */
23382
23383 Roo.bootstrap.HtmlEditor = function(config){
23384     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23385     if (!this.toolbars) {
23386         this.toolbars = [];
23387     }
23388     
23389     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23390     this.addEvents({
23391             /**
23392              * @event initialize
23393              * Fires when the editor is fully initialized (including the iframe)
23394              * @param {HtmlEditor} this
23395              */
23396             initialize: true,
23397             /**
23398              * @event activate
23399              * Fires when the editor is first receives the focus. Any insertion must wait
23400              * until after this event.
23401              * @param {HtmlEditor} this
23402              */
23403             activate: true,
23404              /**
23405              * @event beforesync
23406              * Fires before the textarea is updated with content from the editor iframe. Return false
23407              * to cancel the sync.
23408              * @param {HtmlEditor} this
23409              * @param {String} html
23410              */
23411             beforesync: true,
23412              /**
23413              * @event beforepush
23414              * Fires before the iframe editor is updated with content from the textarea. Return false
23415              * to cancel the push.
23416              * @param {HtmlEditor} this
23417              * @param {String} html
23418              */
23419             beforepush: true,
23420              /**
23421              * @event sync
23422              * Fires when the textarea is updated with content from the editor iframe.
23423              * @param {HtmlEditor} this
23424              * @param {String} html
23425              */
23426             sync: true,
23427              /**
23428              * @event push
23429              * Fires when the iframe editor is updated with content from the textarea.
23430              * @param {HtmlEditor} this
23431              * @param {String} html
23432              */
23433             push: true,
23434              /**
23435              * @event editmodechange
23436              * Fires when the editor switches edit modes
23437              * @param {HtmlEditor} this
23438              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23439              */
23440             editmodechange: true,
23441             /**
23442              * @event editorevent
23443              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23444              * @param {HtmlEditor} this
23445              */
23446             editorevent: true,
23447             /**
23448              * @event firstfocus
23449              * Fires when on first focus - needed by toolbars..
23450              * @param {HtmlEditor} this
23451              */
23452             firstfocus: true,
23453             /**
23454              * @event autosave
23455              * Auto save the htmlEditor value as a file into Events
23456              * @param {HtmlEditor} this
23457              */
23458             autosave: true,
23459             /**
23460              * @event savedpreview
23461              * preview the saved version of htmlEditor
23462              * @param {HtmlEditor} this
23463              */
23464             savedpreview: true
23465         });
23466 };
23467
23468
23469 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23470     
23471     
23472       /**
23473      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23474      */
23475     toolbars : false,
23476     
23477      /**
23478     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23479     */
23480     btns : [],
23481    
23482      /**
23483      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23484      *                        Roo.resizable.
23485      */
23486     resizable : false,
23487      /**
23488      * @cfg {Number} height (in pixels)
23489      */   
23490     height: 300,
23491    /**
23492      * @cfg {Number} width (in pixels)
23493      */   
23494     width: false,
23495     
23496     /**
23497      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23498      * 
23499      */
23500     stylesheets: false,
23501     
23502     // id of frame..
23503     frameId: false,
23504     
23505     // private properties
23506     validationEvent : false,
23507     deferHeight: true,
23508     initialized : false,
23509     activated : false,
23510     
23511     onFocus : Roo.emptyFn,
23512     iframePad:3,
23513     hideMode:'offsets',
23514     
23515     tbContainer : false,
23516     
23517     bodyCls : '',
23518     
23519     toolbarContainer :function() {
23520         return this.wrap.select('.x-html-editor-tb',true).first();
23521     },
23522
23523     /**
23524      * Protected method that will not generally be called directly. It
23525      * is called when the editor creates its toolbar. Override this method if you need to
23526      * add custom toolbar buttons.
23527      * @param {HtmlEditor} editor
23528      */
23529     createToolbar : function(){
23530         Roo.log('renewing');
23531         Roo.log("create toolbars");
23532         
23533         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23534         this.toolbars[0].render(this.toolbarContainer());
23535         
23536         return;
23537         
23538 //        if (!editor.toolbars || !editor.toolbars.length) {
23539 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23540 //        }
23541 //        
23542 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23543 //            editor.toolbars[i] = Roo.factory(
23544 //                    typeof(editor.toolbars[i]) == 'string' ?
23545 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23546 //                Roo.bootstrap.HtmlEditor);
23547 //            editor.toolbars[i].init(editor);
23548 //        }
23549     },
23550
23551      
23552     // private
23553     onRender : function(ct, position)
23554     {
23555        // Roo.log("Call onRender: " + this.xtype);
23556         var _t = this;
23557         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23558       
23559         this.wrap = this.inputEl().wrap({
23560             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23561         });
23562         
23563         this.editorcore.onRender(ct, position);
23564          
23565         if (this.resizable) {
23566             this.resizeEl = new Roo.Resizable(this.wrap, {
23567                 pinned : true,
23568                 wrap: true,
23569                 dynamic : true,
23570                 minHeight : this.height,
23571                 height: this.height,
23572                 handles : this.resizable,
23573                 width: this.width,
23574                 listeners : {
23575                     resize : function(r, w, h) {
23576                         _t.onResize(w,h); // -something
23577                     }
23578                 }
23579             });
23580             
23581         }
23582         this.createToolbar(this);
23583        
23584         
23585         if(!this.width && this.resizable){
23586             this.setSize(this.wrap.getSize());
23587         }
23588         if (this.resizeEl) {
23589             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23590             // should trigger onReize..
23591         }
23592         
23593     },
23594
23595     // private
23596     onResize : function(w, h)
23597     {
23598         Roo.log('resize: ' +w + ',' + h );
23599         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23600         var ew = false;
23601         var eh = false;
23602         
23603         if(this.inputEl() ){
23604             if(typeof w == 'number'){
23605                 var aw = w - this.wrap.getFrameWidth('lr');
23606                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23607                 ew = aw;
23608             }
23609             if(typeof h == 'number'){
23610                  var tbh = -11;  // fixme it needs to tool bar size!
23611                 for (var i =0; i < this.toolbars.length;i++) {
23612                     // fixme - ask toolbars for heights?
23613                     tbh += this.toolbars[i].el.getHeight();
23614                     //if (this.toolbars[i].footer) {
23615                     //    tbh += this.toolbars[i].footer.el.getHeight();
23616                     //}
23617                 }
23618               
23619                 
23620                 
23621                 
23622                 
23623                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23624                 ah -= 5; // knock a few pixes off for look..
23625                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23626                 var eh = ah;
23627             }
23628         }
23629         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23630         this.editorcore.onResize(ew,eh);
23631         
23632     },
23633
23634     /**
23635      * Toggles the editor between standard and source edit mode.
23636      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23637      */
23638     toggleSourceEdit : function(sourceEditMode)
23639     {
23640         this.editorcore.toggleSourceEdit(sourceEditMode);
23641         
23642         if(this.editorcore.sourceEditMode){
23643             Roo.log('editor - showing textarea');
23644             
23645 //            Roo.log('in');
23646 //            Roo.log(this.syncValue());
23647             this.syncValue();
23648             this.inputEl().removeClass(['hide', 'x-hidden']);
23649             this.inputEl().dom.removeAttribute('tabIndex');
23650             this.inputEl().focus();
23651         }else{
23652             Roo.log('editor - hiding textarea');
23653 //            Roo.log('out')
23654 //            Roo.log(this.pushValue()); 
23655             this.pushValue();
23656             
23657             this.inputEl().addClass(['hide', 'x-hidden']);
23658             this.inputEl().dom.setAttribute('tabIndex', -1);
23659             //this.deferFocus();
23660         }
23661          
23662         if(this.resizable){
23663             this.setSize(this.wrap.getSize());
23664         }
23665         
23666         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23667     },
23668  
23669     // private (for BoxComponent)
23670     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23671
23672     // private (for BoxComponent)
23673     getResizeEl : function(){
23674         return this.wrap;
23675     },
23676
23677     // private (for BoxComponent)
23678     getPositionEl : function(){
23679         return this.wrap;
23680     },
23681
23682     // private
23683     initEvents : function(){
23684         this.originalValue = this.getValue();
23685     },
23686
23687 //    /**
23688 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23689 //     * @method
23690 //     */
23691 //    markInvalid : Roo.emptyFn,
23692 //    /**
23693 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23694 //     * @method
23695 //     */
23696 //    clearInvalid : Roo.emptyFn,
23697
23698     setValue : function(v){
23699         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23700         this.editorcore.pushValue();
23701     },
23702
23703      
23704     // private
23705     deferFocus : function(){
23706         this.focus.defer(10, this);
23707     },
23708
23709     // doc'ed in Field
23710     focus : function(){
23711         this.editorcore.focus();
23712         
23713     },
23714       
23715
23716     // private
23717     onDestroy : function(){
23718         
23719         
23720         
23721         if(this.rendered){
23722             
23723             for (var i =0; i < this.toolbars.length;i++) {
23724                 // fixme - ask toolbars for heights?
23725                 this.toolbars[i].onDestroy();
23726             }
23727             
23728             this.wrap.dom.innerHTML = '';
23729             this.wrap.remove();
23730         }
23731     },
23732
23733     // private
23734     onFirstFocus : function(){
23735         //Roo.log("onFirstFocus");
23736         this.editorcore.onFirstFocus();
23737          for (var i =0; i < this.toolbars.length;i++) {
23738             this.toolbars[i].onFirstFocus();
23739         }
23740         
23741     },
23742     
23743     // private
23744     syncValue : function()
23745     {   
23746         this.editorcore.syncValue();
23747     },
23748     
23749     pushValue : function()
23750     {   
23751         this.editorcore.pushValue();
23752     }
23753      
23754     
23755     // hide stuff that is not compatible
23756     /**
23757      * @event blur
23758      * @hide
23759      */
23760     /**
23761      * @event change
23762      * @hide
23763      */
23764     /**
23765      * @event focus
23766      * @hide
23767      */
23768     /**
23769      * @event specialkey
23770      * @hide
23771      */
23772     /**
23773      * @cfg {String} fieldClass @hide
23774      */
23775     /**
23776      * @cfg {String} focusClass @hide
23777      */
23778     /**
23779      * @cfg {String} autoCreate @hide
23780      */
23781     /**
23782      * @cfg {String} inputType @hide
23783      */
23784     /**
23785      * @cfg {String} invalidClass @hide
23786      */
23787     /**
23788      * @cfg {String} invalidText @hide
23789      */
23790     /**
23791      * @cfg {String} msgFx @hide
23792      */
23793     /**
23794      * @cfg {String} validateOnBlur @hide
23795      */
23796 });
23797  
23798     
23799    
23800    
23801    
23802       
23803 Roo.namespace('Roo.bootstrap.htmleditor');
23804 /**
23805  * @class Roo.bootstrap.HtmlEditorToolbar1
23806  * Basic Toolbar
23807  * 
23808  * Usage:
23809  *
23810  new Roo.bootstrap.HtmlEditor({
23811     ....
23812     toolbars : [
23813         new Roo.bootstrap.HtmlEditorToolbar1({
23814             disable : { fonts: 1 , format: 1, ..., ... , ...],
23815             btns : [ .... ]
23816         })
23817     }
23818      
23819  * 
23820  * @cfg {Object} disable List of elements to disable..
23821  * @cfg {Array} btns List of additional buttons.
23822  * 
23823  * 
23824  * NEEDS Extra CSS? 
23825  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23826  */
23827  
23828 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23829 {
23830     
23831     Roo.apply(this, config);
23832     
23833     // default disabled, based on 'good practice'..
23834     this.disable = this.disable || {};
23835     Roo.applyIf(this.disable, {
23836         fontSize : true,
23837         colors : true,
23838         specialElements : true
23839     });
23840     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23841     
23842     this.editor = config.editor;
23843     this.editorcore = config.editor.editorcore;
23844     
23845     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23846     
23847     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23848     // dont call parent... till later.
23849 }
23850 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23851      
23852     bar : true,
23853     
23854     editor : false,
23855     editorcore : false,
23856     
23857     
23858     formats : [
23859         "p" ,  
23860         "h1","h2","h3","h4","h5","h6", 
23861         "pre", "code", 
23862         "abbr", "acronym", "address", "cite", "samp", "var",
23863         'div','span'
23864     ],
23865     
23866     onRender : function(ct, position)
23867     {
23868        // Roo.log("Call onRender: " + this.xtype);
23869         
23870        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23871        Roo.log(this.el);
23872        this.el.dom.style.marginBottom = '0';
23873        var _this = this;
23874        var editorcore = this.editorcore;
23875        var editor= this.editor;
23876        
23877        var children = [];
23878        var btn = function(id,cmd , toggle, handler, html){
23879        
23880             var  event = toggle ? 'toggle' : 'click';
23881        
23882             var a = {
23883                 size : 'sm',
23884                 xtype: 'Button',
23885                 xns: Roo.bootstrap,
23886                 glyphicon : id,
23887                 cmd : id || cmd,
23888                 enableToggle:toggle !== false,
23889                 html : html || '',
23890                 pressed : toggle ? false : null,
23891                 listeners : {}
23892             };
23893             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23894                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23895             };
23896             children.push(a);
23897             return a;
23898        }
23899        
23900     //    var cb_box = function...
23901         
23902         var style = {
23903                 xtype: 'Button',
23904                 size : 'sm',
23905                 xns: Roo.bootstrap,
23906                 glyphicon : 'font',
23907                 //html : 'submit'
23908                 menu : {
23909                     xtype: 'Menu',
23910                     xns: Roo.bootstrap,
23911                     items:  []
23912                 }
23913         };
23914         Roo.each(this.formats, function(f) {
23915             style.menu.items.push({
23916                 xtype :'MenuItem',
23917                 xns: Roo.bootstrap,
23918                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23919                 tagname : f,
23920                 listeners : {
23921                     click : function()
23922                     {
23923                         editorcore.insertTag(this.tagname);
23924                         editor.focus();
23925                     }
23926                 }
23927                 
23928             });
23929         });
23930         children.push(style);   
23931         
23932         btn('bold',false,true);
23933         btn('italic',false,true);
23934         btn('align-left', 'justifyleft',true);
23935         btn('align-center', 'justifycenter',true);
23936         btn('align-right' , 'justifyright',true);
23937         btn('link', false, false, function(btn) {
23938             //Roo.log("create link?");
23939             var url = prompt(this.createLinkText, this.defaultLinkValue);
23940             if(url && url != 'http:/'+'/'){
23941                 this.editorcore.relayCmd('createlink', url);
23942             }
23943         }),
23944         btn('list','insertunorderedlist',true);
23945         btn('pencil', false,true, function(btn){
23946                 Roo.log(this);
23947                 this.toggleSourceEdit(btn.pressed);
23948         });
23949         
23950         if (this.editor.btns.length > 0) {
23951             for (var i = 0; i<this.editor.btns.length; i++) {
23952                 children.push(this.editor.btns[i]);
23953             }
23954         }
23955         
23956         /*
23957         var cog = {
23958                 xtype: 'Button',
23959                 size : 'sm',
23960                 xns: Roo.bootstrap,
23961                 glyphicon : 'cog',
23962                 //html : 'submit'
23963                 menu : {
23964                     xtype: 'Menu',
23965                     xns: Roo.bootstrap,
23966                     items:  []
23967                 }
23968         };
23969         
23970         cog.menu.items.push({
23971             xtype :'MenuItem',
23972             xns: Roo.bootstrap,
23973             html : Clean styles,
23974             tagname : f,
23975             listeners : {
23976                 click : function()
23977                 {
23978                     editorcore.insertTag(this.tagname);
23979                     editor.focus();
23980                 }
23981             }
23982             
23983         });
23984        */
23985         
23986          
23987        this.xtype = 'NavSimplebar';
23988         
23989         for(var i=0;i< children.length;i++) {
23990             
23991             this.buttons.add(this.addxtypeChild(children[i]));
23992             
23993         }
23994         
23995         editor.on('editorevent', this.updateToolbar, this);
23996     },
23997     onBtnClick : function(id)
23998     {
23999        this.editorcore.relayCmd(id);
24000        this.editorcore.focus();
24001     },
24002     
24003     /**
24004      * Protected method that will not generally be called directly. It triggers
24005      * a toolbar update by reading the markup state of the current selection in the editor.
24006      */
24007     updateToolbar: function(){
24008
24009         if(!this.editorcore.activated){
24010             this.editor.onFirstFocus(); // is this neeed?
24011             return;
24012         }
24013
24014         var btns = this.buttons; 
24015         var doc = this.editorcore.doc;
24016         btns.get('bold').setActive(doc.queryCommandState('bold'));
24017         btns.get('italic').setActive(doc.queryCommandState('italic'));
24018         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24019         
24020         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24021         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24022         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24023         
24024         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24025         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24026          /*
24027         
24028         var ans = this.editorcore.getAllAncestors();
24029         if (this.formatCombo) {
24030             
24031             
24032             var store = this.formatCombo.store;
24033             this.formatCombo.setValue("");
24034             for (var i =0; i < ans.length;i++) {
24035                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24036                     // select it..
24037                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24038                     break;
24039                 }
24040             }
24041         }
24042         
24043         
24044         
24045         // hides menus... - so this cant be on a menu...
24046         Roo.bootstrap.MenuMgr.hideAll();
24047         */
24048         Roo.bootstrap.MenuMgr.hideAll();
24049         //this.editorsyncValue();
24050     },
24051     onFirstFocus: function() {
24052         this.buttons.each(function(item){
24053            item.enable();
24054         });
24055     },
24056     toggleSourceEdit : function(sourceEditMode){
24057         
24058           
24059         if(sourceEditMode){
24060             Roo.log("disabling buttons");
24061            this.buttons.each( function(item){
24062                 if(item.cmd != 'pencil'){
24063                     item.disable();
24064                 }
24065             });
24066           
24067         }else{
24068             Roo.log("enabling buttons");
24069             if(this.editorcore.initialized){
24070                 this.buttons.each( function(item){
24071                     item.enable();
24072                 });
24073             }
24074             
24075         }
24076         Roo.log("calling toggole on editor");
24077         // tell the editor that it's been pressed..
24078         this.editor.toggleSourceEdit(sourceEditMode);
24079        
24080     }
24081 });
24082
24083
24084
24085
24086
24087 /**
24088  * @class Roo.bootstrap.Table.AbstractSelectionModel
24089  * @extends Roo.util.Observable
24090  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24091  * implemented by descendant classes.  This class should not be directly instantiated.
24092  * @constructor
24093  */
24094 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24095     this.locked = false;
24096     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24097 };
24098
24099
24100 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24101     /** @ignore Called by the grid automatically. Do not call directly. */
24102     init : function(grid){
24103         this.grid = grid;
24104         this.initEvents();
24105     },
24106
24107     /**
24108      * Locks the selections.
24109      */
24110     lock : function(){
24111         this.locked = true;
24112     },
24113
24114     /**
24115      * Unlocks the selections.
24116      */
24117     unlock : function(){
24118         this.locked = false;
24119     },
24120
24121     /**
24122      * Returns true if the selections are locked.
24123      * @return {Boolean}
24124      */
24125     isLocked : function(){
24126         return this.locked;
24127     }
24128 });
24129 /**
24130  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24131  * @class Roo.bootstrap.Table.RowSelectionModel
24132  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24133  * It supports multiple selections and keyboard selection/navigation. 
24134  * @constructor
24135  * @param {Object} config
24136  */
24137
24138 Roo.bootstrap.Table.RowSelectionModel = function(config){
24139     Roo.apply(this, config);
24140     this.selections = new Roo.util.MixedCollection(false, function(o){
24141         return o.id;
24142     });
24143
24144     this.last = false;
24145     this.lastActive = false;
24146
24147     this.addEvents({
24148         /**
24149              * @event selectionchange
24150              * Fires when the selection changes
24151              * @param {SelectionModel} this
24152              */
24153             "selectionchange" : true,
24154         /**
24155              * @event afterselectionchange
24156              * Fires after the selection changes (eg. by key press or clicking)
24157              * @param {SelectionModel} this
24158              */
24159             "afterselectionchange" : true,
24160         /**
24161              * @event beforerowselect
24162              * Fires when a row is selected being selected, return false to cancel.
24163              * @param {SelectionModel} this
24164              * @param {Number} rowIndex The selected index
24165              * @param {Boolean} keepExisting False if other selections will be cleared
24166              */
24167             "beforerowselect" : true,
24168         /**
24169              * @event rowselect
24170              * Fires when a row is selected.
24171              * @param {SelectionModel} this
24172              * @param {Number} rowIndex The selected index
24173              * @param {Roo.data.Record} r The record
24174              */
24175             "rowselect" : true,
24176         /**
24177              * @event rowdeselect
24178              * Fires when a row is deselected.
24179              * @param {SelectionModel} this
24180              * @param {Number} rowIndex The selected index
24181              */
24182         "rowdeselect" : true
24183     });
24184     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24185     this.locked = false;
24186  };
24187
24188 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24189     /**
24190      * @cfg {Boolean} singleSelect
24191      * True to allow selection of only one row at a time (defaults to false)
24192      */
24193     singleSelect : false,
24194
24195     // private
24196     initEvents : function()
24197     {
24198
24199         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24200         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24201         //}else{ // allow click to work like normal
24202          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24203         //}
24204         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24205         this.grid.on("rowclick", this.handleMouseDown, this);
24206         
24207         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24208             "up" : function(e){
24209                 if(!e.shiftKey){
24210                     this.selectPrevious(e.shiftKey);
24211                 }else if(this.last !== false && this.lastActive !== false){
24212                     var last = this.last;
24213                     this.selectRange(this.last,  this.lastActive-1);
24214                     this.grid.getView().focusRow(this.lastActive);
24215                     if(last !== false){
24216                         this.last = last;
24217                     }
24218                 }else{
24219                     this.selectFirstRow();
24220                 }
24221                 this.fireEvent("afterselectionchange", this);
24222             },
24223             "down" : function(e){
24224                 if(!e.shiftKey){
24225                     this.selectNext(e.shiftKey);
24226                 }else if(this.last !== false && this.lastActive !== false){
24227                     var last = this.last;
24228                     this.selectRange(this.last,  this.lastActive+1);
24229                     this.grid.getView().focusRow(this.lastActive);
24230                     if(last !== false){
24231                         this.last = last;
24232                     }
24233                 }else{
24234                     this.selectFirstRow();
24235                 }
24236                 this.fireEvent("afterselectionchange", this);
24237             },
24238             scope: this
24239         });
24240         this.grid.store.on('load', function(){
24241             this.selections.clear();
24242         },this);
24243         /*
24244         var view = this.grid.view;
24245         view.on("refresh", this.onRefresh, this);
24246         view.on("rowupdated", this.onRowUpdated, this);
24247         view.on("rowremoved", this.onRemove, this);
24248         */
24249     },
24250
24251     // private
24252     onRefresh : function()
24253     {
24254         var ds = this.grid.store, i, v = this.grid.view;
24255         var s = this.selections;
24256         s.each(function(r){
24257             if((i = ds.indexOfId(r.id)) != -1){
24258                 v.onRowSelect(i);
24259             }else{
24260                 s.remove(r);
24261             }
24262         });
24263     },
24264
24265     // private
24266     onRemove : function(v, index, r){
24267         this.selections.remove(r);
24268     },
24269
24270     // private
24271     onRowUpdated : function(v, index, r){
24272         if(this.isSelected(r)){
24273             v.onRowSelect(index);
24274         }
24275     },
24276
24277     /**
24278      * Select records.
24279      * @param {Array} records The records to select
24280      * @param {Boolean} keepExisting (optional) True to keep existing selections
24281      */
24282     selectRecords : function(records, keepExisting)
24283     {
24284         if(!keepExisting){
24285             this.clearSelections();
24286         }
24287             var ds = this.grid.store;
24288         for(var i = 0, len = records.length; i < len; i++){
24289             this.selectRow(ds.indexOf(records[i]), true);
24290         }
24291     },
24292
24293     /**
24294      * Gets the number of selected rows.
24295      * @return {Number}
24296      */
24297     getCount : function(){
24298         return this.selections.length;
24299     },
24300
24301     /**
24302      * Selects the first row in the grid.
24303      */
24304     selectFirstRow : function(){
24305         this.selectRow(0);
24306     },
24307
24308     /**
24309      * Select the last row.
24310      * @param {Boolean} keepExisting (optional) True to keep existing selections
24311      */
24312     selectLastRow : function(keepExisting){
24313         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24314         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24315     },
24316
24317     /**
24318      * Selects the row immediately following the last selected row.
24319      * @param {Boolean} keepExisting (optional) True to keep existing selections
24320      */
24321     selectNext : function(keepExisting)
24322     {
24323             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24324             this.selectRow(this.last+1, keepExisting);
24325             this.grid.getView().focusRow(this.last);
24326         }
24327     },
24328
24329     /**
24330      * Selects the row that precedes the last selected row.
24331      * @param {Boolean} keepExisting (optional) True to keep existing selections
24332      */
24333     selectPrevious : function(keepExisting){
24334         if(this.last){
24335             this.selectRow(this.last-1, keepExisting);
24336             this.grid.getView().focusRow(this.last);
24337         }
24338     },
24339
24340     /**
24341      * Returns the selected records
24342      * @return {Array} Array of selected records
24343      */
24344     getSelections : function(){
24345         return [].concat(this.selections.items);
24346     },
24347
24348     /**
24349      * Returns the first selected record.
24350      * @return {Record}
24351      */
24352     getSelected : function(){
24353         return this.selections.itemAt(0);
24354     },
24355
24356
24357     /**
24358      * Clears all selections.
24359      */
24360     clearSelections : function(fast)
24361     {
24362         if(this.locked) {
24363             return;
24364         }
24365         if(fast !== true){
24366                 var ds = this.grid.store;
24367             var s = this.selections;
24368             s.each(function(r){
24369                 this.deselectRow(ds.indexOfId(r.id));
24370             }, this);
24371             s.clear();
24372         }else{
24373             this.selections.clear();
24374         }
24375         this.last = false;
24376     },
24377
24378
24379     /**
24380      * Selects all rows.
24381      */
24382     selectAll : function(){
24383         if(this.locked) {
24384             return;
24385         }
24386         this.selections.clear();
24387         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24388             this.selectRow(i, true);
24389         }
24390     },
24391
24392     /**
24393      * Returns True if there is a selection.
24394      * @return {Boolean}
24395      */
24396     hasSelection : function(){
24397         return this.selections.length > 0;
24398     },
24399
24400     /**
24401      * Returns True if the specified row is selected.
24402      * @param {Number/Record} record The record or index of the record to check
24403      * @return {Boolean}
24404      */
24405     isSelected : function(index){
24406             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24407         return (r && this.selections.key(r.id) ? true : false);
24408     },
24409
24410     /**
24411      * Returns True if the specified record id is selected.
24412      * @param {String} id The id of record to check
24413      * @return {Boolean}
24414      */
24415     isIdSelected : function(id){
24416         return (this.selections.key(id) ? true : false);
24417     },
24418
24419
24420     // private
24421     handleMouseDBClick : function(e, t){
24422         
24423     },
24424     // private
24425     handleMouseDown : function(e, t)
24426     {
24427             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24428         if(this.isLocked() || rowIndex < 0 ){
24429             return;
24430         };
24431         if(e.shiftKey && this.last !== false){
24432             var last = this.last;
24433             this.selectRange(last, rowIndex, e.ctrlKey);
24434             this.last = last; // reset the last
24435             t.focus();
24436     
24437         }else{
24438             var isSelected = this.isSelected(rowIndex);
24439             //Roo.log("select row:" + rowIndex);
24440             if(isSelected){
24441                 this.deselectRow(rowIndex);
24442             } else {
24443                         this.selectRow(rowIndex, true);
24444             }
24445     
24446             /*
24447                 if(e.button !== 0 && isSelected){
24448                 alert('rowIndex 2: ' + rowIndex);
24449                     view.focusRow(rowIndex);
24450                 }else if(e.ctrlKey && isSelected){
24451                     this.deselectRow(rowIndex);
24452                 }else if(!isSelected){
24453                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24454                     view.focusRow(rowIndex);
24455                 }
24456             */
24457         }
24458         this.fireEvent("afterselectionchange", this);
24459     },
24460     // private
24461     handleDragableRowClick :  function(grid, rowIndex, e) 
24462     {
24463         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24464             this.selectRow(rowIndex, false);
24465             grid.view.focusRow(rowIndex);
24466              this.fireEvent("afterselectionchange", this);
24467         }
24468     },
24469     
24470     /**
24471      * Selects multiple rows.
24472      * @param {Array} rows Array of the indexes of the row to select
24473      * @param {Boolean} keepExisting (optional) True to keep existing selections
24474      */
24475     selectRows : function(rows, keepExisting){
24476         if(!keepExisting){
24477             this.clearSelections();
24478         }
24479         for(var i = 0, len = rows.length; i < len; i++){
24480             this.selectRow(rows[i], true);
24481         }
24482     },
24483
24484     /**
24485      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24486      * @param {Number} startRow The index of the first row in the range
24487      * @param {Number} endRow The index of the last row in the range
24488      * @param {Boolean} keepExisting (optional) True to retain existing selections
24489      */
24490     selectRange : function(startRow, endRow, keepExisting){
24491         if(this.locked) {
24492             return;
24493         }
24494         if(!keepExisting){
24495             this.clearSelections();
24496         }
24497         if(startRow <= endRow){
24498             for(var i = startRow; i <= endRow; i++){
24499                 this.selectRow(i, true);
24500             }
24501         }else{
24502             for(var i = startRow; i >= endRow; i--){
24503                 this.selectRow(i, true);
24504             }
24505         }
24506     },
24507
24508     /**
24509      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24510      * @param {Number} startRow The index of the first row in the range
24511      * @param {Number} endRow The index of the last row in the range
24512      */
24513     deselectRange : function(startRow, endRow, preventViewNotify){
24514         if(this.locked) {
24515             return;
24516         }
24517         for(var i = startRow; i <= endRow; i++){
24518             this.deselectRow(i, preventViewNotify);
24519         }
24520     },
24521
24522     /**
24523      * Selects a row.
24524      * @param {Number} row The index of the row to select
24525      * @param {Boolean} keepExisting (optional) True to keep existing selections
24526      */
24527     selectRow : function(index, keepExisting, preventViewNotify)
24528     {
24529             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24530             return;
24531         }
24532         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24533             if(!keepExisting || this.singleSelect){
24534                 this.clearSelections();
24535             }
24536             
24537             var r = this.grid.store.getAt(index);
24538             //console.log('selectRow - record id :' + r.id);
24539             
24540             this.selections.add(r);
24541             this.last = this.lastActive = index;
24542             if(!preventViewNotify){
24543                 var proxy = new Roo.Element(
24544                                 this.grid.getRowDom(index)
24545                 );
24546                 proxy.addClass('bg-info info');
24547             }
24548             this.fireEvent("rowselect", this, index, r);
24549             this.fireEvent("selectionchange", this);
24550         }
24551     },
24552
24553     /**
24554      * Deselects a row.
24555      * @param {Number} row The index of the row to deselect
24556      */
24557     deselectRow : function(index, preventViewNotify)
24558     {
24559         if(this.locked) {
24560             return;
24561         }
24562         if(this.last == index){
24563             this.last = false;
24564         }
24565         if(this.lastActive == index){
24566             this.lastActive = false;
24567         }
24568         
24569         var r = this.grid.store.getAt(index);
24570         if (!r) {
24571             return;
24572         }
24573         
24574         this.selections.remove(r);
24575         //.console.log('deselectRow - record id :' + r.id);
24576         if(!preventViewNotify){
24577         
24578             var proxy = new Roo.Element(
24579                 this.grid.getRowDom(index)
24580             );
24581             proxy.removeClass('bg-info info');
24582         }
24583         this.fireEvent("rowdeselect", this, index);
24584         this.fireEvent("selectionchange", this);
24585     },
24586
24587     // private
24588     restoreLast : function(){
24589         if(this._last){
24590             this.last = this._last;
24591         }
24592     },
24593
24594     // private
24595     acceptsNav : function(row, col, cm){
24596         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24597     },
24598
24599     // private
24600     onEditorKey : function(field, e){
24601         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24602         if(k == e.TAB){
24603             e.stopEvent();
24604             ed.completeEdit();
24605             if(e.shiftKey){
24606                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24607             }else{
24608                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24609             }
24610         }else if(k == e.ENTER && !e.ctrlKey){
24611             e.stopEvent();
24612             ed.completeEdit();
24613             if(e.shiftKey){
24614                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24615             }else{
24616                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24617             }
24618         }else if(k == e.ESC){
24619             ed.cancelEdit();
24620         }
24621         if(newCell){
24622             g.startEditing(newCell[0], newCell[1]);
24623         }
24624     }
24625 });
24626 /*
24627  * Based on:
24628  * Ext JS Library 1.1.1
24629  * Copyright(c) 2006-2007, Ext JS, LLC.
24630  *
24631  * Originally Released Under LGPL - original licence link has changed is not relivant.
24632  *
24633  * Fork - LGPL
24634  * <script type="text/javascript">
24635  */
24636  
24637 /**
24638  * @class Roo.bootstrap.PagingToolbar
24639  * @extends Roo.bootstrap.NavSimplebar
24640  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24641  * @constructor
24642  * Create a new PagingToolbar
24643  * @param {Object} config The config object
24644  * @param {Roo.data.Store} store
24645  */
24646 Roo.bootstrap.PagingToolbar = function(config)
24647 {
24648     // old args format still supported... - xtype is prefered..
24649         // created from xtype...
24650     
24651     this.ds = config.dataSource;
24652     
24653     if (config.store && !this.ds) {
24654         this.store= Roo.factory(config.store, Roo.data);
24655         this.ds = this.store;
24656         this.ds.xmodule = this.xmodule || false;
24657     }
24658     
24659     this.toolbarItems = [];
24660     if (config.items) {
24661         this.toolbarItems = config.items;
24662     }
24663     
24664     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24665     
24666     this.cursor = 0;
24667     
24668     if (this.ds) { 
24669         this.bind(this.ds);
24670     }
24671     
24672     if (Roo.bootstrap.version == 4) {
24673         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24674     } else {
24675         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24676     }
24677     
24678 };
24679
24680 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24681     /**
24682      * @cfg {Roo.data.Store} dataSource
24683      * The underlying data store providing the paged data
24684      */
24685     /**
24686      * @cfg {String/HTMLElement/Element} container
24687      * container The id or element that will contain the toolbar
24688      */
24689     /**
24690      * @cfg {Boolean} displayInfo
24691      * True to display the displayMsg (defaults to false)
24692      */
24693     /**
24694      * @cfg {Number} pageSize
24695      * The number of records to display per page (defaults to 20)
24696      */
24697     pageSize: 20,
24698     /**
24699      * @cfg {String} displayMsg
24700      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24701      */
24702     displayMsg : 'Displaying {0} - {1} of {2}',
24703     /**
24704      * @cfg {String} emptyMsg
24705      * The message to display when no records are found (defaults to "No data to display")
24706      */
24707     emptyMsg : 'No data to display',
24708     /**
24709      * Customizable piece of the default paging text (defaults to "Page")
24710      * @type String
24711      */
24712     beforePageText : "Page",
24713     /**
24714      * Customizable piece of the default paging text (defaults to "of %0")
24715      * @type String
24716      */
24717     afterPageText : "of {0}",
24718     /**
24719      * Customizable piece of the default paging text (defaults to "First Page")
24720      * @type String
24721      */
24722     firstText : "First Page",
24723     /**
24724      * Customizable piece of the default paging text (defaults to "Previous Page")
24725      * @type String
24726      */
24727     prevText : "Previous Page",
24728     /**
24729      * Customizable piece of the default paging text (defaults to "Next Page")
24730      * @type String
24731      */
24732     nextText : "Next Page",
24733     /**
24734      * Customizable piece of the default paging text (defaults to "Last Page")
24735      * @type String
24736      */
24737     lastText : "Last Page",
24738     /**
24739      * Customizable piece of the default paging text (defaults to "Refresh")
24740      * @type String
24741      */
24742     refreshText : "Refresh",
24743
24744     buttons : false,
24745     // private
24746     onRender : function(ct, position) 
24747     {
24748         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24749         this.navgroup.parentId = this.id;
24750         this.navgroup.onRender(this.el, null);
24751         // add the buttons to the navgroup
24752         
24753         if(this.displayInfo){
24754             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24755             this.displayEl = this.el.select('.x-paging-info', true).first();
24756 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24757 //            this.displayEl = navel.el.select('span',true).first();
24758         }
24759         
24760         var _this = this;
24761         
24762         if(this.buttons){
24763             Roo.each(_this.buttons, function(e){ // this might need to use render????
24764                Roo.factory(e).render(_this.el);
24765             });
24766         }
24767             
24768         Roo.each(_this.toolbarItems, function(e) {
24769             _this.navgroup.addItem(e);
24770         });
24771         
24772         
24773         this.first = this.navgroup.addItem({
24774             tooltip: this.firstText,
24775             cls: "prev btn-outline-secondary",
24776             html : ' <i class="fa fa-step-backward"></i>',
24777             disabled: true,
24778             preventDefault: true,
24779             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24780         });
24781         
24782         this.prev =  this.navgroup.addItem({
24783             tooltip: this.prevText,
24784             cls: "prev btn-outline-secondary",
24785             html : ' <i class="fa fa-backward"></i>',
24786             disabled: true,
24787             preventDefault: true,
24788             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24789         });
24790     //this.addSeparator();
24791         
24792         
24793         var field = this.navgroup.addItem( {
24794             tagtype : 'span',
24795             cls : 'x-paging-position',
24796             
24797             html : this.beforePageText  +
24798                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24799                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24800          } ); //?? escaped?
24801         
24802         this.field = field.el.select('input', true).first();
24803         this.field.on("keydown", this.onPagingKeydown, this);
24804         this.field.on("focus", function(){this.dom.select();});
24805     
24806     
24807         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24808         //this.field.setHeight(18);
24809         //this.addSeparator();
24810         this.next = this.navgroup.addItem({
24811             tooltip: this.nextText,
24812             cls: "next btn-outline-secondary",
24813             html : ' <i class="fa fa-forward"></i>',
24814             disabled: true,
24815             preventDefault: true,
24816             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24817         });
24818         this.last = this.navgroup.addItem({
24819             tooltip: this.lastText,
24820             html : ' <i class="fa fa-step-forward"></i>',
24821             cls: "next btn-outline-secondary",
24822             disabled: true,
24823             preventDefault: true,
24824             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24825         });
24826     //this.addSeparator();
24827         this.loading = this.navgroup.addItem({
24828             tooltip: this.refreshText,
24829             icon: 'fa fa-refresh',
24830             preventDefault: true,
24831             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24832         });
24833         
24834     },
24835
24836     // private
24837     updateInfo : function(){
24838         if(this.displayEl){
24839             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24840             var msg = count == 0 ?
24841                 this.emptyMsg :
24842                 String.format(
24843                     this.displayMsg,
24844                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24845                 );
24846             this.displayEl.update(msg);
24847         }
24848     },
24849
24850     // private
24851     onLoad : function(ds, r, o)
24852     {
24853         this.cursor = o.params.start ? o.params.start : 0;
24854         
24855         var d = this.getPageData(),
24856             ap = d.activePage,
24857             ps = d.pages;
24858         
24859         
24860         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24861         this.field.dom.value = ap;
24862         this.first.setDisabled(ap == 1);
24863         this.prev.setDisabled(ap == 1);
24864         this.next.setDisabled(ap == ps);
24865         this.last.setDisabled(ap == ps);
24866         this.loading.enable();
24867         this.updateInfo();
24868     },
24869
24870     // private
24871     getPageData : function(){
24872         var total = this.ds.getTotalCount();
24873         return {
24874             total : total,
24875             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24876             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24877         };
24878     },
24879
24880     // private
24881     onLoadError : function(){
24882         this.loading.enable();
24883     },
24884
24885     // private
24886     onPagingKeydown : function(e){
24887         var k = e.getKey();
24888         var d = this.getPageData();
24889         if(k == e.RETURN){
24890             var v = this.field.dom.value, pageNum;
24891             if(!v || isNaN(pageNum = parseInt(v, 10))){
24892                 this.field.dom.value = d.activePage;
24893                 return;
24894             }
24895             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24896             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24897             e.stopEvent();
24898         }
24899         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))
24900         {
24901           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24902           this.field.dom.value = pageNum;
24903           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24904           e.stopEvent();
24905         }
24906         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24907         {
24908           var v = this.field.dom.value, pageNum; 
24909           var increment = (e.shiftKey) ? 10 : 1;
24910           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24911                 increment *= -1;
24912           }
24913           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24914             this.field.dom.value = d.activePage;
24915             return;
24916           }
24917           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24918           {
24919             this.field.dom.value = parseInt(v, 10) + increment;
24920             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24921             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24922           }
24923           e.stopEvent();
24924         }
24925     },
24926
24927     // private
24928     beforeLoad : function(){
24929         if(this.loading){
24930             this.loading.disable();
24931         }
24932     },
24933
24934     // private
24935     onClick : function(which){
24936         
24937         var ds = this.ds;
24938         if (!ds) {
24939             return;
24940         }
24941         
24942         switch(which){
24943             case "first":
24944                 ds.load({params:{start: 0, limit: this.pageSize}});
24945             break;
24946             case "prev":
24947                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24948             break;
24949             case "next":
24950                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24951             break;
24952             case "last":
24953                 var total = ds.getTotalCount();
24954                 var extra = total % this.pageSize;
24955                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24956                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24957             break;
24958             case "refresh":
24959                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24960             break;
24961         }
24962     },
24963
24964     /**
24965      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24966      * @param {Roo.data.Store} store The data store to unbind
24967      */
24968     unbind : function(ds){
24969         ds.un("beforeload", this.beforeLoad, this);
24970         ds.un("load", this.onLoad, this);
24971         ds.un("loadexception", this.onLoadError, this);
24972         ds.un("remove", this.updateInfo, this);
24973         ds.un("add", this.updateInfo, this);
24974         this.ds = undefined;
24975     },
24976
24977     /**
24978      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24979      * @param {Roo.data.Store} store The data store to bind
24980      */
24981     bind : function(ds){
24982         ds.on("beforeload", this.beforeLoad, this);
24983         ds.on("load", this.onLoad, this);
24984         ds.on("loadexception", this.onLoadError, this);
24985         ds.on("remove", this.updateInfo, this);
24986         ds.on("add", this.updateInfo, this);
24987         this.ds = ds;
24988     }
24989 });/*
24990  * - LGPL
24991  *
24992  * element
24993  * 
24994  */
24995
24996 /**
24997  * @class Roo.bootstrap.MessageBar
24998  * @extends Roo.bootstrap.Component
24999  * Bootstrap MessageBar class
25000  * @cfg {String} html contents of the MessageBar
25001  * @cfg {String} weight (info | success | warning | danger) default info
25002  * @cfg {String} beforeClass insert the bar before the given class
25003  * @cfg {Boolean} closable (true | false) default false
25004  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25005  * 
25006  * @constructor
25007  * Create a new Element
25008  * @param {Object} config The config object
25009  */
25010
25011 Roo.bootstrap.MessageBar = function(config){
25012     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25013 };
25014
25015 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25016     
25017     html: '',
25018     weight: 'info',
25019     closable: false,
25020     fixed: false,
25021     beforeClass: 'bootstrap-sticky-wrap',
25022     
25023     getAutoCreate : function(){
25024         
25025         var cfg = {
25026             tag: 'div',
25027             cls: 'alert alert-dismissable alert-' + this.weight,
25028             cn: [
25029                 {
25030                     tag: 'span',
25031                     cls: 'message',
25032                     html: this.html || ''
25033                 }
25034             ]
25035         };
25036         
25037         if(this.fixed){
25038             cfg.cls += ' alert-messages-fixed';
25039         }
25040         
25041         if(this.closable){
25042             cfg.cn.push({
25043                 tag: 'button',
25044                 cls: 'close',
25045                 html: 'x'
25046             });
25047         }
25048         
25049         return cfg;
25050     },
25051     
25052     onRender : function(ct, position)
25053     {
25054         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25055         
25056         if(!this.el){
25057             var cfg = Roo.apply({},  this.getAutoCreate());
25058             cfg.id = Roo.id();
25059             
25060             if (this.cls) {
25061                 cfg.cls += ' ' + this.cls;
25062             }
25063             if (this.style) {
25064                 cfg.style = this.style;
25065             }
25066             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25067             
25068             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25069         }
25070         
25071         this.el.select('>button.close').on('click', this.hide, this);
25072         
25073     },
25074     
25075     show : function()
25076     {
25077         if (!this.rendered) {
25078             this.render();
25079         }
25080         
25081         this.el.show();
25082         
25083         this.fireEvent('show', this);
25084         
25085     },
25086     
25087     hide : function()
25088     {
25089         if (!this.rendered) {
25090             this.render();
25091         }
25092         
25093         this.el.hide();
25094         
25095         this.fireEvent('hide', this);
25096     },
25097     
25098     update : function()
25099     {
25100 //        var e = this.el.dom.firstChild;
25101 //        
25102 //        if(this.closable){
25103 //            e = e.nextSibling;
25104 //        }
25105 //        
25106 //        e.data = this.html || '';
25107
25108         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25109     }
25110    
25111 });
25112
25113  
25114
25115      /*
25116  * - LGPL
25117  *
25118  * Graph
25119  * 
25120  */
25121
25122
25123 /**
25124  * @class Roo.bootstrap.Graph
25125  * @extends Roo.bootstrap.Component
25126  * Bootstrap Graph class
25127 > Prameters
25128  -sm {number} sm 4
25129  -md {number} md 5
25130  @cfg {String} graphtype  bar | vbar | pie
25131  @cfg {number} g_x coodinator | centre x (pie)
25132  @cfg {number} g_y coodinator | centre y (pie)
25133  @cfg {number} g_r radius (pie)
25134  @cfg {number} g_height height of the chart (respected by all elements in the set)
25135  @cfg {number} g_width width of the chart (respected by all elements in the set)
25136  @cfg {Object} title The title of the chart
25137     
25138  -{Array}  values
25139  -opts (object) options for the chart 
25140      o {
25141      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25142      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25143      o vgutter (number)
25144      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.
25145      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25146      o to
25147      o stretch (boolean)
25148      o }
25149  -opts (object) options for the pie
25150      o{
25151      o cut
25152      o startAngle (number)
25153      o endAngle (number)
25154      } 
25155  *
25156  * @constructor
25157  * Create a new Input
25158  * @param {Object} config The config object
25159  */
25160
25161 Roo.bootstrap.Graph = function(config){
25162     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25163     
25164     this.addEvents({
25165         // img events
25166         /**
25167          * @event click
25168          * The img click event for the img.
25169          * @param {Roo.EventObject} e
25170          */
25171         "click" : true
25172     });
25173 };
25174
25175 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25176     
25177     sm: 4,
25178     md: 5,
25179     graphtype: 'bar',
25180     g_height: 250,
25181     g_width: 400,
25182     g_x: 50,
25183     g_y: 50,
25184     g_r: 30,
25185     opts:{
25186         //g_colors: this.colors,
25187         g_type: 'soft',
25188         g_gutter: '20%'
25189
25190     },
25191     title : false,
25192
25193     getAutoCreate : function(){
25194         
25195         var cfg = {
25196             tag: 'div',
25197             html : null
25198         };
25199         
25200         
25201         return  cfg;
25202     },
25203
25204     onRender : function(ct,position){
25205         
25206         
25207         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25208         
25209         if (typeof(Raphael) == 'undefined') {
25210             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25211             return;
25212         }
25213         
25214         this.raphael = Raphael(this.el.dom);
25215         
25216                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25217                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25218                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25219                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25220                 /*
25221                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25222                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25223                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25224                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25225                 
25226                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25227                 r.barchart(330, 10, 300, 220, data1);
25228                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25229                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25230                 */
25231                 
25232                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25233                 // r.barchart(30, 30, 560, 250,  xdata, {
25234                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25235                 //     axis : "0 0 1 1",
25236                 //     axisxlabels :  xdata
25237                 //     //yvalues : cols,
25238                    
25239                 // });
25240 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25241 //        
25242 //        this.load(null,xdata,{
25243 //                axis : "0 0 1 1",
25244 //                axisxlabels :  xdata
25245 //                });
25246
25247     },
25248
25249     load : function(graphtype,xdata,opts)
25250     {
25251         this.raphael.clear();
25252         if(!graphtype) {
25253             graphtype = this.graphtype;
25254         }
25255         if(!opts){
25256             opts = this.opts;
25257         }
25258         var r = this.raphael,
25259             fin = function () {
25260                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25261             },
25262             fout = function () {
25263                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25264             },
25265             pfin = function() {
25266                 this.sector.stop();
25267                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25268
25269                 if (this.label) {
25270                     this.label[0].stop();
25271                     this.label[0].attr({ r: 7.5 });
25272                     this.label[1].attr({ "font-weight": 800 });
25273                 }
25274             },
25275             pfout = function() {
25276                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25277
25278                 if (this.label) {
25279                     this.label[0].animate({ r: 5 }, 500, "bounce");
25280                     this.label[1].attr({ "font-weight": 400 });
25281                 }
25282             };
25283
25284         switch(graphtype){
25285             case 'bar':
25286                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25287                 break;
25288             case 'hbar':
25289                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25290                 break;
25291             case 'pie':
25292 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25293 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25294 //            
25295                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25296                 
25297                 break;
25298
25299         }
25300         
25301         if(this.title){
25302             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25303         }
25304         
25305     },
25306     
25307     setTitle: function(o)
25308     {
25309         this.title = o;
25310     },
25311     
25312     initEvents: function() {
25313         
25314         if(!this.href){
25315             this.el.on('click', this.onClick, this);
25316         }
25317     },
25318     
25319     onClick : function(e)
25320     {
25321         Roo.log('img onclick');
25322         this.fireEvent('click', this, e);
25323     }
25324    
25325 });
25326
25327  
25328 /*
25329  * - LGPL
25330  *
25331  * numberBox
25332  * 
25333  */
25334 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25335
25336 /**
25337  * @class Roo.bootstrap.dash.NumberBox
25338  * @extends Roo.bootstrap.Component
25339  * Bootstrap NumberBox class
25340  * @cfg {String} headline Box headline
25341  * @cfg {String} content Box content
25342  * @cfg {String} icon Box icon
25343  * @cfg {String} footer Footer text
25344  * @cfg {String} fhref Footer href
25345  * 
25346  * @constructor
25347  * Create a new NumberBox
25348  * @param {Object} config The config object
25349  */
25350
25351
25352 Roo.bootstrap.dash.NumberBox = function(config){
25353     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25354     
25355 };
25356
25357 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25358     
25359     headline : '',
25360     content : '',
25361     icon : '',
25362     footer : '',
25363     fhref : '',
25364     ficon : '',
25365     
25366     getAutoCreate : function(){
25367         
25368         var cfg = {
25369             tag : 'div',
25370             cls : 'small-box ',
25371             cn : [
25372                 {
25373                     tag : 'div',
25374                     cls : 'inner',
25375                     cn :[
25376                         {
25377                             tag : 'h3',
25378                             cls : 'roo-headline',
25379                             html : this.headline
25380                         },
25381                         {
25382                             tag : 'p',
25383                             cls : 'roo-content',
25384                             html : this.content
25385                         }
25386                     ]
25387                 }
25388             ]
25389         };
25390         
25391         if(this.icon){
25392             cfg.cn.push({
25393                 tag : 'div',
25394                 cls : 'icon',
25395                 cn :[
25396                     {
25397                         tag : 'i',
25398                         cls : 'ion ' + this.icon
25399                     }
25400                 ]
25401             });
25402         }
25403         
25404         if(this.footer){
25405             var footer = {
25406                 tag : 'a',
25407                 cls : 'small-box-footer',
25408                 href : this.fhref || '#',
25409                 html : this.footer
25410             };
25411             
25412             cfg.cn.push(footer);
25413             
25414         }
25415         
25416         return  cfg;
25417     },
25418
25419     onRender : function(ct,position){
25420         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25421
25422
25423        
25424                 
25425     },
25426
25427     setHeadline: function (value)
25428     {
25429         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25430     },
25431     
25432     setFooter: function (value, href)
25433     {
25434         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25435         
25436         if(href){
25437             this.el.select('a.small-box-footer',true).first().attr('href', href);
25438         }
25439         
25440     },
25441
25442     setContent: function (value)
25443     {
25444         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25445     },
25446
25447     initEvents: function() 
25448     {   
25449         
25450     }
25451     
25452 });
25453
25454  
25455 /*
25456  * - LGPL
25457  *
25458  * TabBox
25459  * 
25460  */
25461 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25462
25463 /**
25464  * @class Roo.bootstrap.dash.TabBox
25465  * @extends Roo.bootstrap.Component
25466  * Bootstrap TabBox class
25467  * @cfg {String} title Title of the TabBox
25468  * @cfg {String} icon Icon of the TabBox
25469  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25470  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25471  * 
25472  * @constructor
25473  * Create a new TabBox
25474  * @param {Object} config The config object
25475  */
25476
25477
25478 Roo.bootstrap.dash.TabBox = function(config){
25479     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25480     this.addEvents({
25481         // raw events
25482         /**
25483          * @event addpane
25484          * When a pane is added
25485          * @param {Roo.bootstrap.dash.TabPane} pane
25486          */
25487         "addpane" : true,
25488         /**
25489          * @event activatepane
25490          * When a pane is activated
25491          * @param {Roo.bootstrap.dash.TabPane} pane
25492          */
25493         "activatepane" : true
25494         
25495          
25496     });
25497     
25498     this.panes = [];
25499 };
25500
25501 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25502
25503     title : '',
25504     icon : false,
25505     showtabs : true,
25506     tabScrollable : false,
25507     
25508     getChildContainer : function()
25509     {
25510         return this.el.select('.tab-content', true).first();
25511     },
25512     
25513     getAutoCreate : function(){
25514         
25515         var header = {
25516             tag: 'li',
25517             cls: 'pull-left header',
25518             html: this.title,
25519             cn : []
25520         };
25521         
25522         if(this.icon){
25523             header.cn.push({
25524                 tag: 'i',
25525                 cls: 'fa ' + this.icon
25526             });
25527         }
25528         
25529         var h = {
25530             tag: 'ul',
25531             cls: 'nav nav-tabs pull-right',
25532             cn: [
25533                 header
25534             ]
25535         };
25536         
25537         if(this.tabScrollable){
25538             h = {
25539                 tag: 'div',
25540                 cls: 'tab-header',
25541                 cn: [
25542                     {
25543                         tag: 'ul',
25544                         cls: 'nav nav-tabs pull-right',
25545                         cn: [
25546                             header
25547                         ]
25548                     }
25549                 ]
25550             };
25551         }
25552         
25553         var cfg = {
25554             tag: 'div',
25555             cls: 'nav-tabs-custom',
25556             cn: [
25557                 h,
25558                 {
25559                     tag: 'div',
25560                     cls: 'tab-content no-padding',
25561                     cn: []
25562                 }
25563             ]
25564         };
25565
25566         return  cfg;
25567     },
25568     initEvents : function()
25569     {
25570         //Roo.log('add add pane handler');
25571         this.on('addpane', this.onAddPane, this);
25572     },
25573      /**
25574      * Updates the box title
25575      * @param {String} html to set the title to.
25576      */
25577     setTitle : function(value)
25578     {
25579         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25580     },
25581     onAddPane : function(pane)
25582     {
25583         this.panes.push(pane);
25584         //Roo.log('addpane');
25585         //Roo.log(pane);
25586         // tabs are rendere left to right..
25587         if(!this.showtabs){
25588             return;
25589         }
25590         
25591         var ctr = this.el.select('.nav-tabs', true).first();
25592          
25593          
25594         var existing = ctr.select('.nav-tab',true);
25595         var qty = existing.getCount();;
25596         
25597         
25598         var tab = ctr.createChild({
25599             tag : 'li',
25600             cls : 'nav-tab' + (qty ? '' : ' active'),
25601             cn : [
25602                 {
25603                     tag : 'a',
25604                     href:'#',
25605                     html : pane.title
25606                 }
25607             ]
25608         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25609         pane.tab = tab;
25610         
25611         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25612         if (!qty) {
25613             pane.el.addClass('active');
25614         }
25615         
25616                 
25617     },
25618     onTabClick : function(ev,un,ob,pane)
25619     {
25620         //Roo.log('tab - prev default');
25621         ev.preventDefault();
25622         
25623         
25624         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25625         pane.tab.addClass('active');
25626         //Roo.log(pane.title);
25627         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25628         // technically we should have a deactivate event.. but maybe add later.
25629         // and it should not de-activate the selected tab...
25630         this.fireEvent('activatepane', pane);
25631         pane.el.addClass('active');
25632         pane.fireEvent('activate');
25633         
25634         
25635     },
25636     
25637     getActivePane : function()
25638     {
25639         var r = false;
25640         Roo.each(this.panes, function(p) {
25641             if(p.el.hasClass('active')){
25642                 r = p;
25643                 return false;
25644             }
25645             
25646             return;
25647         });
25648         
25649         return r;
25650     }
25651     
25652     
25653 });
25654
25655  
25656 /*
25657  * - LGPL
25658  *
25659  * Tab pane
25660  * 
25661  */
25662 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25663 /**
25664  * @class Roo.bootstrap.TabPane
25665  * @extends Roo.bootstrap.Component
25666  * Bootstrap TabPane class
25667  * @cfg {Boolean} active (false | true) Default false
25668  * @cfg {String} title title of panel
25669
25670  * 
25671  * @constructor
25672  * Create a new TabPane
25673  * @param {Object} config The config object
25674  */
25675
25676 Roo.bootstrap.dash.TabPane = function(config){
25677     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25678     
25679     this.addEvents({
25680         // raw events
25681         /**
25682          * @event activate
25683          * When a pane is activated
25684          * @param {Roo.bootstrap.dash.TabPane} pane
25685          */
25686         "activate" : true
25687          
25688     });
25689 };
25690
25691 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25692     
25693     active : false,
25694     title : '',
25695     
25696     // the tabBox that this is attached to.
25697     tab : false,
25698      
25699     getAutoCreate : function() 
25700     {
25701         var cfg = {
25702             tag: 'div',
25703             cls: 'tab-pane'
25704         };
25705         
25706         if(this.active){
25707             cfg.cls += ' active';
25708         }
25709         
25710         return cfg;
25711     },
25712     initEvents  : function()
25713     {
25714         //Roo.log('trigger add pane handler');
25715         this.parent().fireEvent('addpane', this)
25716     },
25717     
25718      /**
25719      * Updates the tab title 
25720      * @param {String} html to set the title to.
25721      */
25722     setTitle: function(str)
25723     {
25724         if (!this.tab) {
25725             return;
25726         }
25727         this.title = str;
25728         this.tab.select('a', true).first().dom.innerHTML = str;
25729         
25730     }
25731     
25732     
25733     
25734 });
25735
25736  
25737
25738
25739  /*
25740  * - LGPL
25741  *
25742  * menu
25743  * 
25744  */
25745 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25746
25747 /**
25748  * @class Roo.bootstrap.menu.Menu
25749  * @extends Roo.bootstrap.Component
25750  * Bootstrap Menu class - container for Menu
25751  * @cfg {String} html Text of the menu
25752  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25753  * @cfg {String} icon Font awesome icon
25754  * @cfg {String} pos Menu align to (top | bottom) default bottom
25755  * 
25756  * 
25757  * @constructor
25758  * Create a new Menu
25759  * @param {Object} config The config object
25760  */
25761
25762
25763 Roo.bootstrap.menu.Menu = function(config){
25764     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25765     
25766     this.addEvents({
25767         /**
25768          * @event beforeshow
25769          * Fires before this menu is displayed
25770          * @param {Roo.bootstrap.menu.Menu} this
25771          */
25772         beforeshow : true,
25773         /**
25774          * @event beforehide
25775          * Fires before this menu is hidden
25776          * @param {Roo.bootstrap.menu.Menu} this
25777          */
25778         beforehide : true,
25779         /**
25780          * @event show
25781          * Fires after this menu is displayed
25782          * @param {Roo.bootstrap.menu.Menu} this
25783          */
25784         show : true,
25785         /**
25786          * @event hide
25787          * Fires after this menu is hidden
25788          * @param {Roo.bootstrap.menu.Menu} this
25789          */
25790         hide : true,
25791         /**
25792          * @event click
25793          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25794          * @param {Roo.bootstrap.menu.Menu} this
25795          * @param {Roo.EventObject} e
25796          */
25797         click : true
25798     });
25799     
25800 };
25801
25802 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25803     
25804     submenu : false,
25805     html : '',
25806     weight : 'default',
25807     icon : false,
25808     pos : 'bottom',
25809     
25810     
25811     getChildContainer : function() {
25812         if(this.isSubMenu){
25813             return this.el;
25814         }
25815         
25816         return this.el.select('ul.dropdown-menu', true).first();  
25817     },
25818     
25819     getAutoCreate : function()
25820     {
25821         var text = [
25822             {
25823                 tag : 'span',
25824                 cls : 'roo-menu-text',
25825                 html : this.html
25826             }
25827         ];
25828         
25829         if(this.icon){
25830             text.unshift({
25831                 tag : 'i',
25832                 cls : 'fa ' + this.icon
25833             })
25834         }
25835         
25836         
25837         var cfg = {
25838             tag : 'div',
25839             cls : 'btn-group',
25840             cn : [
25841                 {
25842                     tag : 'button',
25843                     cls : 'dropdown-button btn btn-' + this.weight,
25844                     cn : text
25845                 },
25846                 {
25847                     tag : 'button',
25848                     cls : 'dropdown-toggle btn btn-' + this.weight,
25849                     cn : [
25850                         {
25851                             tag : 'span',
25852                             cls : 'caret'
25853                         }
25854                     ]
25855                 },
25856                 {
25857                     tag : 'ul',
25858                     cls : 'dropdown-menu'
25859                 }
25860             ]
25861             
25862         };
25863         
25864         if(this.pos == 'top'){
25865             cfg.cls += ' dropup';
25866         }
25867         
25868         if(this.isSubMenu){
25869             cfg = {
25870                 tag : 'ul',
25871                 cls : 'dropdown-menu'
25872             }
25873         }
25874         
25875         return cfg;
25876     },
25877     
25878     onRender : function(ct, position)
25879     {
25880         this.isSubMenu = ct.hasClass('dropdown-submenu');
25881         
25882         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25883     },
25884     
25885     initEvents : function() 
25886     {
25887         if(this.isSubMenu){
25888             return;
25889         }
25890         
25891         this.hidden = true;
25892         
25893         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25894         this.triggerEl.on('click', this.onTriggerPress, this);
25895         
25896         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25897         this.buttonEl.on('click', this.onClick, this);
25898         
25899     },
25900     
25901     list : function()
25902     {
25903         if(this.isSubMenu){
25904             return this.el;
25905         }
25906         
25907         return this.el.select('ul.dropdown-menu', true).first();
25908     },
25909     
25910     onClick : function(e)
25911     {
25912         this.fireEvent("click", this, e);
25913     },
25914     
25915     onTriggerPress  : function(e)
25916     {   
25917         if (this.isVisible()) {
25918             this.hide();
25919         } else {
25920             this.show();
25921         }
25922     },
25923     
25924     isVisible : function(){
25925         return !this.hidden;
25926     },
25927     
25928     show : function()
25929     {
25930         this.fireEvent("beforeshow", this);
25931         
25932         this.hidden = false;
25933         this.el.addClass('open');
25934         
25935         Roo.get(document).on("mouseup", this.onMouseUp, this);
25936         
25937         this.fireEvent("show", this);
25938         
25939         
25940     },
25941     
25942     hide : function()
25943     {
25944         this.fireEvent("beforehide", this);
25945         
25946         this.hidden = true;
25947         this.el.removeClass('open');
25948         
25949         Roo.get(document).un("mouseup", this.onMouseUp);
25950         
25951         this.fireEvent("hide", this);
25952     },
25953     
25954     onMouseUp : function()
25955     {
25956         this.hide();
25957     }
25958     
25959 });
25960
25961  
25962  /*
25963  * - LGPL
25964  *
25965  * menu item
25966  * 
25967  */
25968 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25969
25970 /**
25971  * @class Roo.bootstrap.menu.Item
25972  * @extends Roo.bootstrap.Component
25973  * Bootstrap MenuItem class
25974  * @cfg {Boolean} submenu (true | false) default false
25975  * @cfg {String} html text of the item
25976  * @cfg {String} href the link
25977  * @cfg {Boolean} disable (true | false) default false
25978  * @cfg {Boolean} preventDefault (true | false) default true
25979  * @cfg {String} icon Font awesome icon
25980  * @cfg {String} pos Submenu align to (left | right) default right 
25981  * 
25982  * 
25983  * @constructor
25984  * Create a new Item
25985  * @param {Object} config The config object
25986  */
25987
25988
25989 Roo.bootstrap.menu.Item = function(config){
25990     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25991     this.addEvents({
25992         /**
25993          * @event mouseover
25994          * Fires when the mouse is hovering over this menu
25995          * @param {Roo.bootstrap.menu.Item} this
25996          * @param {Roo.EventObject} e
25997          */
25998         mouseover : true,
25999         /**
26000          * @event mouseout
26001          * Fires when the mouse exits this menu
26002          * @param {Roo.bootstrap.menu.Item} this
26003          * @param {Roo.EventObject} e
26004          */
26005         mouseout : true,
26006         // raw events
26007         /**
26008          * @event click
26009          * The raw click event for the entire grid.
26010          * @param {Roo.EventObject} e
26011          */
26012         click : true
26013     });
26014 };
26015
26016 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26017     
26018     submenu : false,
26019     href : '',
26020     html : '',
26021     preventDefault: true,
26022     disable : false,
26023     icon : false,
26024     pos : 'right',
26025     
26026     getAutoCreate : function()
26027     {
26028         var text = [
26029             {
26030                 tag : 'span',
26031                 cls : 'roo-menu-item-text',
26032                 html : this.html
26033             }
26034         ];
26035         
26036         if(this.icon){
26037             text.unshift({
26038                 tag : 'i',
26039                 cls : 'fa ' + this.icon
26040             })
26041         }
26042         
26043         var cfg = {
26044             tag : 'li',
26045             cn : [
26046                 {
26047                     tag : 'a',
26048                     href : this.href || '#',
26049                     cn : text
26050                 }
26051             ]
26052         };
26053         
26054         if(this.disable){
26055             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26056         }
26057         
26058         if(this.submenu){
26059             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26060             
26061             if(this.pos == 'left'){
26062                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26063             }
26064         }
26065         
26066         return cfg;
26067     },
26068     
26069     initEvents : function() 
26070     {
26071         this.el.on('mouseover', this.onMouseOver, this);
26072         this.el.on('mouseout', this.onMouseOut, this);
26073         
26074         this.el.select('a', true).first().on('click', this.onClick, this);
26075         
26076     },
26077     
26078     onClick : function(e)
26079     {
26080         if(this.preventDefault){
26081             e.preventDefault();
26082         }
26083         
26084         this.fireEvent("click", this, e);
26085     },
26086     
26087     onMouseOver : function(e)
26088     {
26089         if(this.submenu && this.pos == 'left'){
26090             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26091         }
26092         
26093         this.fireEvent("mouseover", this, e);
26094     },
26095     
26096     onMouseOut : function(e)
26097     {
26098         this.fireEvent("mouseout", this, e);
26099     }
26100 });
26101
26102  
26103
26104  /*
26105  * - LGPL
26106  *
26107  * menu separator
26108  * 
26109  */
26110 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26111
26112 /**
26113  * @class Roo.bootstrap.menu.Separator
26114  * @extends Roo.bootstrap.Component
26115  * Bootstrap Separator class
26116  * 
26117  * @constructor
26118  * Create a new Separator
26119  * @param {Object} config The config object
26120  */
26121
26122
26123 Roo.bootstrap.menu.Separator = function(config){
26124     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26125 };
26126
26127 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26128     
26129     getAutoCreate : function(){
26130         var cfg = {
26131             tag : 'li',
26132             cls: 'divider'
26133         };
26134         
26135         return cfg;
26136     }
26137    
26138 });
26139
26140  
26141
26142  /*
26143  * - LGPL
26144  *
26145  * Tooltip
26146  * 
26147  */
26148
26149 /**
26150  * @class Roo.bootstrap.Tooltip
26151  * Bootstrap Tooltip class
26152  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26153  * to determine which dom element triggers the tooltip.
26154  * 
26155  * It needs to add support for additional attributes like tooltip-position
26156  * 
26157  * @constructor
26158  * Create a new Toolti
26159  * @param {Object} config The config object
26160  */
26161
26162 Roo.bootstrap.Tooltip = function(config){
26163     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26164     
26165     this.alignment = Roo.bootstrap.Tooltip.alignment;
26166     
26167     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26168         this.alignment = config.alignment;
26169     }
26170     
26171 };
26172
26173 Roo.apply(Roo.bootstrap.Tooltip, {
26174     /**
26175      * @function init initialize tooltip monitoring.
26176      * @static
26177      */
26178     currentEl : false,
26179     currentTip : false,
26180     currentRegion : false,
26181     
26182     //  init : delay?
26183     
26184     init : function()
26185     {
26186         Roo.get(document).on('mouseover', this.enter ,this);
26187         Roo.get(document).on('mouseout', this.leave, this);
26188          
26189         
26190         this.currentTip = new Roo.bootstrap.Tooltip();
26191     },
26192     
26193     enter : function(ev)
26194     {
26195         var dom = ev.getTarget();
26196         
26197         //Roo.log(['enter',dom]);
26198         var el = Roo.fly(dom);
26199         if (this.currentEl) {
26200             //Roo.log(dom);
26201             //Roo.log(this.currentEl);
26202             //Roo.log(this.currentEl.contains(dom));
26203             if (this.currentEl == el) {
26204                 return;
26205             }
26206             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26207                 return;
26208             }
26209
26210         }
26211         
26212         if (this.currentTip.el) {
26213             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26214         }    
26215         //Roo.log(ev);
26216         
26217         if(!el || el.dom == document){
26218             return;
26219         }
26220         
26221         var bindEl = el;
26222         
26223         // you can not look for children, as if el is the body.. then everythign is the child..
26224         if (!el.attr('tooltip')) { //
26225             if (!el.select("[tooltip]").elements.length) {
26226                 return;
26227             }
26228             // is the mouse over this child...?
26229             bindEl = el.select("[tooltip]").first();
26230             var xy = ev.getXY();
26231             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26232                 //Roo.log("not in region.");
26233                 return;
26234             }
26235             //Roo.log("child element over..");
26236             
26237         }
26238         this.currentEl = bindEl;
26239         this.currentTip.bind(bindEl);
26240         this.currentRegion = Roo.lib.Region.getRegion(dom);
26241         this.currentTip.enter();
26242         
26243     },
26244     leave : function(ev)
26245     {
26246         var dom = ev.getTarget();
26247         //Roo.log(['leave',dom]);
26248         if (!this.currentEl) {
26249             return;
26250         }
26251         
26252         
26253         if (dom != this.currentEl.dom) {
26254             return;
26255         }
26256         var xy = ev.getXY();
26257         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26258             return;
26259         }
26260         // only activate leave if mouse cursor is outside... bounding box..
26261         
26262         
26263         
26264         
26265         if (this.currentTip) {
26266             this.currentTip.leave();
26267         }
26268         //Roo.log('clear currentEl');
26269         this.currentEl = false;
26270         
26271         
26272     },
26273     alignment : {
26274         'left' : ['r-l', [-2,0], 'right'],
26275         'right' : ['l-r', [2,0], 'left'],
26276         'bottom' : ['t-b', [0,2], 'top'],
26277         'top' : [ 'b-t', [0,-2], 'bottom']
26278     }
26279     
26280 });
26281
26282
26283 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26284     
26285     
26286     bindEl : false,
26287     
26288     delay : null, // can be { show : 300 , hide: 500}
26289     
26290     timeout : null,
26291     
26292     hoverState : null, //???
26293     
26294     placement : 'bottom', 
26295     
26296     alignment : false,
26297     
26298     getAutoCreate : function(){
26299     
26300         var cfg = {
26301            cls : 'tooltip',
26302            role : 'tooltip',
26303            cn : [
26304                 {
26305                     cls : 'tooltip-arrow'
26306                 },
26307                 {
26308                     cls : 'tooltip-inner'
26309                 }
26310            ]
26311         };
26312         
26313         return cfg;
26314     },
26315     bind : function(el)
26316     {
26317         this.bindEl = el;
26318     },
26319       
26320     
26321     enter : function () {
26322        
26323         if (this.timeout != null) {
26324             clearTimeout(this.timeout);
26325         }
26326         
26327         this.hoverState = 'in';
26328          //Roo.log("enter - show");
26329         if (!this.delay || !this.delay.show) {
26330             this.show();
26331             return;
26332         }
26333         var _t = this;
26334         this.timeout = setTimeout(function () {
26335             if (_t.hoverState == 'in') {
26336                 _t.show();
26337             }
26338         }, this.delay.show);
26339     },
26340     leave : function()
26341     {
26342         clearTimeout(this.timeout);
26343     
26344         this.hoverState = 'out';
26345          if (!this.delay || !this.delay.hide) {
26346             this.hide();
26347             return;
26348         }
26349        
26350         var _t = this;
26351         this.timeout = setTimeout(function () {
26352             //Roo.log("leave - timeout");
26353             
26354             if (_t.hoverState == 'out') {
26355                 _t.hide();
26356                 Roo.bootstrap.Tooltip.currentEl = false;
26357             }
26358         }, delay);
26359     },
26360     
26361     show : function (msg)
26362     {
26363         if (!this.el) {
26364             this.render(document.body);
26365         }
26366         // set content.
26367         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26368         
26369         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26370         
26371         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26372         
26373         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26374         
26375         var placement = typeof this.placement == 'function' ?
26376             this.placement.call(this, this.el, on_el) :
26377             this.placement;
26378             
26379         var autoToken = /\s?auto?\s?/i;
26380         var autoPlace = autoToken.test(placement);
26381         if (autoPlace) {
26382             placement = placement.replace(autoToken, '') || 'top';
26383         }
26384         
26385         //this.el.detach()
26386         //this.el.setXY([0,0]);
26387         this.el.show();
26388         //this.el.dom.style.display='block';
26389         
26390         //this.el.appendTo(on_el);
26391         
26392         var p = this.getPosition();
26393         var box = this.el.getBox();
26394         
26395         if (autoPlace) {
26396             // fixme..
26397         }
26398         
26399         var align = this.alignment[placement];
26400         
26401         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26402         
26403         if(placement == 'top' || placement == 'bottom'){
26404             if(xy[0] < 0){
26405                 placement = 'right';
26406             }
26407             
26408             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26409                 placement = 'left';
26410             }
26411             
26412             var scroll = Roo.select('body', true).first().getScroll();
26413             
26414             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26415                 placement = 'top';
26416             }
26417             
26418             align = this.alignment[placement];
26419         }
26420         
26421         this.el.alignTo(this.bindEl, align[0],align[1]);
26422         //var arrow = this.el.select('.arrow',true).first();
26423         //arrow.set(align[2], 
26424         
26425         this.el.addClass(placement);
26426         
26427         this.el.addClass('in fade');
26428         
26429         this.hoverState = null;
26430         
26431         if (this.el.hasClass('fade')) {
26432             // fade it?
26433         }
26434         
26435     },
26436     hide : function()
26437     {
26438          
26439         if (!this.el) {
26440             return;
26441         }
26442         //this.el.setXY([0,0]);
26443         this.el.removeClass('in');
26444         //this.el.hide();
26445         
26446     }
26447     
26448 });
26449  
26450
26451  /*
26452  * - LGPL
26453  *
26454  * Location Picker
26455  * 
26456  */
26457
26458 /**
26459  * @class Roo.bootstrap.LocationPicker
26460  * @extends Roo.bootstrap.Component
26461  * Bootstrap LocationPicker class
26462  * @cfg {Number} latitude Position when init default 0
26463  * @cfg {Number} longitude Position when init default 0
26464  * @cfg {Number} zoom default 15
26465  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26466  * @cfg {Boolean} mapTypeControl default false
26467  * @cfg {Boolean} disableDoubleClickZoom default false
26468  * @cfg {Boolean} scrollwheel default true
26469  * @cfg {Boolean} streetViewControl default false
26470  * @cfg {Number} radius default 0
26471  * @cfg {String} locationName
26472  * @cfg {Boolean} draggable default true
26473  * @cfg {Boolean} enableAutocomplete default false
26474  * @cfg {Boolean} enableReverseGeocode default true
26475  * @cfg {String} markerTitle
26476  * 
26477  * @constructor
26478  * Create a new LocationPicker
26479  * @param {Object} config The config object
26480  */
26481
26482
26483 Roo.bootstrap.LocationPicker = function(config){
26484     
26485     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26486     
26487     this.addEvents({
26488         /**
26489          * @event initial
26490          * Fires when the picker initialized.
26491          * @param {Roo.bootstrap.LocationPicker} this
26492          * @param {Google Location} location
26493          */
26494         initial : true,
26495         /**
26496          * @event positionchanged
26497          * Fires when the picker position changed.
26498          * @param {Roo.bootstrap.LocationPicker} this
26499          * @param {Google Location} location
26500          */
26501         positionchanged : true,
26502         /**
26503          * @event resize
26504          * Fires when the map resize.
26505          * @param {Roo.bootstrap.LocationPicker} this
26506          */
26507         resize : true,
26508         /**
26509          * @event show
26510          * Fires when the map show.
26511          * @param {Roo.bootstrap.LocationPicker} this
26512          */
26513         show : true,
26514         /**
26515          * @event hide
26516          * Fires when the map hide.
26517          * @param {Roo.bootstrap.LocationPicker} this
26518          */
26519         hide : true,
26520         /**
26521          * @event mapClick
26522          * Fires when click the map.
26523          * @param {Roo.bootstrap.LocationPicker} this
26524          * @param {Map event} e
26525          */
26526         mapClick : true,
26527         /**
26528          * @event mapRightClick
26529          * Fires when right click the map.
26530          * @param {Roo.bootstrap.LocationPicker} this
26531          * @param {Map event} e
26532          */
26533         mapRightClick : true,
26534         /**
26535          * @event markerClick
26536          * Fires when click the marker.
26537          * @param {Roo.bootstrap.LocationPicker} this
26538          * @param {Map event} e
26539          */
26540         markerClick : true,
26541         /**
26542          * @event markerRightClick
26543          * Fires when right click the marker.
26544          * @param {Roo.bootstrap.LocationPicker} this
26545          * @param {Map event} e
26546          */
26547         markerRightClick : true,
26548         /**
26549          * @event OverlayViewDraw
26550          * Fires when OverlayView Draw
26551          * @param {Roo.bootstrap.LocationPicker} this
26552          */
26553         OverlayViewDraw : true,
26554         /**
26555          * @event OverlayViewOnAdd
26556          * Fires when OverlayView Draw
26557          * @param {Roo.bootstrap.LocationPicker} this
26558          */
26559         OverlayViewOnAdd : true,
26560         /**
26561          * @event OverlayViewOnRemove
26562          * Fires when OverlayView Draw
26563          * @param {Roo.bootstrap.LocationPicker} this
26564          */
26565         OverlayViewOnRemove : true,
26566         /**
26567          * @event OverlayViewShow
26568          * Fires when OverlayView Draw
26569          * @param {Roo.bootstrap.LocationPicker} this
26570          * @param {Pixel} cpx
26571          */
26572         OverlayViewShow : true,
26573         /**
26574          * @event OverlayViewHide
26575          * Fires when OverlayView Draw
26576          * @param {Roo.bootstrap.LocationPicker} this
26577          */
26578         OverlayViewHide : true,
26579         /**
26580          * @event loadexception
26581          * Fires when load google lib failed.
26582          * @param {Roo.bootstrap.LocationPicker} this
26583          */
26584         loadexception : true
26585     });
26586         
26587 };
26588
26589 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26590     
26591     gMapContext: false,
26592     
26593     latitude: 0,
26594     longitude: 0,
26595     zoom: 15,
26596     mapTypeId: false,
26597     mapTypeControl: false,
26598     disableDoubleClickZoom: false,
26599     scrollwheel: true,
26600     streetViewControl: false,
26601     radius: 0,
26602     locationName: '',
26603     draggable: true,
26604     enableAutocomplete: false,
26605     enableReverseGeocode: true,
26606     markerTitle: '',
26607     
26608     getAutoCreate: function()
26609     {
26610
26611         var cfg = {
26612             tag: 'div',
26613             cls: 'roo-location-picker'
26614         };
26615         
26616         return cfg
26617     },
26618     
26619     initEvents: function(ct, position)
26620     {       
26621         if(!this.el.getWidth() || this.isApplied()){
26622             return;
26623         }
26624         
26625         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26626         
26627         this.initial();
26628     },
26629     
26630     initial: function()
26631     {
26632         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26633             this.fireEvent('loadexception', this);
26634             return;
26635         }
26636         
26637         if(!this.mapTypeId){
26638             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26639         }
26640         
26641         this.gMapContext = this.GMapContext();
26642         
26643         this.initOverlayView();
26644         
26645         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26646         
26647         var _this = this;
26648                 
26649         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26650             _this.setPosition(_this.gMapContext.marker.position);
26651         });
26652         
26653         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26654             _this.fireEvent('mapClick', this, event);
26655             
26656         });
26657
26658         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26659             _this.fireEvent('mapRightClick', this, event);
26660             
26661         });
26662         
26663         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26664             _this.fireEvent('markerClick', this, event);
26665             
26666         });
26667
26668         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26669             _this.fireEvent('markerRightClick', this, event);
26670             
26671         });
26672         
26673         this.setPosition(this.gMapContext.location);
26674         
26675         this.fireEvent('initial', this, this.gMapContext.location);
26676     },
26677     
26678     initOverlayView: function()
26679     {
26680         var _this = this;
26681         
26682         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26683             
26684             draw: function()
26685             {
26686                 _this.fireEvent('OverlayViewDraw', _this);
26687             },
26688             
26689             onAdd: function()
26690             {
26691                 _this.fireEvent('OverlayViewOnAdd', _this);
26692             },
26693             
26694             onRemove: function()
26695             {
26696                 _this.fireEvent('OverlayViewOnRemove', _this);
26697             },
26698             
26699             show: function(cpx)
26700             {
26701                 _this.fireEvent('OverlayViewShow', _this, cpx);
26702             },
26703             
26704             hide: function()
26705             {
26706                 _this.fireEvent('OverlayViewHide', _this);
26707             }
26708             
26709         });
26710     },
26711     
26712     fromLatLngToContainerPixel: function(event)
26713     {
26714         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26715     },
26716     
26717     isApplied: function() 
26718     {
26719         return this.getGmapContext() == false ? false : true;
26720     },
26721     
26722     getGmapContext: function() 
26723     {
26724         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26725     },
26726     
26727     GMapContext: function() 
26728     {
26729         var position = new google.maps.LatLng(this.latitude, this.longitude);
26730         
26731         var _map = new google.maps.Map(this.el.dom, {
26732             center: position,
26733             zoom: this.zoom,
26734             mapTypeId: this.mapTypeId,
26735             mapTypeControl: this.mapTypeControl,
26736             disableDoubleClickZoom: this.disableDoubleClickZoom,
26737             scrollwheel: this.scrollwheel,
26738             streetViewControl: this.streetViewControl,
26739             locationName: this.locationName,
26740             draggable: this.draggable,
26741             enableAutocomplete: this.enableAutocomplete,
26742             enableReverseGeocode: this.enableReverseGeocode
26743         });
26744         
26745         var _marker = new google.maps.Marker({
26746             position: position,
26747             map: _map,
26748             title: this.markerTitle,
26749             draggable: this.draggable
26750         });
26751         
26752         return {
26753             map: _map,
26754             marker: _marker,
26755             circle: null,
26756             location: position,
26757             radius: this.radius,
26758             locationName: this.locationName,
26759             addressComponents: {
26760                 formatted_address: null,
26761                 addressLine1: null,
26762                 addressLine2: null,
26763                 streetName: null,
26764                 streetNumber: null,
26765                 city: null,
26766                 district: null,
26767                 state: null,
26768                 stateOrProvince: null
26769             },
26770             settings: this,
26771             domContainer: this.el.dom,
26772             geodecoder: new google.maps.Geocoder()
26773         };
26774     },
26775     
26776     drawCircle: function(center, radius, options) 
26777     {
26778         if (this.gMapContext.circle != null) {
26779             this.gMapContext.circle.setMap(null);
26780         }
26781         if (radius > 0) {
26782             radius *= 1;
26783             options = Roo.apply({}, options, {
26784                 strokeColor: "#0000FF",
26785                 strokeOpacity: .35,
26786                 strokeWeight: 2,
26787                 fillColor: "#0000FF",
26788                 fillOpacity: .2
26789             });
26790             
26791             options.map = this.gMapContext.map;
26792             options.radius = radius;
26793             options.center = center;
26794             this.gMapContext.circle = new google.maps.Circle(options);
26795             return this.gMapContext.circle;
26796         }
26797         
26798         return null;
26799     },
26800     
26801     setPosition: function(location) 
26802     {
26803         this.gMapContext.location = location;
26804         this.gMapContext.marker.setPosition(location);
26805         this.gMapContext.map.panTo(location);
26806         this.drawCircle(location, this.gMapContext.radius, {});
26807         
26808         var _this = this;
26809         
26810         if (this.gMapContext.settings.enableReverseGeocode) {
26811             this.gMapContext.geodecoder.geocode({
26812                 latLng: this.gMapContext.location
26813             }, function(results, status) {
26814                 
26815                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26816                     _this.gMapContext.locationName = results[0].formatted_address;
26817                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26818                     
26819                     _this.fireEvent('positionchanged', this, location);
26820                 }
26821             });
26822             
26823             return;
26824         }
26825         
26826         this.fireEvent('positionchanged', this, location);
26827     },
26828     
26829     resize: function()
26830     {
26831         google.maps.event.trigger(this.gMapContext.map, "resize");
26832         
26833         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26834         
26835         this.fireEvent('resize', this);
26836     },
26837     
26838     setPositionByLatLng: function(latitude, longitude)
26839     {
26840         this.setPosition(new google.maps.LatLng(latitude, longitude));
26841     },
26842     
26843     getCurrentPosition: function() 
26844     {
26845         return {
26846             latitude: this.gMapContext.location.lat(),
26847             longitude: this.gMapContext.location.lng()
26848         };
26849     },
26850     
26851     getAddressName: function() 
26852     {
26853         return this.gMapContext.locationName;
26854     },
26855     
26856     getAddressComponents: function() 
26857     {
26858         return this.gMapContext.addressComponents;
26859     },
26860     
26861     address_component_from_google_geocode: function(address_components) 
26862     {
26863         var result = {};
26864         
26865         for (var i = 0; i < address_components.length; i++) {
26866             var component = address_components[i];
26867             if (component.types.indexOf("postal_code") >= 0) {
26868                 result.postalCode = component.short_name;
26869             } else if (component.types.indexOf("street_number") >= 0) {
26870                 result.streetNumber = component.short_name;
26871             } else if (component.types.indexOf("route") >= 0) {
26872                 result.streetName = component.short_name;
26873             } else if (component.types.indexOf("neighborhood") >= 0) {
26874                 result.city = component.short_name;
26875             } else if (component.types.indexOf("locality") >= 0) {
26876                 result.city = component.short_name;
26877             } else if (component.types.indexOf("sublocality") >= 0) {
26878                 result.district = component.short_name;
26879             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26880                 result.stateOrProvince = component.short_name;
26881             } else if (component.types.indexOf("country") >= 0) {
26882                 result.country = component.short_name;
26883             }
26884         }
26885         
26886         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26887         result.addressLine2 = "";
26888         return result;
26889     },
26890     
26891     setZoomLevel: function(zoom)
26892     {
26893         this.gMapContext.map.setZoom(zoom);
26894     },
26895     
26896     show: function()
26897     {
26898         if(!this.el){
26899             return;
26900         }
26901         
26902         this.el.show();
26903         
26904         this.resize();
26905         
26906         this.fireEvent('show', this);
26907     },
26908     
26909     hide: function()
26910     {
26911         if(!this.el){
26912             return;
26913         }
26914         
26915         this.el.hide();
26916         
26917         this.fireEvent('hide', this);
26918     }
26919     
26920 });
26921
26922 Roo.apply(Roo.bootstrap.LocationPicker, {
26923     
26924     OverlayView : function(map, options)
26925     {
26926         options = options || {};
26927         
26928         this.setMap(map);
26929     }
26930     
26931     
26932 });/*
26933  * - LGPL
26934  *
26935  * Alert
26936  * 
26937  */
26938
26939 /**
26940  * @class Roo.bootstrap.Alert
26941  * @extends Roo.bootstrap.Component
26942  * Bootstrap Alert class
26943  * @cfg {String} title The title of alert
26944  * @cfg {String} html The content of alert
26945  * @cfg {String} weight (  success | info | warning | danger )
26946  * @cfg {String} faicon font-awesomeicon
26947  * 
26948  * @constructor
26949  * Create a new alert
26950  * @param {Object} config The config object
26951  */
26952
26953
26954 Roo.bootstrap.Alert = function(config){
26955     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26956     
26957 };
26958
26959 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26960     
26961     title: '',
26962     html: '',
26963     weight: false,
26964     faicon: false,
26965     
26966     getAutoCreate : function()
26967     {
26968         
26969         var cfg = {
26970             tag : 'div',
26971             cls : 'alert',
26972             cn : [
26973                 {
26974                     tag : 'i',
26975                     cls : 'roo-alert-icon'
26976                     
26977                 },
26978                 {
26979                     tag : 'b',
26980                     cls : 'roo-alert-title',
26981                     html : this.title
26982                 },
26983                 {
26984                     tag : 'span',
26985                     cls : 'roo-alert-text',
26986                     html : this.html
26987                 }
26988             ]
26989         };
26990         
26991         if(this.faicon){
26992             cfg.cn[0].cls += ' fa ' + this.faicon;
26993         }
26994         
26995         if(this.weight){
26996             cfg.cls += ' alert-' + this.weight;
26997         }
26998         
26999         return cfg;
27000     },
27001     
27002     initEvents: function() 
27003     {
27004         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27005     },
27006     
27007     setTitle : function(str)
27008     {
27009         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27010     },
27011     
27012     setText : function(str)
27013     {
27014         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27015     },
27016     
27017     setWeight : function(weight)
27018     {
27019         if(this.weight){
27020             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27021         }
27022         
27023         this.weight = weight;
27024         
27025         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27026     },
27027     
27028     setIcon : function(icon)
27029     {
27030         if(this.faicon){
27031             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27032         }
27033         
27034         this.faicon = icon;
27035         
27036         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27037     },
27038     
27039     hide: function() 
27040     {
27041         this.el.hide();   
27042     },
27043     
27044     show: function() 
27045     {  
27046         this.el.show();   
27047     }
27048     
27049 });
27050
27051  
27052 /*
27053 * Licence: LGPL
27054 */
27055
27056 /**
27057  * @class Roo.bootstrap.UploadCropbox
27058  * @extends Roo.bootstrap.Component
27059  * Bootstrap UploadCropbox class
27060  * @cfg {String} emptyText show when image has been loaded
27061  * @cfg {String} rotateNotify show when image too small to rotate
27062  * @cfg {Number} errorTimeout default 3000
27063  * @cfg {Number} minWidth default 300
27064  * @cfg {Number} minHeight default 300
27065  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27066  * @cfg {Boolean} isDocument (true|false) default false
27067  * @cfg {String} url action url
27068  * @cfg {String} paramName default 'imageUpload'
27069  * @cfg {String} method default POST
27070  * @cfg {Boolean} loadMask (true|false) default true
27071  * @cfg {Boolean} loadingText default 'Loading...'
27072  * 
27073  * @constructor
27074  * Create a new UploadCropbox
27075  * @param {Object} config The config object
27076  */
27077
27078 Roo.bootstrap.UploadCropbox = function(config){
27079     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27080     
27081     this.addEvents({
27082         /**
27083          * @event beforeselectfile
27084          * Fire before select file
27085          * @param {Roo.bootstrap.UploadCropbox} this
27086          */
27087         "beforeselectfile" : true,
27088         /**
27089          * @event initial
27090          * Fire after initEvent
27091          * @param {Roo.bootstrap.UploadCropbox} this
27092          */
27093         "initial" : true,
27094         /**
27095          * @event crop
27096          * Fire after initEvent
27097          * @param {Roo.bootstrap.UploadCropbox} this
27098          * @param {String} data
27099          */
27100         "crop" : true,
27101         /**
27102          * @event prepare
27103          * Fire when preparing the file data
27104          * @param {Roo.bootstrap.UploadCropbox} this
27105          * @param {Object} file
27106          */
27107         "prepare" : true,
27108         /**
27109          * @event exception
27110          * Fire when get exception
27111          * @param {Roo.bootstrap.UploadCropbox} this
27112          * @param {XMLHttpRequest} xhr
27113          */
27114         "exception" : true,
27115         /**
27116          * @event beforeloadcanvas
27117          * Fire before load the canvas
27118          * @param {Roo.bootstrap.UploadCropbox} this
27119          * @param {String} src
27120          */
27121         "beforeloadcanvas" : true,
27122         /**
27123          * @event trash
27124          * Fire when trash image
27125          * @param {Roo.bootstrap.UploadCropbox} this
27126          */
27127         "trash" : true,
27128         /**
27129          * @event download
27130          * Fire when download the image
27131          * @param {Roo.bootstrap.UploadCropbox} this
27132          */
27133         "download" : true,
27134         /**
27135          * @event footerbuttonclick
27136          * Fire when footerbuttonclick
27137          * @param {Roo.bootstrap.UploadCropbox} this
27138          * @param {String} type
27139          */
27140         "footerbuttonclick" : true,
27141         /**
27142          * @event resize
27143          * Fire when resize
27144          * @param {Roo.bootstrap.UploadCropbox} this
27145          */
27146         "resize" : true,
27147         /**
27148          * @event rotate
27149          * Fire when rotate the image
27150          * @param {Roo.bootstrap.UploadCropbox} this
27151          * @param {String} pos
27152          */
27153         "rotate" : true,
27154         /**
27155          * @event inspect
27156          * Fire when inspect the file
27157          * @param {Roo.bootstrap.UploadCropbox} this
27158          * @param {Object} file
27159          */
27160         "inspect" : true,
27161         /**
27162          * @event upload
27163          * Fire when xhr upload the file
27164          * @param {Roo.bootstrap.UploadCropbox} this
27165          * @param {Object} data
27166          */
27167         "upload" : true,
27168         /**
27169          * @event arrange
27170          * Fire when arrange the file data
27171          * @param {Roo.bootstrap.UploadCropbox} this
27172          * @param {Object} formData
27173          */
27174         "arrange" : true
27175     });
27176     
27177     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27178 };
27179
27180 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27181     
27182     emptyText : 'Click to upload image',
27183     rotateNotify : 'Image is too small to rotate',
27184     errorTimeout : 3000,
27185     scale : 0,
27186     baseScale : 1,
27187     rotate : 0,
27188     dragable : false,
27189     pinching : false,
27190     mouseX : 0,
27191     mouseY : 0,
27192     cropData : false,
27193     minWidth : 300,
27194     minHeight : 300,
27195     file : false,
27196     exif : {},
27197     baseRotate : 1,
27198     cropType : 'image/jpeg',
27199     buttons : false,
27200     canvasLoaded : false,
27201     isDocument : false,
27202     method : 'POST',
27203     paramName : 'imageUpload',
27204     loadMask : true,
27205     loadingText : 'Loading...',
27206     maskEl : false,
27207     
27208     getAutoCreate : function()
27209     {
27210         var cfg = {
27211             tag : 'div',
27212             cls : 'roo-upload-cropbox',
27213             cn : [
27214                 {
27215                     tag : 'input',
27216                     cls : 'roo-upload-cropbox-selector',
27217                     type : 'file'
27218                 },
27219                 {
27220                     tag : 'div',
27221                     cls : 'roo-upload-cropbox-body',
27222                     style : 'cursor:pointer',
27223                     cn : [
27224                         {
27225                             tag : 'div',
27226                             cls : 'roo-upload-cropbox-preview'
27227                         },
27228                         {
27229                             tag : 'div',
27230                             cls : 'roo-upload-cropbox-thumb'
27231                         },
27232                         {
27233                             tag : 'div',
27234                             cls : 'roo-upload-cropbox-empty-notify',
27235                             html : this.emptyText
27236                         },
27237                         {
27238                             tag : 'div',
27239                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27240                             html : this.rotateNotify
27241                         }
27242                     ]
27243                 },
27244                 {
27245                     tag : 'div',
27246                     cls : 'roo-upload-cropbox-footer',
27247                     cn : {
27248                         tag : 'div',
27249                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27250                         cn : []
27251                     }
27252                 }
27253             ]
27254         };
27255         
27256         return cfg;
27257     },
27258     
27259     onRender : function(ct, position)
27260     {
27261         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27262         
27263         if (this.buttons.length) {
27264             
27265             Roo.each(this.buttons, function(bb) {
27266                 
27267                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27268                 
27269                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27270                 
27271             }, this);
27272         }
27273         
27274         if(this.loadMask){
27275             this.maskEl = this.el;
27276         }
27277     },
27278     
27279     initEvents : function()
27280     {
27281         this.urlAPI = (window.createObjectURL && window) || 
27282                                 (window.URL && URL.revokeObjectURL && URL) || 
27283                                 (window.webkitURL && webkitURL);
27284                         
27285         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27286         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27287         
27288         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27289         this.selectorEl.hide();
27290         
27291         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27292         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27293         
27294         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27295         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27296         this.thumbEl.hide();
27297         
27298         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27299         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27300         
27301         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27302         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27303         this.errorEl.hide();
27304         
27305         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27306         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27307         this.footerEl.hide();
27308         
27309         this.setThumbBoxSize();
27310         
27311         this.bind();
27312         
27313         this.resize();
27314         
27315         this.fireEvent('initial', this);
27316     },
27317
27318     bind : function()
27319     {
27320         var _this = this;
27321         
27322         window.addEventListener("resize", function() { _this.resize(); } );
27323         
27324         this.bodyEl.on('click', this.beforeSelectFile, this);
27325         
27326         if(Roo.isTouch){
27327             this.bodyEl.on('touchstart', this.onTouchStart, this);
27328             this.bodyEl.on('touchmove', this.onTouchMove, this);
27329             this.bodyEl.on('touchend', this.onTouchEnd, this);
27330         }
27331         
27332         if(!Roo.isTouch){
27333             this.bodyEl.on('mousedown', this.onMouseDown, this);
27334             this.bodyEl.on('mousemove', this.onMouseMove, this);
27335             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27336             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27337             Roo.get(document).on('mouseup', this.onMouseUp, this);
27338         }
27339         
27340         this.selectorEl.on('change', this.onFileSelected, this);
27341     },
27342     
27343     reset : function()
27344     {    
27345         this.scale = 0;
27346         this.baseScale = 1;
27347         this.rotate = 0;
27348         this.baseRotate = 1;
27349         this.dragable = false;
27350         this.pinching = false;
27351         this.mouseX = 0;
27352         this.mouseY = 0;
27353         this.cropData = false;
27354         this.notifyEl.dom.innerHTML = this.emptyText;
27355         
27356         this.selectorEl.dom.value = '';
27357         
27358     },
27359     
27360     resize : function()
27361     {
27362         if(this.fireEvent('resize', this) != false){
27363             this.setThumbBoxPosition();
27364             this.setCanvasPosition();
27365         }
27366     },
27367     
27368     onFooterButtonClick : function(e, el, o, type)
27369     {
27370         switch (type) {
27371             case 'rotate-left' :
27372                 this.onRotateLeft(e);
27373                 break;
27374             case 'rotate-right' :
27375                 this.onRotateRight(e);
27376                 break;
27377             case 'picture' :
27378                 this.beforeSelectFile(e);
27379                 break;
27380             case 'trash' :
27381                 this.trash(e);
27382                 break;
27383             case 'crop' :
27384                 this.crop(e);
27385                 break;
27386             case 'download' :
27387                 this.download(e);
27388                 break;
27389             default :
27390                 break;
27391         }
27392         
27393         this.fireEvent('footerbuttonclick', this, type);
27394     },
27395     
27396     beforeSelectFile : function(e)
27397     {
27398         e.preventDefault();
27399         
27400         if(this.fireEvent('beforeselectfile', this) != false){
27401             this.selectorEl.dom.click();
27402         }
27403     },
27404     
27405     onFileSelected : function(e)
27406     {
27407         e.preventDefault();
27408         
27409         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27410             return;
27411         }
27412         
27413         var file = this.selectorEl.dom.files[0];
27414         
27415         if(this.fireEvent('inspect', this, file) != false){
27416             this.prepare(file);
27417         }
27418         
27419     },
27420     
27421     trash : function(e)
27422     {
27423         this.fireEvent('trash', this);
27424     },
27425     
27426     download : function(e)
27427     {
27428         this.fireEvent('download', this);
27429     },
27430     
27431     loadCanvas : function(src)
27432     {   
27433         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27434             
27435             this.reset();
27436             
27437             this.imageEl = document.createElement('img');
27438             
27439             var _this = this;
27440             
27441             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27442             
27443             this.imageEl.src = src;
27444         }
27445     },
27446     
27447     onLoadCanvas : function()
27448     {   
27449         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27450         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27451         
27452         this.bodyEl.un('click', this.beforeSelectFile, this);
27453         
27454         this.notifyEl.hide();
27455         this.thumbEl.show();
27456         this.footerEl.show();
27457         
27458         this.baseRotateLevel();
27459         
27460         if(this.isDocument){
27461             this.setThumbBoxSize();
27462         }
27463         
27464         this.setThumbBoxPosition();
27465         
27466         this.baseScaleLevel();
27467         
27468         this.draw();
27469         
27470         this.resize();
27471         
27472         this.canvasLoaded = true;
27473         
27474         if(this.loadMask){
27475             this.maskEl.unmask();
27476         }
27477         
27478     },
27479     
27480     setCanvasPosition : function()
27481     {   
27482         if(!this.canvasEl){
27483             return;
27484         }
27485         
27486         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27487         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27488         
27489         this.previewEl.setLeft(pw);
27490         this.previewEl.setTop(ph);
27491         
27492     },
27493     
27494     onMouseDown : function(e)
27495     {   
27496         e.stopEvent();
27497         
27498         this.dragable = true;
27499         this.pinching = false;
27500         
27501         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27502             this.dragable = false;
27503             return;
27504         }
27505         
27506         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27507         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27508         
27509     },
27510     
27511     onMouseMove : function(e)
27512     {   
27513         e.stopEvent();
27514         
27515         if(!this.canvasLoaded){
27516             return;
27517         }
27518         
27519         if (!this.dragable){
27520             return;
27521         }
27522         
27523         var minX = Math.ceil(this.thumbEl.getLeft(true));
27524         var minY = Math.ceil(this.thumbEl.getTop(true));
27525         
27526         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27527         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27528         
27529         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27530         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27531         
27532         x = x - this.mouseX;
27533         y = y - this.mouseY;
27534         
27535         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27536         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27537         
27538         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27539         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27540         
27541         this.previewEl.setLeft(bgX);
27542         this.previewEl.setTop(bgY);
27543         
27544         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27545         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27546     },
27547     
27548     onMouseUp : function(e)
27549     {   
27550         e.stopEvent();
27551         
27552         this.dragable = false;
27553     },
27554     
27555     onMouseWheel : function(e)
27556     {   
27557         e.stopEvent();
27558         
27559         this.startScale = this.scale;
27560         
27561         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27562         
27563         if(!this.zoomable()){
27564             this.scale = this.startScale;
27565             return;
27566         }
27567         
27568         this.draw();
27569         
27570         return;
27571     },
27572     
27573     zoomable : function()
27574     {
27575         var minScale = this.thumbEl.getWidth() / this.minWidth;
27576         
27577         if(this.minWidth < this.minHeight){
27578             minScale = this.thumbEl.getHeight() / this.minHeight;
27579         }
27580         
27581         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27582         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27583         
27584         if(
27585                 this.isDocument &&
27586                 (this.rotate == 0 || this.rotate == 180) && 
27587                 (
27588                     width > this.imageEl.OriginWidth || 
27589                     height > this.imageEl.OriginHeight ||
27590                     (width < this.minWidth && height < this.minHeight)
27591                 )
27592         ){
27593             return false;
27594         }
27595         
27596         if(
27597                 this.isDocument &&
27598                 (this.rotate == 90 || this.rotate == 270) && 
27599                 (
27600                     width > this.imageEl.OriginWidth || 
27601                     height > this.imageEl.OriginHeight ||
27602                     (width < this.minHeight && height < this.minWidth)
27603                 )
27604         ){
27605             return false;
27606         }
27607         
27608         if(
27609                 !this.isDocument &&
27610                 (this.rotate == 0 || this.rotate == 180) && 
27611                 (
27612                     width < this.minWidth || 
27613                     width > this.imageEl.OriginWidth || 
27614                     height < this.minHeight || 
27615                     height > this.imageEl.OriginHeight
27616                 )
27617         ){
27618             return false;
27619         }
27620         
27621         if(
27622                 !this.isDocument &&
27623                 (this.rotate == 90 || this.rotate == 270) && 
27624                 (
27625                     width < this.minHeight || 
27626                     width > this.imageEl.OriginWidth || 
27627                     height < this.minWidth || 
27628                     height > this.imageEl.OriginHeight
27629                 )
27630         ){
27631             return false;
27632         }
27633         
27634         return true;
27635         
27636     },
27637     
27638     onRotateLeft : function(e)
27639     {   
27640         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27641             
27642             var minScale = this.thumbEl.getWidth() / this.minWidth;
27643             
27644             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27645             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27646             
27647             this.startScale = this.scale;
27648             
27649             while (this.getScaleLevel() < minScale){
27650             
27651                 this.scale = this.scale + 1;
27652                 
27653                 if(!this.zoomable()){
27654                     break;
27655                 }
27656                 
27657                 if(
27658                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27659                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27660                 ){
27661                     continue;
27662                 }
27663                 
27664                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27665
27666                 this.draw();
27667                 
27668                 return;
27669             }
27670             
27671             this.scale = this.startScale;
27672             
27673             this.onRotateFail();
27674             
27675             return false;
27676         }
27677         
27678         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27679
27680         if(this.isDocument){
27681             this.setThumbBoxSize();
27682             this.setThumbBoxPosition();
27683             this.setCanvasPosition();
27684         }
27685         
27686         this.draw();
27687         
27688         this.fireEvent('rotate', this, 'left');
27689         
27690     },
27691     
27692     onRotateRight : function(e)
27693     {
27694         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27695             
27696             var minScale = this.thumbEl.getWidth() / this.minWidth;
27697         
27698             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27699             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27700             
27701             this.startScale = this.scale;
27702             
27703             while (this.getScaleLevel() < minScale){
27704             
27705                 this.scale = this.scale + 1;
27706                 
27707                 if(!this.zoomable()){
27708                     break;
27709                 }
27710                 
27711                 if(
27712                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27713                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27714                 ){
27715                     continue;
27716                 }
27717                 
27718                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27719
27720                 this.draw();
27721                 
27722                 return;
27723             }
27724             
27725             this.scale = this.startScale;
27726             
27727             this.onRotateFail();
27728             
27729             return false;
27730         }
27731         
27732         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27733
27734         if(this.isDocument){
27735             this.setThumbBoxSize();
27736             this.setThumbBoxPosition();
27737             this.setCanvasPosition();
27738         }
27739         
27740         this.draw();
27741         
27742         this.fireEvent('rotate', this, 'right');
27743     },
27744     
27745     onRotateFail : function()
27746     {
27747         this.errorEl.show(true);
27748         
27749         var _this = this;
27750         
27751         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27752     },
27753     
27754     draw : function()
27755     {
27756         this.previewEl.dom.innerHTML = '';
27757         
27758         var canvasEl = document.createElement("canvas");
27759         
27760         var contextEl = canvasEl.getContext("2d");
27761         
27762         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27763         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27764         var center = this.imageEl.OriginWidth / 2;
27765         
27766         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27767             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27768             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27769             center = this.imageEl.OriginHeight / 2;
27770         }
27771         
27772         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27773         
27774         contextEl.translate(center, center);
27775         contextEl.rotate(this.rotate * Math.PI / 180);
27776
27777         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27778         
27779         this.canvasEl = document.createElement("canvas");
27780         
27781         this.contextEl = this.canvasEl.getContext("2d");
27782         
27783         switch (this.rotate) {
27784             case 0 :
27785                 
27786                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27787                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27788                 
27789                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27790                 
27791                 break;
27792             case 90 : 
27793                 
27794                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27795                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27796                 
27797                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
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                     break;
27800                 }
27801                 
27802                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27803                 
27804                 break;
27805             case 180 :
27806                 
27807                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27808                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27809                 
27810                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
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                     break;
27813                 }
27814                 
27815                 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);
27816                 
27817                 break;
27818             case 270 :
27819                 
27820                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27821                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27822         
27823                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27824                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27825                     break;
27826                 }
27827                 
27828                 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);
27829                 
27830                 break;
27831             default : 
27832                 break;
27833         }
27834         
27835         this.previewEl.appendChild(this.canvasEl);
27836         
27837         this.setCanvasPosition();
27838     },
27839     
27840     crop : function()
27841     {
27842         if(!this.canvasLoaded){
27843             return;
27844         }
27845         
27846         var imageCanvas = document.createElement("canvas");
27847         
27848         var imageContext = imageCanvas.getContext("2d");
27849         
27850         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27851         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27852         
27853         var center = imageCanvas.width / 2;
27854         
27855         imageContext.translate(center, center);
27856         
27857         imageContext.rotate(this.rotate * Math.PI / 180);
27858         
27859         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27860         
27861         var canvas = document.createElement("canvas");
27862         
27863         var context = canvas.getContext("2d");
27864                 
27865         canvas.width = this.minWidth;
27866         canvas.height = this.minHeight;
27867
27868         switch (this.rotate) {
27869             case 0 :
27870                 
27871                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27872                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27873                 
27874                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27875                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27876                 
27877                 var targetWidth = this.minWidth - 2 * x;
27878                 var targetHeight = this.minHeight - 2 * y;
27879                 
27880                 var scale = 1;
27881                 
27882                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27883                     scale = targetWidth / width;
27884                 }
27885                 
27886                 if(x > 0 && y == 0){
27887                     scale = targetHeight / height;
27888                 }
27889                 
27890                 if(x > 0 && y > 0){
27891                     scale = targetWidth / width;
27892                     
27893                     if(width < height){
27894                         scale = targetHeight / height;
27895                     }
27896                 }
27897                 
27898                 context.scale(scale, scale);
27899                 
27900                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27901                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27902
27903                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27904                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27905
27906                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27907                 
27908                 break;
27909             case 90 : 
27910                 
27911                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27912                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27913                 
27914                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27915                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27916                 
27917                 var targetWidth = this.minWidth - 2 * x;
27918                 var targetHeight = this.minHeight - 2 * y;
27919                 
27920                 var scale = 1;
27921                 
27922                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27923                     scale = targetWidth / width;
27924                 }
27925                 
27926                 if(x > 0 && y == 0){
27927                     scale = targetHeight / height;
27928                 }
27929                 
27930                 if(x > 0 && y > 0){
27931                     scale = targetWidth / width;
27932                     
27933                     if(width < height){
27934                         scale = targetHeight / height;
27935                     }
27936                 }
27937                 
27938                 context.scale(scale, scale);
27939                 
27940                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27941                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27942
27943                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27944                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27945                 
27946                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27947                 
27948                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27949                 
27950                 break;
27951             case 180 :
27952                 
27953                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27954                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27955                 
27956                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27957                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27958                 
27959                 var targetWidth = this.minWidth - 2 * x;
27960                 var targetHeight = this.minHeight - 2 * y;
27961                 
27962                 var scale = 1;
27963                 
27964                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27965                     scale = targetWidth / width;
27966                 }
27967                 
27968                 if(x > 0 && y == 0){
27969                     scale = targetHeight / height;
27970                 }
27971                 
27972                 if(x > 0 && y > 0){
27973                     scale = targetWidth / width;
27974                     
27975                     if(width < height){
27976                         scale = targetHeight / height;
27977                     }
27978                 }
27979                 
27980                 context.scale(scale, scale);
27981                 
27982                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27983                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27984
27985                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27986                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27987
27988                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27989                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27990                 
27991                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27992                 
27993                 break;
27994             case 270 :
27995                 
27996                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27997                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27998                 
27999                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28000                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28001                 
28002                 var targetWidth = this.minWidth - 2 * x;
28003                 var targetHeight = this.minHeight - 2 * y;
28004                 
28005                 var scale = 1;
28006                 
28007                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28008                     scale = targetWidth / width;
28009                 }
28010                 
28011                 if(x > 0 && y == 0){
28012                     scale = targetHeight / height;
28013                 }
28014                 
28015                 if(x > 0 && y > 0){
28016                     scale = targetWidth / width;
28017                     
28018                     if(width < height){
28019                         scale = targetHeight / height;
28020                     }
28021                 }
28022                 
28023                 context.scale(scale, scale);
28024                 
28025                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28026                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28027
28028                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28029                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28030                 
28031                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28032                 
28033                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28034                 
28035                 break;
28036             default : 
28037                 break;
28038         }
28039         
28040         this.cropData = canvas.toDataURL(this.cropType);
28041         
28042         if(this.fireEvent('crop', this, this.cropData) !== false){
28043             this.process(this.file, this.cropData);
28044         }
28045         
28046         return;
28047         
28048     },
28049     
28050     setThumbBoxSize : function()
28051     {
28052         var width, height;
28053         
28054         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28055             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28056             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28057             
28058             this.minWidth = width;
28059             this.minHeight = height;
28060             
28061             if(this.rotate == 90 || this.rotate == 270){
28062                 this.minWidth = height;
28063                 this.minHeight = width;
28064             }
28065         }
28066         
28067         height = 300;
28068         width = Math.ceil(this.minWidth * height / this.minHeight);
28069         
28070         if(this.minWidth > this.minHeight){
28071             width = 300;
28072             height = Math.ceil(this.minHeight * width / this.minWidth);
28073         }
28074         
28075         this.thumbEl.setStyle({
28076             width : width + 'px',
28077             height : height + 'px'
28078         });
28079
28080         return;
28081             
28082     },
28083     
28084     setThumbBoxPosition : function()
28085     {
28086         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28087         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28088         
28089         this.thumbEl.setLeft(x);
28090         this.thumbEl.setTop(y);
28091         
28092     },
28093     
28094     baseRotateLevel : function()
28095     {
28096         this.baseRotate = 1;
28097         
28098         if(
28099                 typeof(this.exif) != 'undefined' &&
28100                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28101                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28102         ){
28103             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28104         }
28105         
28106         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28107         
28108     },
28109     
28110     baseScaleLevel : function()
28111     {
28112         var width, height;
28113         
28114         if(this.isDocument){
28115             
28116             if(this.baseRotate == 6 || this.baseRotate == 8){
28117             
28118                 height = this.thumbEl.getHeight();
28119                 this.baseScale = height / this.imageEl.OriginWidth;
28120
28121                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28122                     width = this.thumbEl.getWidth();
28123                     this.baseScale = width / this.imageEl.OriginHeight;
28124                 }
28125
28126                 return;
28127             }
28128
28129             height = this.thumbEl.getHeight();
28130             this.baseScale = height / this.imageEl.OriginHeight;
28131
28132             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28133                 width = this.thumbEl.getWidth();
28134                 this.baseScale = width / this.imageEl.OriginWidth;
28135             }
28136
28137             return;
28138         }
28139         
28140         if(this.baseRotate == 6 || this.baseRotate == 8){
28141             
28142             width = this.thumbEl.getHeight();
28143             this.baseScale = width / this.imageEl.OriginHeight;
28144             
28145             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28146                 height = this.thumbEl.getWidth();
28147                 this.baseScale = height / this.imageEl.OriginHeight;
28148             }
28149             
28150             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28151                 height = this.thumbEl.getWidth();
28152                 this.baseScale = height / this.imageEl.OriginHeight;
28153                 
28154                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28155                     width = this.thumbEl.getHeight();
28156                     this.baseScale = width / this.imageEl.OriginWidth;
28157                 }
28158             }
28159             
28160             return;
28161         }
28162         
28163         width = this.thumbEl.getWidth();
28164         this.baseScale = width / this.imageEl.OriginWidth;
28165         
28166         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28167             height = this.thumbEl.getHeight();
28168             this.baseScale = height / this.imageEl.OriginHeight;
28169         }
28170         
28171         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28172             
28173             height = this.thumbEl.getHeight();
28174             this.baseScale = height / this.imageEl.OriginHeight;
28175             
28176             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28177                 width = this.thumbEl.getWidth();
28178                 this.baseScale = width / this.imageEl.OriginWidth;
28179             }
28180             
28181         }
28182         
28183         return;
28184     },
28185     
28186     getScaleLevel : function()
28187     {
28188         return this.baseScale * Math.pow(1.1, this.scale);
28189     },
28190     
28191     onTouchStart : function(e)
28192     {
28193         if(!this.canvasLoaded){
28194             this.beforeSelectFile(e);
28195             return;
28196         }
28197         
28198         var touches = e.browserEvent.touches;
28199         
28200         if(!touches){
28201             return;
28202         }
28203         
28204         if(touches.length == 1){
28205             this.onMouseDown(e);
28206             return;
28207         }
28208         
28209         if(touches.length != 2){
28210             return;
28211         }
28212         
28213         var coords = [];
28214         
28215         for(var i = 0, finger; finger = touches[i]; i++){
28216             coords.push(finger.pageX, finger.pageY);
28217         }
28218         
28219         var x = Math.pow(coords[0] - coords[2], 2);
28220         var y = Math.pow(coords[1] - coords[3], 2);
28221         
28222         this.startDistance = Math.sqrt(x + y);
28223         
28224         this.startScale = this.scale;
28225         
28226         this.pinching = true;
28227         this.dragable = false;
28228         
28229     },
28230     
28231     onTouchMove : function(e)
28232     {
28233         if(!this.pinching && !this.dragable){
28234             return;
28235         }
28236         
28237         var touches = e.browserEvent.touches;
28238         
28239         if(!touches){
28240             return;
28241         }
28242         
28243         if(this.dragable){
28244             this.onMouseMove(e);
28245             return;
28246         }
28247         
28248         var coords = [];
28249         
28250         for(var i = 0, finger; finger = touches[i]; i++){
28251             coords.push(finger.pageX, finger.pageY);
28252         }
28253         
28254         var x = Math.pow(coords[0] - coords[2], 2);
28255         var y = Math.pow(coords[1] - coords[3], 2);
28256         
28257         this.endDistance = Math.sqrt(x + y);
28258         
28259         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28260         
28261         if(!this.zoomable()){
28262             this.scale = this.startScale;
28263             return;
28264         }
28265         
28266         this.draw();
28267         
28268     },
28269     
28270     onTouchEnd : function(e)
28271     {
28272         this.pinching = false;
28273         this.dragable = false;
28274         
28275     },
28276     
28277     process : function(file, crop)
28278     {
28279         if(this.loadMask){
28280             this.maskEl.mask(this.loadingText);
28281         }
28282         
28283         this.xhr = new XMLHttpRequest();
28284         
28285         file.xhr = this.xhr;
28286
28287         this.xhr.open(this.method, this.url, true);
28288         
28289         var headers = {
28290             "Accept": "application/json",
28291             "Cache-Control": "no-cache",
28292             "X-Requested-With": "XMLHttpRequest"
28293         };
28294         
28295         for (var headerName in headers) {
28296             var headerValue = headers[headerName];
28297             if (headerValue) {
28298                 this.xhr.setRequestHeader(headerName, headerValue);
28299             }
28300         }
28301         
28302         var _this = this;
28303         
28304         this.xhr.onload = function()
28305         {
28306             _this.xhrOnLoad(_this.xhr);
28307         }
28308         
28309         this.xhr.onerror = function()
28310         {
28311             _this.xhrOnError(_this.xhr);
28312         }
28313         
28314         var formData = new FormData();
28315
28316         formData.append('returnHTML', 'NO');
28317         
28318         if(crop){
28319             formData.append('crop', crop);
28320         }
28321         
28322         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28323             formData.append(this.paramName, file, file.name);
28324         }
28325         
28326         if(typeof(file.filename) != 'undefined'){
28327             formData.append('filename', file.filename);
28328         }
28329         
28330         if(typeof(file.mimetype) != 'undefined'){
28331             formData.append('mimetype', file.mimetype);
28332         }
28333         
28334         if(this.fireEvent('arrange', this, formData) != false){
28335             this.xhr.send(formData);
28336         };
28337     },
28338     
28339     xhrOnLoad : function(xhr)
28340     {
28341         if(this.loadMask){
28342             this.maskEl.unmask();
28343         }
28344         
28345         if (xhr.readyState !== 4) {
28346             this.fireEvent('exception', this, xhr);
28347             return;
28348         }
28349
28350         var response = Roo.decode(xhr.responseText);
28351         
28352         if(!response.success){
28353             this.fireEvent('exception', this, xhr);
28354             return;
28355         }
28356         
28357         var response = Roo.decode(xhr.responseText);
28358         
28359         this.fireEvent('upload', this, response);
28360         
28361     },
28362     
28363     xhrOnError : function()
28364     {
28365         if(this.loadMask){
28366             this.maskEl.unmask();
28367         }
28368         
28369         Roo.log('xhr on error');
28370         
28371         var response = Roo.decode(xhr.responseText);
28372           
28373         Roo.log(response);
28374         
28375     },
28376     
28377     prepare : function(file)
28378     {   
28379         if(this.loadMask){
28380             this.maskEl.mask(this.loadingText);
28381         }
28382         
28383         this.file = false;
28384         this.exif = {};
28385         
28386         if(typeof(file) === 'string'){
28387             this.loadCanvas(file);
28388             return;
28389         }
28390         
28391         if(!file || !this.urlAPI){
28392             return;
28393         }
28394         
28395         this.file = file;
28396         this.cropType = file.type;
28397         
28398         var _this = this;
28399         
28400         if(this.fireEvent('prepare', this, this.file) != false){
28401             
28402             var reader = new FileReader();
28403             
28404             reader.onload = function (e) {
28405                 if (e.target.error) {
28406                     Roo.log(e.target.error);
28407                     return;
28408                 }
28409                 
28410                 var buffer = e.target.result,
28411                     dataView = new DataView(buffer),
28412                     offset = 2,
28413                     maxOffset = dataView.byteLength - 4,
28414                     markerBytes,
28415                     markerLength;
28416                 
28417                 if (dataView.getUint16(0) === 0xffd8) {
28418                     while (offset < maxOffset) {
28419                         markerBytes = dataView.getUint16(offset);
28420                         
28421                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28422                             markerLength = dataView.getUint16(offset + 2) + 2;
28423                             if (offset + markerLength > dataView.byteLength) {
28424                                 Roo.log('Invalid meta data: Invalid segment size.');
28425                                 break;
28426                             }
28427                             
28428                             if(markerBytes == 0xffe1){
28429                                 _this.parseExifData(
28430                                     dataView,
28431                                     offset,
28432                                     markerLength
28433                                 );
28434                             }
28435                             
28436                             offset += markerLength;
28437                             
28438                             continue;
28439                         }
28440                         
28441                         break;
28442                     }
28443                     
28444                 }
28445                 
28446                 var url = _this.urlAPI.createObjectURL(_this.file);
28447                 
28448                 _this.loadCanvas(url);
28449                 
28450                 return;
28451             }
28452             
28453             reader.readAsArrayBuffer(this.file);
28454             
28455         }
28456         
28457     },
28458     
28459     parseExifData : function(dataView, offset, length)
28460     {
28461         var tiffOffset = offset + 10,
28462             littleEndian,
28463             dirOffset;
28464     
28465         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28466             // No Exif data, might be XMP data instead
28467             return;
28468         }
28469         
28470         // Check for the ASCII code for "Exif" (0x45786966):
28471         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28472             // No Exif data, might be XMP data instead
28473             return;
28474         }
28475         if (tiffOffset + 8 > dataView.byteLength) {
28476             Roo.log('Invalid Exif data: Invalid segment size.');
28477             return;
28478         }
28479         // Check for the two null bytes:
28480         if (dataView.getUint16(offset + 8) !== 0x0000) {
28481             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28482             return;
28483         }
28484         // Check the byte alignment:
28485         switch (dataView.getUint16(tiffOffset)) {
28486         case 0x4949:
28487             littleEndian = true;
28488             break;
28489         case 0x4D4D:
28490             littleEndian = false;
28491             break;
28492         default:
28493             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28494             return;
28495         }
28496         // Check for the TIFF tag marker (0x002A):
28497         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28498             Roo.log('Invalid Exif data: Missing TIFF marker.');
28499             return;
28500         }
28501         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28502         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28503         
28504         this.parseExifTags(
28505             dataView,
28506             tiffOffset,
28507             tiffOffset + dirOffset,
28508             littleEndian
28509         );
28510     },
28511     
28512     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28513     {
28514         var tagsNumber,
28515             dirEndOffset,
28516             i;
28517         if (dirOffset + 6 > dataView.byteLength) {
28518             Roo.log('Invalid Exif data: Invalid directory offset.');
28519             return;
28520         }
28521         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28522         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28523         if (dirEndOffset + 4 > dataView.byteLength) {
28524             Roo.log('Invalid Exif data: Invalid directory size.');
28525             return;
28526         }
28527         for (i = 0; i < tagsNumber; i += 1) {
28528             this.parseExifTag(
28529                 dataView,
28530                 tiffOffset,
28531                 dirOffset + 2 + 12 * i, // tag offset
28532                 littleEndian
28533             );
28534         }
28535         // Return the offset to the next directory:
28536         return dataView.getUint32(dirEndOffset, littleEndian);
28537     },
28538     
28539     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28540     {
28541         var tag = dataView.getUint16(offset, littleEndian);
28542         
28543         this.exif[tag] = this.getExifValue(
28544             dataView,
28545             tiffOffset,
28546             offset,
28547             dataView.getUint16(offset + 2, littleEndian), // tag type
28548             dataView.getUint32(offset + 4, littleEndian), // tag length
28549             littleEndian
28550         );
28551     },
28552     
28553     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28554     {
28555         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28556             tagSize,
28557             dataOffset,
28558             values,
28559             i,
28560             str,
28561             c;
28562     
28563         if (!tagType) {
28564             Roo.log('Invalid Exif data: Invalid tag type.');
28565             return;
28566         }
28567         
28568         tagSize = tagType.size * length;
28569         // Determine if the value is contained in the dataOffset bytes,
28570         // or if the value at the dataOffset is a pointer to the actual data:
28571         dataOffset = tagSize > 4 ?
28572                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28573         if (dataOffset + tagSize > dataView.byteLength) {
28574             Roo.log('Invalid Exif data: Invalid data offset.');
28575             return;
28576         }
28577         if (length === 1) {
28578             return tagType.getValue(dataView, dataOffset, littleEndian);
28579         }
28580         values = [];
28581         for (i = 0; i < length; i += 1) {
28582             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28583         }
28584         
28585         if (tagType.ascii) {
28586             str = '';
28587             // Concatenate the chars:
28588             for (i = 0; i < values.length; i += 1) {
28589                 c = values[i];
28590                 // Ignore the terminating NULL byte(s):
28591                 if (c === '\u0000') {
28592                     break;
28593                 }
28594                 str += c;
28595             }
28596             return str;
28597         }
28598         return values;
28599     }
28600     
28601 });
28602
28603 Roo.apply(Roo.bootstrap.UploadCropbox, {
28604     tags : {
28605         'Orientation': 0x0112
28606     },
28607     
28608     Orientation: {
28609             1: 0, //'top-left',
28610 //            2: 'top-right',
28611             3: 180, //'bottom-right',
28612 //            4: 'bottom-left',
28613 //            5: 'left-top',
28614             6: 90, //'right-top',
28615 //            7: 'right-bottom',
28616             8: 270 //'left-bottom'
28617     },
28618     
28619     exifTagTypes : {
28620         // byte, 8-bit unsigned int:
28621         1: {
28622             getValue: function (dataView, dataOffset) {
28623                 return dataView.getUint8(dataOffset);
28624             },
28625             size: 1
28626         },
28627         // ascii, 8-bit byte:
28628         2: {
28629             getValue: function (dataView, dataOffset) {
28630                 return String.fromCharCode(dataView.getUint8(dataOffset));
28631             },
28632             size: 1,
28633             ascii: true
28634         },
28635         // short, 16 bit int:
28636         3: {
28637             getValue: function (dataView, dataOffset, littleEndian) {
28638                 return dataView.getUint16(dataOffset, littleEndian);
28639             },
28640             size: 2
28641         },
28642         // long, 32 bit int:
28643         4: {
28644             getValue: function (dataView, dataOffset, littleEndian) {
28645                 return dataView.getUint32(dataOffset, littleEndian);
28646             },
28647             size: 4
28648         },
28649         // rational = two long values, first is numerator, second is denominator:
28650         5: {
28651             getValue: function (dataView, dataOffset, littleEndian) {
28652                 return dataView.getUint32(dataOffset, littleEndian) /
28653                     dataView.getUint32(dataOffset + 4, littleEndian);
28654             },
28655             size: 8
28656         },
28657         // slong, 32 bit signed int:
28658         9: {
28659             getValue: function (dataView, dataOffset, littleEndian) {
28660                 return dataView.getInt32(dataOffset, littleEndian);
28661             },
28662             size: 4
28663         },
28664         // srational, two slongs, first is numerator, second is denominator:
28665         10: {
28666             getValue: function (dataView, dataOffset, littleEndian) {
28667                 return dataView.getInt32(dataOffset, littleEndian) /
28668                     dataView.getInt32(dataOffset + 4, littleEndian);
28669             },
28670             size: 8
28671         }
28672     },
28673     
28674     footer : {
28675         STANDARD : [
28676             {
28677                 tag : 'div',
28678                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28679                 action : 'rotate-left',
28680                 cn : [
28681                     {
28682                         tag : 'button',
28683                         cls : 'btn btn-default',
28684                         html : '<i class="fa fa-undo"></i>'
28685                     }
28686                 ]
28687             },
28688             {
28689                 tag : 'div',
28690                 cls : 'btn-group roo-upload-cropbox-picture',
28691                 action : 'picture',
28692                 cn : [
28693                     {
28694                         tag : 'button',
28695                         cls : 'btn btn-default',
28696                         html : '<i class="fa fa-picture-o"></i>'
28697                     }
28698                 ]
28699             },
28700             {
28701                 tag : 'div',
28702                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28703                 action : 'rotate-right',
28704                 cn : [
28705                     {
28706                         tag : 'button',
28707                         cls : 'btn btn-default',
28708                         html : '<i class="fa fa-repeat"></i>'
28709                     }
28710                 ]
28711             }
28712         ],
28713         DOCUMENT : [
28714             {
28715                 tag : 'div',
28716                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28717                 action : 'rotate-left',
28718                 cn : [
28719                     {
28720                         tag : 'button',
28721                         cls : 'btn btn-default',
28722                         html : '<i class="fa fa-undo"></i>'
28723                     }
28724                 ]
28725             },
28726             {
28727                 tag : 'div',
28728                 cls : 'btn-group roo-upload-cropbox-download',
28729                 action : 'download',
28730                 cn : [
28731                     {
28732                         tag : 'button',
28733                         cls : 'btn btn-default',
28734                         html : '<i class="fa fa-download"></i>'
28735                     }
28736                 ]
28737             },
28738             {
28739                 tag : 'div',
28740                 cls : 'btn-group roo-upload-cropbox-crop',
28741                 action : 'crop',
28742                 cn : [
28743                     {
28744                         tag : 'button',
28745                         cls : 'btn btn-default',
28746                         html : '<i class="fa fa-crop"></i>'
28747                     }
28748                 ]
28749             },
28750             {
28751                 tag : 'div',
28752                 cls : 'btn-group roo-upload-cropbox-trash',
28753                 action : 'trash',
28754                 cn : [
28755                     {
28756                         tag : 'button',
28757                         cls : 'btn btn-default',
28758                         html : '<i class="fa fa-trash"></i>'
28759                     }
28760                 ]
28761             },
28762             {
28763                 tag : 'div',
28764                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28765                 action : 'rotate-right',
28766                 cn : [
28767                     {
28768                         tag : 'button',
28769                         cls : 'btn btn-default',
28770                         html : '<i class="fa fa-repeat"></i>'
28771                     }
28772                 ]
28773             }
28774         ],
28775         ROTATOR : [
28776             {
28777                 tag : 'div',
28778                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28779                 action : 'rotate-left',
28780                 cn : [
28781                     {
28782                         tag : 'button',
28783                         cls : 'btn btn-default',
28784                         html : '<i class="fa fa-undo"></i>'
28785                     }
28786                 ]
28787             },
28788             {
28789                 tag : 'div',
28790                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28791                 action : 'rotate-right',
28792                 cn : [
28793                     {
28794                         tag : 'button',
28795                         cls : 'btn btn-default',
28796                         html : '<i class="fa fa-repeat"></i>'
28797                     }
28798                 ]
28799             }
28800         ]
28801     }
28802 });
28803
28804 /*
28805 * Licence: LGPL
28806 */
28807
28808 /**
28809  * @class Roo.bootstrap.DocumentManager
28810  * @extends Roo.bootstrap.Component
28811  * Bootstrap DocumentManager class
28812  * @cfg {String} paramName default 'imageUpload'
28813  * @cfg {String} toolTipName default 'filename'
28814  * @cfg {String} method default POST
28815  * @cfg {String} url action url
28816  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28817  * @cfg {Boolean} multiple multiple upload default true
28818  * @cfg {Number} thumbSize default 300
28819  * @cfg {String} fieldLabel
28820  * @cfg {Number} labelWidth default 4
28821  * @cfg {String} labelAlign (left|top) default left
28822  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28823 * @cfg {Number} labellg set the width of label (1-12)
28824  * @cfg {Number} labelmd set the width of label (1-12)
28825  * @cfg {Number} labelsm set the width of label (1-12)
28826  * @cfg {Number} labelxs set the width of label (1-12)
28827  * 
28828  * @constructor
28829  * Create a new DocumentManager
28830  * @param {Object} config The config object
28831  */
28832
28833 Roo.bootstrap.DocumentManager = function(config){
28834     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28835     
28836     this.files = [];
28837     this.delegates = [];
28838     
28839     this.addEvents({
28840         /**
28841          * @event initial
28842          * Fire when initial the DocumentManager
28843          * @param {Roo.bootstrap.DocumentManager} this
28844          */
28845         "initial" : true,
28846         /**
28847          * @event inspect
28848          * inspect selected file
28849          * @param {Roo.bootstrap.DocumentManager} this
28850          * @param {File} file
28851          */
28852         "inspect" : true,
28853         /**
28854          * @event exception
28855          * Fire when xhr load exception
28856          * @param {Roo.bootstrap.DocumentManager} this
28857          * @param {XMLHttpRequest} xhr
28858          */
28859         "exception" : true,
28860         /**
28861          * @event afterupload
28862          * Fire when xhr load exception
28863          * @param {Roo.bootstrap.DocumentManager} this
28864          * @param {XMLHttpRequest} xhr
28865          */
28866         "afterupload" : true,
28867         /**
28868          * @event prepare
28869          * prepare the form data
28870          * @param {Roo.bootstrap.DocumentManager} this
28871          * @param {Object} formData
28872          */
28873         "prepare" : true,
28874         /**
28875          * @event remove
28876          * Fire when remove the file
28877          * @param {Roo.bootstrap.DocumentManager} this
28878          * @param {Object} file
28879          */
28880         "remove" : true,
28881         /**
28882          * @event refresh
28883          * Fire after refresh the file
28884          * @param {Roo.bootstrap.DocumentManager} this
28885          */
28886         "refresh" : true,
28887         /**
28888          * @event click
28889          * Fire after click the image
28890          * @param {Roo.bootstrap.DocumentManager} this
28891          * @param {Object} file
28892          */
28893         "click" : true,
28894         /**
28895          * @event edit
28896          * Fire when upload a image and editable set to true
28897          * @param {Roo.bootstrap.DocumentManager} this
28898          * @param {Object} file
28899          */
28900         "edit" : true,
28901         /**
28902          * @event beforeselectfile
28903          * Fire before select file
28904          * @param {Roo.bootstrap.DocumentManager} this
28905          */
28906         "beforeselectfile" : true,
28907         /**
28908          * @event process
28909          * Fire before process file
28910          * @param {Roo.bootstrap.DocumentManager} this
28911          * @param {Object} file
28912          */
28913         "process" : true,
28914         /**
28915          * @event previewrendered
28916          * Fire when preview rendered
28917          * @param {Roo.bootstrap.DocumentManager} this
28918          * @param {Object} file
28919          */
28920         "previewrendered" : true,
28921         /**
28922          */
28923         "previewResize" : true
28924         
28925     });
28926 };
28927
28928 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28929     
28930     boxes : 0,
28931     inputName : '',
28932     thumbSize : 300,
28933     multiple : true,
28934     files : false,
28935     method : 'POST',
28936     url : '',
28937     paramName : 'imageUpload',
28938     toolTipName : 'filename',
28939     fieldLabel : '',
28940     labelWidth : 4,
28941     labelAlign : 'left',
28942     editable : true,
28943     delegates : false,
28944     xhr : false, 
28945     
28946     labellg : 0,
28947     labelmd : 0,
28948     labelsm : 0,
28949     labelxs : 0,
28950     
28951     getAutoCreate : function()
28952     {   
28953         var managerWidget = {
28954             tag : 'div',
28955             cls : 'roo-document-manager',
28956             cn : [
28957                 {
28958                     tag : 'input',
28959                     cls : 'roo-document-manager-selector',
28960                     type : 'file'
28961                 },
28962                 {
28963                     tag : 'div',
28964                     cls : 'roo-document-manager-uploader',
28965                     cn : [
28966                         {
28967                             tag : 'div',
28968                             cls : 'roo-document-manager-upload-btn',
28969                             html : '<i class="fa fa-plus"></i>'
28970                         }
28971                     ]
28972                     
28973                 }
28974             ]
28975         };
28976         
28977         var content = [
28978             {
28979                 tag : 'div',
28980                 cls : 'column col-md-12',
28981                 cn : managerWidget
28982             }
28983         ];
28984         
28985         if(this.fieldLabel.length){
28986             
28987             content = [
28988                 {
28989                     tag : 'div',
28990                     cls : 'column col-md-12',
28991                     html : this.fieldLabel
28992                 },
28993                 {
28994                     tag : 'div',
28995                     cls : 'column col-md-12',
28996                     cn : managerWidget
28997                 }
28998             ];
28999
29000             if(this.labelAlign == 'left'){
29001                 content = [
29002                     {
29003                         tag : 'div',
29004                         cls : 'column',
29005                         html : this.fieldLabel
29006                     },
29007                     {
29008                         tag : 'div',
29009                         cls : 'column',
29010                         cn : managerWidget
29011                     }
29012                 ];
29013                 
29014                 if(this.labelWidth > 12){
29015                     content[0].style = "width: " + this.labelWidth + 'px';
29016                 }
29017
29018                 if(this.labelWidth < 13 && this.labelmd == 0){
29019                     this.labelmd = this.labelWidth;
29020                 }
29021
29022                 if(this.labellg > 0){
29023                     content[0].cls += ' col-lg-' + this.labellg;
29024                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29025                 }
29026
29027                 if(this.labelmd > 0){
29028                     content[0].cls += ' col-md-' + this.labelmd;
29029                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29030                 }
29031
29032                 if(this.labelsm > 0){
29033                     content[0].cls += ' col-sm-' + this.labelsm;
29034                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29035                 }
29036
29037                 if(this.labelxs > 0){
29038                     content[0].cls += ' col-xs-' + this.labelxs;
29039                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29040                 }
29041                 
29042             }
29043         }
29044         
29045         var cfg = {
29046             tag : 'div',
29047             cls : 'row clearfix',
29048             cn : content
29049         };
29050         
29051         return cfg;
29052         
29053     },
29054     
29055     initEvents : function()
29056     {
29057         this.managerEl = this.el.select('.roo-document-manager', true).first();
29058         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29059         
29060         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29061         this.selectorEl.hide();
29062         
29063         if(this.multiple){
29064             this.selectorEl.attr('multiple', 'multiple');
29065         }
29066         
29067         this.selectorEl.on('change', this.onFileSelected, this);
29068         
29069         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29070         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29071         
29072         this.uploader.on('click', this.onUploaderClick, this);
29073         
29074         this.renderProgressDialog();
29075         
29076         var _this = this;
29077         
29078         window.addEventListener("resize", function() { _this.refresh(); } );
29079         
29080         this.fireEvent('initial', this);
29081     },
29082     
29083     renderProgressDialog : function()
29084     {
29085         var _this = this;
29086         
29087         this.progressDialog = new Roo.bootstrap.Modal({
29088             cls : 'roo-document-manager-progress-dialog',
29089             allow_close : false,
29090             title : '',
29091             buttons : [
29092                 {
29093                     name  :'cancel',
29094                     weight : 'danger',
29095                     html : 'Cancel'
29096                 }
29097             ], 
29098             listeners : { 
29099                 btnclick : function() {
29100                     _this.uploadCancel();
29101                     this.hide();
29102                 }
29103             }
29104         });
29105          
29106         this.progressDialog.render(Roo.get(document.body));
29107          
29108         this.progress = new Roo.bootstrap.Progress({
29109             cls : 'roo-document-manager-progress',
29110             active : true,
29111             striped : true
29112         });
29113         
29114         this.progress.render(this.progressDialog.getChildContainer());
29115         
29116         this.progressBar = new Roo.bootstrap.ProgressBar({
29117             cls : 'roo-document-manager-progress-bar',
29118             aria_valuenow : 0,
29119             aria_valuemin : 0,
29120             aria_valuemax : 12,
29121             panel : 'success'
29122         });
29123         
29124         this.progressBar.render(this.progress.getChildContainer());
29125     },
29126     
29127     onUploaderClick : function(e)
29128     {
29129         e.preventDefault();
29130      
29131         if(this.fireEvent('beforeselectfile', this) != false){
29132             this.selectorEl.dom.click();
29133         }
29134         
29135     },
29136     
29137     onFileSelected : function(e)
29138     {
29139         e.preventDefault();
29140         
29141         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29142             return;
29143         }
29144         
29145         Roo.each(this.selectorEl.dom.files, function(file){
29146             if(this.fireEvent('inspect', this, file) != false){
29147                 this.files.push(file);
29148             }
29149         }, this);
29150         
29151         this.queue();
29152         
29153     },
29154     
29155     queue : function()
29156     {
29157         this.selectorEl.dom.value = '';
29158         
29159         if(!this.files || !this.files.length){
29160             return;
29161         }
29162         
29163         if(this.boxes > 0 && this.files.length > this.boxes){
29164             this.files = this.files.slice(0, this.boxes);
29165         }
29166         
29167         this.uploader.show();
29168         
29169         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29170             this.uploader.hide();
29171         }
29172         
29173         var _this = this;
29174         
29175         var files = [];
29176         
29177         var docs = [];
29178         
29179         Roo.each(this.files, function(file){
29180             
29181             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29182                 var f = this.renderPreview(file);
29183                 files.push(f);
29184                 return;
29185             }
29186             
29187             if(file.type.indexOf('image') != -1){
29188                 this.delegates.push(
29189                     (function(){
29190                         _this.process(file);
29191                     }).createDelegate(this)
29192                 );
29193         
29194                 return;
29195             }
29196             
29197             docs.push(
29198                 (function(){
29199                     _this.process(file);
29200                 }).createDelegate(this)
29201             );
29202             
29203         }, this);
29204         
29205         this.files = files;
29206         
29207         this.delegates = this.delegates.concat(docs);
29208         
29209         if(!this.delegates.length){
29210             this.refresh();
29211             return;
29212         }
29213         
29214         this.progressBar.aria_valuemax = this.delegates.length;
29215         
29216         this.arrange();
29217         
29218         return;
29219     },
29220     
29221     arrange : function()
29222     {
29223         if(!this.delegates.length){
29224             this.progressDialog.hide();
29225             this.refresh();
29226             return;
29227         }
29228         
29229         var delegate = this.delegates.shift();
29230         
29231         this.progressDialog.show();
29232         
29233         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29234         
29235         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29236         
29237         delegate();
29238     },
29239     
29240     refresh : function()
29241     {
29242         this.uploader.show();
29243         
29244         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29245             this.uploader.hide();
29246         }
29247         
29248         Roo.isTouch ? this.closable(false) : this.closable(true);
29249         
29250         this.fireEvent('refresh', this);
29251     },
29252     
29253     onRemove : function(e, el, o)
29254     {
29255         e.preventDefault();
29256         
29257         this.fireEvent('remove', this, o);
29258         
29259     },
29260     
29261     remove : function(o)
29262     {
29263         var files = [];
29264         
29265         Roo.each(this.files, function(file){
29266             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29267                 files.push(file);
29268                 return;
29269             }
29270
29271             o.target.remove();
29272
29273         }, this);
29274         
29275         this.files = files;
29276         
29277         this.refresh();
29278     },
29279     
29280     clear : function()
29281     {
29282         Roo.each(this.files, function(file){
29283             if(!file.target){
29284                 return;
29285             }
29286             
29287             file.target.remove();
29288
29289         }, this);
29290         
29291         this.files = [];
29292         
29293         this.refresh();
29294     },
29295     
29296     onClick : function(e, el, o)
29297     {
29298         e.preventDefault();
29299         
29300         this.fireEvent('click', this, o);
29301         
29302     },
29303     
29304     closable : function(closable)
29305     {
29306         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29307             
29308             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29309             
29310             if(closable){
29311                 el.show();
29312                 return;
29313             }
29314             
29315             el.hide();
29316             
29317         }, this);
29318     },
29319     
29320     xhrOnLoad : function(xhr)
29321     {
29322         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29323             el.remove();
29324         }, this);
29325         
29326         if (xhr.readyState !== 4) {
29327             this.arrange();
29328             this.fireEvent('exception', this, xhr);
29329             return;
29330         }
29331
29332         var response = Roo.decode(xhr.responseText);
29333         
29334         if(!response.success){
29335             this.arrange();
29336             this.fireEvent('exception', this, xhr);
29337             return;
29338         }
29339         
29340         var file = this.renderPreview(response.data);
29341         
29342         this.files.push(file);
29343         
29344         this.arrange();
29345         
29346         this.fireEvent('afterupload', this, xhr);
29347         
29348     },
29349     
29350     xhrOnError : function(xhr)
29351     {
29352         Roo.log('xhr on error');
29353         
29354         var response = Roo.decode(xhr.responseText);
29355           
29356         Roo.log(response);
29357         
29358         this.arrange();
29359     },
29360     
29361     process : function(file)
29362     {
29363         if(this.fireEvent('process', this, file) !== false){
29364             if(this.editable && file.type.indexOf('image') != -1){
29365                 this.fireEvent('edit', this, file);
29366                 return;
29367             }
29368
29369             this.uploadStart(file, false);
29370
29371             return;
29372         }
29373         
29374     },
29375     
29376     uploadStart : function(file, crop)
29377     {
29378         this.xhr = new XMLHttpRequest();
29379         
29380         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29381             this.arrange();
29382             return;
29383         }
29384         
29385         file.xhr = this.xhr;
29386             
29387         this.managerEl.createChild({
29388             tag : 'div',
29389             cls : 'roo-document-manager-loading',
29390             cn : [
29391                 {
29392                     tag : 'div',
29393                     tooltip : file.name,
29394                     cls : 'roo-document-manager-thumb',
29395                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29396                 }
29397             ]
29398
29399         });
29400
29401         this.xhr.open(this.method, this.url, true);
29402         
29403         var headers = {
29404             "Accept": "application/json",
29405             "Cache-Control": "no-cache",
29406             "X-Requested-With": "XMLHttpRequest"
29407         };
29408         
29409         for (var headerName in headers) {
29410             var headerValue = headers[headerName];
29411             if (headerValue) {
29412                 this.xhr.setRequestHeader(headerName, headerValue);
29413             }
29414         }
29415         
29416         var _this = this;
29417         
29418         this.xhr.onload = function()
29419         {
29420             _this.xhrOnLoad(_this.xhr);
29421         }
29422         
29423         this.xhr.onerror = function()
29424         {
29425             _this.xhrOnError(_this.xhr);
29426         }
29427         
29428         var formData = new FormData();
29429
29430         formData.append('returnHTML', 'NO');
29431         
29432         if(crop){
29433             formData.append('crop', crop);
29434         }
29435         
29436         formData.append(this.paramName, file, file.name);
29437         
29438         var options = {
29439             file : file, 
29440             manually : false
29441         };
29442         
29443         if(this.fireEvent('prepare', this, formData, options) != false){
29444             
29445             if(options.manually){
29446                 return;
29447             }
29448             
29449             this.xhr.send(formData);
29450             return;
29451         };
29452         
29453         this.uploadCancel();
29454     },
29455     
29456     uploadCancel : function()
29457     {
29458         if (this.xhr) {
29459             this.xhr.abort();
29460         }
29461         
29462         this.delegates = [];
29463         
29464         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29465             el.remove();
29466         }, this);
29467         
29468         this.arrange();
29469     },
29470     
29471     renderPreview : function(file)
29472     {
29473         if(typeof(file.target) != 'undefined' && file.target){
29474             return file;
29475         }
29476         
29477         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29478         
29479         var previewEl = this.managerEl.createChild({
29480             tag : 'div',
29481             cls : 'roo-document-manager-preview',
29482             cn : [
29483                 {
29484                     tag : 'div',
29485                     tooltip : file[this.toolTipName],
29486                     cls : 'roo-document-manager-thumb',
29487                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29488                 },
29489                 {
29490                     tag : 'button',
29491                     cls : 'close',
29492                     html : '<i class="fa fa-times-circle"></i>'
29493                 }
29494             ]
29495         });
29496
29497         var close = previewEl.select('button.close', true).first();
29498
29499         close.on('click', this.onRemove, this, file);
29500
29501         file.target = previewEl;
29502
29503         var image = previewEl.select('img', true).first();
29504         
29505         var _this = this;
29506         
29507         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29508         
29509         image.on('click', this.onClick, this, file);
29510         
29511         this.fireEvent('previewrendered', this, file);
29512         
29513         return file;
29514         
29515     },
29516     
29517     onPreviewLoad : function(file, image)
29518     {
29519         if(typeof(file.target) == 'undefined' || !file.target){
29520             return;
29521         }
29522         
29523         var width = image.dom.naturalWidth || image.dom.width;
29524         var height = image.dom.naturalHeight || image.dom.height;
29525         
29526         if(!this.previewResize) {
29527             return;
29528         }
29529         
29530         if(width > height){
29531             file.target.addClass('wide');
29532             return;
29533         }
29534         
29535         file.target.addClass('tall');
29536         return;
29537         
29538     },
29539     
29540     uploadFromSource : function(file, crop)
29541     {
29542         this.xhr = new XMLHttpRequest();
29543         
29544         this.managerEl.createChild({
29545             tag : 'div',
29546             cls : 'roo-document-manager-loading',
29547             cn : [
29548                 {
29549                     tag : 'div',
29550                     tooltip : file.name,
29551                     cls : 'roo-document-manager-thumb',
29552                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29553                 }
29554             ]
29555
29556         });
29557
29558         this.xhr.open(this.method, this.url, true);
29559         
29560         var headers = {
29561             "Accept": "application/json",
29562             "Cache-Control": "no-cache",
29563             "X-Requested-With": "XMLHttpRequest"
29564         };
29565         
29566         for (var headerName in headers) {
29567             var headerValue = headers[headerName];
29568             if (headerValue) {
29569                 this.xhr.setRequestHeader(headerName, headerValue);
29570             }
29571         }
29572         
29573         var _this = this;
29574         
29575         this.xhr.onload = function()
29576         {
29577             _this.xhrOnLoad(_this.xhr);
29578         }
29579         
29580         this.xhr.onerror = function()
29581         {
29582             _this.xhrOnError(_this.xhr);
29583         }
29584         
29585         var formData = new FormData();
29586
29587         formData.append('returnHTML', 'NO');
29588         
29589         formData.append('crop', crop);
29590         
29591         if(typeof(file.filename) != 'undefined'){
29592             formData.append('filename', file.filename);
29593         }
29594         
29595         if(typeof(file.mimetype) != 'undefined'){
29596             formData.append('mimetype', file.mimetype);
29597         }
29598         
29599         Roo.log(formData);
29600         
29601         if(this.fireEvent('prepare', this, formData) != false){
29602             this.xhr.send(formData);
29603         };
29604     }
29605 });
29606
29607 /*
29608 * Licence: LGPL
29609 */
29610
29611 /**
29612  * @class Roo.bootstrap.DocumentViewer
29613  * @extends Roo.bootstrap.Component
29614  * Bootstrap DocumentViewer class
29615  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29616  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29617  * 
29618  * @constructor
29619  * Create a new DocumentViewer
29620  * @param {Object} config The config object
29621  */
29622
29623 Roo.bootstrap.DocumentViewer = function(config){
29624     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29625     
29626     this.addEvents({
29627         /**
29628          * @event initial
29629          * Fire after initEvent
29630          * @param {Roo.bootstrap.DocumentViewer} this
29631          */
29632         "initial" : true,
29633         /**
29634          * @event click
29635          * Fire after click
29636          * @param {Roo.bootstrap.DocumentViewer} this
29637          */
29638         "click" : true,
29639         /**
29640          * @event download
29641          * Fire after download button
29642          * @param {Roo.bootstrap.DocumentViewer} this
29643          */
29644         "download" : true,
29645         /**
29646          * @event trash
29647          * Fire after trash button
29648          * @param {Roo.bootstrap.DocumentViewer} this
29649          */
29650         "trash" : true
29651         
29652     });
29653 };
29654
29655 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29656     
29657     showDownload : true,
29658     
29659     showTrash : true,
29660     
29661     getAutoCreate : function()
29662     {
29663         var cfg = {
29664             tag : 'div',
29665             cls : 'roo-document-viewer',
29666             cn : [
29667                 {
29668                     tag : 'div',
29669                     cls : 'roo-document-viewer-body',
29670                     cn : [
29671                         {
29672                             tag : 'div',
29673                             cls : 'roo-document-viewer-thumb',
29674                             cn : [
29675                                 {
29676                                     tag : 'img',
29677                                     cls : 'roo-document-viewer-image'
29678                                 }
29679                             ]
29680                         }
29681                     ]
29682                 },
29683                 {
29684                     tag : 'div',
29685                     cls : 'roo-document-viewer-footer',
29686                     cn : {
29687                         tag : 'div',
29688                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29689                         cn : [
29690                             {
29691                                 tag : 'div',
29692                                 cls : 'btn-group roo-document-viewer-download',
29693                                 cn : [
29694                                     {
29695                                         tag : 'button',
29696                                         cls : 'btn btn-default',
29697                                         html : '<i class="fa fa-download"></i>'
29698                                     }
29699                                 ]
29700                             },
29701                             {
29702                                 tag : 'div',
29703                                 cls : 'btn-group roo-document-viewer-trash',
29704                                 cn : [
29705                                     {
29706                                         tag : 'button',
29707                                         cls : 'btn btn-default',
29708                                         html : '<i class="fa fa-trash"></i>'
29709                                     }
29710                                 ]
29711                             }
29712                         ]
29713                     }
29714                 }
29715             ]
29716         };
29717         
29718         return cfg;
29719     },
29720     
29721     initEvents : function()
29722     {
29723         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29724         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29725         
29726         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29727         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29728         
29729         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29730         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29731         
29732         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29733         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29734         
29735         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29736         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29737         
29738         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29739         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29740         
29741         this.bodyEl.on('click', this.onClick, this);
29742         this.downloadBtn.on('click', this.onDownload, this);
29743         this.trashBtn.on('click', this.onTrash, this);
29744         
29745         this.downloadBtn.hide();
29746         this.trashBtn.hide();
29747         
29748         if(this.showDownload){
29749             this.downloadBtn.show();
29750         }
29751         
29752         if(this.showTrash){
29753             this.trashBtn.show();
29754         }
29755         
29756         if(!this.showDownload && !this.showTrash) {
29757             this.footerEl.hide();
29758         }
29759         
29760     },
29761     
29762     initial : function()
29763     {
29764         this.fireEvent('initial', this);
29765         
29766     },
29767     
29768     onClick : function(e)
29769     {
29770         e.preventDefault();
29771         
29772         this.fireEvent('click', this);
29773     },
29774     
29775     onDownload : function(e)
29776     {
29777         e.preventDefault();
29778         
29779         this.fireEvent('download', this);
29780     },
29781     
29782     onTrash : function(e)
29783     {
29784         e.preventDefault();
29785         
29786         this.fireEvent('trash', this);
29787     }
29788     
29789 });
29790 /*
29791  * - LGPL
29792  *
29793  * nav progress bar
29794  * 
29795  */
29796
29797 /**
29798  * @class Roo.bootstrap.NavProgressBar
29799  * @extends Roo.bootstrap.Component
29800  * Bootstrap NavProgressBar class
29801  * 
29802  * @constructor
29803  * Create a new nav progress bar
29804  * @param {Object} config The config object
29805  */
29806
29807 Roo.bootstrap.NavProgressBar = function(config){
29808     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29809
29810     this.bullets = this.bullets || [];
29811    
29812 //    Roo.bootstrap.NavProgressBar.register(this);
29813      this.addEvents({
29814         /**
29815              * @event changed
29816              * Fires when the active item changes
29817              * @param {Roo.bootstrap.NavProgressBar} this
29818              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29819              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29820          */
29821         'changed': true
29822      });
29823     
29824 };
29825
29826 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29827     
29828     bullets : [],
29829     barItems : [],
29830     
29831     getAutoCreate : function()
29832     {
29833         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29834         
29835         cfg = {
29836             tag : 'div',
29837             cls : 'roo-navigation-bar-group',
29838             cn : [
29839                 {
29840                     tag : 'div',
29841                     cls : 'roo-navigation-top-bar'
29842                 },
29843                 {
29844                     tag : 'div',
29845                     cls : 'roo-navigation-bullets-bar',
29846                     cn : [
29847                         {
29848                             tag : 'ul',
29849                             cls : 'roo-navigation-bar'
29850                         }
29851                     ]
29852                 },
29853                 
29854                 {
29855                     tag : 'div',
29856                     cls : 'roo-navigation-bottom-bar'
29857                 }
29858             ]
29859             
29860         };
29861         
29862         return cfg;
29863         
29864     },
29865     
29866     initEvents: function() 
29867     {
29868         
29869     },
29870     
29871     onRender : function(ct, position) 
29872     {
29873         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29874         
29875         if(this.bullets.length){
29876             Roo.each(this.bullets, function(b){
29877                this.addItem(b);
29878             }, this);
29879         }
29880         
29881         this.format();
29882         
29883     },
29884     
29885     addItem : function(cfg)
29886     {
29887         var item = new Roo.bootstrap.NavProgressItem(cfg);
29888         
29889         item.parentId = this.id;
29890         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29891         
29892         if(cfg.html){
29893             var top = new Roo.bootstrap.Element({
29894                 tag : 'div',
29895                 cls : 'roo-navigation-bar-text'
29896             });
29897             
29898             var bottom = new Roo.bootstrap.Element({
29899                 tag : 'div',
29900                 cls : 'roo-navigation-bar-text'
29901             });
29902             
29903             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29904             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29905             
29906             var topText = new Roo.bootstrap.Element({
29907                 tag : 'span',
29908                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29909             });
29910             
29911             var bottomText = new Roo.bootstrap.Element({
29912                 tag : 'span',
29913                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29914             });
29915             
29916             topText.onRender(top.el, null);
29917             bottomText.onRender(bottom.el, null);
29918             
29919             item.topEl = top;
29920             item.bottomEl = bottom;
29921         }
29922         
29923         this.barItems.push(item);
29924         
29925         return item;
29926     },
29927     
29928     getActive : function()
29929     {
29930         var active = false;
29931         
29932         Roo.each(this.barItems, function(v){
29933             
29934             if (!v.isActive()) {
29935                 return;
29936             }
29937             
29938             active = v;
29939             return false;
29940             
29941         });
29942         
29943         return active;
29944     },
29945     
29946     setActiveItem : function(item)
29947     {
29948         var prev = false;
29949         
29950         Roo.each(this.barItems, function(v){
29951             if (v.rid == item.rid) {
29952                 return ;
29953             }
29954             
29955             if (v.isActive()) {
29956                 v.setActive(false);
29957                 prev = v;
29958             }
29959         });
29960
29961         item.setActive(true);
29962         
29963         this.fireEvent('changed', this, item, prev);
29964     },
29965     
29966     getBarItem: function(rid)
29967     {
29968         var ret = false;
29969         
29970         Roo.each(this.barItems, function(e) {
29971             if (e.rid != rid) {
29972                 return;
29973             }
29974             
29975             ret =  e;
29976             return false;
29977         });
29978         
29979         return ret;
29980     },
29981     
29982     indexOfItem : function(item)
29983     {
29984         var index = false;
29985         
29986         Roo.each(this.barItems, function(v, i){
29987             
29988             if (v.rid != item.rid) {
29989                 return;
29990             }
29991             
29992             index = i;
29993             return false
29994         });
29995         
29996         return index;
29997     },
29998     
29999     setActiveNext : function()
30000     {
30001         var i = this.indexOfItem(this.getActive());
30002         
30003         if (i > this.barItems.length) {
30004             return;
30005         }
30006         
30007         this.setActiveItem(this.barItems[i+1]);
30008     },
30009     
30010     setActivePrev : function()
30011     {
30012         var i = this.indexOfItem(this.getActive());
30013         
30014         if (i  < 1) {
30015             return;
30016         }
30017         
30018         this.setActiveItem(this.barItems[i-1]);
30019     },
30020     
30021     format : function()
30022     {
30023         if(!this.barItems.length){
30024             return;
30025         }
30026      
30027         var width = 100 / this.barItems.length;
30028         
30029         Roo.each(this.barItems, function(i){
30030             i.el.setStyle('width', width + '%');
30031             i.topEl.el.setStyle('width', width + '%');
30032             i.bottomEl.el.setStyle('width', width + '%');
30033         }, this);
30034         
30035     }
30036     
30037 });
30038 /*
30039  * - LGPL
30040  *
30041  * Nav Progress Item
30042  * 
30043  */
30044
30045 /**
30046  * @class Roo.bootstrap.NavProgressItem
30047  * @extends Roo.bootstrap.Component
30048  * Bootstrap NavProgressItem class
30049  * @cfg {String} rid the reference id
30050  * @cfg {Boolean} active (true|false) Is item active default false
30051  * @cfg {Boolean} disabled (true|false) Is item active default false
30052  * @cfg {String} html
30053  * @cfg {String} position (top|bottom) text position default bottom
30054  * @cfg {String} icon show icon instead of number
30055  * 
30056  * @constructor
30057  * Create a new NavProgressItem
30058  * @param {Object} config The config object
30059  */
30060 Roo.bootstrap.NavProgressItem = function(config){
30061     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30062     this.addEvents({
30063         // raw events
30064         /**
30065          * @event click
30066          * The raw click event for the entire grid.
30067          * @param {Roo.bootstrap.NavProgressItem} this
30068          * @param {Roo.EventObject} e
30069          */
30070         "click" : true
30071     });
30072    
30073 };
30074
30075 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30076     
30077     rid : '',
30078     active : false,
30079     disabled : false,
30080     html : '',
30081     position : 'bottom',
30082     icon : false,
30083     
30084     getAutoCreate : function()
30085     {
30086         var iconCls = 'roo-navigation-bar-item-icon';
30087         
30088         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30089         
30090         var cfg = {
30091             tag: 'li',
30092             cls: 'roo-navigation-bar-item',
30093             cn : [
30094                 {
30095                     tag : 'i',
30096                     cls : iconCls
30097                 }
30098             ]
30099         };
30100         
30101         if(this.active){
30102             cfg.cls += ' active';
30103         }
30104         if(this.disabled){
30105             cfg.cls += ' disabled';
30106         }
30107         
30108         return cfg;
30109     },
30110     
30111     disable : function()
30112     {
30113         this.setDisabled(true);
30114     },
30115     
30116     enable : function()
30117     {
30118         this.setDisabled(false);
30119     },
30120     
30121     initEvents: function() 
30122     {
30123         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30124         
30125         this.iconEl.on('click', this.onClick, this);
30126     },
30127     
30128     onClick : function(e)
30129     {
30130         e.preventDefault();
30131         
30132         if(this.disabled){
30133             return;
30134         }
30135         
30136         if(this.fireEvent('click', this, e) === false){
30137             return;
30138         };
30139         
30140         this.parent().setActiveItem(this);
30141     },
30142     
30143     isActive: function () 
30144     {
30145         return this.active;
30146     },
30147     
30148     setActive : function(state)
30149     {
30150         if(this.active == state){
30151             return;
30152         }
30153         
30154         this.active = state;
30155         
30156         if (state) {
30157             this.el.addClass('active');
30158             return;
30159         }
30160         
30161         this.el.removeClass('active');
30162         
30163         return;
30164     },
30165     
30166     setDisabled : function(state)
30167     {
30168         if(this.disabled == state){
30169             return;
30170         }
30171         
30172         this.disabled = state;
30173         
30174         if (state) {
30175             this.el.addClass('disabled');
30176             return;
30177         }
30178         
30179         this.el.removeClass('disabled');
30180     },
30181     
30182     tooltipEl : function()
30183     {
30184         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30185     }
30186 });
30187  
30188
30189  /*
30190  * - LGPL
30191  *
30192  * FieldLabel
30193  * 
30194  */
30195
30196 /**
30197  * @class Roo.bootstrap.FieldLabel
30198  * @extends Roo.bootstrap.Component
30199  * Bootstrap FieldLabel class
30200  * @cfg {String} html contents of the element
30201  * @cfg {String} tag tag of the element default label
30202  * @cfg {String} cls class of the element
30203  * @cfg {String} target label target 
30204  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30205  * @cfg {String} invalidClass default "text-warning"
30206  * @cfg {String} validClass default "text-success"
30207  * @cfg {String} iconTooltip default "This field is required"
30208  * @cfg {String} indicatorpos (left|right) default left
30209  * 
30210  * @constructor
30211  * Create a new FieldLabel
30212  * @param {Object} config The config object
30213  */
30214
30215 Roo.bootstrap.FieldLabel = function(config){
30216     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30217     
30218     this.addEvents({
30219             /**
30220              * @event invalid
30221              * Fires after the field has been marked as invalid.
30222              * @param {Roo.form.FieldLabel} this
30223              * @param {String} msg The validation message
30224              */
30225             invalid : true,
30226             /**
30227              * @event valid
30228              * Fires after the field has been validated with no errors.
30229              * @param {Roo.form.FieldLabel} this
30230              */
30231             valid : true
30232         });
30233 };
30234
30235 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30236     
30237     tag: 'label',
30238     cls: '',
30239     html: '',
30240     target: '',
30241     allowBlank : true,
30242     invalidClass : 'has-warning',
30243     validClass : 'has-success',
30244     iconTooltip : 'This field is required',
30245     indicatorpos : 'left',
30246     
30247     getAutoCreate : function(){
30248         
30249         var cls = "";
30250         if (!this.allowBlank) {
30251             cls  = "visible";
30252         }
30253         
30254         var cfg = {
30255             tag : this.tag,
30256             cls : 'roo-bootstrap-field-label ' + this.cls,
30257             for : this.target,
30258             cn : [
30259                 {
30260                     tag : 'i',
30261                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30262                     tooltip : this.iconTooltip
30263                 },
30264                 {
30265                     tag : 'span',
30266                     html : this.html
30267                 }
30268             ] 
30269         };
30270         
30271         if(this.indicatorpos == 'right'){
30272             var cfg = {
30273                 tag : this.tag,
30274                 cls : 'roo-bootstrap-field-label ' + this.cls,
30275                 for : this.target,
30276                 cn : [
30277                     {
30278                         tag : 'span',
30279                         html : this.html
30280                     },
30281                     {
30282                         tag : 'i',
30283                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30284                         tooltip : this.iconTooltip
30285                     }
30286                 ] 
30287             };
30288         }
30289         
30290         return cfg;
30291     },
30292     
30293     initEvents: function() 
30294     {
30295         Roo.bootstrap.Element.superclass.initEvents.call(this);
30296         
30297         this.indicator = this.indicatorEl();
30298         
30299         if(this.indicator){
30300             this.indicator.removeClass('visible');
30301             this.indicator.addClass('invisible');
30302         }
30303         
30304         Roo.bootstrap.FieldLabel.register(this);
30305     },
30306     
30307     indicatorEl : function()
30308     {
30309         var indicator = this.el.select('i.roo-required-indicator',true).first();
30310         
30311         if(!indicator){
30312             return false;
30313         }
30314         
30315         return indicator;
30316         
30317     },
30318     
30319     /**
30320      * Mark this field as valid
30321      */
30322     markValid : function()
30323     {
30324         if(this.indicator){
30325             this.indicator.removeClass('visible');
30326             this.indicator.addClass('invisible');
30327         }
30328         
30329         this.el.removeClass(this.invalidClass);
30330         
30331         this.el.addClass(this.validClass);
30332         
30333         this.fireEvent('valid', this);
30334     },
30335     
30336     /**
30337      * Mark this field as invalid
30338      * @param {String} msg The validation message
30339      */
30340     markInvalid : function(msg)
30341     {
30342         if(this.indicator){
30343             this.indicator.removeClass('invisible');
30344             this.indicator.addClass('visible');
30345         }
30346         
30347         this.el.removeClass(this.validClass);
30348         
30349         this.el.addClass(this.invalidClass);
30350         
30351         this.fireEvent('invalid', this, msg);
30352     }
30353     
30354    
30355 });
30356
30357 Roo.apply(Roo.bootstrap.FieldLabel, {
30358     
30359     groups: {},
30360     
30361      /**
30362     * register a FieldLabel Group
30363     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30364     */
30365     register : function(label)
30366     {
30367         if(this.groups.hasOwnProperty(label.target)){
30368             return;
30369         }
30370      
30371         this.groups[label.target] = label;
30372         
30373     },
30374     /**
30375     * fetch a FieldLabel Group based on the target
30376     * @param {string} target
30377     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30378     */
30379     get: function(target) {
30380         if (typeof(this.groups[target]) == 'undefined') {
30381             return false;
30382         }
30383         
30384         return this.groups[target] ;
30385     }
30386 });
30387
30388  
30389
30390  /*
30391  * - LGPL
30392  *
30393  * page DateSplitField.
30394  * 
30395  */
30396
30397
30398 /**
30399  * @class Roo.bootstrap.DateSplitField
30400  * @extends Roo.bootstrap.Component
30401  * Bootstrap DateSplitField class
30402  * @cfg {string} fieldLabel - the label associated
30403  * @cfg {Number} labelWidth set the width of label (0-12)
30404  * @cfg {String} labelAlign (top|left)
30405  * @cfg {Boolean} dayAllowBlank (true|false) default false
30406  * @cfg {Boolean} monthAllowBlank (true|false) default false
30407  * @cfg {Boolean} yearAllowBlank (true|false) default false
30408  * @cfg {string} dayPlaceholder 
30409  * @cfg {string} monthPlaceholder
30410  * @cfg {string} yearPlaceholder
30411  * @cfg {string} dayFormat default 'd'
30412  * @cfg {string} monthFormat default 'm'
30413  * @cfg {string} yearFormat default 'Y'
30414  * @cfg {Number} labellg set the width of label (1-12)
30415  * @cfg {Number} labelmd set the width of label (1-12)
30416  * @cfg {Number} labelsm set the width of label (1-12)
30417  * @cfg {Number} labelxs set the width of label (1-12)
30418
30419  *     
30420  * @constructor
30421  * Create a new DateSplitField
30422  * @param {Object} config The config object
30423  */
30424
30425 Roo.bootstrap.DateSplitField = function(config){
30426     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30427     
30428     this.addEvents({
30429         // raw events
30430          /**
30431          * @event years
30432          * getting the data of years
30433          * @param {Roo.bootstrap.DateSplitField} this
30434          * @param {Object} years
30435          */
30436         "years" : true,
30437         /**
30438          * @event days
30439          * getting the data of days
30440          * @param {Roo.bootstrap.DateSplitField} this
30441          * @param {Object} days
30442          */
30443         "days" : true,
30444         /**
30445          * @event invalid
30446          * Fires after the field has been marked as invalid.
30447          * @param {Roo.form.Field} this
30448          * @param {String} msg The validation message
30449          */
30450         invalid : true,
30451        /**
30452          * @event valid
30453          * Fires after the field has been validated with no errors.
30454          * @param {Roo.form.Field} this
30455          */
30456         valid : true
30457     });
30458 };
30459
30460 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30461     
30462     fieldLabel : '',
30463     labelAlign : 'top',
30464     labelWidth : 3,
30465     dayAllowBlank : false,
30466     monthAllowBlank : false,
30467     yearAllowBlank : false,
30468     dayPlaceholder : '',
30469     monthPlaceholder : '',
30470     yearPlaceholder : '',
30471     dayFormat : 'd',
30472     monthFormat : 'm',
30473     yearFormat : 'Y',
30474     isFormField : true,
30475     labellg : 0,
30476     labelmd : 0,
30477     labelsm : 0,
30478     labelxs : 0,
30479     
30480     getAutoCreate : function()
30481     {
30482         var cfg = {
30483             tag : 'div',
30484             cls : 'row roo-date-split-field-group',
30485             cn : [
30486                 {
30487                     tag : 'input',
30488                     type : 'hidden',
30489                     cls : 'form-hidden-field roo-date-split-field-group-value',
30490                     name : this.name
30491                 }
30492             ]
30493         };
30494         
30495         var labelCls = 'col-md-12';
30496         var contentCls = 'col-md-4';
30497         
30498         if(this.fieldLabel){
30499             
30500             var label = {
30501                 tag : 'div',
30502                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30503                 cn : [
30504                     {
30505                         tag : 'label',
30506                         html : this.fieldLabel
30507                     }
30508                 ]
30509             };
30510             
30511             if(this.labelAlign == 'left'){
30512             
30513                 if(this.labelWidth > 12){
30514                     label.style = "width: " + this.labelWidth + 'px';
30515                 }
30516
30517                 if(this.labelWidth < 13 && this.labelmd == 0){
30518                     this.labelmd = this.labelWidth;
30519                 }
30520
30521                 if(this.labellg > 0){
30522                     labelCls = ' col-lg-' + this.labellg;
30523                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30524                 }
30525
30526                 if(this.labelmd > 0){
30527                     labelCls = ' col-md-' + this.labelmd;
30528                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30529                 }
30530
30531                 if(this.labelsm > 0){
30532                     labelCls = ' col-sm-' + this.labelsm;
30533                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30534                 }
30535
30536                 if(this.labelxs > 0){
30537                     labelCls = ' col-xs-' + this.labelxs;
30538                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30539                 }
30540             }
30541             
30542             label.cls += ' ' + labelCls;
30543             
30544             cfg.cn.push(label);
30545         }
30546         
30547         Roo.each(['day', 'month', 'year'], function(t){
30548             cfg.cn.push({
30549                 tag : 'div',
30550                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30551             });
30552         }, this);
30553         
30554         return cfg;
30555     },
30556     
30557     inputEl: function ()
30558     {
30559         return this.el.select('.roo-date-split-field-group-value', true).first();
30560     },
30561     
30562     onRender : function(ct, position) 
30563     {
30564         var _this = this;
30565         
30566         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30567         
30568         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30569         
30570         this.dayField = new Roo.bootstrap.ComboBox({
30571             allowBlank : this.dayAllowBlank,
30572             alwaysQuery : true,
30573             displayField : 'value',
30574             editable : false,
30575             fieldLabel : '',
30576             forceSelection : true,
30577             mode : 'local',
30578             placeholder : this.dayPlaceholder,
30579             selectOnFocus : true,
30580             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30581             triggerAction : 'all',
30582             typeAhead : true,
30583             valueField : 'value',
30584             store : new Roo.data.SimpleStore({
30585                 data : (function() {    
30586                     var days = [];
30587                     _this.fireEvent('days', _this, days);
30588                     return days;
30589                 })(),
30590                 fields : [ 'value' ]
30591             }),
30592             listeners : {
30593                 select : function (_self, record, index)
30594                 {
30595                     _this.setValue(_this.getValue());
30596                 }
30597             }
30598         });
30599
30600         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30601         
30602         this.monthField = new Roo.bootstrap.MonthField({
30603             after : '<i class=\"fa fa-calendar\"></i>',
30604             allowBlank : this.monthAllowBlank,
30605             placeholder : this.monthPlaceholder,
30606             readOnly : true,
30607             listeners : {
30608                 render : function (_self)
30609                 {
30610                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30611                         e.preventDefault();
30612                         _self.focus();
30613                     });
30614                 },
30615                 select : function (_self, oldvalue, newvalue)
30616                 {
30617                     _this.setValue(_this.getValue());
30618                 }
30619             }
30620         });
30621         
30622         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30623         
30624         this.yearField = new Roo.bootstrap.ComboBox({
30625             allowBlank : this.yearAllowBlank,
30626             alwaysQuery : true,
30627             displayField : 'value',
30628             editable : false,
30629             fieldLabel : '',
30630             forceSelection : true,
30631             mode : 'local',
30632             placeholder : this.yearPlaceholder,
30633             selectOnFocus : true,
30634             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30635             triggerAction : 'all',
30636             typeAhead : true,
30637             valueField : 'value',
30638             store : new Roo.data.SimpleStore({
30639                 data : (function() {
30640                     var years = [];
30641                     _this.fireEvent('years', _this, years);
30642                     return years;
30643                 })(),
30644                 fields : [ 'value' ]
30645             }),
30646             listeners : {
30647                 select : function (_self, record, index)
30648                 {
30649                     _this.setValue(_this.getValue());
30650                 }
30651             }
30652         });
30653
30654         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30655     },
30656     
30657     setValue : function(v, format)
30658     {
30659         this.inputEl.dom.value = v;
30660         
30661         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30662         
30663         var d = Date.parseDate(v, f);
30664         
30665         if(!d){
30666             this.validate();
30667             return;
30668         }
30669         
30670         this.setDay(d.format(this.dayFormat));
30671         this.setMonth(d.format(this.monthFormat));
30672         this.setYear(d.format(this.yearFormat));
30673         
30674         this.validate();
30675         
30676         return;
30677     },
30678     
30679     setDay : function(v)
30680     {
30681         this.dayField.setValue(v);
30682         this.inputEl.dom.value = this.getValue();
30683         this.validate();
30684         return;
30685     },
30686     
30687     setMonth : function(v)
30688     {
30689         this.monthField.setValue(v, true);
30690         this.inputEl.dom.value = this.getValue();
30691         this.validate();
30692         return;
30693     },
30694     
30695     setYear : function(v)
30696     {
30697         this.yearField.setValue(v);
30698         this.inputEl.dom.value = this.getValue();
30699         this.validate();
30700         return;
30701     },
30702     
30703     getDay : function()
30704     {
30705         return this.dayField.getValue();
30706     },
30707     
30708     getMonth : function()
30709     {
30710         return this.monthField.getValue();
30711     },
30712     
30713     getYear : function()
30714     {
30715         return this.yearField.getValue();
30716     },
30717     
30718     getValue : function()
30719     {
30720         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30721         
30722         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30723         
30724         return date;
30725     },
30726     
30727     reset : function()
30728     {
30729         this.setDay('');
30730         this.setMonth('');
30731         this.setYear('');
30732         this.inputEl.dom.value = '';
30733         this.validate();
30734         return;
30735     },
30736     
30737     validate : function()
30738     {
30739         var d = this.dayField.validate();
30740         var m = this.monthField.validate();
30741         var y = this.yearField.validate();
30742         
30743         var valid = true;
30744         
30745         if(
30746                 (!this.dayAllowBlank && !d) ||
30747                 (!this.monthAllowBlank && !m) ||
30748                 (!this.yearAllowBlank && !y)
30749         ){
30750             valid = false;
30751         }
30752         
30753         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30754             return valid;
30755         }
30756         
30757         if(valid){
30758             this.markValid();
30759             return valid;
30760         }
30761         
30762         this.markInvalid();
30763         
30764         return valid;
30765     },
30766     
30767     markValid : function()
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             icon.remove();
30775         }
30776         
30777         this.fireEvent('valid', this);
30778     },
30779     
30780      /**
30781      * Mark this field as invalid
30782      * @param {String} msg The validation message
30783      */
30784     markInvalid : function(msg)
30785     {
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             this.el.select('.roo-date-split-field-label', true).createChild({
30792                 tag : 'i',
30793                 cls : 'text-danger fa fa-lg fa-star',
30794                 tooltip : 'This field is required',
30795                 style : 'margin-right:5px;'
30796             }, label, true);
30797         }
30798         
30799         this.fireEvent('invalid', this, msg);
30800     },
30801     
30802     clearInvalid : function()
30803     {
30804         var label = this.el.select('label', true).first();
30805         var icon = this.el.select('i.fa-star', true).first();
30806
30807         if(label && icon){
30808             icon.remove();
30809         }
30810         
30811         this.fireEvent('valid', this);
30812     },
30813     
30814     getName: function()
30815     {
30816         return this.name;
30817     }
30818     
30819 });
30820
30821  /**
30822  *
30823  * This is based on 
30824  * http://masonry.desandro.com
30825  *
30826  * The idea is to render all the bricks based on vertical width...
30827  *
30828  * The original code extends 'outlayer' - we might need to use that....
30829  * 
30830  */
30831
30832
30833 /**
30834  * @class Roo.bootstrap.LayoutMasonry
30835  * @extends Roo.bootstrap.Component
30836  * Bootstrap Layout Masonry class
30837  * 
30838  * @constructor
30839  * Create a new Element
30840  * @param {Object} config The config object
30841  */
30842
30843 Roo.bootstrap.LayoutMasonry = function(config){
30844     
30845     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30846     
30847     this.bricks = [];
30848     
30849     Roo.bootstrap.LayoutMasonry.register(this);
30850     
30851     this.addEvents({
30852         // raw events
30853         /**
30854          * @event layout
30855          * Fire after layout the items
30856          * @param {Roo.bootstrap.LayoutMasonry} this
30857          * @param {Roo.EventObject} e
30858          */
30859         "layout" : true
30860     });
30861     
30862 };
30863
30864 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30865     
30866     /**
30867      * @cfg {Boolean} isLayoutInstant = no animation?
30868      */   
30869     isLayoutInstant : false, // needed?
30870    
30871     /**
30872      * @cfg {Number} boxWidth  width of the columns
30873      */   
30874     boxWidth : 450,
30875     
30876       /**
30877      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30878      */   
30879     boxHeight : 0,
30880     
30881     /**
30882      * @cfg {Number} padWidth padding below box..
30883      */   
30884     padWidth : 10, 
30885     
30886     /**
30887      * @cfg {Number} gutter gutter width..
30888      */   
30889     gutter : 10,
30890     
30891      /**
30892      * @cfg {Number} maxCols maximum number of columns
30893      */   
30894     
30895     maxCols: 0,
30896     
30897     /**
30898      * @cfg {Boolean} isAutoInitial defalut true
30899      */   
30900     isAutoInitial : true, 
30901     
30902     containerWidth: 0,
30903     
30904     /**
30905      * @cfg {Boolean} isHorizontal defalut false
30906      */   
30907     isHorizontal : false, 
30908
30909     currentSize : null,
30910     
30911     tag: 'div',
30912     
30913     cls: '',
30914     
30915     bricks: null, //CompositeElement
30916     
30917     cols : 1,
30918     
30919     _isLayoutInited : false,
30920     
30921 //    isAlternative : false, // only use for vertical layout...
30922     
30923     /**
30924      * @cfg {Number} alternativePadWidth padding below box..
30925      */   
30926     alternativePadWidth : 50,
30927     
30928     selectedBrick : [],
30929     
30930     getAutoCreate : function(){
30931         
30932         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30933         
30934         var cfg = {
30935             tag: this.tag,
30936             cls: 'blog-masonary-wrapper ' + this.cls,
30937             cn : {
30938                 cls : 'mas-boxes masonary'
30939             }
30940         };
30941         
30942         return cfg;
30943     },
30944     
30945     getChildContainer: function( )
30946     {
30947         if (this.boxesEl) {
30948             return this.boxesEl;
30949         }
30950         
30951         this.boxesEl = this.el.select('.mas-boxes').first();
30952         
30953         return this.boxesEl;
30954     },
30955     
30956     
30957     initEvents : function()
30958     {
30959         var _this = this;
30960         
30961         if(this.isAutoInitial){
30962             Roo.log('hook children rendered');
30963             this.on('childrenrendered', function() {
30964                 Roo.log('children rendered');
30965                 _this.initial();
30966             } ,this);
30967         }
30968     },
30969     
30970     initial : function()
30971     {
30972         this.selectedBrick = [];
30973         
30974         this.currentSize = this.el.getBox(true);
30975         
30976         Roo.EventManager.onWindowResize(this.resize, this); 
30977
30978         if(!this.isAutoInitial){
30979             this.layout();
30980             return;
30981         }
30982         
30983         this.layout();
30984         
30985         return;
30986         //this.layout.defer(500,this);
30987         
30988     },
30989     
30990     resize : function()
30991     {
30992         var cs = this.el.getBox(true);
30993         
30994         if (
30995                 this.currentSize.width == cs.width && 
30996                 this.currentSize.x == cs.x && 
30997                 this.currentSize.height == cs.height && 
30998                 this.currentSize.y == cs.y 
30999         ) {
31000             Roo.log("no change in with or X or Y");
31001             return;
31002         }
31003         
31004         this.currentSize = cs;
31005         
31006         this.layout();
31007         
31008     },
31009     
31010     layout : function()
31011     {   
31012         this._resetLayout();
31013         
31014         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31015         
31016         this.layoutItems( isInstant );
31017       
31018         this._isLayoutInited = true;
31019         
31020         this.fireEvent('layout', this);
31021         
31022     },
31023     
31024     _resetLayout : function()
31025     {
31026         if(this.isHorizontal){
31027             this.horizontalMeasureColumns();
31028             return;
31029         }
31030         
31031         this.verticalMeasureColumns();
31032         
31033     },
31034     
31035     verticalMeasureColumns : function()
31036     {
31037         this.getContainerWidth();
31038         
31039 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31040 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31041 //            return;
31042 //        }
31043         
31044         var boxWidth = this.boxWidth + this.padWidth;
31045         
31046         if(this.containerWidth < this.boxWidth){
31047             boxWidth = this.containerWidth
31048         }
31049         
31050         var containerWidth = this.containerWidth;
31051         
31052         var cols = Math.floor(containerWidth / boxWidth);
31053         
31054         this.cols = Math.max( cols, 1 );
31055         
31056         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31057         
31058         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31059         
31060         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31061         
31062         this.colWidth = boxWidth + avail - this.padWidth;
31063         
31064         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31065         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31066     },
31067     
31068     horizontalMeasureColumns : function()
31069     {
31070         this.getContainerWidth();
31071         
31072         var boxWidth = this.boxWidth;
31073         
31074         if(this.containerWidth < boxWidth){
31075             boxWidth = this.containerWidth;
31076         }
31077         
31078         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31079         
31080         this.el.setHeight(boxWidth);
31081         
31082     },
31083     
31084     getContainerWidth : function()
31085     {
31086         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31087     },
31088     
31089     layoutItems : function( isInstant )
31090     {
31091         Roo.log(this.bricks);
31092         
31093         var items = Roo.apply([], this.bricks);
31094         
31095         if(this.isHorizontal){
31096             this._horizontalLayoutItems( items , isInstant );
31097             return;
31098         }
31099         
31100 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31101 //            this._verticalAlternativeLayoutItems( items , isInstant );
31102 //            return;
31103 //        }
31104         
31105         this._verticalLayoutItems( items , isInstant );
31106         
31107     },
31108     
31109     _verticalLayoutItems : function ( items , isInstant)
31110     {
31111         if ( !items || !items.length ) {
31112             return;
31113         }
31114         
31115         var standard = [
31116             ['xs', 'xs', 'xs', 'tall'],
31117             ['xs', 'xs', 'tall'],
31118             ['xs', 'xs', 'sm'],
31119             ['xs', 'xs', 'xs'],
31120             ['xs', 'tall'],
31121             ['xs', 'sm'],
31122             ['xs', 'xs'],
31123             ['xs'],
31124             
31125             ['sm', 'xs', 'xs'],
31126             ['sm', 'xs'],
31127             ['sm'],
31128             
31129             ['tall', 'xs', 'xs', 'xs'],
31130             ['tall', 'xs', 'xs'],
31131             ['tall', 'xs'],
31132             ['tall']
31133             
31134         ];
31135         
31136         var queue = [];
31137         
31138         var boxes = [];
31139         
31140         var box = [];
31141         
31142         Roo.each(items, function(item, k){
31143             
31144             switch (item.size) {
31145                 // these layouts take up a full box,
31146                 case 'md' :
31147                 case 'md-left' :
31148                 case 'md-right' :
31149                 case 'wide' :
31150                     
31151                     if(box.length){
31152                         boxes.push(box);
31153                         box = [];
31154                     }
31155                     
31156                     boxes.push([item]);
31157                     
31158                     break;
31159                     
31160                 case 'xs' :
31161                 case 'sm' :
31162                 case 'tall' :
31163                     
31164                     box.push(item);
31165                     
31166                     break;
31167                 default :
31168                     break;
31169                     
31170             }
31171             
31172         }, this);
31173         
31174         if(box.length){
31175             boxes.push(box);
31176             box = [];
31177         }
31178         
31179         var filterPattern = function(box, length)
31180         {
31181             if(!box.length){
31182                 return;
31183             }
31184             
31185             var match = false;
31186             
31187             var pattern = box.slice(0, length);
31188             
31189             var format = [];
31190             
31191             Roo.each(pattern, function(i){
31192                 format.push(i.size);
31193             }, this);
31194             
31195             Roo.each(standard, function(s){
31196                 
31197                 if(String(s) != String(format)){
31198                     return;
31199                 }
31200                 
31201                 match = true;
31202                 return false;
31203                 
31204             }, this);
31205             
31206             if(!match && length == 1){
31207                 return;
31208             }
31209             
31210             if(!match){
31211                 filterPattern(box, length - 1);
31212                 return;
31213             }
31214                 
31215             queue.push(pattern);
31216
31217             box = box.slice(length, box.length);
31218
31219             filterPattern(box, 4);
31220
31221             return;
31222             
31223         }
31224         
31225         Roo.each(boxes, function(box, k){
31226             
31227             if(!box.length){
31228                 return;
31229             }
31230             
31231             if(box.length == 1){
31232                 queue.push(box);
31233                 return;
31234             }
31235             
31236             filterPattern(box, 4);
31237             
31238         }, this);
31239         
31240         this._processVerticalLayoutQueue( queue, isInstant );
31241         
31242     },
31243     
31244 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31245 //    {
31246 //        if ( !items || !items.length ) {
31247 //            return;
31248 //        }
31249 //
31250 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31251 //        
31252 //    },
31253     
31254     _horizontalLayoutItems : function ( items , isInstant)
31255     {
31256         if ( !items || !items.length || items.length < 3) {
31257             return;
31258         }
31259         
31260         items.reverse();
31261         
31262         var eItems = items.slice(0, 3);
31263         
31264         items = items.slice(3, items.length);
31265         
31266         var standard = [
31267             ['xs', 'xs', 'xs', 'wide'],
31268             ['xs', 'xs', 'wide'],
31269             ['xs', 'xs', 'sm'],
31270             ['xs', 'xs', 'xs'],
31271             ['xs', 'wide'],
31272             ['xs', 'sm'],
31273             ['xs', 'xs'],
31274             ['xs'],
31275             
31276             ['sm', 'xs', 'xs'],
31277             ['sm', 'xs'],
31278             ['sm'],
31279             
31280             ['wide', 'xs', 'xs', 'xs'],
31281             ['wide', 'xs', 'xs'],
31282             ['wide', 'xs'],
31283             ['wide'],
31284             
31285             ['wide-thin']
31286         ];
31287         
31288         var queue = [];
31289         
31290         var boxes = [];
31291         
31292         var box = [];
31293         
31294         Roo.each(items, function(item, k){
31295             
31296             switch (item.size) {
31297                 case 'md' :
31298                 case 'md-left' :
31299                 case 'md-right' :
31300                 case 'tall' :
31301                     
31302                     if(box.length){
31303                         boxes.push(box);
31304                         box = [];
31305                     }
31306                     
31307                     boxes.push([item]);
31308                     
31309                     break;
31310                     
31311                 case 'xs' :
31312                 case 'sm' :
31313                 case 'wide' :
31314                 case 'wide-thin' :
31315                     
31316                     box.push(item);
31317                     
31318                     break;
31319                 default :
31320                     break;
31321                     
31322             }
31323             
31324         }, this);
31325         
31326         if(box.length){
31327             boxes.push(box);
31328             box = [];
31329         }
31330         
31331         var filterPattern = function(box, length)
31332         {
31333             if(!box.length){
31334                 return;
31335             }
31336             
31337             var match = false;
31338             
31339             var pattern = box.slice(0, length);
31340             
31341             var format = [];
31342             
31343             Roo.each(pattern, function(i){
31344                 format.push(i.size);
31345             }, this);
31346             
31347             Roo.each(standard, function(s){
31348                 
31349                 if(String(s) != String(format)){
31350                     return;
31351                 }
31352                 
31353                 match = true;
31354                 return false;
31355                 
31356             }, this);
31357             
31358             if(!match && length == 1){
31359                 return;
31360             }
31361             
31362             if(!match){
31363                 filterPattern(box, length - 1);
31364                 return;
31365             }
31366                 
31367             queue.push(pattern);
31368
31369             box = box.slice(length, box.length);
31370
31371             filterPattern(box, 4);
31372
31373             return;
31374             
31375         }
31376         
31377         Roo.each(boxes, function(box, k){
31378             
31379             if(!box.length){
31380                 return;
31381             }
31382             
31383             if(box.length == 1){
31384                 queue.push(box);
31385                 return;
31386             }
31387             
31388             filterPattern(box, 4);
31389             
31390         }, this);
31391         
31392         
31393         var prune = [];
31394         
31395         var pos = this.el.getBox(true);
31396         
31397         var minX = pos.x;
31398         
31399         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31400         
31401         var hit_end = false;
31402         
31403         Roo.each(queue, function(box){
31404             
31405             if(hit_end){
31406                 
31407                 Roo.each(box, function(b){
31408                 
31409                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31410                     b.el.hide();
31411
31412                 }, this);
31413
31414                 return;
31415             }
31416             
31417             var mx = 0;
31418             
31419             Roo.each(box, function(b){
31420                 
31421                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31422                 b.el.show();
31423
31424                 mx = Math.max(mx, b.x);
31425                 
31426             }, this);
31427             
31428             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31429             
31430             if(maxX < minX){
31431                 
31432                 Roo.each(box, function(b){
31433                 
31434                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31435                     b.el.hide();
31436                     
31437                 }, this);
31438                 
31439                 hit_end = true;
31440                 
31441                 return;
31442             }
31443             
31444             prune.push(box);
31445             
31446         }, this);
31447         
31448         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31449     },
31450     
31451     /** Sets position of item in DOM
31452     * @param {Element} item
31453     * @param {Number} x - horizontal position
31454     * @param {Number} y - vertical position
31455     * @param {Boolean} isInstant - disables transitions
31456     */
31457     _processVerticalLayoutQueue : function( queue, isInstant )
31458     {
31459         var pos = this.el.getBox(true);
31460         var x = pos.x;
31461         var y = pos.y;
31462         var maxY = [];
31463         
31464         for (var i = 0; i < this.cols; i++){
31465             maxY[i] = pos.y;
31466         }
31467         
31468         Roo.each(queue, function(box, k){
31469             
31470             var col = k % this.cols;
31471             
31472             Roo.each(box, function(b,kk){
31473                 
31474                 b.el.position('absolute');
31475                 
31476                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31477                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31478                 
31479                 if(b.size == 'md-left' || b.size == 'md-right'){
31480                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31481                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31482                 }
31483                 
31484                 b.el.setWidth(width);
31485                 b.el.setHeight(height);
31486                 // iframe?
31487                 b.el.select('iframe',true).setSize(width,height);
31488                 
31489             }, this);
31490             
31491             for (var i = 0; i < this.cols; i++){
31492                 
31493                 if(maxY[i] < maxY[col]){
31494                     col = i;
31495                     continue;
31496                 }
31497                 
31498                 col = Math.min(col, i);
31499                 
31500             }
31501             
31502             x = pos.x + col * (this.colWidth + this.padWidth);
31503             
31504             y = maxY[col];
31505             
31506             var positions = [];
31507             
31508             switch (box.length){
31509                 case 1 :
31510                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31511                     break;
31512                 case 2 :
31513                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31514                     break;
31515                 case 3 :
31516                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31517                     break;
31518                 case 4 :
31519                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31520                     break;
31521                 default :
31522                     break;
31523             }
31524             
31525             Roo.each(box, function(b,kk){
31526                 
31527                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31528                 
31529                 var sz = b.el.getSize();
31530                 
31531                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31532                 
31533             }, this);
31534             
31535         }, this);
31536         
31537         var mY = 0;
31538         
31539         for (var i = 0; i < this.cols; i++){
31540             mY = Math.max(mY, maxY[i]);
31541         }
31542         
31543         this.el.setHeight(mY - pos.y);
31544         
31545     },
31546     
31547 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31548 //    {
31549 //        var pos = this.el.getBox(true);
31550 //        var x = pos.x;
31551 //        var y = pos.y;
31552 //        var maxX = pos.right;
31553 //        
31554 //        var maxHeight = 0;
31555 //        
31556 //        Roo.each(items, function(item, k){
31557 //            
31558 //            var c = k % 2;
31559 //            
31560 //            item.el.position('absolute');
31561 //                
31562 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31563 //
31564 //            item.el.setWidth(width);
31565 //
31566 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31567 //
31568 //            item.el.setHeight(height);
31569 //            
31570 //            if(c == 0){
31571 //                item.el.setXY([x, y], isInstant ? false : true);
31572 //            } else {
31573 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31574 //            }
31575 //            
31576 //            y = y + height + this.alternativePadWidth;
31577 //            
31578 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31579 //            
31580 //        }, this);
31581 //        
31582 //        this.el.setHeight(maxHeight);
31583 //        
31584 //    },
31585     
31586     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31587     {
31588         var pos = this.el.getBox(true);
31589         
31590         var minX = pos.x;
31591         var minY = pos.y;
31592         
31593         var maxX = pos.right;
31594         
31595         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31596         
31597         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31598         
31599         Roo.each(queue, function(box, k){
31600             
31601             Roo.each(box, function(b, kk){
31602                 
31603                 b.el.position('absolute');
31604                 
31605                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31606                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31607                 
31608                 if(b.size == 'md-left' || b.size == 'md-right'){
31609                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31610                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31611                 }
31612                 
31613                 b.el.setWidth(width);
31614                 b.el.setHeight(height);
31615                 
31616             }, this);
31617             
31618             if(!box.length){
31619                 return;
31620             }
31621             
31622             var positions = [];
31623             
31624             switch (box.length){
31625                 case 1 :
31626                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31627                     break;
31628                 case 2 :
31629                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31630                     break;
31631                 case 3 :
31632                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31633                     break;
31634                 case 4 :
31635                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31636                     break;
31637                 default :
31638                     break;
31639             }
31640             
31641             Roo.each(box, function(b,kk){
31642                 
31643                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31644                 
31645                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31646                 
31647             }, this);
31648             
31649         }, this);
31650         
31651     },
31652     
31653     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31654     {
31655         Roo.each(eItems, function(b,k){
31656             
31657             b.size = (k == 0) ? 'sm' : 'xs';
31658             b.x = (k == 0) ? 2 : 1;
31659             b.y = (k == 0) ? 2 : 1;
31660             
31661             b.el.position('absolute');
31662             
31663             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31664                 
31665             b.el.setWidth(width);
31666             
31667             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31668             
31669             b.el.setHeight(height);
31670             
31671         }, this);
31672
31673         var positions = [];
31674         
31675         positions.push({
31676             x : maxX - this.unitWidth * 2 - this.gutter,
31677             y : minY
31678         });
31679         
31680         positions.push({
31681             x : maxX - this.unitWidth,
31682             y : minY + (this.unitWidth + this.gutter) * 2
31683         });
31684         
31685         positions.push({
31686             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31687             y : minY
31688         });
31689         
31690         Roo.each(eItems, function(b,k){
31691             
31692             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31693
31694         }, this);
31695         
31696     },
31697     
31698     getVerticalOneBoxColPositions : function(x, y, box)
31699     {
31700         var pos = [];
31701         
31702         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31703         
31704         if(box[0].size == 'md-left'){
31705             rand = 0;
31706         }
31707         
31708         if(box[0].size == 'md-right'){
31709             rand = 1;
31710         }
31711         
31712         pos.push({
31713             x : x + (this.unitWidth + this.gutter) * rand,
31714             y : y
31715         });
31716         
31717         return pos;
31718     },
31719     
31720     getVerticalTwoBoxColPositions : function(x, y, box)
31721     {
31722         var pos = [];
31723         
31724         if(box[0].size == 'xs'){
31725             
31726             pos.push({
31727                 x : x,
31728                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31729             });
31730
31731             pos.push({
31732                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31733                 y : y
31734             });
31735             
31736             return pos;
31737             
31738         }
31739         
31740         pos.push({
31741             x : x,
31742             y : y
31743         });
31744
31745         pos.push({
31746             x : x + (this.unitWidth + this.gutter) * 2,
31747             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31748         });
31749         
31750         return pos;
31751         
31752     },
31753     
31754     getVerticalThreeBoxColPositions : function(x, y, box)
31755     {
31756         var pos = [];
31757         
31758         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31759             
31760             pos.push({
31761                 x : x,
31762                 y : y
31763             });
31764
31765             pos.push({
31766                 x : x + (this.unitWidth + this.gutter) * 1,
31767                 y : y
31768             });
31769             
31770             pos.push({
31771                 x : x + (this.unitWidth + this.gutter) * 2,
31772                 y : y
31773             });
31774             
31775             return pos;
31776             
31777         }
31778         
31779         if(box[0].size == 'xs' && box[1].size == 'xs'){
31780             
31781             pos.push({
31782                 x : x,
31783                 y : y
31784             });
31785
31786             pos.push({
31787                 x : x,
31788                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31789             });
31790             
31791             pos.push({
31792                 x : x + (this.unitWidth + this.gutter) * 1,
31793                 y : y
31794             });
31795             
31796             return pos;
31797             
31798         }
31799         
31800         pos.push({
31801             x : x,
31802             y : y
31803         });
31804
31805         pos.push({
31806             x : x + (this.unitWidth + this.gutter) * 2,
31807             y : y
31808         });
31809
31810         pos.push({
31811             x : x + (this.unitWidth + this.gutter) * 2,
31812             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31813         });
31814             
31815         return pos;
31816         
31817     },
31818     
31819     getVerticalFourBoxColPositions : function(x, y, box)
31820     {
31821         var pos = [];
31822         
31823         if(box[0].size == 'xs'){
31824             
31825             pos.push({
31826                 x : x,
31827                 y : y
31828             });
31829
31830             pos.push({
31831                 x : x,
31832                 y : y + (this.unitHeight + this.gutter) * 1
31833             });
31834             
31835             pos.push({
31836                 x : x,
31837                 y : y + (this.unitHeight + this.gutter) * 2
31838             });
31839             
31840             pos.push({
31841                 x : x + (this.unitWidth + this.gutter) * 1,
31842                 y : y
31843             });
31844             
31845             return pos;
31846             
31847         }
31848         
31849         pos.push({
31850             x : x,
31851             y : y
31852         });
31853
31854         pos.push({
31855             x : x + (this.unitWidth + this.gutter) * 2,
31856             y : y
31857         });
31858
31859         pos.push({
31860             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31861             y : y + (this.unitHeight + this.gutter) * 1
31862         });
31863
31864         pos.push({
31865             x : x + (this.unitWidth + this.gutter) * 2,
31866             y : y + (this.unitWidth + this.gutter) * 2
31867         });
31868
31869         return pos;
31870         
31871     },
31872     
31873     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31874     {
31875         var pos = [];
31876         
31877         if(box[0].size == 'md-left'){
31878             pos.push({
31879                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31880                 y : minY
31881             });
31882             
31883             return pos;
31884         }
31885         
31886         if(box[0].size == 'md-right'){
31887             pos.push({
31888                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31889                 y : minY + (this.unitWidth + this.gutter) * 1
31890             });
31891             
31892             return pos;
31893         }
31894         
31895         var rand = Math.floor(Math.random() * (4 - box[0].y));
31896         
31897         pos.push({
31898             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31899             y : minY + (this.unitWidth + this.gutter) * rand
31900         });
31901         
31902         return pos;
31903         
31904     },
31905     
31906     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31907     {
31908         var pos = [];
31909         
31910         if(box[0].size == 'xs'){
31911             
31912             pos.push({
31913                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31914                 y : minY
31915             });
31916
31917             pos.push({
31918                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31919                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31920             });
31921             
31922             return pos;
31923             
31924         }
31925         
31926         pos.push({
31927             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31928             y : minY
31929         });
31930
31931         pos.push({
31932             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31933             y : minY + (this.unitWidth + this.gutter) * 2
31934         });
31935         
31936         return pos;
31937         
31938     },
31939     
31940     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31941     {
31942         var pos = [];
31943         
31944         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31945             
31946             pos.push({
31947                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31948                 y : minY
31949             });
31950
31951             pos.push({
31952                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31953                 y : minY + (this.unitWidth + this.gutter) * 1
31954             });
31955             
31956             pos.push({
31957                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31958                 y : minY + (this.unitWidth + this.gutter) * 2
31959             });
31960             
31961             return pos;
31962             
31963         }
31964         
31965         if(box[0].size == 'xs' && box[1].size == 'xs'){
31966             
31967             pos.push({
31968                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31969                 y : minY
31970             });
31971
31972             pos.push({
31973                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31974                 y : minY
31975             });
31976             
31977             pos.push({
31978                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31979                 y : minY + (this.unitWidth + this.gutter) * 1
31980             });
31981             
31982             return pos;
31983             
31984         }
31985         
31986         pos.push({
31987             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31988             y : minY
31989         });
31990
31991         pos.push({
31992             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31993             y : minY + (this.unitWidth + this.gutter) * 2
31994         });
31995
31996         pos.push({
31997             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31998             y : minY + (this.unitWidth + this.gutter) * 2
31999         });
32000             
32001         return pos;
32002         
32003     },
32004     
32005     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32006     {
32007         var pos = [];
32008         
32009         if(box[0].size == 'xs'){
32010             
32011             pos.push({
32012                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32013                 y : minY
32014             });
32015
32016             pos.push({
32017                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32018                 y : minY
32019             });
32020             
32021             pos.push({
32022                 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),
32023                 y : minY
32024             });
32025             
32026             pos.push({
32027                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32028                 y : minY + (this.unitWidth + this.gutter) * 1
32029             });
32030             
32031             return pos;
32032             
32033         }
32034         
32035         pos.push({
32036             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32037             y : minY
32038         });
32039         
32040         pos.push({
32041             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32042             y : minY + (this.unitWidth + this.gutter) * 2
32043         });
32044         
32045         pos.push({
32046             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32047             y : minY + (this.unitWidth + this.gutter) * 2
32048         });
32049         
32050         pos.push({
32051             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),
32052             y : minY + (this.unitWidth + this.gutter) * 2
32053         });
32054
32055         return pos;
32056         
32057     },
32058     
32059     /**
32060     * remove a Masonry Brick
32061     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32062     */
32063     removeBrick : function(brick_id)
32064     {
32065         if (!brick_id) {
32066             return;
32067         }
32068         
32069         for (var i = 0; i<this.bricks.length; i++) {
32070             if (this.bricks[i].id == brick_id) {
32071                 this.bricks.splice(i,1);
32072                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32073                 this.initial();
32074             }
32075         }
32076     },
32077     
32078     /**
32079     * adds a Masonry Brick
32080     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32081     */
32082     addBrick : function(cfg)
32083     {
32084         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32085         //this.register(cn);
32086         cn.parentId = this.id;
32087         cn.render(this.el);
32088         return cn;
32089     },
32090     
32091     /**
32092     * register a Masonry Brick
32093     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32094     */
32095     
32096     register : function(brick)
32097     {
32098         this.bricks.push(brick);
32099         brick.masonryId = this.id;
32100     },
32101     
32102     /**
32103     * clear all the Masonry Brick
32104     */
32105     clearAll : function()
32106     {
32107         this.bricks = [];
32108         //this.getChildContainer().dom.innerHTML = "";
32109         this.el.dom.innerHTML = '';
32110     },
32111     
32112     getSelected : function()
32113     {
32114         if (!this.selectedBrick) {
32115             return false;
32116         }
32117         
32118         return this.selectedBrick;
32119     }
32120 });
32121
32122 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32123     
32124     groups: {},
32125      /**
32126     * register a Masonry Layout
32127     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32128     */
32129     
32130     register : function(layout)
32131     {
32132         this.groups[layout.id] = layout;
32133     },
32134     /**
32135     * fetch a  Masonry Layout based on the masonry layout ID
32136     * @param {string} the masonry layout to add
32137     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32138     */
32139     
32140     get: function(layout_id) {
32141         if (typeof(this.groups[layout_id]) == 'undefined') {
32142             return false;
32143         }
32144         return this.groups[layout_id] ;
32145     }
32146     
32147     
32148     
32149 });
32150
32151  
32152
32153  /**
32154  *
32155  * This is based on 
32156  * http://masonry.desandro.com
32157  *
32158  * The idea is to render all the bricks based on vertical width...
32159  *
32160  * The original code extends 'outlayer' - we might need to use that....
32161  * 
32162  */
32163
32164
32165 /**
32166  * @class Roo.bootstrap.LayoutMasonryAuto
32167  * @extends Roo.bootstrap.Component
32168  * Bootstrap Layout Masonry class
32169  * 
32170  * @constructor
32171  * Create a new Element
32172  * @param {Object} config The config object
32173  */
32174
32175 Roo.bootstrap.LayoutMasonryAuto = function(config){
32176     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32177 };
32178
32179 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32180     
32181       /**
32182      * @cfg {Boolean} isFitWidth  - resize the width..
32183      */   
32184     isFitWidth : false,  // options..
32185     /**
32186      * @cfg {Boolean} isOriginLeft = left align?
32187      */   
32188     isOriginLeft : true,
32189     /**
32190      * @cfg {Boolean} isOriginTop = top align?
32191      */   
32192     isOriginTop : false,
32193     /**
32194      * @cfg {Boolean} isLayoutInstant = no animation?
32195      */   
32196     isLayoutInstant : false, // needed?
32197     /**
32198      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32199      */   
32200     isResizingContainer : true,
32201     /**
32202      * @cfg {Number} columnWidth  width of the columns 
32203      */   
32204     
32205     columnWidth : 0,
32206     
32207     /**
32208      * @cfg {Number} maxCols maximum number of columns
32209      */   
32210     
32211     maxCols: 0,
32212     /**
32213      * @cfg {Number} padHeight padding below box..
32214      */   
32215     
32216     padHeight : 10, 
32217     
32218     /**
32219      * @cfg {Boolean} isAutoInitial defalut true
32220      */   
32221     
32222     isAutoInitial : true, 
32223     
32224     // private?
32225     gutter : 0,
32226     
32227     containerWidth: 0,
32228     initialColumnWidth : 0,
32229     currentSize : null,
32230     
32231     colYs : null, // array.
32232     maxY : 0,
32233     padWidth: 10,
32234     
32235     
32236     tag: 'div',
32237     cls: '',
32238     bricks: null, //CompositeElement
32239     cols : 0, // array?
32240     // element : null, // wrapped now this.el
32241     _isLayoutInited : null, 
32242     
32243     
32244     getAutoCreate : function(){
32245         
32246         var cfg = {
32247             tag: this.tag,
32248             cls: 'blog-masonary-wrapper ' + this.cls,
32249             cn : {
32250                 cls : 'mas-boxes masonary'
32251             }
32252         };
32253         
32254         return cfg;
32255     },
32256     
32257     getChildContainer: function( )
32258     {
32259         if (this.boxesEl) {
32260             return this.boxesEl;
32261         }
32262         
32263         this.boxesEl = this.el.select('.mas-boxes').first();
32264         
32265         return this.boxesEl;
32266     },
32267     
32268     
32269     initEvents : function()
32270     {
32271         var _this = this;
32272         
32273         if(this.isAutoInitial){
32274             Roo.log('hook children rendered');
32275             this.on('childrenrendered', function() {
32276                 Roo.log('children rendered');
32277                 _this.initial();
32278             } ,this);
32279         }
32280         
32281     },
32282     
32283     initial : function()
32284     {
32285         this.reloadItems();
32286
32287         this.currentSize = this.el.getBox(true);
32288
32289         /// was window resize... - let's see if this works..
32290         Roo.EventManager.onWindowResize(this.resize, this); 
32291
32292         if(!this.isAutoInitial){
32293             this.layout();
32294             return;
32295         }
32296         
32297         this.layout.defer(500,this);
32298     },
32299     
32300     reloadItems: function()
32301     {
32302         this.bricks = this.el.select('.masonry-brick', true);
32303         
32304         this.bricks.each(function(b) {
32305             //Roo.log(b.getSize());
32306             if (!b.attr('originalwidth')) {
32307                 b.attr('originalwidth',  b.getSize().width);
32308             }
32309             
32310         });
32311         
32312         Roo.log(this.bricks.elements.length);
32313     },
32314     
32315     resize : function()
32316     {
32317         Roo.log('resize');
32318         var cs = this.el.getBox(true);
32319         
32320         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32321             Roo.log("no change in with or X");
32322             return;
32323         }
32324         this.currentSize = cs;
32325         this.layout();
32326     },
32327     
32328     layout : function()
32329     {
32330          Roo.log('layout');
32331         this._resetLayout();
32332         //this._manageStamps();
32333       
32334         // don't animate first layout
32335         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32336         this.layoutItems( isInstant );
32337       
32338         // flag for initalized
32339         this._isLayoutInited = true;
32340     },
32341     
32342     layoutItems : function( isInstant )
32343     {
32344         //var items = this._getItemsForLayout( this.items );
32345         // original code supports filtering layout items.. we just ignore it..
32346         
32347         this._layoutItems( this.bricks , isInstant );
32348       
32349         this._postLayout();
32350     },
32351     _layoutItems : function ( items , isInstant)
32352     {
32353        //this.fireEvent( 'layout', this, items );
32354     
32355
32356         if ( !items || !items.elements.length ) {
32357           // no items, emit event with empty array
32358             return;
32359         }
32360
32361         var queue = [];
32362         items.each(function(item) {
32363             Roo.log("layout item");
32364             Roo.log(item);
32365             // get x/y object from method
32366             var position = this._getItemLayoutPosition( item );
32367             // enqueue
32368             position.item = item;
32369             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32370             queue.push( position );
32371         }, this);
32372       
32373         this._processLayoutQueue( queue );
32374     },
32375     /** Sets position of item in DOM
32376     * @param {Element} item
32377     * @param {Number} x - horizontal position
32378     * @param {Number} y - vertical position
32379     * @param {Boolean} isInstant - disables transitions
32380     */
32381     _processLayoutQueue : function( queue )
32382     {
32383         for ( var i=0, len = queue.length; i < len; i++ ) {
32384             var obj = queue[i];
32385             obj.item.position('absolute');
32386             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32387         }
32388     },
32389       
32390     
32391     /**
32392     * Any logic you want to do after each layout,
32393     * i.e. size the container
32394     */
32395     _postLayout : function()
32396     {
32397         this.resizeContainer();
32398     },
32399     
32400     resizeContainer : function()
32401     {
32402         if ( !this.isResizingContainer ) {
32403             return;
32404         }
32405         var size = this._getContainerSize();
32406         if ( size ) {
32407             this.el.setSize(size.width,size.height);
32408             this.boxesEl.setSize(size.width,size.height);
32409         }
32410     },
32411     
32412     
32413     
32414     _resetLayout : function()
32415     {
32416         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32417         this.colWidth = this.el.getWidth();
32418         //this.gutter = this.el.getWidth(); 
32419         
32420         this.measureColumns();
32421
32422         // reset column Y
32423         var i = this.cols;
32424         this.colYs = [];
32425         while (i--) {
32426             this.colYs.push( 0 );
32427         }
32428     
32429         this.maxY = 0;
32430     },
32431
32432     measureColumns : function()
32433     {
32434         this.getContainerWidth();
32435       // if columnWidth is 0, default to outerWidth of first item
32436         if ( !this.columnWidth ) {
32437             var firstItem = this.bricks.first();
32438             Roo.log(firstItem);
32439             this.columnWidth  = this.containerWidth;
32440             if (firstItem && firstItem.attr('originalwidth') ) {
32441                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32442             }
32443             // columnWidth fall back to item of first element
32444             Roo.log("set column width?");
32445                         this.initialColumnWidth = this.columnWidth  ;
32446
32447             // if first elem has no width, default to size of container
32448             
32449         }
32450         
32451         
32452         if (this.initialColumnWidth) {
32453             this.columnWidth = this.initialColumnWidth;
32454         }
32455         
32456         
32457             
32458         // column width is fixed at the top - however if container width get's smaller we should
32459         // reduce it...
32460         
32461         // this bit calcs how man columns..
32462             
32463         var columnWidth = this.columnWidth += this.gutter;
32464       
32465         // calculate columns
32466         var containerWidth = this.containerWidth + this.gutter;
32467         
32468         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32469         // fix rounding errors, typically with gutters
32470         var excess = columnWidth - containerWidth % columnWidth;
32471         
32472         
32473         // if overshoot is less than a pixel, round up, otherwise floor it
32474         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32475         cols = Math[ mathMethod ]( cols );
32476         this.cols = Math.max( cols, 1 );
32477         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32478         
32479          // padding positioning..
32480         var totalColWidth = this.cols * this.columnWidth;
32481         var padavail = this.containerWidth - totalColWidth;
32482         // so for 2 columns - we need 3 'pads'
32483         
32484         var padNeeded = (1+this.cols) * this.padWidth;
32485         
32486         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32487         
32488         this.columnWidth += padExtra
32489         //this.padWidth = Math.floor(padavail /  ( this.cols));
32490         
32491         // adjust colum width so that padding is fixed??
32492         
32493         // we have 3 columns ... total = width * 3
32494         // we have X left over... that should be used by 
32495         
32496         //if (this.expandC) {
32497             
32498         //}
32499         
32500         
32501         
32502     },
32503     
32504     getContainerWidth : function()
32505     {
32506        /* // container is parent if fit width
32507         var container = this.isFitWidth ? this.element.parentNode : this.element;
32508         // check that this.size and size are there
32509         // IE8 triggers resize on body size change, so they might not be
32510         
32511         var size = getSize( container );  //FIXME
32512         this.containerWidth = size && size.innerWidth; //FIXME
32513         */
32514          
32515         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32516         
32517     },
32518     
32519     _getItemLayoutPosition : function( item )  // what is item?
32520     {
32521         // we resize the item to our columnWidth..
32522       
32523         item.setWidth(this.columnWidth);
32524         item.autoBoxAdjust  = false;
32525         
32526         var sz = item.getSize();
32527  
32528         // how many columns does this brick span
32529         var remainder = this.containerWidth % this.columnWidth;
32530         
32531         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32532         // round if off by 1 pixel, otherwise use ceil
32533         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32534         colSpan = Math.min( colSpan, this.cols );
32535         
32536         // normally this should be '1' as we dont' currently allow multi width columns..
32537         
32538         var colGroup = this._getColGroup( colSpan );
32539         // get the minimum Y value from the columns
32540         var minimumY = Math.min.apply( Math, colGroup );
32541         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32542         
32543         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32544          
32545         // position the brick
32546         var position = {
32547             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32548             y: this.currentSize.y + minimumY + this.padHeight
32549         };
32550         
32551         Roo.log(position);
32552         // apply setHeight to necessary columns
32553         var setHeight = minimumY + sz.height + this.padHeight;
32554         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32555         
32556         var setSpan = this.cols + 1 - colGroup.length;
32557         for ( var i = 0; i < setSpan; i++ ) {
32558           this.colYs[ shortColIndex + i ] = setHeight ;
32559         }
32560       
32561         return position;
32562     },
32563     
32564     /**
32565      * @param {Number} colSpan - number of columns the element spans
32566      * @returns {Array} colGroup
32567      */
32568     _getColGroup : function( colSpan )
32569     {
32570         if ( colSpan < 2 ) {
32571           // if brick spans only one column, use all the column Ys
32572           return this.colYs;
32573         }
32574       
32575         var colGroup = [];
32576         // how many different places could this brick fit horizontally
32577         var groupCount = this.cols + 1 - colSpan;
32578         // for each group potential horizontal position
32579         for ( var i = 0; i < groupCount; i++ ) {
32580           // make an array of colY values for that one group
32581           var groupColYs = this.colYs.slice( i, i + colSpan );
32582           // and get the max value of the array
32583           colGroup[i] = Math.max.apply( Math, groupColYs );
32584         }
32585         return colGroup;
32586     },
32587     /*
32588     _manageStamp : function( stamp )
32589     {
32590         var stampSize =  stamp.getSize();
32591         var offset = stamp.getBox();
32592         // get the columns that this stamp affects
32593         var firstX = this.isOriginLeft ? offset.x : offset.right;
32594         var lastX = firstX + stampSize.width;
32595         var firstCol = Math.floor( firstX / this.columnWidth );
32596         firstCol = Math.max( 0, firstCol );
32597         
32598         var lastCol = Math.floor( lastX / this.columnWidth );
32599         // lastCol should not go over if multiple of columnWidth #425
32600         lastCol -= lastX % this.columnWidth ? 0 : 1;
32601         lastCol = Math.min( this.cols - 1, lastCol );
32602         
32603         // set colYs to bottom of the stamp
32604         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32605             stampSize.height;
32606             
32607         for ( var i = firstCol; i <= lastCol; i++ ) {
32608           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32609         }
32610     },
32611     */
32612     
32613     _getContainerSize : function()
32614     {
32615         this.maxY = Math.max.apply( Math, this.colYs );
32616         var size = {
32617             height: this.maxY
32618         };
32619       
32620         if ( this.isFitWidth ) {
32621             size.width = this._getContainerFitWidth();
32622         }
32623       
32624         return size;
32625     },
32626     
32627     _getContainerFitWidth : function()
32628     {
32629         var unusedCols = 0;
32630         // count unused columns
32631         var i = this.cols;
32632         while ( --i ) {
32633           if ( this.colYs[i] !== 0 ) {
32634             break;
32635           }
32636           unusedCols++;
32637         }
32638         // fit container to columns that have been used
32639         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32640     },
32641     
32642     needsResizeLayout : function()
32643     {
32644         var previousWidth = this.containerWidth;
32645         this.getContainerWidth();
32646         return previousWidth !== this.containerWidth;
32647     }
32648  
32649 });
32650
32651  
32652
32653  /*
32654  * - LGPL
32655  *
32656  * element
32657  * 
32658  */
32659
32660 /**
32661  * @class Roo.bootstrap.MasonryBrick
32662  * @extends Roo.bootstrap.Component
32663  * Bootstrap MasonryBrick class
32664  * 
32665  * @constructor
32666  * Create a new MasonryBrick
32667  * @param {Object} config The config object
32668  */
32669
32670 Roo.bootstrap.MasonryBrick = function(config){
32671     
32672     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32673     
32674     Roo.bootstrap.MasonryBrick.register(this);
32675     
32676     this.addEvents({
32677         // raw events
32678         /**
32679          * @event click
32680          * When a MasonryBrick is clcik
32681          * @param {Roo.bootstrap.MasonryBrick} this
32682          * @param {Roo.EventObject} e
32683          */
32684         "click" : true
32685     });
32686 };
32687
32688 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32689     
32690     /**
32691      * @cfg {String} title
32692      */   
32693     title : '',
32694     /**
32695      * @cfg {String} html
32696      */   
32697     html : '',
32698     /**
32699      * @cfg {String} bgimage
32700      */   
32701     bgimage : '',
32702     /**
32703      * @cfg {String} videourl
32704      */   
32705     videourl : '',
32706     /**
32707      * @cfg {String} cls
32708      */   
32709     cls : '',
32710     /**
32711      * @cfg {String} href
32712      */   
32713     href : '',
32714     /**
32715      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32716      */   
32717     size : 'xs',
32718     
32719     /**
32720      * @cfg {String} placetitle (center|bottom)
32721      */   
32722     placetitle : '',
32723     
32724     /**
32725      * @cfg {Boolean} isFitContainer defalut true
32726      */   
32727     isFitContainer : true, 
32728     
32729     /**
32730      * @cfg {Boolean} preventDefault defalut false
32731      */   
32732     preventDefault : false, 
32733     
32734     /**
32735      * @cfg {Boolean} inverse defalut false
32736      */   
32737     maskInverse : false, 
32738     
32739     getAutoCreate : function()
32740     {
32741         if(!this.isFitContainer){
32742             return this.getSplitAutoCreate();
32743         }
32744         
32745         var cls = 'masonry-brick masonry-brick-full';
32746         
32747         if(this.href.length){
32748             cls += ' masonry-brick-link';
32749         }
32750         
32751         if(this.bgimage.length){
32752             cls += ' masonry-brick-image';
32753         }
32754         
32755         if(this.maskInverse){
32756             cls += ' mask-inverse';
32757         }
32758         
32759         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32760             cls += ' enable-mask';
32761         }
32762         
32763         if(this.size){
32764             cls += ' masonry-' + this.size + '-brick';
32765         }
32766         
32767         if(this.placetitle.length){
32768             
32769             switch (this.placetitle) {
32770                 case 'center' :
32771                     cls += ' masonry-center-title';
32772                     break;
32773                 case 'bottom' :
32774                     cls += ' masonry-bottom-title';
32775                     break;
32776                 default:
32777                     break;
32778             }
32779             
32780         } else {
32781             if(!this.html.length && !this.bgimage.length){
32782                 cls += ' masonry-center-title';
32783             }
32784
32785             if(!this.html.length && this.bgimage.length){
32786                 cls += ' masonry-bottom-title';
32787             }
32788         }
32789         
32790         if(this.cls){
32791             cls += ' ' + this.cls;
32792         }
32793         
32794         var cfg = {
32795             tag: (this.href.length) ? 'a' : 'div',
32796             cls: cls,
32797             cn: [
32798                 {
32799                     tag: 'div',
32800                     cls: 'masonry-brick-mask'
32801                 },
32802                 {
32803                     tag: 'div',
32804                     cls: 'masonry-brick-paragraph',
32805                     cn: []
32806                 }
32807             ]
32808         };
32809         
32810         if(this.href.length){
32811             cfg.href = this.href;
32812         }
32813         
32814         var cn = cfg.cn[1].cn;
32815         
32816         if(this.title.length){
32817             cn.push({
32818                 tag: 'h4',
32819                 cls: 'masonry-brick-title',
32820                 html: this.title
32821             });
32822         }
32823         
32824         if(this.html.length){
32825             cn.push({
32826                 tag: 'p',
32827                 cls: 'masonry-brick-text',
32828                 html: this.html
32829             });
32830         }
32831         
32832         if (!this.title.length && !this.html.length) {
32833             cfg.cn[1].cls += ' hide';
32834         }
32835         
32836         if(this.bgimage.length){
32837             cfg.cn.push({
32838                 tag: 'img',
32839                 cls: 'masonry-brick-image-view',
32840                 src: this.bgimage
32841             });
32842         }
32843         
32844         if(this.videourl.length){
32845             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32846             // youtube support only?
32847             cfg.cn.push({
32848                 tag: 'iframe',
32849                 cls: 'masonry-brick-image-view',
32850                 src: vurl,
32851                 frameborder : 0,
32852                 allowfullscreen : true
32853             });
32854         }
32855         
32856         return cfg;
32857         
32858     },
32859     
32860     getSplitAutoCreate : function()
32861     {
32862         var cls = 'masonry-brick masonry-brick-split';
32863         
32864         if(this.href.length){
32865             cls += ' masonry-brick-link';
32866         }
32867         
32868         if(this.bgimage.length){
32869             cls += ' masonry-brick-image';
32870         }
32871         
32872         if(this.size){
32873             cls += ' masonry-' + this.size + '-brick';
32874         }
32875         
32876         switch (this.placetitle) {
32877             case 'center' :
32878                 cls += ' masonry-center-title';
32879                 break;
32880             case 'bottom' :
32881                 cls += ' masonry-bottom-title';
32882                 break;
32883             default:
32884                 if(!this.bgimage.length){
32885                     cls += ' masonry-center-title';
32886                 }
32887
32888                 if(this.bgimage.length){
32889                     cls += ' masonry-bottom-title';
32890                 }
32891                 break;
32892         }
32893         
32894         if(this.cls){
32895             cls += ' ' + this.cls;
32896         }
32897         
32898         var cfg = {
32899             tag: (this.href.length) ? 'a' : 'div',
32900             cls: cls,
32901             cn: [
32902                 {
32903                     tag: 'div',
32904                     cls: 'masonry-brick-split-head',
32905                     cn: [
32906                         {
32907                             tag: 'div',
32908                             cls: 'masonry-brick-paragraph',
32909                             cn: []
32910                         }
32911                     ]
32912                 },
32913                 {
32914                     tag: 'div',
32915                     cls: 'masonry-brick-split-body',
32916                     cn: []
32917                 }
32918             ]
32919         };
32920         
32921         if(this.href.length){
32922             cfg.href = this.href;
32923         }
32924         
32925         if(this.title.length){
32926             cfg.cn[0].cn[0].cn.push({
32927                 tag: 'h4',
32928                 cls: 'masonry-brick-title',
32929                 html: this.title
32930             });
32931         }
32932         
32933         if(this.html.length){
32934             cfg.cn[1].cn.push({
32935                 tag: 'p',
32936                 cls: 'masonry-brick-text',
32937                 html: this.html
32938             });
32939         }
32940
32941         if(this.bgimage.length){
32942             cfg.cn[0].cn.push({
32943                 tag: 'img',
32944                 cls: 'masonry-brick-image-view',
32945                 src: this.bgimage
32946             });
32947         }
32948         
32949         if(this.videourl.length){
32950             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32951             // youtube support only?
32952             cfg.cn[0].cn.cn.push({
32953                 tag: 'iframe',
32954                 cls: 'masonry-brick-image-view',
32955                 src: vurl,
32956                 frameborder : 0,
32957                 allowfullscreen : true
32958             });
32959         }
32960         
32961         return cfg;
32962     },
32963     
32964     initEvents: function() 
32965     {
32966         switch (this.size) {
32967             case 'xs' :
32968                 this.x = 1;
32969                 this.y = 1;
32970                 break;
32971             case 'sm' :
32972                 this.x = 2;
32973                 this.y = 2;
32974                 break;
32975             case 'md' :
32976             case 'md-left' :
32977             case 'md-right' :
32978                 this.x = 3;
32979                 this.y = 3;
32980                 break;
32981             case 'tall' :
32982                 this.x = 2;
32983                 this.y = 3;
32984                 break;
32985             case 'wide' :
32986                 this.x = 3;
32987                 this.y = 2;
32988                 break;
32989             case 'wide-thin' :
32990                 this.x = 3;
32991                 this.y = 1;
32992                 break;
32993                         
32994             default :
32995                 break;
32996         }
32997         
32998         if(Roo.isTouch){
32999             this.el.on('touchstart', this.onTouchStart, this);
33000             this.el.on('touchmove', this.onTouchMove, this);
33001             this.el.on('touchend', this.onTouchEnd, this);
33002             this.el.on('contextmenu', this.onContextMenu, this);
33003         } else {
33004             this.el.on('mouseenter'  ,this.enter, this);
33005             this.el.on('mouseleave', this.leave, this);
33006             this.el.on('click', this.onClick, this);
33007         }
33008         
33009         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33010             this.parent().bricks.push(this);   
33011         }
33012         
33013     },
33014     
33015     onClick: function(e, el)
33016     {
33017         var time = this.endTimer - this.startTimer;
33018         // Roo.log(e.preventDefault());
33019         if(Roo.isTouch){
33020             if(time > 1000){
33021                 e.preventDefault();
33022                 return;
33023             }
33024         }
33025         
33026         if(!this.preventDefault){
33027             return;
33028         }
33029         
33030         e.preventDefault();
33031         
33032         if (this.activeClass != '') {
33033             this.selectBrick();
33034         }
33035         
33036         this.fireEvent('click', this, e);
33037     },
33038     
33039     enter: function(e, el)
33040     {
33041         e.preventDefault();
33042         
33043         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33044             return;
33045         }
33046         
33047         if(this.bgimage.length && this.html.length){
33048             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33049         }
33050     },
33051     
33052     leave: function(e, el)
33053     {
33054         e.preventDefault();
33055         
33056         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33057             return;
33058         }
33059         
33060         if(this.bgimage.length && this.html.length){
33061             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33062         }
33063     },
33064     
33065     onTouchStart: function(e, el)
33066     {
33067 //        e.preventDefault();
33068         
33069         this.touchmoved = false;
33070         
33071         if(!this.isFitContainer){
33072             return;
33073         }
33074         
33075         if(!this.bgimage.length || !this.html.length){
33076             return;
33077         }
33078         
33079         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33080         
33081         this.timer = new Date().getTime();
33082         
33083     },
33084     
33085     onTouchMove: function(e, el)
33086     {
33087         this.touchmoved = true;
33088     },
33089     
33090     onContextMenu : function(e,el)
33091     {
33092         e.preventDefault();
33093         e.stopPropagation();
33094         return false;
33095     },
33096     
33097     onTouchEnd: function(e, el)
33098     {
33099 //        e.preventDefault();
33100         
33101         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33102         
33103             this.leave(e,el);
33104             
33105             return;
33106         }
33107         
33108         if(!this.bgimage.length || !this.html.length){
33109             
33110             if(this.href.length){
33111                 window.location.href = this.href;
33112             }
33113             
33114             return;
33115         }
33116         
33117         if(!this.isFitContainer){
33118             return;
33119         }
33120         
33121         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33122         
33123         window.location.href = this.href;
33124     },
33125     
33126     //selection on single brick only
33127     selectBrick : function() {
33128         
33129         if (!this.parentId) {
33130             return;
33131         }
33132         
33133         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33134         var index = m.selectedBrick.indexOf(this.id);
33135         
33136         if ( index > -1) {
33137             m.selectedBrick.splice(index,1);
33138             this.el.removeClass(this.activeClass);
33139             return;
33140         }
33141         
33142         for(var i = 0; i < m.selectedBrick.length; i++) {
33143             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33144             b.el.removeClass(b.activeClass);
33145         }
33146         
33147         m.selectedBrick = [];
33148         
33149         m.selectedBrick.push(this.id);
33150         this.el.addClass(this.activeClass);
33151         return;
33152     },
33153     
33154     isSelected : function(){
33155         return this.el.hasClass(this.activeClass);
33156         
33157     }
33158 });
33159
33160 Roo.apply(Roo.bootstrap.MasonryBrick, {
33161     
33162     //groups: {},
33163     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33164      /**
33165     * register a Masonry Brick
33166     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33167     */
33168     
33169     register : function(brick)
33170     {
33171         //this.groups[brick.id] = brick;
33172         this.groups.add(brick.id, brick);
33173     },
33174     /**
33175     * fetch a  masonry brick based on the masonry brick ID
33176     * @param {string} the masonry brick to add
33177     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33178     */
33179     
33180     get: function(brick_id) 
33181     {
33182         // if (typeof(this.groups[brick_id]) == 'undefined') {
33183         //     return false;
33184         // }
33185         // return this.groups[brick_id] ;
33186         
33187         if(this.groups.key(brick_id)) {
33188             return this.groups.key(brick_id);
33189         }
33190         
33191         return false;
33192     }
33193     
33194     
33195     
33196 });
33197
33198  /*
33199  * - LGPL
33200  *
33201  * element
33202  * 
33203  */
33204
33205 /**
33206  * @class Roo.bootstrap.Brick
33207  * @extends Roo.bootstrap.Component
33208  * Bootstrap Brick class
33209  * 
33210  * @constructor
33211  * Create a new Brick
33212  * @param {Object} config The config object
33213  */
33214
33215 Roo.bootstrap.Brick = function(config){
33216     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33217     
33218     this.addEvents({
33219         // raw events
33220         /**
33221          * @event click
33222          * When a Brick is click
33223          * @param {Roo.bootstrap.Brick} this
33224          * @param {Roo.EventObject} e
33225          */
33226         "click" : true
33227     });
33228 };
33229
33230 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33231     
33232     /**
33233      * @cfg {String} title
33234      */   
33235     title : '',
33236     /**
33237      * @cfg {String} html
33238      */   
33239     html : '',
33240     /**
33241      * @cfg {String} bgimage
33242      */   
33243     bgimage : '',
33244     /**
33245      * @cfg {String} cls
33246      */   
33247     cls : '',
33248     /**
33249      * @cfg {String} href
33250      */   
33251     href : '',
33252     /**
33253      * @cfg {String} video
33254      */   
33255     video : '',
33256     /**
33257      * @cfg {Boolean} square
33258      */   
33259     square : true,
33260     
33261     getAutoCreate : function()
33262     {
33263         var cls = 'roo-brick';
33264         
33265         if(this.href.length){
33266             cls += ' roo-brick-link';
33267         }
33268         
33269         if(this.bgimage.length){
33270             cls += ' roo-brick-image';
33271         }
33272         
33273         if(!this.html.length && !this.bgimage.length){
33274             cls += ' roo-brick-center-title';
33275         }
33276         
33277         if(!this.html.length && this.bgimage.length){
33278             cls += ' roo-brick-bottom-title';
33279         }
33280         
33281         if(this.cls){
33282             cls += ' ' + this.cls;
33283         }
33284         
33285         var cfg = {
33286             tag: (this.href.length) ? 'a' : 'div',
33287             cls: cls,
33288             cn: [
33289                 {
33290                     tag: 'div',
33291                     cls: 'roo-brick-paragraph',
33292                     cn: []
33293                 }
33294             ]
33295         };
33296         
33297         if(this.href.length){
33298             cfg.href = this.href;
33299         }
33300         
33301         var cn = cfg.cn[0].cn;
33302         
33303         if(this.title.length){
33304             cn.push({
33305                 tag: 'h4',
33306                 cls: 'roo-brick-title',
33307                 html: this.title
33308             });
33309         }
33310         
33311         if(this.html.length){
33312             cn.push({
33313                 tag: 'p',
33314                 cls: 'roo-brick-text',
33315                 html: this.html
33316             });
33317         } else {
33318             cn.cls += ' hide';
33319         }
33320         
33321         if(this.bgimage.length){
33322             cfg.cn.push({
33323                 tag: 'img',
33324                 cls: 'roo-brick-image-view',
33325                 src: this.bgimage
33326             });
33327         }
33328         
33329         return cfg;
33330     },
33331     
33332     initEvents: function() 
33333     {
33334         if(this.title.length || this.html.length){
33335             this.el.on('mouseenter'  ,this.enter, this);
33336             this.el.on('mouseleave', this.leave, this);
33337         }
33338         
33339         Roo.EventManager.onWindowResize(this.resize, this); 
33340         
33341         if(this.bgimage.length){
33342             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33343             this.imageEl.on('load', this.onImageLoad, this);
33344             return;
33345         }
33346         
33347         this.resize();
33348     },
33349     
33350     onImageLoad : function()
33351     {
33352         this.resize();
33353     },
33354     
33355     resize : function()
33356     {
33357         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33358         
33359         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33360         
33361         if(this.bgimage.length){
33362             var image = this.el.select('.roo-brick-image-view', true).first();
33363             
33364             image.setWidth(paragraph.getWidth());
33365             
33366             if(this.square){
33367                 image.setHeight(paragraph.getWidth());
33368             }
33369             
33370             this.el.setHeight(image.getHeight());
33371             paragraph.setHeight(image.getHeight());
33372             
33373         }
33374         
33375     },
33376     
33377     enter: function(e, el)
33378     {
33379         e.preventDefault();
33380         
33381         if(this.bgimage.length){
33382             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33383             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33384         }
33385     },
33386     
33387     leave: function(e, el)
33388     {
33389         e.preventDefault();
33390         
33391         if(this.bgimage.length){
33392             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33393             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33394         }
33395     }
33396     
33397 });
33398
33399  
33400
33401  /*
33402  * - LGPL
33403  *
33404  * Number field 
33405  */
33406
33407 /**
33408  * @class Roo.bootstrap.NumberField
33409  * @extends Roo.bootstrap.Input
33410  * Bootstrap NumberField class
33411  * 
33412  * 
33413  * 
33414  * 
33415  * @constructor
33416  * Create a new NumberField
33417  * @param {Object} config The config object
33418  */
33419
33420 Roo.bootstrap.NumberField = function(config){
33421     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33422 };
33423
33424 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33425     
33426     /**
33427      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33428      */
33429     allowDecimals : true,
33430     /**
33431      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33432      */
33433     decimalSeparator : ".",
33434     /**
33435      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33436      */
33437     decimalPrecision : 2,
33438     /**
33439      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33440      */
33441     allowNegative : true,
33442     
33443     /**
33444      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33445      */
33446     allowZero: true,
33447     /**
33448      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33449      */
33450     minValue : Number.NEGATIVE_INFINITY,
33451     /**
33452      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33453      */
33454     maxValue : Number.MAX_VALUE,
33455     /**
33456      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33457      */
33458     minText : "The minimum value for this field is {0}",
33459     /**
33460      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33461      */
33462     maxText : "The maximum value for this field is {0}",
33463     /**
33464      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33465      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33466      */
33467     nanText : "{0} is not a valid number",
33468     /**
33469      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33470      */
33471     thousandsDelimiter : false,
33472     /**
33473      * @cfg {String} valueAlign alignment of value
33474      */
33475     valueAlign : "left",
33476
33477     getAutoCreate : function()
33478     {
33479         var hiddenInput = {
33480             tag: 'input',
33481             type: 'hidden',
33482             id: Roo.id(),
33483             cls: 'hidden-number-input'
33484         };
33485         
33486         if (this.name) {
33487             hiddenInput.name = this.name;
33488         }
33489         
33490         this.name = '';
33491         
33492         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33493         
33494         this.name = hiddenInput.name;
33495         
33496         if(cfg.cn.length > 0) {
33497             cfg.cn.push(hiddenInput);
33498         }
33499         
33500         return cfg;
33501     },
33502
33503     // private
33504     initEvents : function()
33505     {   
33506         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33507         
33508         var allowed = "0123456789";
33509         
33510         if(this.allowDecimals){
33511             allowed += this.decimalSeparator;
33512         }
33513         
33514         if(this.allowNegative){
33515             allowed += "-";
33516         }
33517         
33518         if(this.thousandsDelimiter) {
33519             allowed += ",";
33520         }
33521         
33522         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33523         
33524         var keyPress = function(e){
33525             
33526             var k = e.getKey();
33527             
33528             var c = e.getCharCode();
33529             
33530             if(
33531                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33532                     allowed.indexOf(String.fromCharCode(c)) === -1
33533             ){
33534                 e.stopEvent();
33535                 return;
33536             }
33537             
33538             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33539                 return;
33540             }
33541             
33542             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33543                 e.stopEvent();
33544             }
33545         };
33546         
33547         this.el.on("keypress", keyPress, this);
33548     },
33549     
33550     validateValue : function(value)
33551     {
33552         
33553         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33554             return false;
33555         }
33556         
33557         var num = this.parseValue(value);
33558         
33559         if(isNaN(num)){
33560             this.markInvalid(String.format(this.nanText, value));
33561             return false;
33562         }
33563         
33564         if(num < this.minValue){
33565             this.markInvalid(String.format(this.minText, this.minValue));
33566             return false;
33567         }
33568         
33569         if(num > this.maxValue){
33570             this.markInvalid(String.format(this.maxText, this.maxValue));
33571             return false;
33572         }
33573         
33574         return true;
33575     },
33576
33577     getValue : function()
33578     {
33579         var v = this.hiddenEl().getValue();
33580         
33581         return this.fixPrecision(this.parseValue(v));
33582     },
33583
33584     parseValue : function(value)
33585     {
33586         if(this.thousandsDelimiter) {
33587             value += "";
33588             r = new RegExp(",", "g");
33589             value = value.replace(r, "");
33590         }
33591         
33592         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33593         return isNaN(value) ? '' : value;
33594     },
33595
33596     fixPrecision : function(value)
33597     {
33598         if(this.thousandsDelimiter) {
33599             value += "";
33600             r = new RegExp(",", "g");
33601             value = value.replace(r, "");
33602         }
33603         
33604         var nan = isNaN(value);
33605         
33606         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33607             return nan ? '' : value;
33608         }
33609         return parseFloat(value).toFixed(this.decimalPrecision);
33610     },
33611
33612     setValue : function(v)
33613     {
33614         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33615         
33616         this.value = v;
33617         
33618         if(this.rendered){
33619             
33620             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33621             
33622             this.inputEl().dom.value = (v == '') ? '' :
33623                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33624             
33625             if(!this.allowZero && v === '0') {
33626                 this.hiddenEl().dom.value = '';
33627                 this.inputEl().dom.value = '';
33628             }
33629             
33630             this.validate();
33631         }
33632     },
33633
33634     decimalPrecisionFcn : function(v)
33635     {
33636         return Math.floor(v);
33637     },
33638
33639     beforeBlur : function()
33640     {
33641         var v = this.parseValue(this.getRawValue());
33642         
33643         if(v || v === 0 || v === ''){
33644             this.setValue(v);
33645         }
33646     },
33647     
33648     hiddenEl : function()
33649     {
33650         return this.el.select('input.hidden-number-input',true).first();
33651     }
33652     
33653 });
33654
33655  
33656
33657 /*
33658 * Licence: LGPL
33659 */
33660
33661 /**
33662  * @class Roo.bootstrap.DocumentSlider
33663  * @extends Roo.bootstrap.Component
33664  * Bootstrap DocumentSlider class
33665  * 
33666  * @constructor
33667  * Create a new DocumentViewer
33668  * @param {Object} config The config object
33669  */
33670
33671 Roo.bootstrap.DocumentSlider = function(config){
33672     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33673     
33674     this.files = [];
33675     
33676     this.addEvents({
33677         /**
33678          * @event initial
33679          * Fire after initEvent
33680          * @param {Roo.bootstrap.DocumentSlider} this
33681          */
33682         "initial" : true,
33683         /**
33684          * @event update
33685          * Fire after update
33686          * @param {Roo.bootstrap.DocumentSlider} this
33687          */
33688         "update" : true,
33689         /**
33690          * @event click
33691          * Fire after click
33692          * @param {Roo.bootstrap.DocumentSlider} this
33693          */
33694         "click" : true
33695     });
33696 };
33697
33698 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33699     
33700     files : false,
33701     
33702     indicator : 0,
33703     
33704     getAutoCreate : function()
33705     {
33706         var cfg = {
33707             tag : 'div',
33708             cls : 'roo-document-slider',
33709             cn : [
33710                 {
33711                     tag : 'div',
33712                     cls : 'roo-document-slider-header',
33713                     cn : [
33714                         {
33715                             tag : 'div',
33716                             cls : 'roo-document-slider-header-title'
33717                         }
33718                     ]
33719                 },
33720                 {
33721                     tag : 'div',
33722                     cls : 'roo-document-slider-body',
33723                     cn : [
33724                         {
33725                             tag : 'div',
33726                             cls : 'roo-document-slider-prev',
33727                             cn : [
33728                                 {
33729                                     tag : 'i',
33730                                     cls : 'fa fa-chevron-left'
33731                                 }
33732                             ]
33733                         },
33734                         {
33735                             tag : 'div',
33736                             cls : 'roo-document-slider-thumb',
33737                             cn : [
33738                                 {
33739                                     tag : 'img',
33740                                     cls : 'roo-document-slider-image'
33741                                 }
33742                             ]
33743                         },
33744                         {
33745                             tag : 'div',
33746                             cls : 'roo-document-slider-next',
33747                             cn : [
33748                                 {
33749                                     tag : 'i',
33750                                     cls : 'fa fa-chevron-right'
33751                                 }
33752                             ]
33753                         }
33754                     ]
33755                 }
33756             ]
33757         };
33758         
33759         return cfg;
33760     },
33761     
33762     initEvents : function()
33763     {
33764         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33765         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33766         
33767         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33768         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33769         
33770         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33771         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33772         
33773         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33774         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33775         
33776         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33777         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33778         
33779         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33780         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33781         
33782         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33783         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33784         
33785         this.thumbEl.on('click', this.onClick, this);
33786         
33787         this.prevIndicator.on('click', this.prev, this);
33788         
33789         this.nextIndicator.on('click', this.next, this);
33790         
33791     },
33792     
33793     initial : function()
33794     {
33795         if(this.files.length){
33796             this.indicator = 1;
33797             this.update()
33798         }
33799         
33800         this.fireEvent('initial', this);
33801     },
33802     
33803     update : function()
33804     {
33805         this.imageEl.attr('src', this.files[this.indicator - 1]);
33806         
33807         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33808         
33809         this.prevIndicator.show();
33810         
33811         if(this.indicator == 1){
33812             this.prevIndicator.hide();
33813         }
33814         
33815         this.nextIndicator.show();
33816         
33817         if(this.indicator == this.files.length){
33818             this.nextIndicator.hide();
33819         }
33820         
33821         this.thumbEl.scrollTo('top');
33822         
33823         this.fireEvent('update', this);
33824     },
33825     
33826     onClick : function(e)
33827     {
33828         e.preventDefault();
33829         
33830         this.fireEvent('click', this);
33831     },
33832     
33833     prev : function(e)
33834     {
33835         e.preventDefault();
33836         
33837         this.indicator = Math.max(1, this.indicator - 1);
33838         
33839         this.update();
33840     },
33841     
33842     next : function(e)
33843     {
33844         e.preventDefault();
33845         
33846         this.indicator = Math.min(this.files.length, this.indicator + 1);
33847         
33848         this.update();
33849     }
33850 });
33851 /*
33852  * - LGPL
33853  *
33854  * RadioSet
33855  *
33856  *
33857  */
33858
33859 /**
33860  * @class Roo.bootstrap.RadioSet
33861  * @extends Roo.bootstrap.Input
33862  * Bootstrap RadioSet class
33863  * @cfg {String} indicatorpos (left|right) default left
33864  * @cfg {Boolean} inline (true|false) inline the element (default true)
33865  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33866  * @constructor
33867  * Create a new RadioSet
33868  * @param {Object} config The config object
33869  */
33870
33871 Roo.bootstrap.RadioSet = function(config){
33872     
33873     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33874     
33875     this.radioes = [];
33876     
33877     Roo.bootstrap.RadioSet.register(this);
33878     
33879     this.addEvents({
33880         /**
33881         * @event check
33882         * Fires when the element is checked or unchecked.
33883         * @param {Roo.bootstrap.RadioSet} this This radio
33884         * @param {Roo.bootstrap.Radio} item The checked item
33885         */
33886        check : true,
33887        /**
33888         * @event click
33889         * Fires when the element is click.
33890         * @param {Roo.bootstrap.RadioSet} this This radio set
33891         * @param {Roo.bootstrap.Radio} item The checked item
33892         * @param {Roo.EventObject} e The event object
33893         */
33894        click : true
33895     });
33896     
33897 };
33898
33899 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33900
33901     radioes : false,
33902     
33903     inline : true,
33904     
33905     weight : '',
33906     
33907     indicatorpos : 'left',
33908     
33909     getAutoCreate : function()
33910     {
33911         var label = {
33912             tag : 'label',
33913             cls : 'roo-radio-set-label',
33914             cn : [
33915                 {
33916                     tag : 'span',
33917                     html : this.fieldLabel
33918                 }
33919             ]
33920         };
33921         
33922         if(this.indicatorpos == 'left'){
33923             label.cn.unshift({
33924                 tag : 'i',
33925                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33926                 tooltip : 'This field is required'
33927             });
33928         } else {
33929             label.cn.push({
33930                 tag : 'i',
33931                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33932                 tooltip : 'This field is required'
33933             });
33934         }
33935         
33936         var items = {
33937             tag : 'div',
33938             cls : 'roo-radio-set-items'
33939         };
33940         
33941         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33942         
33943         if (align === 'left' && this.fieldLabel.length) {
33944             
33945             items = {
33946                 cls : "roo-radio-set-right", 
33947                 cn: [
33948                     items
33949                 ]
33950             };
33951             
33952             if(this.labelWidth > 12){
33953                 label.style = "width: " + this.labelWidth + 'px';
33954             }
33955             
33956             if(this.labelWidth < 13 && this.labelmd == 0){
33957                 this.labelmd = this.labelWidth;
33958             }
33959             
33960             if(this.labellg > 0){
33961                 label.cls += ' col-lg-' + this.labellg;
33962                 items.cls += ' col-lg-' + (12 - this.labellg);
33963             }
33964             
33965             if(this.labelmd > 0){
33966                 label.cls += ' col-md-' + this.labelmd;
33967                 items.cls += ' col-md-' + (12 - this.labelmd);
33968             }
33969             
33970             if(this.labelsm > 0){
33971                 label.cls += ' col-sm-' + this.labelsm;
33972                 items.cls += ' col-sm-' + (12 - this.labelsm);
33973             }
33974             
33975             if(this.labelxs > 0){
33976                 label.cls += ' col-xs-' + this.labelxs;
33977                 items.cls += ' col-xs-' + (12 - this.labelxs);
33978             }
33979         }
33980         
33981         var cfg = {
33982             tag : 'div',
33983             cls : 'roo-radio-set',
33984             cn : [
33985                 {
33986                     tag : 'input',
33987                     cls : 'roo-radio-set-input',
33988                     type : 'hidden',
33989                     name : this.name,
33990                     value : this.value ? this.value :  ''
33991                 },
33992                 label,
33993                 items
33994             ]
33995         };
33996         
33997         if(this.weight.length){
33998             cfg.cls += ' roo-radio-' + this.weight;
33999         }
34000         
34001         if(this.inline) {
34002             cfg.cls += ' roo-radio-set-inline';
34003         }
34004         
34005         var settings=this;
34006         ['xs','sm','md','lg'].map(function(size){
34007             if (settings[size]) {
34008                 cfg.cls += ' col-' + size + '-' + settings[size];
34009             }
34010         });
34011         
34012         return cfg;
34013         
34014     },
34015
34016     initEvents : function()
34017     {
34018         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34019         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34020         
34021         if(!this.fieldLabel.length){
34022             this.labelEl.hide();
34023         }
34024         
34025         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34026         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34027         
34028         this.indicator = this.indicatorEl();
34029         
34030         if(this.indicator){
34031             this.indicator.addClass('invisible');
34032         }
34033         
34034         this.originalValue = this.getValue();
34035         
34036     },
34037     
34038     inputEl: function ()
34039     {
34040         return this.el.select('.roo-radio-set-input', true).first();
34041     },
34042     
34043     getChildContainer : function()
34044     {
34045         return this.itemsEl;
34046     },
34047     
34048     register : function(item)
34049     {
34050         this.radioes.push(item);
34051         
34052     },
34053     
34054     validate : function()
34055     {   
34056         if(this.getVisibilityEl().hasClass('hidden')){
34057             return true;
34058         }
34059         
34060         var valid = false;
34061         
34062         Roo.each(this.radioes, function(i){
34063             if(!i.checked){
34064                 return;
34065             }
34066             
34067             valid = true;
34068             return false;
34069         });
34070         
34071         if(this.allowBlank) {
34072             return true;
34073         }
34074         
34075         if(this.disabled || valid){
34076             this.markValid();
34077             return true;
34078         }
34079         
34080         this.markInvalid();
34081         return false;
34082         
34083     },
34084     
34085     markValid : function()
34086     {
34087         if(this.labelEl.isVisible(true)){
34088             this.indicatorEl().removeClass('visible');
34089             this.indicatorEl().addClass('invisible');
34090         }
34091         
34092         this.el.removeClass([this.invalidClass, this.validClass]);
34093         this.el.addClass(this.validClass);
34094         
34095         this.fireEvent('valid', this);
34096     },
34097     
34098     markInvalid : function(msg)
34099     {
34100         if(this.allowBlank || this.disabled){
34101             return;
34102         }
34103         
34104         if(this.labelEl.isVisible(true)){
34105             this.indicatorEl().removeClass('invisible');
34106             this.indicatorEl().addClass('visible');
34107         }
34108         
34109         this.el.removeClass([this.invalidClass, this.validClass]);
34110         this.el.addClass(this.invalidClass);
34111         
34112         this.fireEvent('invalid', this, msg);
34113         
34114     },
34115     
34116     setValue : function(v, suppressEvent)
34117     {   
34118         if(this.value === v){
34119             return;
34120         }
34121         
34122         this.value = v;
34123         
34124         if(this.rendered){
34125             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34126         }
34127         
34128         Roo.each(this.radioes, function(i){
34129             i.checked = false;
34130             i.el.removeClass('checked');
34131         });
34132         
34133         Roo.each(this.radioes, function(i){
34134             
34135             if(i.value === v || i.value.toString() === v.toString()){
34136                 i.checked = true;
34137                 i.el.addClass('checked');
34138                 
34139                 if(suppressEvent !== true){
34140                     this.fireEvent('check', this, i);
34141                 }
34142                 
34143                 return false;
34144             }
34145             
34146         }, this);
34147         
34148         this.validate();
34149     },
34150     
34151     clearInvalid : function(){
34152         
34153         if(!this.el || this.preventMark){
34154             return;
34155         }
34156         
34157         this.el.removeClass([this.invalidClass]);
34158         
34159         this.fireEvent('valid', this);
34160     }
34161     
34162 });
34163
34164 Roo.apply(Roo.bootstrap.RadioSet, {
34165     
34166     groups: {},
34167     
34168     register : function(set)
34169     {
34170         this.groups[set.name] = set;
34171     },
34172     
34173     get: function(name) 
34174     {
34175         if (typeof(this.groups[name]) == 'undefined') {
34176             return false;
34177         }
34178         
34179         return this.groups[name] ;
34180     }
34181     
34182 });
34183 /*
34184  * Based on:
34185  * Ext JS Library 1.1.1
34186  * Copyright(c) 2006-2007, Ext JS, LLC.
34187  *
34188  * Originally Released Under LGPL - original licence link has changed is not relivant.
34189  *
34190  * Fork - LGPL
34191  * <script type="text/javascript">
34192  */
34193
34194
34195 /**
34196  * @class Roo.bootstrap.SplitBar
34197  * @extends Roo.util.Observable
34198  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34199  * <br><br>
34200  * Usage:
34201  * <pre><code>
34202 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34203                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34204 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34205 split.minSize = 100;
34206 split.maxSize = 600;
34207 split.animate = true;
34208 split.on('moved', splitterMoved);
34209 </code></pre>
34210  * @constructor
34211  * Create a new SplitBar
34212  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34213  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34214  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34215  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34216                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34217                         position of the SplitBar).
34218  */
34219 Roo.bootstrap.SplitBar = function(cfg){
34220     
34221     /** @private */
34222     
34223     //{
34224     //  dragElement : elm
34225     //  resizingElement: el,
34226         // optional..
34227     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34228     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34229         // existingProxy ???
34230     //}
34231     
34232     this.el = Roo.get(cfg.dragElement, true);
34233     this.el.dom.unselectable = "on";
34234     /** @private */
34235     this.resizingEl = Roo.get(cfg.resizingElement, true);
34236
34237     /**
34238      * @private
34239      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34240      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34241      * @type Number
34242      */
34243     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34244     
34245     /**
34246      * The minimum size of the resizing element. (Defaults to 0)
34247      * @type Number
34248      */
34249     this.minSize = 0;
34250     
34251     /**
34252      * The maximum size of the resizing element. (Defaults to 2000)
34253      * @type Number
34254      */
34255     this.maxSize = 2000;
34256     
34257     /**
34258      * Whether to animate the transition to the new size
34259      * @type Boolean
34260      */
34261     this.animate = false;
34262     
34263     /**
34264      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34265      * @type Boolean
34266      */
34267     this.useShim = false;
34268     
34269     /** @private */
34270     this.shim = null;
34271     
34272     if(!cfg.existingProxy){
34273         /** @private */
34274         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34275     }else{
34276         this.proxy = Roo.get(cfg.existingProxy).dom;
34277     }
34278     /** @private */
34279     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34280     
34281     /** @private */
34282     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34283     
34284     /** @private */
34285     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34286     
34287     /** @private */
34288     this.dragSpecs = {};
34289     
34290     /**
34291      * @private The adapter to use to positon and resize elements
34292      */
34293     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34294     this.adapter.init(this);
34295     
34296     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34297         /** @private */
34298         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34299         this.el.addClass("roo-splitbar-h");
34300     }else{
34301         /** @private */
34302         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34303         this.el.addClass("roo-splitbar-v");
34304     }
34305     
34306     this.addEvents({
34307         /**
34308          * @event resize
34309          * Fires when the splitter is moved (alias for {@link #event-moved})
34310          * @param {Roo.bootstrap.SplitBar} this
34311          * @param {Number} newSize the new width or height
34312          */
34313         "resize" : true,
34314         /**
34315          * @event moved
34316          * Fires when the splitter is moved
34317          * @param {Roo.bootstrap.SplitBar} this
34318          * @param {Number} newSize the new width or height
34319          */
34320         "moved" : true,
34321         /**
34322          * @event beforeresize
34323          * Fires before the splitter is dragged
34324          * @param {Roo.bootstrap.SplitBar} this
34325          */
34326         "beforeresize" : true,
34327
34328         "beforeapply" : true
34329     });
34330
34331     Roo.util.Observable.call(this);
34332 };
34333
34334 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34335     onStartProxyDrag : function(x, y){
34336         this.fireEvent("beforeresize", this);
34337         if(!this.overlay){
34338             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34339             o.unselectable();
34340             o.enableDisplayMode("block");
34341             // all splitbars share the same overlay
34342             Roo.bootstrap.SplitBar.prototype.overlay = o;
34343         }
34344         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34345         this.overlay.show();
34346         Roo.get(this.proxy).setDisplayed("block");
34347         var size = this.adapter.getElementSize(this);
34348         this.activeMinSize = this.getMinimumSize();;
34349         this.activeMaxSize = this.getMaximumSize();;
34350         var c1 = size - this.activeMinSize;
34351         var c2 = Math.max(this.activeMaxSize - size, 0);
34352         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34353             this.dd.resetConstraints();
34354             this.dd.setXConstraint(
34355                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34356                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34357             );
34358             this.dd.setYConstraint(0, 0);
34359         }else{
34360             this.dd.resetConstraints();
34361             this.dd.setXConstraint(0, 0);
34362             this.dd.setYConstraint(
34363                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34364                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34365             );
34366          }
34367         this.dragSpecs.startSize = size;
34368         this.dragSpecs.startPoint = [x, y];
34369         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34370     },
34371     
34372     /** 
34373      * @private Called after the drag operation by the DDProxy
34374      */
34375     onEndProxyDrag : function(e){
34376         Roo.get(this.proxy).setDisplayed(false);
34377         var endPoint = Roo.lib.Event.getXY(e);
34378         if(this.overlay){
34379             this.overlay.hide();
34380         }
34381         var newSize;
34382         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34383             newSize = this.dragSpecs.startSize + 
34384                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34385                     endPoint[0] - this.dragSpecs.startPoint[0] :
34386                     this.dragSpecs.startPoint[0] - endPoint[0]
34387                 );
34388         }else{
34389             newSize = this.dragSpecs.startSize + 
34390                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34391                     endPoint[1] - this.dragSpecs.startPoint[1] :
34392                     this.dragSpecs.startPoint[1] - endPoint[1]
34393                 );
34394         }
34395         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34396         if(newSize != this.dragSpecs.startSize){
34397             if(this.fireEvent('beforeapply', this, newSize) !== false){
34398                 this.adapter.setElementSize(this, newSize);
34399                 this.fireEvent("moved", this, newSize);
34400                 this.fireEvent("resize", this, newSize);
34401             }
34402         }
34403     },
34404     
34405     /**
34406      * Get the adapter this SplitBar uses
34407      * @return The adapter object
34408      */
34409     getAdapter : function(){
34410         return this.adapter;
34411     },
34412     
34413     /**
34414      * Set the adapter this SplitBar uses
34415      * @param {Object} adapter A SplitBar adapter object
34416      */
34417     setAdapter : function(adapter){
34418         this.adapter = adapter;
34419         this.adapter.init(this);
34420     },
34421     
34422     /**
34423      * Gets the minimum size for the resizing element
34424      * @return {Number} The minimum size
34425      */
34426     getMinimumSize : function(){
34427         return this.minSize;
34428     },
34429     
34430     /**
34431      * Sets the minimum size for the resizing element
34432      * @param {Number} minSize The minimum size
34433      */
34434     setMinimumSize : function(minSize){
34435         this.minSize = minSize;
34436     },
34437     
34438     /**
34439      * Gets the maximum size for the resizing element
34440      * @return {Number} The maximum size
34441      */
34442     getMaximumSize : function(){
34443         return this.maxSize;
34444     },
34445     
34446     /**
34447      * Sets the maximum size for the resizing element
34448      * @param {Number} maxSize The maximum size
34449      */
34450     setMaximumSize : function(maxSize){
34451         this.maxSize = maxSize;
34452     },
34453     
34454     /**
34455      * Sets the initialize size for the resizing element
34456      * @param {Number} size The initial size
34457      */
34458     setCurrentSize : function(size){
34459         var oldAnimate = this.animate;
34460         this.animate = false;
34461         this.adapter.setElementSize(this, size);
34462         this.animate = oldAnimate;
34463     },
34464     
34465     /**
34466      * Destroy this splitbar. 
34467      * @param {Boolean} removeEl True to remove the element
34468      */
34469     destroy : function(removeEl){
34470         if(this.shim){
34471             this.shim.remove();
34472         }
34473         this.dd.unreg();
34474         this.proxy.parentNode.removeChild(this.proxy);
34475         if(removeEl){
34476             this.el.remove();
34477         }
34478     }
34479 });
34480
34481 /**
34482  * @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.
34483  */
34484 Roo.bootstrap.SplitBar.createProxy = function(dir){
34485     var proxy = new Roo.Element(document.createElement("div"));
34486     proxy.unselectable();
34487     var cls = 'roo-splitbar-proxy';
34488     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34489     document.body.appendChild(proxy.dom);
34490     return proxy.dom;
34491 };
34492
34493 /** 
34494  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34495  * Default Adapter. It assumes the splitter and resizing element are not positioned
34496  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34497  */
34498 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34499 };
34500
34501 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34502     // do nothing for now
34503     init : function(s){
34504     
34505     },
34506     /**
34507      * Called before drag operations to get the current size of the resizing element. 
34508      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34509      */
34510      getElementSize : function(s){
34511         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34512             return s.resizingEl.getWidth();
34513         }else{
34514             return s.resizingEl.getHeight();
34515         }
34516     },
34517     
34518     /**
34519      * Called after drag operations to set the size of the resizing element.
34520      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34521      * @param {Number} newSize The new size to set
34522      * @param {Function} onComplete A function to be invoked when resizing is complete
34523      */
34524     setElementSize : function(s, newSize, onComplete){
34525         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34526             if(!s.animate){
34527                 s.resizingEl.setWidth(newSize);
34528                 if(onComplete){
34529                     onComplete(s, newSize);
34530                 }
34531             }else{
34532                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34533             }
34534         }else{
34535             
34536             if(!s.animate){
34537                 s.resizingEl.setHeight(newSize);
34538                 if(onComplete){
34539                     onComplete(s, newSize);
34540                 }
34541             }else{
34542                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34543             }
34544         }
34545     }
34546 };
34547
34548 /** 
34549  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34550  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34551  * Adapter that  moves the splitter element to align with the resized sizing element. 
34552  * Used with an absolute positioned SplitBar.
34553  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34554  * document.body, make sure you assign an id to the body element.
34555  */
34556 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34557     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34558     this.container = Roo.get(container);
34559 };
34560
34561 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34562     init : function(s){
34563         this.basic.init(s);
34564     },
34565     
34566     getElementSize : function(s){
34567         return this.basic.getElementSize(s);
34568     },
34569     
34570     setElementSize : function(s, newSize, onComplete){
34571         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34572     },
34573     
34574     moveSplitter : function(s){
34575         var yes = Roo.bootstrap.SplitBar;
34576         switch(s.placement){
34577             case yes.LEFT:
34578                 s.el.setX(s.resizingEl.getRight());
34579                 break;
34580             case yes.RIGHT:
34581                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34582                 break;
34583             case yes.TOP:
34584                 s.el.setY(s.resizingEl.getBottom());
34585                 break;
34586             case yes.BOTTOM:
34587                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34588                 break;
34589         }
34590     }
34591 };
34592
34593 /**
34594  * Orientation constant - Create a vertical SplitBar
34595  * @static
34596  * @type Number
34597  */
34598 Roo.bootstrap.SplitBar.VERTICAL = 1;
34599
34600 /**
34601  * Orientation constant - Create a horizontal SplitBar
34602  * @static
34603  * @type Number
34604  */
34605 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34606
34607 /**
34608  * Placement constant - The resizing element is to the left of the splitter element
34609  * @static
34610  * @type Number
34611  */
34612 Roo.bootstrap.SplitBar.LEFT = 1;
34613
34614 /**
34615  * Placement constant - The resizing element is to the right of the splitter element
34616  * @static
34617  * @type Number
34618  */
34619 Roo.bootstrap.SplitBar.RIGHT = 2;
34620
34621 /**
34622  * Placement constant - The resizing element is positioned above the splitter element
34623  * @static
34624  * @type Number
34625  */
34626 Roo.bootstrap.SplitBar.TOP = 3;
34627
34628 /**
34629  * Placement constant - The resizing element is positioned under splitter element
34630  * @static
34631  * @type Number
34632  */
34633 Roo.bootstrap.SplitBar.BOTTOM = 4;
34634 Roo.namespace("Roo.bootstrap.layout");/*
34635  * Based on:
34636  * Ext JS Library 1.1.1
34637  * Copyright(c) 2006-2007, Ext JS, LLC.
34638  *
34639  * Originally Released Under LGPL - original licence link has changed is not relivant.
34640  *
34641  * Fork - LGPL
34642  * <script type="text/javascript">
34643  */
34644
34645 /**
34646  * @class Roo.bootstrap.layout.Manager
34647  * @extends Roo.bootstrap.Component
34648  * Base class for layout managers.
34649  */
34650 Roo.bootstrap.layout.Manager = function(config)
34651 {
34652     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34653
34654
34655
34656
34657
34658     /** false to disable window resize monitoring @type Boolean */
34659     this.monitorWindowResize = true;
34660     this.regions = {};
34661     this.addEvents({
34662         /**
34663          * @event layout
34664          * Fires when a layout is performed.
34665          * @param {Roo.LayoutManager} this
34666          */
34667         "layout" : true,
34668         /**
34669          * @event regionresized
34670          * Fires when the user resizes a region.
34671          * @param {Roo.LayoutRegion} region The resized region
34672          * @param {Number} newSize The new size (width for east/west, height for north/south)
34673          */
34674         "regionresized" : true,
34675         /**
34676          * @event regioncollapsed
34677          * Fires when a region is collapsed.
34678          * @param {Roo.LayoutRegion} region The collapsed region
34679          */
34680         "regioncollapsed" : true,
34681         /**
34682          * @event regionexpanded
34683          * Fires when a region is expanded.
34684          * @param {Roo.LayoutRegion} region The expanded region
34685          */
34686         "regionexpanded" : true
34687     });
34688     this.updating = false;
34689
34690     if (config.el) {
34691         this.el = Roo.get(config.el);
34692         this.initEvents();
34693     }
34694
34695 };
34696
34697 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34698
34699
34700     regions : null,
34701
34702     monitorWindowResize : true,
34703
34704
34705     updating : false,
34706
34707
34708     onRender : function(ct, position)
34709     {
34710         if(!this.el){
34711             this.el = Roo.get(ct);
34712             this.initEvents();
34713         }
34714         //this.fireEvent('render',this);
34715     },
34716
34717
34718     initEvents: function()
34719     {
34720
34721
34722         // ie scrollbar fix
34723         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34724             document.body.scroll = "no";
34725         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34726             this.el.position('relative');
34727         }
34728         this.id = this.el.id;
34729         this.el.addClass("roo-layout-container");
34730         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34731         if(this.el.dom != document.body ) {
34732             this.el.on('resize', this.layout,this);
34733             this.el.on('show', this.layout,this);
34734         }
34735
34736     },
34737
34738     /**
34739      * Returns true if this layout is currently being updated
34740      * @return {Boolean}
34741      */
34742     isUpdating : function(){
34743         return this.updating;
34744     },
34745
34746     /**
34747      * Suspend the LayoutManager from doing auto-layouts while
34748      * making multiple add or remove calls
34749      */
34750     beginUpdate : function(){
34751         this.updating = true;
34752     },
34753
34754     /**
34755      * Restore auto-layouts and optionally disable the manager from performing a layout
34756      * @param {Boolean} noLayout true to disable a layout update
34757      */
34758     endUpdate : function(noLayout){
34759         this.updating = false;
34760         if(!noLayout){
34761             this.layout();
34762         }
34763     },
34764
34765     layout: function(){
34766         // abstract...
34767     },
34768
34769     onRegionResized : function(region, newSize){
34770         this.fireEvent("regionresized", region, newSize);
34771         this.layout();
34772     },
34773
34774     onRegionCollapsed : function(region){
34775         this.fireEvent("regioncollapsed", region);
34776     },
34777
34778     onRegionExpanded : function(region){
34779         this.fireEvent("regionexpanded", region);
34780     },
34781
34782     /**
34783      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34784      * performs box-model adjustments.
34785      * @return {Object} The size as an object {width: (the width), height: (the height)}
34786      */
34787     getViewSize : function()
34788     {
34789         var size;
34790         if(this.el.dom != document.body){
34791             size = this.el.getSize();
34792         }else{
34793             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34794         }
34795         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34796         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34797         return size;
34798     },
34799
34800     /**
34801      * Returns the Element this layout is bound to.
34802      * @return {Roo.Element}
34803      */
34804     getEl : function(){
34805         return this.el;
34806     },
34807
34808     /**
34809      * Returns the specified region.
34810      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34811      * @return {Roo.LayoutRegion}
34812      */
34813     getRegion : function(target){
34814         return this.regions[target.toLowerCase()];
34815     },
34816
34817     onWindowResize : function(){
34818         if(this.monitorWindowResize){
34819             this.layout();
34820         }
34821     }
34822 });
34823 /*
34824  * Based on:
34825  * Ext JS Library 1.1.1
34826  * Copyright(c) 2006-2007, Ext JS, LLC.
34827  *
34828  * Originally Released Under LGPL - original licence link has changed is not relivant.
34829  *
34830  * Fork - LGPL
34831  * <script type="text/javascript">
34832  */
34833 /**
34834  * @class Roo.bootstrap.layout.Border
34835  * @extends Roo.bootstrap.layout.Manager
34836  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34837  * please see: examples/bootstrap/nested.html<br><br>
34838  
34839 <b>The container the layout is rendered into can be either the body element or any other element.
34840 If it is not the body element, the container needs to either be an absolute positioned element,
34841 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34842 the container size if it is not the body element.</b>
34843
34844 * @constructor
34845 * Create a new Border
34846 * @param {Object} config Configuration options
34847  */
34848 Roo.bootstrap.layout.Border = function(config){
34849     config = config || {};
34850     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34851     
34852     
34853     
34854     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34855         if(config[region]){
34856             config[region].region = region;
34857             this.addRegion(config[region]);
34858         }
34859     },this);
34860     
34861 };
34862
34863 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34864
34865 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34866     /**
34867      * Creates and adds a new region if it doesn't already exist.
34868      * @param {String} target The target region key (north, south, east, west or center).
34869      * @param {Object} config The regions config object
34870      * @return {BorderLayoutRegion} The new region
34871      */
34872     addRegion : function(config)
34873     {
34874         if(!this.regions[config.region]){
34875             var r = this.factory(config);
34876             this.bindRegion(r);
34877         }
34878         return this.regions[config.region];
34879     },
34880
34881     // private (kinda)
34882     bindRegion : function(r){
34883         this.regions[r.config.region] = r;
34884         
34885         r.on("visibilitychange",    this.layout, this);
34886         r.on("paneladded",          this.layout, this);
34887         r.on("panelremoved",        this.layout, this);
34888         r.on("invalidated",         this.layout, this);
34889         r.on("resized",             this.onRegionResized, this);
34890         r.on("collapsed",           this.onRegionCollapsed, this);
34891         r.on("expanded",            this.onRegionExpanded, this);
34892     },
34893
34894     /**
34895      * Performs a layout update.
34896      */
34897     layout : function()
34898     {
34899         if(this.updating) {
34900             return;
34901         }
34902         
34903         // render all the rebions if they have not been done alreayd?
34904         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34905             if(this.regions[region] && !this.regions[region].bodyEl){
34906                 this.regions[region].onRender(this.el)
34907             }
34908         },this);
34909         
34910         var size = this.getViewSize();
34911         var w = size.width;
34912         var h = size.height;
34913         var centerW = w;
34914         var centerH = h;
34915         var centerY = 0;
34916         var centerX = 0;
34917         //var x = 0, y = 0;
34918
34919         var rs = this.regions;
34920         var north = rs["north"];
34921         var south = rs["south"]; 
34922         var west = rs["west"];
34923         var east = rs["east"];
34924         var center = rs["center"];
34925         //if(this.hideOnLayout){ // not supported anymore
34926             //c.el.setStyle("display", "none");
34927         //}
34928         if(north && north.isVisible()){
34929             var b = north.getBox();
34930             var m = north.getMargins();
34931             b.width = w - (m.left+m.right);
34932             b.x = m.left;
34933             b.y = m.top;
34934             centerY = b.height + b.y + m.bottom;
34935             centerH -= centerY;
34936             north.updateBox(this.safeBox(b));
34937         }
34938         if(south && south.isVisible()){
34939             var b = south.getBox();
34940             var m = south.getMargins();
34941             b.width = w - (m.left+m.right);
34942             b.x = m.left;
34943             var totalHeight = (b.height + m.top + m.bottom);
34944             b.y = h - totalHeight + m.top;
34945             centerH -= totalHeight;
34946             south.updateBox(this.safeBox(b));
34947         }
34948         if(west && west.isVisible()){
34949             var b = west.getBox();
34950             var m = west.getMargins();
34951             b.height = centerH - (m.top+m.bottom);
34952             b.x = m.left;
34953             b.y = centerY + m.top;
34954             var totalWidth = (b.width + m.left + m.right);
34955             centerX += totalWidth;
34956             centerW -= totalWidth;
34957             west.updateBox(this.safeBox(b));
34958         }
34959         if(east && east.isVisible()){
34960             var b = east.getBox();
34961             var m = east.getMargins();
34962             b.height = centerH - (m.top+m.bottom);
34963             var totalWidth = (b.width + m.left + m.right);
34964             b.x = w - totalWidth + m.left;
34965             b.y = centerY + m.top;
34966             centerW -= totalWidth;
34967             east.updateBox(this.safeBox(b));
34968         }
34969         if(center){
34970             var m = center.getMargins();
34971             var centerBox = {
34972                 x: centerX + m.left,
34973                 y: centerY + m.top,
34974                 width: centerW - (m.left+m.right),
34975                 height: centerH - (m.top+m.bottom)
34976             };
34977             //if(this.hideOnLayout){
34978                 //center.el.setStyle("display", "block");
34979             //}
34980             center.updateBox(this.safeBox(centerBox));
34981         }
34982         this.el.repaint();
34983         this.fireEvent("layout", this);
34984     },
34985
34986     // private
34987     safeBox : function(box){
34988         box.width = Math.max(0, box.width);
34989         box.height = Math.max(0, box.height);
34990         return box;
34991     },
34992
34993     /**
34994      * Adds a ContentPanel (or subclass) to this layout.
34995      * @param {String} target The target region key (north, south, east, west or center).
34996      * @param {Roo.ContentPanel} panel The panel to add
34997      * @return {Roo.ContentPanel} The added panel
34998      */
34999     add : function(target, panel){
35000          
35001         target = target.toLowerCase();
35002         return this.regions[target].add(panel);
35003     },
35004
35005     /**
35006      * Remove a ContentPanel (or subclass) to this layout.
35007      * @param {String} target The target region key (north, south, east, west or center).
35008      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35009      * @return {Roo.ContentPanel} The removed panel
35010      */
35011     remove : function(target, panel){
35012         target = target.toLowerCase();
35013         return this.regions[target].remove(panel);
35014     },
35015
35016     /**
35017      * Searches all regions for a panel with the specified id
35018      * @param {String} panelId
35019      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35020      */
35021     findPanel : function(panelId){
35022         var rs = this.regions;
35023         for(var target in rs){
35024             if(typeof rs[target] != "function"){
35025                 var p = rs[target].getPanel(panelId);
35026                 if(p){
35027                     return p;
35028                 }
35029             }
35030         }
35031         return null;
35032     },
35033
35034     /**
35035      * Searches all regions for a panel with the specified id and activates (shows) it.
35036      * @param {String/ContentPanel} panelId The panels id or the panel itself
35037      * @return {Roo.ContentPanel} The shown panel or null
35038      */
35039     showPanel : function(panelId) {
35040       var rs = this.regions;
35041       for(var target in rs){
35042          var r = rs[target];
35043          if(typeof r != "function"){
35044             if(r.hasPanel(panelId)){
35045                return r.showPanel(panelId);
35046             }
35047          }
35048       }
35049       return null;
35050    },
35051
35052    /**
35053      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35054      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35055      */
35056    /*
35057     restoreState : function(provider){
35058         if(!provider){
35059             provider = Roo.state.Manager;
35060         }
35061         var sm = new Roo.LayoutStateManager();
35062         sm.init(this, provider);
35063     },
35064 */
35065  
35066  
35067     /**
35068      * Adds a xtype elements to the layout.
35069      * <pre><code>
35070
35071 layout.addxtype({
35072        xtype : 'ContentPanel',
35073        region: 'west',
35074        items: [ .... ]
35075    }
35076 );
35077
35078 layout.addxtype({
35079         xtype : 'NestedLayoutPanel',
35080         region: 'west',
35081         layout: {
35082            center: { },
35083            west: { }   
35084         },
35085         items : [ ... list of content panels or nested layout panels.. ]
35086    }
35087 );
35088 </code></pre>
35089      * @param {Object} cfg Xtype definition of item to add.
35090      */
35091     addxtype : function(cfg)
35092     {
35093         // basically accepts a pannel...
35094         // can accept a layout region..!?!?
35095         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35096         
35097         
35098         // theory?  children can only be panels??
35099         
35100         //if (!cfg.xtype.match(/Panel$/)) {
35101         //    return false;
35102         //}
35103         var ret = false;
35104         
35105         if (typeof(cfg.region) == 'undefined') {
35106             Roo.log("Failed to add Panel, region was not set");
35107             Roo.log(cfg);
35108             return false;
35109         }
35110         var region = cfg.region;
35111         delete cfg.region;
35112         
35113           
35114         var xitems = [];
35115         if (cfg.items) {
35116             xitems = cfg.items;
35117             delete cfg.items;
35118         }
35119         var nb = false;
35120         
35121         switch(cfg.xtype) 
35122         {
35123             case 'Content':  // ContentPanel (el, cfg)
35124             case 'Scroll':  // ContentPanel (el, cfg)
35125             case 'View': 
35126                 cfg.autoCreate = true;
35127                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35128                 //} else {
35129                 //    var el = this.el.createChild();
35130                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35131                 //}
35132                 
35133                 this.add(region, ret);
35134                 break;
35135             
35136             /*
35137             case 'TreePanel': // our new panel!
35138                 cfg.el = this.el.createChild();
35139                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35140                 this.add(region, ret);
35141                 break;
35142             */
35143             
35144             case 'Nest': 
35145                 // create a new Layout (which is  a Border Layout...
35146                 
35147                 var clayout = cfg.layout;
35148                 clayout.el  = this.el.createChild();
35149                 clayout.items   = clayout.items  || [];
35150                 
35151                 delete cfg.layout;
35152                 
35153                 // replace this exitems with the clayout ones..
35154                 xitems = clayout.items;
35155                  
35156                 // force background off if it's in center...
35157                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35158                     cfg.background = false;
35159                 }
35160                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35161                 
35162                 
35163                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35164                 //console.log('adding nested layout panel '  + cfg.toSource());
35165                 this.add(region, ret);
35166                 nb = {}; /// find first...
35167                 break;
35168             
35169             case 'Grid':
35170                 
35171                 // needs grid and region
35172                 
35173                 //var el = this.getRegion(region).el.createChild();
35174                 /*
35175                  *var el = this.el.createChild();
35176                 // create the grid first...
35177                 cfg.grid.container = el;
35178                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35179                 */
35180                 
35181                 if (region == 'center' && this.active ) {
35182                     cfg.background = false;
35183                 }
35184                 
35185                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35186                 
35187                 this.add(region, ret);
35188                 /*
35189                 if (cfg.background) {
35190                     // render grid on panel activation (if panel background)
35191                     ret.on('activate', function(gp) {
35192                         if (!gp.grid.rendered) {
35193                     //        gp.grid.render(el);
35194                         }
35195                     });
35196                 } else {
35197                   //  cfg.grid.render(el);
35198                 }
35199                 */
35200                 break;
35201            
35202            
35203             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35204                 // it was the old xcomponent building that caused this before.
35205                 // espeically if border is the top element in the tree.
35206                 ret = this;
35207                 break; 
35208                 
35209                     
35210                 
35211                 
35212                 
35213             default:
35214                 /*
35215                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35216                     
35217                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35218                     this.add(region, ret);
35219                 } else {
35220                 */
35221                     Roo.log(cfg);
35222                     throw "Can not add '" + cfg.xtype + "' to Border";
35223                     return null;
35224              
35225                                 
35226              
35227         }
35228         this.beginUpdate();
35229         // add children..
35230         var region = '';
35231         var abn = {};
35232         Roo.each(xitems, function(i)  {
35233             region = nb && i.region ? i.region : false;
35234             
35235             var add = ret.addxtype(i);
35236            
35237             if (region) {
35238                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35239                 if (!i.background) {
35240                     abn[region] = nb[region] ;
35241                 }
35242             }
35243             
35244         });
35245         this.endUpdate();
35246
35247         // make the last non-background panel active..
35248         //if (nb) { Roo.log(abn); }
35249         if (nb) {
35250             
35251             for(var r in abn) {
35252                 region = this.getRegion(r);
35253                 if (region) {
35254                     // tried using nb[r], but it does not work..
35255                      
35256                     region.showPanel(abn[r]);
35257                    
35258                 }
35259             }
35260         }
35261         return ret;
35262         
35263     },
35264     
35265     
35266 // private
35267     factory : function(cfg)
35268     {
35269         
35270         var validRegions = Roo.bootstrap.layout.Border.regions;
35271
35272         var target = cfg.region;
35273         cfg.mgr = this;
35274         
35275         var r = Roo.bootstrap.layout;
35276         Roo.log(target);
35277         switch(target){
35278             case "north":
35279                 return new r.North(cfg);
35280             case "south":
35281                 return new r.South(cfg);
35282             case "east":
35283                 return new r.East(cfg);
35284             case "west":
35285                 return new r.West(cfg);
35286             case "center":
35287                 return new r.Center(cfg);
35288         }
35289         throw 'Layout region "'+target+'" not supported.';
35290     }
35291     
35292     
35293 });
35294  /*
35295  * Based on:
35296  * Ext JS Library 1.1.1
35297  * Copyright(c) 2006-2007, Ext JS, LLC.
35298  *
35299  * Originally Released Under LGPL - original licence link has changed is not relivant.
35300  *
35301  * Fork - LGPL
35302  * <script type="text/javascript">
35303  */
35304  
35305 /**
35306  * @class Roo.bootstrap.layout.Basic
35307  * @extends Roo.util.Observable
35308  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35309  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35310  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35311  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35312  * @cfg {string}   region  the region that it inhabits..
35313  * @cfg {bool}   skipConfig skip config?
35314  * 
35315
35316  */
35317 Roo.bootstrap.layout.Basic = function(config){
35318     
35319     this.mgr = config.mgr;
35320     
35321     this.position = config.region;
35322     
35323     var skipConfig = config.skipConfig;
35324     
35325     this.events = {
35326         /**
35327          * @scope Roo.BasicLayoutRegion
35328          */
35329         
35330         /**
35331          * @event beforeremove
35332          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35333          * @param {Roo.LayoutRegion} this
35334          * @param {Roo.ContentPanel} panel The panel
35335          * @param {Object} e The cancel event object
35336          */
35337         "beforeremove" : true,
35338         /**
35339          * @event invalidated
35340          * Fires when the layout for this region is changed.
35341          * @param {Roo.LayoutRegion} this
35342          */
35343         "invalidated" : true,
35344         /**
35345          * @event visibilitychange
35346          * Fires when this region is shown or hidden 
35347          * @param {Roo.LayoutRegion} this
35348          * @param {Boolean} visibility true or false
35349          */
35350         "visibilitychange" : true,
35351         /**
35352          * @event paneladded
35353          * Fires when a panel is added. 
35354          * @param {Roo.LayoutRegion} this
35355          * @param {Roo.ContentPanel} panel The panel
35356          */
35357         "paneladded" : true,
35358         /**
35359          * @event panelremoved
35360          * Fires when a panel is removed. 
35361          * @param {Roo.LayoutRegion} this
35362          * @param {Roo.ContentPanel} panel The panel
35363          */
35364         "panelremoved" : true,
35365         /**
35366          * @event beforecollapse
35367          * Fires when this region before collapse.
35368          * @param {Roo.LayoutRegion} this
35369          */
35370         "beforecollapse" : true,
35371         /**
35372          * @event collapsed
35373          * Fires when this region is collapsed.
35374          * @param {Roo.LayoutRegion} this
35375          */
35376         "collapsed" : true,
35377         /**
35378          * @event expanded
35379          * Fires when this region is expanded.
35380          * @param {Roo.LayoutRegion} this
35381          */
35382         "expanded" : true,
35383         /**
35384          * @event slideshow
35385          * Fires when this region is slid into view.
35386          * @param {Roo.LayoutRegion} this
35387          */
35388         "slideshow" : true,
35389         /**
35390          * @event slidehide
35391          * Fires when this region slides out of view. 
35392          * @param {Roo.LayoutRegion} this
35393          */
35394         "slidehide" : true,
35395         /**
35396          * @event panelactivated
35397          * Fires when a panel is activated. 
35398          * @param {Roo.LayoutRegion} this
35399          * @param {Roo.ContentPanel} panel The activated panel
35400          */
35401         "panelactivated" : true,
35402         /**
35403          * @event resized
35404          * Fires when the user resizes this region. 
35405          * @param {Roo.LayoutRegion} this
35406          * @param {Number} newSize The new size (width for east/west, height for north/south)
35407          */
35408         "resized" : true
35409     };
35410     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35411     this.panels = new Roo.util.MixedCollection();
35412     this.panels.getKey = this.getPanelId.createDelegate(this);
35413     this.box = null;
35414     this.activePanel = null;
35415     // ensure listeners are added...
35416     
35417     if (config.listeners || config.events) {
35418         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35419             listeners : config.listeners || {},
35420             events : config.events || {}
35421         });
35422     }
35423     
35424     if(skipConfig !== true){
35425         this.applyConfig(config);
35426     }
35427 };
35428
35429 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35430 {
35431     getPanelId : function(p){
35432         return p.getId();
35433     },
35434     
35435     applyConfig : function(config){
35436         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35437         this.config = config;
35438         
35439     },
35440     
35441     /**
35442      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35443      * the width, for horizontal (north, south) the height.
35444      * @param {Number} newSize The new width or height
35445      */
35446     resizeTo : function(newSize){
35447         var el = this.el ? this.el :
35448                  (this.activePanel ? this.activePanel.getEl() : null);
35449         if(el){
35450             switch(this.position){
35451                 case "east":
35452                 case "west":
35453                     el.setWidth(newSize);
35454                     this.fireEvent("resized", this, newSize);
35455                 break;
35456                 case "north":
35457                 case "south":
35458                     el.setHeight(newSize);
35459                     this.fireEvent("resized", this, newSize);
35460                 break;                
35461             }
35462         }
35463     },
35464     
35465     getBox : function(){
35466         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35467     },
35468     
35469     getMargins : function(){
35470         return this.margins;
35471     },
35472     
35473     updateBox : function(box){
35474         this.box = box;
35475         var el = this.activePanel.getEl();
35476         el.dom.style.left = box.x + "px";
35477         el.dom.style.top = box.y + "px";
35478         this.activePanel.setSize(box.width, box.height);
35479     },
35480     
35481     /**
35482      * Returns the container element for this region.
35483      * @return {Roo.Element}
35484      */
35485     getEl : function(){
35486         return this.activePanel;
35487     },
35488     
35489     /**
35490      * Returns true if this region is currently visible.
35491      * @return {Boolean}
35492      */
35493     isVisible : function(){
35494         return this.activePanel ? true : false;
35495     },
35496     
35497     setActivePanel : function(panel){
35498         panel = this.getPanel(panel);
35499         if(this.activePanel && this.activePanel != panel){
35500             this.activePanel.setActiveState(false);
35501             this.activePanel.getEl().setLeftTop(-10000,-10000);
35502         }
35503         this.activePanel = panel;
35504         panel.setActiveState(true);
35505         if(this.box){
35506             panel.setSize(this.box.width, this.box.height);
35507         }
35508         this.fireEvent("panelactivated", this, panel);
35509         this.fireEvent("invalidated");
35510     },
35511     
35512     /**
35513      * Show the specified panel.
35514      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35515      * @return {Roo.ContentPanel} The shown panel or null
35516      */
35517     showPanel : function(panel){
35518         panel = this.getPanel(panel);
35519         if(panel){
35520             this.setActivePanel(panel);
35521         }
35522         return panel;
35523     },
35524     
35525     /**
35526      * Get the active panel for this region.
35527      * @return {Roo.ContentPanel} The active panel or null
35528      */
35529     getActivePanel : function(){
35530         return this.activePanel;
35531     },
35532     
35533     /**
35534      * Add the passed ContentPanel(s)
35535      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35536      * @return {Roo.ContentPanel} The panel added (if only one was added)
35537      */
35538     add : function(panel){
35539         if(arguments.length > 1){
35540             for(var i = 0, len = arguments.length; i < len; i++) {
35541                 this.add(arguments[i]);
35542             }
35543             return null;
35544         }
35545         if(this.hasPanel(panel)){
35546             this.showPanel(panel);
35547             return panel;
35548         }
35549         var el = panel.getEl();
35550         if(el.dom.parentNode != this.mgr.el.dom){
35551             this.mgr.el.dom.appendChild(el.dom);
35552         }
35553         if(panel.setRegion){
35554             panel.setRegion(this);
35555         }
35556         this.panels.add(panel);
35557         el.setStyle("position", "absolute");
35558         if(!panel.background){
35559             this.setActivePanel(panel);
35560             if(this.config.initialSize && this.panels.getCount()==1){
35561                 this.resizeTo(this.config.initialSize);
35562             }
35563         }
35564         this.fireEvent("paneladded", this, panel);
35565         return panel;
35566     },
35567     
35568     /**
35569      * Returns true if the panel is in this region.
35570      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35571      * @return {Boolean}
35572      */
35573     hasPanel : function(panel){
35574         if(typeof panel == "object"){ // must be panel obj
35575             panel = panel.getId();
35576         }
35577         return this.getPanel(panel) ? true : false;
35578     },
35579     
35580     /**
35581      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35582      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35583      * @param {Boolean} preservePanel Overrides the config preservePanel option
35584      * @return {Roo.ContentPanel} The panel that was removed
35585      */
35586     remove : function(panel, preservePanel){
35587         panel = this.getPanel(panel);
35588         if(!panel){
35589             return null;
35590         }
35591         var e = {};
35592         this.fireEvent("beforeremove", this, panel, e);
35593         if(e.cancel === true){
35594             return null;
35595         }
35596         var panelId = panel.getId();
35597         this.panels.removeKey(panelId);
35598         return panel;
35599     },
35600     
35601     /**
35602      * Returns the panel specified or null if it's not in this region.
35603      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35604      * @return {Roo.ContentPanel}
35605      */
35606     getPanel : function(id){
35607         if(typeof id == "object"){ // must be panel obj
35608             return id;
35609         }
35610         return this.panels.get(id);
35611     },
35612     
35613     /**
35614      * Returns this regions position (north/south/east/west/center).
35615      * @return {String} 
35616      */
35617     getPosition: function(){
35618         return this.position;    
35619     }
35620 });/*
35621  * Based on:
35622  * Ext JS Library 1.1.1
35623  * Copyright(c) 2006-2007, Ext JS, LLC.
35624  *
35625  * Originally Released Under LGPL - original licence link has changed is not relivant.
35626  *
35627  * Fork - LGPL
35628  * <script type="text/javascript">
35629  */
35630  
35631 /**
35632  * @class Roo.bootstrap.layout.Region
35633  * @extends Roo.bootstrap.layout.Basic
35634  * This class represents a region in a layout manager.
35635  
35636  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35637  * @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})
35638  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35639  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35640  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35641  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35642  * @cfg {String}    title           The title for the region (overrides panel titles)
35643  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35644  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35645  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35646  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35647  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35648  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35649  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35650  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35651  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35652  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35653
35654  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35655  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35656  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35657  * @cfg {Number}    width           For East/West panels
35658  * @cfg {Number}    height          For North/South panels
35659  * @cfg {Boolean}   split           To show the splitter
35660  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35661  * 
35662  * @cfg {string}   cls             Extra CSS classes to add to region
35663  * 
35664  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35665  * @cfg {string}   region  the region that it inhabits..
35666  *
35667
35668  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35669  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35670
35671  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35672  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35673  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35674  */
35675 Roo.bootstrap.layout.Region = function(config)
35676 {
35677     this.applyConfig(config);
35678
35679     var mgr = config.mgr;
35680     var pos = config.region;
35681     config.skipConfig = true;
35682     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35683     
35684     if (mgr.el) {
35685         this.onRender(mgr.el);   
35686     }
35687      
35688     this.visible = true;
35689     this.collapsed = false;
35690     this.unrendered_panels = [];
35691 };
35692
35693 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35694
35695     position: '', // set by wrapper (eg. north/south etc..)
35696     unrendered_panels : null,  // unrendered panels.
35697     createBody : function(){
35698         /** This region's body element 
35699         * @type Roo.Element */
35700         this.bodyEl = this.el.createChild({
35701                 tag: "div",
35702                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35703         });
35704     },
35705
35706     onRender: function(ctr, pos)
35707     {
35708         var dh = Roo.DomHelper;
35709         /** This region's container element 
35710         * @type Roo.Element */
35711         this.el = dh.append(ctr.dom, {
35712                 tag: "div",
35713                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35714             }, true);
35715         /** This region's title element 
35716         * @type Roo.Element */
35717     
35718         this.titleEl = dh.append(this.el.dom,
35719             {
35720                     tag: "div",
35721                     unselectable: "on",
35722                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35723                     children:[
35724                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35725                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35726                     ]}, true);
35727         
35728         this.titleEl.enableDisplayMode();
35729         /** This region's title text element 
35730         * @type HTMLElement */
35731         this.titleTextEl = this.titleEl.dom.firstChild;
35732         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35733         /*
35734         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35735         this.closeBtn.enableDisplayMode();
35736         this.closeBtn.on("click", this.closeClicked, this);
35737         this.closeBtn.hide();
35738     */
35739         this.createBody(this.config);
35740         if(this.config.hideWhenEmpty){
35741             this.hide();
35742             this.on("paneladded", this.validateVisibility, this);
35743             this.on("panelremoved", this.validateVisibility, this);
35744         }
35745         if(this.autoScroll){
35746             this.bodyEl.setStyle("overflow", "auto");
35747         }else{
35748             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35749         }
35750         //if(c.titlebar !== false){
35751             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35752                 this.titleEl.hide();
35753             }else{
35754                 this.titleEl.show();
35755                 if(this.config.title){
35756                     this.titleTextEl.innerHTML = this.config.title;
35757                 }
35758             }
35759         //}
35760         if(this.config.collapsed){
35761             this.collapse(true);
35762         }
35763         if(this.config.hidden){
35764             this.hide();
35765         }
35766         
35767         if (this.unrendered_panels && this.unrendered_panels.length) {
35768             for (var i =0;i< this.unrendered_panels.length; i++) {
35769                 this.add(this.unrendered_panels[i]);
35770             }
35771             this.unrendered_panels = null;
35772             
35773         }
35774         
35775     },
35776     
35777     applyConfig : function(c)
35778     {
35779         /*
35780          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35781             var dh = Roo.DomHelper;
35782             if(c.titlebar !== false){
35783                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35784                 this.collapseBtn.on("click", this.collapse, this);
35785                 this.collapseBtn.enableDisplayMode();
35786                 /*
35787                 if(c.showPin === true || this.showPin){
35788                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35789                     this.stickBtn.enableDisplayMode();
35790                     this.stickBtn.on("click", this.expand, this);
35791                     this.stickBtn.hide();
35792                 }
35793                 
35794             }
35795             */
35796             /** This region's collapsed element
35797             * @type Roo.Element */
35798             /*
35799              *
35800             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35801                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35802             ]}, true);
35803             
35804             if(c.floatable !== false){
35805                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35806                this.collapsedEl.on("click", this.collapseClick, this);
35807             }
35808
35809             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35810                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35811                    id: "message", unselectable: "on", style:{"float":"left"}});
35812                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35813              }
35814             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35815             this.expandBtn.on("click", this.expand, this);
35816             
35817         }
35818         
35819         if(this.collapseBtn){
35820             this.collapseBtn.setVisible(c.collapsible == true);
35821         }
35822         
35823         this.cmargins = c.cmargins || this.cmargins ||
35824                          (this.position == "west" || this.position == "east" ?
35825                              {top: 0, left: 2, right:2, bottom: 0} :
35826                              {top: 2, left: 0, right:0, bottom: 2});
35827         */
35828         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35829         
35830         
35831         this.bottomTabs = c.tabPosition != "top";
35832         
35833         this.autoScroll = c.autoScroll || false;
35834         
35835         
35836        
35837         
35838         this.duration = c.duration || .30;
35839         this.slideDuration = c.slideDuration || .45;
35840         this.config = c;
35841        
35842     },
35843     /**
35844      * Returns true if this region is currently visible.
35845      * @return {Boolean}
35846      */
35847     isVisible : function(){
35848         return this.visible;
35849     },
35850
35851     /**
35852      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35853      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35854      */
35855     //setCollapsedTitle : function(title){
35856     //    title = title || "&#160;";
35857      //   if(this.collapsedTitleTextEl){
35858       //      this.collapsedTitleTextEl.innerHTML = title;
35859        // }
35860     //},
35861
35862     getBox : function(){
35863         var b;
35864       //  if(!this.collapsed){
35865             b = this.el.getBox(false, true);
35866        // }else{
35867           //  b = this.collapsedEl.getBox(false, true);
35868         //}
35869         return b;
35870     },
35871
35872     getMargins : function(){
35873         return this.margins;
35874         //return this.collapsed ? this.cmargins : this.margins;
35875     },
35876 /*
35877     highlight : function(){
35878         this.el.addClass("x-layout-panel-dragover");
35879     },
35880
35881     unhighlight : function(){
35882         this.el.removeClass("x-layout-panel-dragover");
35883     },
35884 */
35885     updateBox : function(box)
35886     {
35887         if (!this.bodyEl) {
35888             return; // not rendered yet..
35889         }
35890         
35891         this.box = box;
35892         if(!this.collapsed){
35893             this.el.dom.style.left = box.x + "px";
35894             this.el.dom.style.top = box.y + "px";
35895             this.updateBody(box.width, box.height);
35896         }else{
35897             this.collapsedEl.dom.style.left = box.x + "px";
35898             this.collapsedEl.dom.style.top = box.y + "px";
35899             this.collapsedEl.setSize(box.width, box.height);
35900         }
35901         if(this.tabs){
35902             this.tabs.autoSizeTabs();
35903         }
35904     },
35905
35906     updateBody : function(w, h)
35907     {
35908         if(w !== null){
35909             this.el.setWidth(w);
35910             w -= this.el.getBorderWidth("rl");
35911             if(this.config.adjustments){
35912                 w += this.config.adjustments[0];
35913             }
35914         }
35915         if(h !== null && h > 0){
35916             this.el.setHeight(h);
35917             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35918             h -= this.el.getBorderWidth("tb");
35919             if(this.config.adjustments){
35920                 h += this.config.adjustments[1];
35921             }
35922             this.bodyEl.setHeight(h);
35923             if(this.tabs){
35924                 h = this.tabs.syncHeight(h);
35925             }
35926         }
35927         if(this.panelSize){
35928             w = w !== null ? w : this.panelSize.width;
35929             h = h !== null ? h : this.panelSize.height;
35930         }
35931         if(this.activePanel){
35932             var el = this.activePanel.getEl();
35933             w = w !== null ? w : el.getWidth();
35934             h = h !== null ? h : el.getHeight();
35935             this.panelSize = {width: w, height: h};
35936             this.activePanel.setSize(w, h);
35937         }
35938         if(Roo.isIE && this.tabs){
35939             this.tabs.el.repaint();
35940         }
35941     },
35942
35943     /**
35944      * Returns the container element for this region.
35945      * @return {Roo.Element}
35946      */
35947     getEl : function(){
35948         return this.el;
35949     },
35950
35951     /**
35952      * Hides this region.
35953      */
35954     hide : function(){
35955         //if(!this.collapsed){
35956             this.el.dom.style.left = "-2000px";
35957             this.el.hide();
35958         //}else{
35959          //   this.collapsedEl.dom.style.left = "-2000px";
35960          //   this.collapsedEl.hide();
35961        // }
35962         this.visible = false;
35963         this.fireEvent("visibilitychange", this, false);
35964     },
35965
35966     /**
35967      * Shows this region if it was previously hidden.
35968      */
35969     show : function(){
35970         //if(!this.collapsed){
35971             this.el.show();
35972         //}else{
35973         //    this.collapsedEl.show();
35974        // }
35975         this.visible = true;
35976         this.fireEvent("visibilitychange", this, true);
35977     },
35978 /*
35979     closeClicked : function(){
35980         if(this.activePanel){
35981             this.remove(this.activePanel);
35982         }
35983     },
35984
35985     collapseClick : function(e){
35986         if(this.isSlid){
35987            e.stopPropagation();
35988            this.slideIn();
35989         }else{
35990            e.stopPropagation();
35991            this.slideOut();
35992         }
35993     },
35994 */
35995     /**
35996      * Collapses this region.
35997      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35998      */
35999     /*
36000     collapse : function(skipAnim, skipCheck = false){
36001         if(this.collapsed) {
36002             return;
36003         }
36004         
36005         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36006             
36007             this.collapsed = true;
36008             if(this.split){
36009                 this.split.el.hide();
36010             }
36011             if(this.config.animate && skipAnim !== true){
36012                 this.fireEvent("invalidated", this);
36013                 this.animateCollapse();
36014             }else{
36015                 this.el.setLocation(-20000,-20000);
36016                 this.el.hide();
36017                 this.collapsedEl.show();
36018                 this.fireEvent("collapsed", this);
36019                 this.fireEvent("invalidated", this);
36020             }
36021         }
36022         
36023     },
36024 */
36025     animateCollapse : function(){
36026         // overridden
36027     },
36028
36029     /**
36030      * Expands this region if it was previously collapsed.
36031      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36032      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36033      */
36034     /*
36035     expand : function(e, skipAnim){
36036         if(e) {
36037             e.stopPropagation();
36038         }
36039         if(!this.collapsed || this.el.hasActiveFx()) {
36040             return;
36041         }
36042         if(this.isSlid){
36043             this.afterSlideIn();
36044             skipAnim = true;
36045         }
36046         this.collapsed = false;
36047         if(this.config.animate && skipAnim !== true){
36048             this.animateExpand();
36049         }else{
36050             this.el.show();
36051             if(this.split){
36052                 this.split.el.show();
36053             }
36054             this.collapsedEl.setLocation(-2000,-2000);
36055             this.collapsedEl.hide();
36056             this.fireEvent("invalidated", this);
36057             this.fireEvent("expanded", this);
36058         }
36059     },
36060 */
36061     animateExpand : function(){
36062         // overridden
36063     },
36064
36065     initTabs : function()
36066     {
36067         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36068         
36069         var ts = new Roo.bootstrap.panel.Tabs({
36070                 el: this.bodyEl.dom,
36071                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36072                 disableTooltips: this.config.disableTabTips,
36073                 toolbar : this.config.toolbar
36074             });
36075         
36076         if(this.config.hideTabs){
36077             ts.stripWrap.setDisplayed(false);
36078         }
36079         this.tabs = ts;
36080         ts.resizeTabs = this.config.resizeTabs === true;
36081         ts.minTabWidth = this.config.minTabWidth || 40;
36082         ts.maxTabWidth = this.config.maxTabWidth || 250;
36083         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36084         ts.monitorResize = false;
36085         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36086         ts.bodyEl.addClass('roo-layout-tabs-body');
36087         this.panels.each(this.initPanelAsTab, this);
36088     },
36089
36090     initPanelAsTab : function(panel){
36091         var ti = this.tabs.addTab(
36092             panel.getEl().id,
36093             panel.getTitle(),
36094             null,
36095             this.config.closeOnTab && panel.isClosable(),
36096             panel.tpl
36097         );
36098         if(panel.tabTip !== undefined){
36099             ti.setTooltip(panel.tabTip);
36100         }
36101         ti.on("activate", function(){
36102               this.setActivePanel(panel);
36103         }, this);
36104         
36105         if(this.config.closeOnTab){
36106             ti.on("beforeclose", function(t, e){
36107                 e.cancel = true;
36108                 this.remove(panel);
36109             }, this);
36110         }
36111         
36112         panel.tabItem = ti;
36113         
36114         return ti;
36115     },
36116
36117     updatePanelTitle : function(panel, title)
36118     {
36119         if(this.activePanel == panel){
36120             this.updateTitle(title);
36121         }
36122         if(this.tabs){
36123             var ti = this.tabs.getTab(panel.getEl().id);
36124             ti.setText(title);
36125             if(panel.tabTip !== undefined){
36126                 ti.setTooltip(panel.tabTip);
36127             }
36128         }
36129     },
36130
36131     updateTitle : function(title){
36132         if(this.titleTextEl && !this.config.title){
36133             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36134         }
36135     },
36136
36137     setActivePanel : function(panel)
36138     {
36139         panel = this.getPanel(panel);
36140         if(this.activePanel && this.activePanel != panel){
36141             if(this.activePanel.setActiveState(false) === false){
36142                 return;
36143             }
36144         }
36145         this.activePanel = panel;
36146         panel.setActiveState(true);
36147         if(this.panelSize){
36148             panel.setSize(this.panelSize.width, this.panelSize.height);
36149         }
36150         if(this.closeBtn){
36151             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36152         }
36153         this.updateTitle(panel.getTitle());
36154         if(this.tabs){
36155             this.fireEvent("invalidated", this);
36156         }
36157         this.fireEvent("panelactivated", this, panel);
36158     },
36159
36160     /**
36161      * Shows the specified panel.
36162      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36163      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36164      */
36165     showPanel : function(panel)
36166     {
36167         panel = this.getPanel(panel);
36168         if(panel){
36169             if(this.tabs){
36170                 var tab = this.tabs.getTab(panel.getEl().id);
36171                 if(tab.isHidden()){
36172                     this.tabs.unhideTab(tab.id);
36173                 }
36174                 tab.activate();
36175             }else{
36176                 this.setActivePanel(panel);
36177             }
36178         }
36179         return panel;
36180     },
36181
36182     /**
36183      * Get the active panel for this region.
36184      * @return {Roo.ContentPanel} The active panel or null
36185      */
36186     getActivePanel : function(){
36187         return this.activePanel;
36188     },
36189
36190     validateVisibility : function(){
36191         if(this.panels.getCount() < 1){
36192             this.updateTitle("&#160;");
36193             this.closeBtn.hide();
36194             this.hide();
36195         }else{
36196             if(!this.isVisible()){
36197                 this.show();
36198             }
36199         }
36200     },
36201
36202     /**
36203      * Adds the passed ContentPanel(s) to this region.
36204      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36205      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36206      */
36207     add : function(panel)
36208     {
36209         if(arguments.length > 1){
36210             for(var i = 0, len = arguments.length; i < len; i++) {
36211                 this.add(arguments[i]);
36212             }
36213             return null;
36214         }
36215         
36216         // if we have not been rendered yet, then we can not really do much of this..
36217         if (!this.bodyEl) {
36218             this.unrendered_panels.push(panel);
36219             return panel;
36220         }
36221         
36222         
36223         
36224         
36225         if(this.hasPanel(panel)){
36226             this.showPanel(panel);
36227             return panel;
36228         }
36229         panel.setRegion(this);
36230         this.panels.add(panel);
36231        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36232             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36233             // and hide them... ???
36234             this.bodyEl.dom.appendChild(panel.getEl().dom);
36235             if(panel.background !== true){
36236                 this.setActivePanel(panel);
36237             }
36238             this.fireEvent("paneladded", this, panel);
36239             return panel;
36240         }
36241         */
36242         if(!this.tabs){
36243             this.initTabs();
36244         }else{
36245             this.initPanelAsTab(panel);
36246         }
36247         
36248         
36249         if(panel.background !== true){
36250             this.tabs.activate(panel.getEl().id);
36251         }
36252         this.fireEvent("paneladded", this, panel);
36253         return panel;
36254     },
36255
36256     /**
36257      * Hides the tab for the specified panel.
36258      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36259      */
36260     hidePanel : function(panel){
36261         if(this.tabs && (panel = this.getPanel(panel))){
36262             this.tabs.hideTab(panel.getEl().id);
36263         }
36264     },
36265
36266     /**
36267      * Unhides the tab for a previously hidden panel.
36268      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36269      */
36270     unhidePanel : function(panel){
36271         if(this.tabs && (panel = this.getPanel(panel))){
36272             this.tabs.unhideTab(panel.getEl().id);
36273         }
36274     },
36275
36276     clearPanels : function(){
36277         while(this.panels.getCount() > 0){
36278              this.remove(this.panels.first());
36279         }
36280     },
36281
36282     /**
36283      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36284      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36285      * @param {Boolean} preservePanel Overrides the config preservePanel option
36286      * @return {Roo.ContentPanel} The panel that was removed
36287      */
36288     remove : function(panel, preservePanel)
36289     {
36290         panel = this.getPanel(panel);
36291         if(!panel){
36292             return null;
36293         }
36294         var e = {};
36295         this.fireEvent("beforeremove", this, panel, e);
36296         if(e.cancel === true){
36297             return null;
36298         }
36299         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36300         var panelId = panel.getId();
36301         this.panels.removeKey(panelId);
36302         if(preservePanel){
36303             document.body.appendChild(panel.getEl().dom);
36304         }
36305         if(this.tabs){
36306             this.tabs.removeTab(panel.getEl().id);
36307         }else if (!preservePanel){
36308             this.bodyEl.dom.removeChild(panel.getEl().dom);
36309         }
36310         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36311             var p = this.panels.first();
36312             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36313             tempEl.appendChild(p.getEl().dom);
36314             this.bodyEl.update("");
36315             this.bodyEl.dom.appendChild(p.getEl().dom);
36316             tempEl = null;
36317             this.updateTitle(p.getTitle());
36318             this.tabs = null;
36319             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36320             this.setActivePanel(p);
36321         }
36322         panel.setRegion(null);
36323         if(this.activePanel == panel){
36324             this.activePanel = null;
36325         }
36326         if(this.config.autoDestroy !== false && preservePanel !== true){
36327             try{panel.destroy();}catch(e){}
36328         }
36329         this.fireEvent("panelremoved", this, panel);
36330         return panel;
36331     },
36332
36333     /**
36334      * Returns the TabPanel component used by this region
36335      * @return {Roo.TabPanel}
36336      */
36337     getTabs : function(){
36338         return this.tabs;
36339     },
36340
36341     createTool : function(parentEl, className){
36342         var btn = Roo.DomHelper.append(parentEl, {
36343             tag: "div",
36344             cls: "x-layout-tools-button",
36345             children: [ {
36346                 tag: "div",
36347                 cls: "roo-layout-tools-button-inner " + className,
36348                 html: "&#160;"
36349             }]
36350         }, true);
36351         btn.addClassOnOver("roo-layout-tools-button-over");
36352         return btn;
36353     }
36354 });/*
36355  * Based on:
36356  * Ext JS Library 1.1.1
36357  * Copyright(c) 2006-2007, Ext JS, LLC.
36358  *
36359  * Originally Released Under LGPL - original licence link has changed is not relivant.
36360  *
36361  * Fork - LGPL
36362  * <script type="text/javascript">
36363  */
36364  
36365
36366
36367 /**
36368  * @class Roo.SplitLayoutRegion
36369  * @extends Roo.LayoutRegion
36370  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36371  */
36372 Roo.bootstrap.layout.Split = function(config){
36373     this.cursor = config.cursor;
36374     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36375 };
36376
36377 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36378 {
36379     splitTip : "Drag to resize.",
36380     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36381     useSplitTips : false,
36382
36383     applyConfig : function(config){
36384         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36385     },
36386     
36387     onRender : function(ctr,pos) {
36388         
36389         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36390         if(!this.config.split){
36391             return;
36392         }
36393         if(!this.split){
36394             
36395             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36396                             tag: "div",
36397                             id: this.el.id + "-split",
36398                             cls: "roo-layout-split roo-layout-split-"+this.position,
36399                             html: "&#160;"
36400             });
36401             /** The SplitBar for this region 
36402             * @type Roo.SplitBar */
36403             // does not exist yet...
36404             Roo.log([this.position, this.orientation]);
36405             
36406             this.split = new Roo.bootstrap.SplitBar({
36407                 dragElement : splitEl,
36408                 resizingElement: this.el,
36409                 orientation : this.orientation
36410             });
36411             
36412             this.split.on("moved", this.onSplitMove, this);
36413             this.split.useShim = this.config.useShim === true;
36414             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36415             if(this.useSplitTips){
36416                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36417             }
36418             //if(config.collapsible){
36419             //    this.split.el.on("dblclick", this.collapse,  this);
36420             //}
36421         }
36422         if(typeof this.config.minSize != "undefined"){
36423             this.split.minSize = this.config.minSize;
36424         }
36425         if(typeof this.config.maxSize != "undefined"){
36426             this.split.maxSize = this.config.maxSize;
36427         }
36428         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36429             this.hideSplitter();
36430         }
36431         
36432     },
36433
36434     getHMaxSize : function(){
36435          var cmax = this.config.maxSize || 10000;
36436          var center = this.mgr.getRegion("center");
36437          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36438     },
36439
36440     getVMaxSize : function(){
36441          var cmax = this.config.maxSize || 10000;
36442          var center = this.mgr.getRegion("center");
36443          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36444     },
36445
36446     onSplitMove : function(split, newSize){
36447         this.fireEvent("resized", this, newSize);
36448     },
36449     
36450     /** 
36451      * Returns the {@link Roo.SplitBar} for this region.
36452      * @return {Roo.SplitBar}
36453      */
36454     getSplitBar : function(){
36455         return this.split;
36456     },
36457     
36458     hide : function(){
36459         this.hideSplitter();
36460         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36461     },
36462
36463     hideSplitter : function(){
36464         if(this.split){
36465             this.split.el.setLocation(-2000,-2000);
36466             this.split.el.hide();
36467         }
36468     },
36469
36470     show : function(){
36471         if(this.split){
36472             this.split.el.show();
36473         }
36474         Roo.bootstrap.layout.Split.superclass.show.call(this);
36475     },
36476     
36477     beforeSlide: function(){
36478         if(Roo.isGecko){// firefox overflow auto bug workaround
36479             this.bodyEl.clip();
36480             if(this.tabs) {
36481                 this.tabs.bodyEl.clip();
36482             }
36483             if(this.activePanel){
36484                 this.activePanel.getEl().clip();
36485                 
36486                 if(this.activePanel.beforeSlide){
36487                     this.activePanel.beforeSlide();
36488                 }
36489             }
36490         }
36491     },
36492     
36493     afterSlide : function(){
36494         if(Roo.isGecko){// firefox overflow auto bug workaround
36495             this.bodyEl.unclip();
36496             if(this.tabs) {
36497                 this.tabs.bodyEl.unclip();
36498             }
36499             if(this.activePanel){
36500                 this.activePanel.getEl().unclip();
36501                 if(this.activePanel.afterSlide){
36502                     this.activePanel.afterSlide();
36503                 }
36504             }
36505         }
36506     },
36507
36508     initAutoHide : function(){
36509         if(this.autoHide !== false){
36510             if(!this.autoHideHd){
36511                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36512                 this.autoHideHd = {
36513                     "mouseout": function(e){
36514                         if(!e.within(this.el, true)){
36515                             st.delay(500);
36516                         }
36517                     },
36518                     "mouseover" : function(e){
36519                         st.cancel();
36520                     },
36521                     scope : this
36522                 };
36523             }
36524             this.el.on(this.autoHideHd);
36525         }
36526     },
36527
36528     clearAutoHide : function(){
36529         if(this.autoHide !== false){
36530             this.el.un("mouseout", this.autoHideHd.mouseout);
36531             this.el.un("mouseover", this.autoHideHd.mouseover);
36532         }
36533     },
36534
36535     clearMonitor : function(){
36536         Roo.get(document).un("click", this.slideInIf, this);
36537     },
36538
36539     // these names are backwards but not changed for compat
36540     slideOut : function(){
36541         if(this.isSlid || this.el.hasActiveFx()){
36542             return;
36543         }
36544         this.isSlid = true;
36545         if(this.collapseBtn){
36546             this.collapseBtn.hide();
36547         }
36548         this.closeBtnState = this.closeBtn.getStyle('display');
36549         this.closeBtn.hide();
36550         if(this.stickBtn){
36551             this.stickBtn.show();
36552         }
36553         this.el.show();
36554         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36555         this.beforeSlide();
36556         this.el.setStyle("z-index", 10001);
36557         this.el.slideIn(this.getSlideAnchor(), {
36558             callback: function(){
36559                 this.afterSlide();
36560                 this.initAutoHide();
36561                 Roo.get(document).on("click", this.slideInIf, this);
36562                 this.fireEvent("slideshow", this);
36563             },
36564             scope: this,
36565             block: true
36566         });
36567     },
36568
36569     afterSlideIn : function(){
36570         this.clearAutoHide();
36571         this.isSlid = false;
36572         this.clearMonitor();
36573         this.el.setStyle("z-index", "");
36574         if(this.collapseBtn){
36575             this.collapseBtn.show();
36576         }
36577         this.closeBtn.setStyle('display', this.closeBtnState);
36578         if(this.stickBtn){
36579             this.stickBtn.hide();
36580         }
36581         this.fireEvent("slidehide", this);
36582     },
36583
36584     slideIn : function(cb){
36585         if(!this.isSlid || this.el.hasActiveFx()){
36586             Roo.callback(cb);
36587             return;
36588         }
36589         this.isSlid = false;
36590         this.beforeSlide();
36591         this.el.slideOut(this.getSlideAnchor(), {
36592             callback: function(){
36593                 this.el.setLeftTop(-10000, -10000);
36594                 this.afterSlide();
36595                 this.afterSlideIn();
36596                 Roo.callback(cb);
36597             },
36598             scope: this,
36599             block: true
36600         });
36601     },
36602     
36603     slideInIf : function(e){
36604         if(!e.within(this.el)){
36605             this.slideIn();
36606         }
36607     },
36608
36609     animateCollapse : function(){
36610         this.beforeSlide();
36611         this.el.setStyle("z-index", 20000);
36612         var anchor = this.getSlideAnchor();
36613         this.el.slideOut(anchor, {
36614             callback : function(){
36615                 this.el.setStyle("z-index", "");
36616                 this.collapsedEl.slideIn(anchor, {duration:.3});
36617                 this.afterSlide();
36618                 this.el.setLocation(-10000,-10000);
36619                 this.el.hide();
36620                 this.fireEvent("collapsed", this);
36621             },
36622             scope: this,
36623             block: true
36624         });
36625     },
36626
36627     animateExpand : function(){
36628         this.beforeSlide();
36629         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36630         this.el.setStyle("z-index", 20000);
36631         this.collapsedEl.hide({
36632             duration:.1
36633         });
36634         this.el.slideIn(this.getSlideAnchor(), {
36635             callback : function(){
36636                 this.el.setStyle("z-index", "");
36637                 this.afterSlide();
36638                 if(this.split){
36639                     this.split.el.show();
36640                 }
36641                 this.fireEvent("invalidated", this);
36642                 this.fireEvent("expanded", this);
36643             },
36644             scope: this,
36645             block: true
36646         });
36647     },
36648
36649     anchors : {
36650         "west" : "left",
36651         "east" : "right",
36652         "north" : "top",
36653         "south" : "bottom"
36654     },
36655
36656     sanchors : {
36657         "west" : "l",
36658         "east" : "r",
36659         "north" : "t",
36660         "south" : "b"
36661     },
36662
36663     canchors : {
36664         "west" : "tl-tr",
36665         "east" : "tr-tl",
36666         "north" : "tl-bl",
36667         "south" : "bl-tl"
36668     },
36669
36670     getAnchor : function(){
36671         return this.anchors[this.position];
36672     },
36673
36674     getCollapseAnchor : function(){
36675         return this.canchors[this.position];
36676     },
36677
36678     getSlideAnchor : function(){
36679         return this.sanchors[this.position];
36680     },
36681
36682     getAlignAdj : function(){
36683         var cm = this.cmargins;
36684         switch(this.position){
36685             case "west":
36686                 return [0, 0];
36687             break;
36688             case "east":
36689                 return [0, 0];
36690             break;
36691             case "north":
36692                 return [0, 0];
36693             break;
36694             case "south":
36695                 return [0, 0];
36696             break;
36697         }
36698     },
36699
36700     getExpandAdj : function(){
36701         var c = this.collapsedEl, cm = this.cmargins;
36702         switch(this.position){
36703             case "west":
36704                 return [-(cm.right+c.getWidth()+cm.left), 0];
36705             break;
36706             case "east":
36707                 return [cm.right+c.getWidth()+cm.left, 0];
36708             break;
36709             case "north":
36710                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36711             break;
36712             case "south":
36713                 return [0, cm.top+cm.bottom+c.getHeight()];
36714             break;
36715         }
36716     }
36717 });/*
36718  * Based on:
36719  * Ext JS Library 1.1.1
36720  * Copyright(c) 2006-2007, Ext JS, LLC.
36721  *
36722  * Originally Released Under LGPL - original licence link has changed is not relivant.
36723  *
36724  * Fork - LGPL
36725  * <script type="text/javascript">
36726  */
36727 /*
36728  * These classes are private internal classes
36729  */
36730 Roo.bootstrap.layout.Center = function(config){
36731     config.region = "center";
36732     Roo.bootstrap.layout.Region.call(this, config);
36733     this.visible = true;
36734     this.minWidth = config.minWidth || 20;
36735     this.minHeight = config.minHeight || 20;
36736 };
36737
36738 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36739     hide : function(){
36740         // center panel can't be hidden
36741     },
36742     
36743     show : function(){
36744         // center panel can't be hidden
36745     },
36746     
36747     getMinWidth: function(){
36748         return this.minWidth;
36749     },
36750     
36751     getMinHeight: function(){
36752         return this.minHeight;
36753     }
36754 });
36755
36756
36757
36758
36759  
36760
36761
36762
36763
36764
36765 Roo.bootstrap.layout.North = function(config)
36766 {
36767     config.region = 'north';
36768     config.cursor = 'n-resize';
36769     
36770     Roo.bootstrap.layout.Split.call(this, config);
36771     
36772     
36773     if(this.split){
36774         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36775         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36776         this.split.el.addClass("roo-layout-split-v");
36777     }
36778     var size = config.initialSize || config.height;
36779     if(typeof size != "undefined"){
36780         this.el.setHeight(size);
36781     }
36782 };
36783 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36784 {
36785     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36786     
36787     
36788     
36789     getBox : function(){
36790         if(this.collapsed){
36791             return this.collapsedEl.getBox();
36792         }
36793         var box = this.el.getBox();
36794         if(this.split){
36795             box.height += this.split.el.getHeight();
36796         }
36797         return box;
36798     },
36799     
36800     updateBox : function(box){
36801         if(this.split && !this.collapsed){
36802             box.height -= this.split.el.getHeight();
36803             this.split.el.setLeft(box.x);
36804             this.split.el.setTop(box.y+box.height);
36805             this.split.el.setWidth(box.width);
36806         }
36807         if(this.collapsed){
36808             this.updateBody(box.width, null);
36809         }
36810         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36811     }
36812 });
36813
36814
36815
36816
36817
36818 Roo.bootstrap.layout.South = function(config){
36819     config.region = 'south';
36820     config.cursor = 's-resize';
36821     Roo.bootstrap.layout.Split.call(this, config);
36822     if(this.split){
36823         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36824         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36825         this.split.el.addClass("roo-layout-split-v");
36826     }
36827     var size = config.initialSize || config.height;
36828     if(typeof size != "undefined"){
36829         this.el.setHeight(size);
36830     }
36831 };
36832
36833 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36834     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36835     getBox : function(){
36836         if(this.collapsed){
36837             return this.collapsedEl.getBox();
36838         }
36839         var box = this.el.getBox();
36840         if(this.split){
36841             var sh = this.split.el.getHeight();
36842             box.height += sh;
36843             box.y -= sh;
36844         }
36845         return box;
36846     },
36847     
36848     updateBox : function(box){
36849         if(this.split && !this.collapsed){
36850             var sh = this.split.el.getHeight();
36851             box.height -= sh;
36852             box.y += sh;
36853             this.split.el.setLeft(box.x);
36854             this.split.el.setTop(box.y-sh);
36855             this.split.el.setWidth(box.width);
36856         }
36857         if(this.collapsed){
36858             this.updateBody(box.width, null);
36859         }
36860         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36861     }
36862 });
36863
36864 Roo.bootstrap.layout.East = function(config){
36865     config.region = "east";
36866     config.cursor = "e-resize";
36867     Roo.bootstrap.layout.Split.call(this, config);
36868     if(this.split){
36869         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36870         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36871         this.split.el.addClass("roo-layout-split-h");
36872     }
36873     var size = config.initialSize || config.width;
36874     if(typeof size != "undefined"){
36875         this.el.setWidth(size);
36876     }
36877 };
36878 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36879     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36880     getBox : function(){
36881         if(this.collapsed){
36882             return this.collapsedEl.getBox();
36883         }
36884         var box = this.el.getBox();
36885         if(this.split){
36886             var sw = this.split.el.getWidth();
36887             box.width += sw;
36888             box.x -= sw;
36889         }
36890         return box;
36891     },
36892
36893     updateBox : function(box){
36894         if(this.split && !this.collapsed){
36895             var sw = this.split.el.getWidth();
36896             box.width -= sw;
36897             this.split.el.setLeft(box.x);
36898             this.split.el.setTop(box.y);
36899             this.split.el.setHeight(box.height);
36900             box.x += sw;
36901         }
36902         if(this.collapsed){
36903             this.updateBody(null, box.height);
36904         }
36905         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36906     }
36907 });
36908
36909 Roo.bootstrap.layout.West = function(config){
36910     config.region = "west";
36911     config.cursor = "w-resize";
36912     
36913     Roo.bootstrap.layout.Split.call(this, config);
36914     if(this.split){
36915         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36916         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36917         this.split.el.addClass("roo-layout-split-h");
36918     }
36919     
36920 };
36921 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36922     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36923     
36924     onRender: function(ctr, pos)
36925     {
36926         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36927         var size = this.config.initialSize || this.config.width;
36928         if(typeof size != "undefined"){
36929             this.el.setWidth(size);
36930         }
36931     },
36932     
36933     getBox : function(){
36934         if(this.collapsed){
36935             return this.collapsedEl.getBox();
36936         }
36937         var box = this.el.getBox();
36938         if(this.split){
36939             box.width += this.split.el.getWidth();
36940         }
36941         return box;
36942     },
36943     
36944     updateBox : function(box){
36945         if(this.split && !this.collapsed){
36946             var sw = this.split.el.getWidth();
36947             box.width -= sw;
36948             this.split.el.setLeft(box.x+box.width);
36949             this.split.el.setTop(box.y);
36950             this.split.el.setHeight(box.height);
36951         }
36952         if(this.collapsed){
36953             this.updateBody(null, box.height);
36954         }
36955         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36956     }
36957 });
36958 Roo.namespace("Roo.bootstrap.panel");/*
36959  * Based on:
36960  * Ext JS Library 1.1.1
36961  * Copyright(c) 2006-2007, Ext JS, LLC.
36962  *
36963  * Originally Released Under LGPL - original licence link has changed is not relivant.
36964  *
36965  * Fork - LGPL
36966  * <script type="text/javascript">
36967  */
36968 /**
36969  * @class Roo.ContentPanel
36970  * @extends Roo.util.Observable
36971  * A basic ContentPanel element.
36972  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36973  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36974  * @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
36975  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36976  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36977  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36978  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36979  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36980  * @cfg {String} title          The title for this panel
36981  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36982  * @cfg {String} url            Calls {@link #setUrl} with this value
36983  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36984  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36985  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36986  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36987  * @cfg {Boolean} badges render the badges
36988
36989  * @constructor
36990  * Create a new ContentPanel.
36991  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36992  * @param {String/Object} config A string to set only the title or a config object
36993  * @param {String} content (optional) Set the HTML content for this panel
36994  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36995  */
36996 Roo.bootstrap.panel.Content = function( config){
36997     
36998     this.tpl = config.tpl || false;
36999     
37000     var el = config.el;
37001     var content = config.content;
37002
37003     if(config.autoCreate){ // xtype is available if this is called from factory
37004         el = Roo.id();
37005     }
37006     this.el = Roo.get(el);
37007     if(!this.el && config && config.autoCreate){
37008         if(typeof config.autoCreate == "object"){
37009             if(!config.autoCreate.id){
37010                 config.autoCreate.id = config.id||el;
37011             }
37012             this.el = Roo.DomHelper.append(document.body,
37013                         config.autoCreate, true);
37014         }else{
37015             var elcfg =  {   tag: "div",
37016                             cls: "roo-layout-inactive-content",
37017                             id: config.id||el
37018                             };
37019             if (config.html) {
37020                 elcfg.html = config.html;
37021                 
37022             }
37023                         
37024             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37025         }
37026     } 
37027     this.closable = false;
37028     this.loaded = false;
37029     this.active = false;
37030    
37031       
37032     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37033         
37034         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37035         
37036         this.wrapEl = this.el; //this.el.wrap();
37037         var ti = [];
37038         if (config.toolbar.items) {
37039             ti = config.toolbar.items ;
37040             delete config.toolbar.items ;
37041         }
37042         
37043         var nitems = [];
37044         this.toolbar.render(this.wrapEl, 'before');
37045         for(var i =0;i < ti.length;i++) {
37046           //  Roo.log(['add child', items[i]]);
37047             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37048         }
37049         this.toolbar.items = nitems;
37050         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37051         delete config.toolbar;
37052         
37053     }
37054     /*
37055     // xtype created footer. - not sure if will work as we normally have to render first..
37056     if (this.footer && !this.footer.el && this.footer.xtype) {
37057         if (!this.wrapEl) {
37058             this.wrapEl = this.el.wrap();
37059         }
37060     
37061         this.footer.container = this.wrapEl.createChild();
37062          
37063         this.footer = Roo.factory(this.footer, Roo);
37064         
37065     }
37066     */
37067     
37068      if(typeof config == "string"){
37069         this.title = config;
37070     }else{
37071         Roo.apply(this, config);
37072     }
37073     
37074     if(this.resizeEl){
37075         this.resizeEl = Roo.get(this.resizeEl, true);
37076     }else{
37077         this.resizeEl = this.el;
37078     }
37079     // handle view.xtype
37080     
37081  
37082     
37083     
37084     this.addEvents({
37085         /**
37086          * @event activate
37087          * Fires when this panel is activated. 
37088          * @param {Roo.ContentPanel} this
37089          */
37090         "activate" : true,
37091         /**
37092          * @event deactivate
37093          * Fires when this panel is activated. 
37094          * @param {Roo.ContentPanel} this
37095          */
37096         "deactivate" : true,
37097
37098         /**
37099          * @event resize
37100          * Fires when this panel is resized if fitToFrame is true.
37101          * @param {Roo.ContentPanel} this
37102          * @param {Number} width The width after any component adjustments
37103          * @param {Number} height The height after any component adjustments
37104          */
37105         "resize" : true,
37106         
37107          /**
37108          * @event render
37109          * Fires when this tab is created
37110          * @param {Roo.ContentPanel} this
37111          */
37112         "render" : true
37113         
37114         
37115         
37116     });
37117     
37118
37119     
37120     
37121     if(this.autoScroll){
37122         this.resizeEl.setStyle("overflow", "auto");
37123     } else {
37124         // fix randome scrolling
37125         //this.el.on('scroll', function() {
37126         //    Roo.log('fix random scolling');
37127         //    this.scrollTo('top',0); 
37128         //});
37129     }
37130     content = content || this.content;
37131     if(content){
37132         this.setContent(content);
37133     }
37134     if(config && config.url){
37135         this.setUrl(this.url, this.params, this.loadOnce);
37136     }
37137     
37138     
37139     
37140     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37141     
37142     if (this.view && typeof(this.view.xtype) != 'undefined') {
37143         this.view.el = this.el.appendChild(document.createElement("div"));
37144         this.view = Roo.factory(this.view); 
37145         this.view.render  &&  this.view.render(false, '');  
37146     }
37147     
37148     
37149     this.fireEvent('render', this);
37150 };
37151
37152 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37153     
37154     tabTip : '',
37155     
37156     setRegion : function(region){
37157         this.region = region;
37158         this.setActiveClass(region && !this.background);
37159     },
37160     
37161     
37162     setActiveClass: function(state)
37163     {
37164         if(state){
37165            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37166            this.el.setStyle('position','relative');
37167         }else{
37168            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37169            this.el.setStyle('position', 'absolute');
37170         } 
37171     },
37172     
37173     /**
37174      * Returns the toolbar for this Panel if one was configured. 
37175      * @return {Roo.Toolbar} 
37176      */
37177     getToolbar : function(){
37178         return this.toolbar;
37179     },
37180     
37181     setActiveState : function(active)
37182     {
37183         this.active = active;
37184         this.setActiveClass(active);
37185         if(!active){
37186             if(this.fireEvent("deactivate", this) === false){
37187                 return false;
37188             }
37189             return true;
37190         }
37191         this.fireEvent("activate", this);
37192         return true;
37193     },
37194     /**
37195      * Updates this panel's element
37196      * @param {String} content The new content
37197      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37198     */
37199     setContent : function(content, loadScripts){
37200         this.el.update(content, loadScripts);
37201     },
37202
37203     ignoreResize : function(w, h){
37204         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37205             return true;
37206         }else{
37207             this.lastSize = {width: w, height: h};
37208             return false;
37209         }
37210     },
37211     /**
37212      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37213      * @return {Roo.UpdateManager} The UpdateManager
37214      */
37215     getUpdateManager : function(){
37216         return this.el.getUpdateManager();
37217     },
37218      /**
37219      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37220      * @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:
37221 <pre><code>
37222 panel.load({
37223     url: "your-url.php",
37224     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37225     callback: yourFunction,
37226     scope: yourObject, //(optional scope)
37227     discardUrl: false,
37228     nocache: false,
37229     text: "Loading...",
37230     timeout: 30,
37231     scripts: false
37232 });
37233 </code></pre>
37234      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37235      * 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.
37236      * @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}
37237      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37238      * @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.
37239      * @return {Roo.ContentPanel} this
37240      */
37241     load : function(){
37242         var um = this.el.getUpdateManager();
37243         um.update.apply(um, arguments);
37244         return this;
37245     },
37246
37247
37248     /**
37249      * 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.
37250      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37251      * @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)
37252      * @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)
37253      * @return {Roo.UpdateManager} The UpdateManager
37254      */
37255     setUrl : function(url, params, loadOnce){
37256         if(this.refreshDelegate){
37257             this.removeListener("activate", this.refreshDelegate);
37258         }
37259         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37260         this.on("activate", this.refreshDelegate);
37261         return this.el.getUpdateManager();
37262     },
37263     
37264     _handleRefresh : function(url, params, loadOnce){
37265         if(!loadOnce || !this.loaded){
37266             var updater = this.el.getUpdateManager();
37267             updater.update(url, params, this._setLoaded.createDelegate(this));
37268         }
37269     },
37270     
37271     _setLoaded : function(){
37272         this.loaded = true;
37273     }, 
37274     
37275     /**
37276      * Returns this panel's id
37277      * @return {String} 
37278      */
37279     getId : function(){
37280         return this.el.id;
37281     },
37282     
37283     /** 
37284      * Returns this panel's element - used by regiosn to add.
37285      * @return {Roo.Element} 
37286      */
37287     getEl : function(){
37288         return this.wrapEl || this.el;
37289     },
37290     
37291    
37292     
37293     adjustForComponents : function(width, height)
37294     {
37295         //Roo.log('adjustForComponents ');
37296         if(this.resizeEl != this.el){
37297             width -= this.el.getFrameWidth('lr');
37298             height -= this.el.getFrameWidth('tb');
37299         }
37300         if(this.toolbar){
37301             var te = this.toolbar.getEl();
37302             te.setWidth(width);
37303             height -= te.getHeight();
37304         }
37305         if(this.footer){
37306             var te = this.footer.getEl();
37307             te.setWidth(width);
37308             height -= te.getHeight();
37309         }
37310         
37311         
37312         if(this.adjustments){
37313             width += this.adjustments[0];
37314             height += this.adjustments[1];
37315         }
37316         return {"width": width, "height": height};
37317     },
37318     
37319     setSize : function(width, height){
37320         if(this.fitToFrame && !this.ignoreResize(width, height)){
37321             if(this.fitContainer && this.resizeEl != this.el){
37322                 this.el.setSize(width, height);
37323             }
37324             var size = this.adjustForComponents(width, height);
37325             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37326             this.fireEvent('resize', this, size.width, size.height);
37327         }
37328     },
37329     
37330     /**
37331      * Returns this panel's title
37332      * @return {String} 
37333      */
37334     getTitle : function(){
37335         
37336         if (typeof(this.title) != 'object') {
37337             return this.title;
37338         }
37339         
37340         var t = '';
37341         for (var k in this.title) {
37342             if (!this.title.hasOwnProperty(k)) {
37343                 continue;
37344             }
37345             
37346             if (k.indexOf('-') >= 0) {
37347                 var s = k.split('-');
37348                 for (var i = 0; i<s.length; i++) {
37349                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37350                 }
37351             } else {
37352                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37353             }
37354         }
37355         return t;
37356     },
37357     
37358     /**
37359      * Set this panel's title
37360      * @param {String} title
37361      */
37362     setTitle : function(title){
37363         this.title = title;
37364         if(this.region){
37365             this.region.updatePanelTitle(this, title);
37366         }
37367     },
37368     
37369     /**
37370      * Returns true is this panel was configured to be closable
37371      * @return {Boolean} 
37372      */
37373     isClosable : function(){
37374         return this.closable;
37375     },
37376     
37377     beforeSlide : function(){
37378         this.el.clip();
37379         this.resizeEl.clip();
37380     },
37381     
37382     afterSlide : function(){
37383         this.el.unclip();
37384         this.resizeEl.unclip();
37385     },
37386     
37387     /**
37388      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37389      *   Will fail silently if the {@link #setUrl} method has not been called.
37390      *   This does not activate the panel, just updates its content.
37391      */
37392     refresh : function(){
37393         if(this.refreshDelegate){
37394            this.loaded = false;
37395            this.refreshDelegate();
37396         }
37397     },
37398     
37399     /**
37400      * Destroys this panel
37401      */
37402     destroy : function(){
37403         this.el.removeAllListeners();
37404         var tempEl = document.createElement("span");
37405         tempEl.appendChild(this.el.dom);
37406         tempEl.innerHTML = "";
37407         this.el.remove();
37408         this.el = null;
37409     },
37410     
37411     /**
37412      * form - if the content panel contains a form - this is a reference to it.
37413      * @type {Roo.form.Form}
37414      */
37415     form : false,
37416     /**
37417      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37418      *    This contains a reference to it.
37419      * @type {Roo.View}
37420      */
37421     view : false,
37422     
37423       /**
37424      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37425      * <pre><code>
37426
37427 layout.addxtype({
37428        xtype : 'Form',
37429        items: [ .... ]
37430    }
37431 );
37432
37433 </code></pre>
37434      * @param {Object} cfg Xtype definition of item to add.
37435      */
37436     
37437     
37438     getChildContainer: function () {
37439         return this.getEl();
37440     }
37441     
37442     
37443     /*
37444         var  ret = new Roo.factory(cfg);
37445         return ret;
37446         
37447         
37448         // add form..
37449         if (cfg.xtype.match(/^Form$/)) {
37450             
37451             var el;
37452             //if (this.footer) {
37453             //    el = this.footer.container.insertSibling(false, 'before');
37454             //} else {
37455                 el = this.el.createChild();
37456             //}
37457
37458             this.form = new  Roo.form.Form(cfg);
37459             
37460             
37461             if ( this.form.allItems.length) {
37462                 this.form.render(el.dom);
37463             }
37464             return this.form;
37465         }
37466         // should only have one of theses..
37467         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37468             // views.. should not be just added - used named prop 'view''
37469             
37470             cfg.el = this.el.appendChild(document.createElement("div"));
37471             // factory?
37472             
37473             var ret = new Roo.factory(cfg);
37474              
37475              ret.render && ret.render(false, ''); // render blank..
37476             this.view = ret;
37477             return ret;
37478         }
37479         return false;
37480     }
37481     \*/
37482 });
37483  
37484 /**
37485  * @class Roo.bootstrap.panel.Grid
37486  * @extends Roo.bootstrap.panel.Content
37487  * @constructor
37488  * Create a new GridPanel.
37489  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37490  * @param {Object} config A the config object
37491   
37492  */
37493
37494
37495
37496 Roo.bootstrap.panel.Grid = function(config)
37497 {
37498     
37499       
37500     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37501         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37502
37503     config.el = this.wrapper;
37504     //this.el = this.wrapper;
37505     
37506       if (config.container) {
37507         // ctor'ed from a Border/panel.grid
37508         
37509         
37510         this.wrapper.setStyle("overflow", "hidden");
37511         this.wrapper.addClass('roo-grid-container');
37512
37513     }
37514     
37515     
37516     if(config.toolbar){
37517         var tool_el = this.wrapper.createChild();    
37518         this.toolbar = Roo.factory(config.toolbar);
37519         var ti = [];
37520         if (config.toolbar.items) {
37521             ti = config.toolbar.items ;
37522             delete config.toolbar.items ;
37523         }
37524         
37525         var nitems = [];
37526         this.toolbar.render(tool_el);
37527         for(var i =0;i < ti.length;i++) {
37528           //  Roo.log(['add child', items[i]]);
37529             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37530         }
37531         this.toolbar.items = nitems;
37532         
37533         delete config.toolbar;
37534     }
37535     
37536     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37537     config.grid.scrollBody = true;;
37538     config.grid.monitorWindowResize = false; // turn off autosizing
37539     config.grid.autoHeight = false;
37540     config.grid.autoWidth = false;
37541     
37542     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37543     
37544     if (config.background) {
37545         // render grid on panel activation (if panel background)
37546         this.on('activate', function(gp) {
37547             if (!gp.grid.rendered) {
37548                 gp.grid.render(this.wrapper);
37549                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37550             }
37551         });
37552             
37553     } else {
37554         this.grid.render(this.wrapper);
37555         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37556
37557     }
37558     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37559     // ??? needed ??? config.el = this.wrapper;
37560     
37561     
37562     
37563   
37564     // xtype created footer. - not sure if will work as we normally have to render first..
37565     if (this.footer && !this.footer.el && this.footer.xtype) {
37566         
37567         var ctr = this.grid.getView().getFooterPanel(true);
37568         this.footer.dataSource = this.grid.dataSource;
37569         this.footer = Roo.factory(this.footer, Roo);
37570         this.footer.render(ctr);
37571         
37572     }
37573     
37574     
37575     
37576     
37577      
37578 };
37579
37580 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37581     getId : function(){
37582         return this.grid.id;
37583     },
37584     
37585     /**
37586      * Returns the grid for this panel
37587      * @return {Roo.bootstrap.Table} 
37588      */
37589     getGrid : function(){
37590         return this.grid;    
37591     },
37592     
37593     setSize : function(width, height){
37594         if(!this.ignoreResize(width, height)){
37595             var grid = this.grid;
37596             var size = this.adjustForComponents(width, height);
37597             var gridel = grid.getGridEl();
37598             gridel.setSize(size.width, size.height);
37599             /*
37600             var thd = grid.getGridEl().select('thead',true).first();
37601             var tbd = grid.getGridEl().select('tbody', true).first();
37602             if (tbd) {
37603                 tbd.setSize(width, height - thd.getHeight());
37604             }
37605             */
37606             grid.autoSize();
37607         }
37608     },
37609      
37610     
37611     
37612     beforeSlide : function(){
37613         this.grid.getView().scroller.clip();
37614     },
37615     
37616     afterSlide : function(){
37617         this.grid.getView().scroller.unclip();
37618     },
37619     
37620     destroy : function(){
37621         this.grid.destroy();
37622         delete this.grid;
37623         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37624     }
37625 });
37626
37627 /**
37628  * @class Roo.bootstrap.panel.Nest
37629  * @extends Roo.bootstrap.panel.Content
37630  * @constructor
37631  * Create a new Panel, that can contain a layout.Border.
37632  * 
37633  * 
37634  * @param {Roo.BorderLayout} layout The layout for this panel
37635  * @param {String/Object} config A string to set only the title or a config object
37636  */
37637 Roo.bootstrap.panel.Nest = function(config)
37638 {
37639     // construct with only one argument..
37640     /* FIXME - implement nicer consturctors
37641     if (layout.layout) {
37642         config = layout;
37643         layout = config.layout;
37644         delete config.layout;
37645     }
37646     if (layout.xtype && !layout.getEl) {
37647         // then layout needs constructing..
37648         layout = Roo.factory(layout, Roo);
37649     }
37650     */
37651     
37652     config.el =  config.layout.getEl();
37653     
37654     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37655     
37656     config.layout.monitorWindowResize = false; // turn off autosizing
37657     this.layout = config.layout;
37658     this.layout.getEl().addClass("roo-layout-nested-layout");
37659     
37660     
37661     
37662     
37663 };
37664
37665 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37666
37667     setSize : function(width, height){
37668         if(!this.ignoreResize(width, height)){
37669             var size = this.adjustForComponents(width, height);
37670             var el = this.layout.getEl();
37671             if (size.height < 1) {
37672                 el.setWidth(size.width);   
37673             } else {
37674                 el.setSize(size.width, size.height);
37675             }
37676             var touch = el.dom.offsetWidth;
37677             this.layout.layout();
37678             // ie requires a double layout on the first pass
37679             if(Roo.isIE && !this.initialized){
37680                 this.initialized = true;
37681                 this.layout.layout();
37682             }
37683         }
37684     },
37685     
37686     // activate all subpanels if not currently active..
37687     
37688     setActiveState : function(active){
37689         this.active = active;
37690         this.setActiveClass(active);
37691         
37692         if(!active){
37693             this.fireEvent("deactivate", this);
37694             return;
37695         }
37696         
37697         this.fireEvent("activate", this);
37698         // not sure if this should happen before or after..
37699         if (!this.layout) {
37700             return; // should not happen..
37701         }
37702         var reg = false;
37703         for (var r in this.layout.regions) {
37704             reg = this.layout.getRegion(r);
37705             if (reg.getActivePanel()) {
37706                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37707                 reg.setActivePanel(reg.getActivePanel());
37708                 continue;
37709             }
37710             if (!reg.panels.length) {
37711                 continue;
37712             }
37713             reg.showPanel(reg.getPanel(0));
37714         }
37715         
37716         
37717         
37718         
37719     },
37720     
37721     /**
37722      * Returns the nested BorderLayout for this panel
37723      * @return {Roo.BorderLayout} 
37724      */
37725     getLayout : function(){
37726         return this.layout;
37727     },
37728     
37729      /**
37730      * Adds a xtype elements to the layout of the nested panel
37731      * <pre><code>
37732
37733 panel.addxtype({
37734        xtype : 'ContentPanel',
37735        region: 'west',
37736        items: [ .... ]
37737    }
37738 );
37739
37740 panel.addxtype({
37741         xtype : 'NestedLayoutPanel',
37742         region: 'west',
37743         layout: {
37744            center: { },
37745            west: { }   
37746         },
37747         items : [ ... list of content panels or nested layout panels.. ]
37748    }
37749 );
37750 </code></pre>
37751      * @param {Object} cfg Xtype definition of item to add.
37752      */
37753     addxtype : function(cfg) {
37754         return this.layout.addxtype(cfg);
37755     
37756     }
37757 });        /*
37758  * Based on:
37759  * Ext JS Library 1.1.1
37760  * Copyright(c) 2006-2007, Ext JS, LLC.
37761  *
37762  * Originally Released Under LGPL - original licence link has changed is not relivant.
37763  *
37764  * Fork - LGPL
37765  * <script type="text/javascript">
37766  */
37767 /**
37768  * @class Roo.TabPanel
37769  * @extends Roo.util.Observable
37770  * A lightweight tab container.
37771  * <br><br>
37772  * Usage:
37773  * <pre><code>
37774 // basic tabs 1, built from existing content
37775 var tabs = new Roo.TabPanel("tabs1");
37776 tabs.addTab("script", "View Script");
37777 tabs.addTab("markup", "View Markup");
37778 tabs.activate("script");
37779
37780 // more advanced tabs, built from javascript
37781 var jtabs = new Roo.TabPanel("jtabs");
37782 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37783
37784 // set up the UpdateManager
37785 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37786 var updater = tab2.getUpdateManager();
37787 updater.setDefaultUrl("ajax1.htm");
37788 tab2.on('activate', updater.refresh, updater, true);
37789
37790 // Use setUrl for Ajax loading
37791 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37792 tab3.setUrl("ajax2.htm", null, true);
37793
37794 // Disabled tab
37795 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37796 tab4.disable();
37797
37798 jtabs.activate("jtabs-1");
37799  * </code></pre>
37800  * @constructor
37801  * Create a new TabPanel.
37802  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37803  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37804  */
37805 Roo.bootstrap.panel.Tabs = function(config){
37806     /**
37807     * The container element for this TabPanel.
37808     * @type Roo.Element
37809     */
37810     this.el = Roo.get(config.el);
37811     delete config.el;
37812     if(config){
37813         if(typeof config == "boolean"){
37814             this.tabPosition = config ? "bottom" : "top";
37815         }else{
37816             Roo.apply(this, config);
37817         }
37818     }
37819     
37820     if(this.tabPosition == "bottom"){
37821         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37822         this.el.addClass("roo-tabs-bottom");
37823     }
37824     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37825     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37826     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37827     if(Roo.isIE){
37828         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37829     }
37830     if(this.tabPosition != "bottom"){
37831         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37832          * @type Roo.Element
37833          */
37834         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37835         this.el.addClass("roo-tabs-top");
37836     }
37837     this.items = [];
37838
37839     this.bodyEl.setStyle("position", "relative");
37840
37841     this.active = null;
37842     this.activateDelegate = this.activate.createDelegate(this);
37843
37844     this.addEvents({
37845         /**
37846          * @event tabchange
37847          * Fires when the active tab changes
37848          * @param {Roo.TabPanel} this
37849          * @param {Roo.TabPanelItem} activePanel The new active tab
37850          */
37851         "tabchange": true,
37852         /**
37853          * @event beforetabchange
37854          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37855          * @param {Roo.TabPanel} this
37856          * @param {Object} e Set cancel to true on this object to cancel the tab change
37857          * @param {Roo.TabPanelItem} tab The tab being changed to
37858          */
37859         "beforetabchange" : true
37860     });
37861
37862     Roo.EventManager.onWindowResize(this.onResize, this);
37863     this.cpad = this.el.getPadding("lr");
37864     this.hiddenCount = 0;
37865
37866
37867     // toolbar on the tabbar support...
37868     if (this.toolbar) {
37869         alert("no toolbar support yet");
37870         this.toolbar  = false;
37871         /*
37872         var tcfg = this.toolbar;
37873         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37874         this.toolbar = new Roo.Toolbar(tcfg);
37875         if (Roo.isSafari) {
37876             var tbl = tcfg.container.child('table', true);
37877             tbl.setAttribute('width', '100%');
37878         }
37879         */
37880         
37881     }
37882    
37883
37884
37885     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37886 };
37887
37888 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37889     /*
37890      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37891      */
37892     tabPosition : "top",
37893     /*
37894      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37895      */
37896     currentTabWidth : 0,
37897     /*
37898      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37899      */
37900     minTabWidth : 40,
37901     /*
37902      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37903      */
37904     maxTabWidth : 250,
37905     /*
37906      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37907      */
37908     preferredTabWidth : 175,
37909     /*
37910      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37911      */
37912     resizeTabs : false,
37913     /*
37914      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37915      */
37916     monitorResize : true,
37917     /*
37918      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37919      */
37920     toolbar : false,
37921
37922     /**
37923      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37924      * @param {String} id The id of the div to use <b>or create</b>
37925      * @param {String} text The text for the tab
37926      * @param {String} content (optional) Content to put in the TabPanelItem body
37927      * @param {Boolean} closable (optional) True to create a close icon on the tab
37928      * @return {Roo.TabPanelItem} The created TabPanelItem
37929      */
37930     addTab : function(id, text, content, closable, tpl)
37931     {
37932         var item = new Roo.bootstrap.panel.TabItem({
37933             panel: this,
37934             id : id,
37935             text : text,
37936             closable : closable,
37937             tpl : tpl
37938         });
37939         this.addTabItem(item);
37940         if(content){
37941             item.setContent(content);
37942         }
37943         return item;
37944     },
37945
37946     /**
37947      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37948      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37949      * @return {Roo.TabPanelItem}
37950      */
37951     getTab : function(id){
37952         return this.items[id];
37953     },
37954
37955     /**
37956      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37957      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37958      */
37959     hideTab : function(id){
37960         var t = this.items[id];
37961         if(!t.isHidden()){
37962            t.setHidden(true);
37963            this.hiddenCount++;
37964            this.autoSizeTabs();
37965         }
37966     },
37967
37968     /**
37969      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37970      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37971      */
37972     unhideTab : function(id){
37973         var t = this.items[id];
37974         if(t.isHidden()){
37975            t.setHidden(false);
37976            this.hiddenCount--;
37977            this.autoSizeTabs();
37978         }
37979     },
37980
37981     /**
37982      * Adds an existing {@link Roo.TabPanelItem}.
37983      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37984      */
37985     addTabItem : function(item){
37986         this.items[item.id] = item;
37987         this.items.push(item);
37988       //  if(this.resizeTabs){
37989     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37990   //         this.autoSizeTabs();
37991 //        }else{
37992 //            item.autoSize();
37993        // }
37994     },
37995
37996     /**
37997      * Removes a {@link Roo.TabPanelItem}.
37998      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37999      */
38000     removeTab : function(id){
38001         var items = this.items;
38002         var tab = items[id];
38003         if(!tab) { return; }
38004         var index = items.indexOf(tab);
38005         if(this.active == tab && items.length > 1){
38006             var newTab = this.getNextAvailable(index);
38007             if(newTab) {
38008                 newTab.activate();
38009             }
38010         }
38011         this.stripEl.dom.removeChild(tab.pnode.dom);
38012         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38013             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38014         }
38015         items.splice(index, 1);
38016         delete this.items[tab.id];
38017         tab.fireEvent("close", tab);
38018         tab.purgeListeners();
38019         this.autoSizeTabs();
38020     },
38021
38022     getNextAvailable : function(start){
38023         var items = this.items;
38024         var index = start;
38025         // look for a next tab that will slide over to
38026         // replace the one being removed
38027         while(index < items.length){
38028             var item = items[++index];
38029             if(item && !item.isHidden()){
38030                 return item;
38031             }
38032         }
38033         // if one isn't found select the previous tab (on the left)
38034         index = start;
38035         while(index >= 0){
38036             var item = items[--index];
38037             if(item && !item.isHidden()){
38038                 return item;
38039             }
38040         }
38041         return null;
38042     },
38043
38044     /**
38045      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38046      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38047      */
38048     disableTab : function(id){
38049         var tab = this.items[id];
38050         if(tab && this.active != tab){
38051             tab.disable();
38052         }
38053     },
38054
38055     /**
38056      * Enables a {@link Roo.TabPanelItem} that is disabled.
38057      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38058      */
38059     enableTab : function(id){
38060         var tab = this.items[id];
38061         tab.enable();
38062     },
38063
38064     /**
38065      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38066      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38067      * @return {Roo.TabPanelItem} The TabPanelItem.
38068      */
38069     activate : function(id){
38070         var tab = this.items[id];
38071         if(!tab){
38072             return null;
38073         }
38074         if(tab == this.active || tab.disabled){
38075             return tab;
38076         }
38077         var e = {};
38078         this.fireEvent("beforetabchange", this, e, tab);
38079         if(e.cancel !== true && !tab.disabled){
38080             if(this.active){
38081                 this.active.hide();
38082             }
38083             this.active = this.items[id];
38084             this.active.show();
38085             this.fireEvent("tabchange", this, this.active);
38086         }
38087         return tab;
38088     },
38089
38090     /**
38091      * Gets the active {@link Roo.TabPanelItem}.
38092      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38093      */
38094     getActiveTab : function(){
38095         return this.active;
38096     },
38097
38098     /**
38099      * Updates the tab body element to fit the height of the container element
38100      * for overflow scrolling
38101      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38102      */
38103     syncHeight : function(targetHeight){
38104         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38105         var bm = this.bodyEl.getMargins();
38106         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38107         this.bodyEl.setHeight(newHeight);
38108         return newHeight;
38109     },
38110
38111     onResize : function(){
38112         if(this.monitorResize){
38113             this.autoSizeTabs();
38114         }
38115     },
38116
38117     /**
38118      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38119      */
38120     beginUpdate : function(){
38121         this.updating = true;
38122     },
38123
38124     /**
38125      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38126      */
38127     endUpdate : function(){
38128         this.updating = false;
38129         this.autoSizeTabs();
38130     },
38131
38132     /**
38133      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38134      */
38135     autoSizeTabs : function(){
38136         var count = this.items.length;
38137         var vcount = count - this.hiddenCount;
38138         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38139             return;
38140         }
38141         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38142         var availWidth = Math.floor(w / vcount);
38143         var b = this.stripBody;
38144         if(b.getWidth() > w){
38145             var tabs = this.items;
38146             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38147             if(availWidth < this.minTabWidth){
38148                 /*if(!this.sleft){    // incomplete scrolling code
38149                     this.createScrollButtons();
38150                 }
38151                 this.showScroll();
38152                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38153             }
38154         }else{
38155             if(this.currentTabWidth < this.preferredTabWidth){
38156                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38157             }
38158         }
38159     },
38160
38161     /**
38162      * Returns the number of tabs in this TabPanel.
38163      * @return {Number}
38164      */
38165      getCount : function(){
38166          return this.items.length;
38167      },
38168
38169     /**
38170      * Resizes all the tabs to the passed width
38171      * @param {Number} The new width
38172      */
38173     setTabWidth : function(width){
38174         this.currentTabWidth = width;
38175         for(var i = 0, len = this.items.length; i < len; i++) {
38176                 if(!this.items[i].isHidden()) {
38177                 this.items[i].setWidth(width);
38178             }
38179         }
38180     },
38181
38182     /**
38183      * Destroys this TabPanel
38184      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38185      */
38186     destroy : function(removeEl){
38187         Roo.EventManager.removeResizeListener(this.onResize, this);
38188         for(var i = 0, len = this.items.length; i < len; i++){
38189             this.items[i].purgeListeners();
38190         }
38191         if(removeEl === true){
38192             this.el.update("");
38193             this.el.remove();
38194         }
38195     },
38196     
38197     createStrip : function(container)
38198     {
38199         var strip = document.createElement("nav");
38200         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38201         container.appendChild(strip);
38202         return strip;
38203     },
38204     
38205     createStripList : function(strip)
38206     {
38207         // div wrapper for retard IE
38208         // returns the "tr" element.
38209         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38210         //'<div class="x-tabs-strip-wrap">'+
38211           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38212           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38213         return strip.firstChild; //.firstChild.firstChild.firstChild;
38214     },
38215     createBody : function(container)
38216     {
38217         var body = document.createElement("div");
38218         Roo.id(body, "tab-body");
38219         //Roo.fly(body).addClass("x-tabs-body");
38220         Roo.fly(body).addClass("tab-content");
38221         container.appendChild(body);
38222         return body;
38223     },
38224     createItemBody :function(bodyEl, id){
38225         var body = Roo.getDom(id);
38226         if(!body){
38227             body = document.createElement("div");
38228             body.id = id;
38229         }
38230         //Roo.fly(body).addClass("x-tabs-item-body");
38231         Roo.fly(body).addClass("tab-pane");
38232          bodyEl.insertBefore(body, bodyEl.firstChild);
38233         return body;
38234     },
38235     /** @private */
38236     createStripElements :  function(stripEl, text, closable, tpl)
38237     {
38238         var td = document.createElement("li"); // was td..
38239         
38240         
38241         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38242         
38243         
38244         stripEl.appendChild(td);
38245         /*if(closable){
38246             td.className = "x-tabs-closable";
38247             if(!this.closeTpl){
38248                 this.closeTpl = new Roo.Template(
38249                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38250                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38251                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38252                 );
38253             }
38254             var el = this.closeTpl.overwrite(td, {"text": text});
38255             var close = el.getElementsByTagName("div")[0];
38256             var inner = el.getElementsByTagName("em")[0];
38257             return {"el": el, "close": close, "inner": inner};
38258         } else {
38259         */
38260         // not sure what this is..
38261 //            if(!this.tabTpl){
38262                 //this.tabTpl = new Roo.Template(
38263                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38264                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38265                 //);
38266 //                this.tabTpl = new Roo.Template(
38267 //                   '<a href="#">' +
38268 //                   '<span unselectable="on"' +
38269 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38270 //                            ' >{text}</span></a>'
38271 //                );
38272 //                
38273 //            }
38274
38275
38276             var template = tpl || this.tabTpl || false;
38277             
38278             if(!template){
38279                 
38280                 template = new Roo.Template(
38281                    '<a href="#">' +
38282                    '<span unselectable="on"' +
38283                             (this.disableTooltips ? '' : ' title="{text}"') +
38284                             ' >{text}</span></a>'
38285                 );
38286             }
38287             
38288             switch (typeof(template)) {
38289                 case 'object' :
38290                     break;
38291                 case 'string' :
38292                     template = new Roo.Template(template);
38293                     break;
38294                 default :
38295                     break;
38296             }
38297             
38298             var el = template.overwrite(td, {"text": text});
38299             
38300             var inner = el.getElementsByTagName("span")[0];
38301             
38302             return {"el": el, "inner": inner};
38303             
38304     }
38305         
38306     
38307 });
38308
38309 /**
38310  * @class Roo.TabPanelItem
38311  * @extends Roo.util.Observable
38312  * Represents an individual item (tab plus body) in a TabPanel.
38313  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38314  * @param {String} id The id of this TabPanelItem
38315  * @param {String} text The text for the tab of this TabPanelItem
38316  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38317  */
38318 Roo.bootstrap.panel.TabItem = function(config){
38319     /**
38320      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38321      * @type Roo.TabPanel
38322      */
38323     this.tabPanel = config.panel;
38324     /**
38325      * The id for this TabPanelItem
38326      * @type String
38327      */
38328     this.id = config.id;
38329     /** @private */
38330     this.disabled = false;
38331     /** @private */
38332     this.text = config.text;
38333     /** @private */
38334     this.loaded = false;
38335     this.closable = config.closable;
38336
38337     /**
38338      * The body element for this TabPanelItem.
38339      * @type Roo.Element
38340      */
38341     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38342     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38343     this.bodyEl.setStyle("display", "block");
38344     this.bodyEl.setStyle("zoom", "1");
38345     //this.hideAction();
38346
38347     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38348     /** @private */
38349     this.el = Roo.get(els.el);
38350     this.inner = Roo.get(els.inner, true);
38351     this.textEl = Roo.get(this.el.dom.firstChild, true);
38352     this.pnode = Roo.get(els.el.parentNode, true);
38353 //    this.el.on("mousedown", this.onTabMouseDown, this);
38354     this.el.on("click", this.onTabClick, this);
38355     /** @private */
38356     if(config.closable){
38357         var c = Roo.get(els.close, true);
38358         c.dom.title = this.closeText;
38359         c.addClassOnOver("close-over");
38360         c.on("click", this.closeClick, this);
38361      }
38362
38363     this.addEvents({
38364          /**
38365          * @event activate
38366          * Fires when this tab becomes the active tab.
38367          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38368          * @param {Roo.TabPanelItem} this
38369          */
38370         "activate": true,
38371         /**
38372          * @event beforeclose
38373          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38374          * @param {Roo.TabPanelItem} this
38375          * @param {Object} e Set cancel to true on this object to cancel the close.
38376          */
38377         "beforeclose": true,
38378         /**
38379          * @event close
38380          * Fires when this tab is closed.
38381          * @param {Roo.TabPanelItem} this
38382          */
38383          "close": true,
38384         /**
38385          * @event deactivate
38386          * Fires when this tab is no longer the active tab.
38387          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38388          * @param {Roo.TabPanelItem} this
38389          */
38390          "deactivate" : true
38391     });
38392     this.hidden = false;
38393
38394     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38395 };
38396
38397 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38398            {
38399     purgeListeners : function(){
38400        Roo.util.Observable.prototype.purgeListeners.call(this);
38401        this.el.removeAllListeners();
38402     },
38403     /**
38404      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38405      */
38406     show : function(){
38407         this.pnode.addClass("active");
38408         this.showAction();
38409         if(Roo.isOpera){
38410             this.tabPanel.stripWrap.repaint();
38411         }
38412         this.fireEvent("activate", this.tabPanel, this);
38413     },
38414
38415     /**
38416      * Returns true if this tab is the active tab.
38417      * @return {Boolean}
38418      */
38419     isActive : function(){
38420         return this.tabPanel.getActiveTab() == this;
38421     },
38422
38423     /**
38424      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38425      */
38426     hide : function(){
38427         this.pnode.removeClass("active");
38428         this.hideAction();
38429         this.fireEvent("deactivate", this.tabPanel, this);
38430     },
38431
38432     hideAction : function(){
38433         this.bodyEl.hide();
38434         this.bodyEl.setStyle("position", "absolute");
38435         this.bodyEl.setLeft("-20000px");
38436         this.bodyEl.setTop("-20000px");
38437     },
38438
38439     showAction : function(){
38440         this.bodyEl.setStyle("position", "relative");
38441         this.bodyEl.setTop("");
38442         this.bodyEl.setLeft("");
38443         this.bodyEl.show();
38444     },
38445
38446     /**
38447      * Set the tooltip for the tab.
38448      * @param {String} tooltip The tab's tooltip
38449      */
38450     setTooltip : function(text){
38451         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38452             this.textEl.dom.qtip = text;
38453             this.textEl.dom.removeAttribute('title');
38454         }else{
38455             this.textEl.dom.title = text;
38456         }
38457     },
38458
38459     onTabClick : function(e){
38460         e.preventDefault();
38461         this.tabPanel.activate(this.id);
38462     },
38463
38464     onTabMouseDown : function(e){
38465         e.preventDefault();
38466         this.tabPanel.activate(this.id);
38467     },
38468 /*
38469     getWidth : function(){
38470         return this.inner.getWidth();
38471     },
38472
38473     setWidth : function(width){
38474         var iwidth = width - this.pnode.getPadding("lr");
38475         this.inner.setWidth(iwidth);
38476         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38477         this.pnode.setWidth(width);
38478     },
38479 */
38480     /**
38481      * Show or hide the tab
38482      * @param {Boolean} hidden True to hide or false to show.
38483      */
38484     setHidden : function(hidden){
38485         this.hidden = hidden;
38486         this.pnode.setStyle("display", hidden ? "none" : "");
38487     },
38488
38489     /**
38490      * Returns true if this tab is "hidden"
38491      * @return {Boolean}
38492      */
38493     isHidden : function(){
38494         return this.hidden;
38495     },
38496
38497     /**
38498      * Returns the text for this tab
38499      * @return {String}
38500      */
38501     getText : function(){
38502         return this.text;
38503     },
38504     /*
38505     autoSize : function(){
38506         //this.el.beginMeasure();
38507         this.textEl.setWidth(1);
38508         /*
38509          *  #2804 [new] Tabs in Roojs
38510          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38511          */
38512         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38513         //this.el.endMeasure();
38514     //},
38515
38516     /**
38517      * Sets the text for the tab (Note: this also sets the tooltip text)
38518      * @param {String} text The tab's text and tooltip
38519      */
38520     setText : function(text){
38521         this.text = text;
38522         this.textEl.update(text);
38523         this.setTooltip(text);
38524         //if(!this.tabPanel.resizeTabs){
38525         //    this.autoSize();
38526         //}
38527     },
38528     /**
38529      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38530      */
38531     activate : function(){
38532         this.tabPanel.activate(this.id);
38533     },
38534
38535     /**
38536      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38537      */
38538     disable : function(){
38539         if(this.tabPanel.active != this){
38540             this.disabled = true;
38541             this.pnode.addClass("disabled");
38542         }
38543     },
38544
38545     /**
38546      * Enables this TabPanelItem if it was previously disabled.
38547      */
38548     enable : function(){
38549         this.disabled = false;
38550         this.pnode.removeClass("disabled");
38551     },
38552
38553     /**
38554      * Sets the content for this TabPanelItem.
38555      * @param {String} content The content
38556      * @param {Boolean} loadScripts true to look for and load scripts
38557      */
38558     setContent : function(content, loadScripts){
38559         this.bodyEl.update(content, loadScripts);
38560     },
38561
38562     /**
38563      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38564      * @return {Roo.UpdateManager} The UpdateManager
38565      */
38566     getUpdateManager : function(){
38567         return this.bodyEl.getUpdateManager();
38568     },
38569
38570     /**
38571      * Set a URL to be used to load the content for this TabPanelItem.
38572      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38573      * @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)
38574      * @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)
38575      * @return {Roo.UpdateManager} The UpdateManager
38576      */
38577     setUrl : function(url, params, loadOnce){
38578         if(this.refreshDelegate){
38579             this.un('activate', this.refreshDelegate);
38580         }
38581         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38582         this.on("activate", this.refreshDelegate);
38583         return this.bodyEl.getUpdateManager();
38584     },
38585
38586     /** @private */
38587     _handleRefresh : function(url, params, loadOnce){
38588         if(!loadOnce || !this.loaded){
38589             var updater = this.bodyEl.getUpdateManager();
38590             updater.update(url, params, this._setLoaded.createDelegate(this));
38591         }
38592     },
38593
38594     /**
38595      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38596      *   Will fail silently if the setUrl method has not been called.
38597      *   This does not activate the panel, just updates its content.
38598      */
38599     refresh : function(){
38600         if(this.refreshDelegate){
38601            this.loaded = false;
38602            this.refreshDelegate();
38603         }
38604     },
38605
38606     /** @private */
38607     _setLoaded : function(){
38608         this.loaded = true;
38609     },
38610
38611     /** @private */
38612     closeClick : function(e){
38613         var o = {};
38614         e.stopEvent();
38615         this.fireEvent("beforeclose", this, o);
38616         if(o.cancel !== true){
38617             this.tabPanel.removeTab(this.id);
38618         }
38619     },
38620     /**
38621      * The text displayed in the tooltip for the close icon.
38622      * @type String
38623      */
38624     closeText : "Close this tab"
38625 });
38626 /**
38627 *    This script refer to:
38628 *    Title: International Telephone Input
38629 *    Author: Jack O'Connor
38630 *    Code version:  v12.1.12
38631 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38632 **/
38633
38634 Roo.bootstrap.PhoneInputData = function() {
38635     var d = [
38636       [
38637         "Afghanistan (‫افغانستان‬‎)",
38638         "af",
38639         "93"
38640       ],
38641       [
38642         "Albania (Shqipëri)",
38643         "al",
38644         "355"
38645       ],
38646       [
38647         "Algeria (‫الجزائر‬‎)",
38648         "dz",
38649         "213"
38650       ],
38651       [
38652         "American Samoa",
38653         "as",
38654         "1684"
38655       ],
38656       [
38657         "Andorra",
38658         "ad",
38659         "376"
38660       ],
38661       [
38662         "Angola",
38663         "ao",
38664         "244"
38665       ],
38666       [
38667         "Anguilla",
38668         "ai",
38669         "1264"
38670       ],
38671       [
38672         "Antigua and Barbuda",
38673         "ag",
38674         "1268"
38675       ],
38676       [
38677         "Argentina",
38678         "ar",
38679         "54"
38680       ],
38681       [
38682         "Armenia (Հայաստան)",
38683         "am",
38684         "374"
38685       ],
38686       [
38687         "Aruba",
38688         "aw",
38689         "297"
38690       ],
38691       [
38692         "Australia",
38693         "au",
38694         "61",
38695         0
38696       ],
38697       [
38698         "Austria (Österreich)",
38699         "at",
38700         "43"
38701       ],
38702       [
38703         "Azerbaijan (Azərbaycan)",
38704         "az",
38705         "994"
38706       ],
38707       [
38708         "Bahamas",
38709         "bs",
38710         "1242"
38711       ],
38712       [
38713         "Bahrain (‫البحرين‬‎)",
38714         "bh",
38715         "973"
38716       ],
38717       [
38718         "Bangladesh (বাংলাদেশ)",
38719         "bd",
38720         "880"
38721       ],
38722       [
38723         "Barbados",
38724         "bb",
38725         "1246"
38726       ],
38727       [
38728         "Belarus (Беларусь)",
38729         "by",
38730         "375"
38731       ],
38732       [
38733         "Belgium (België)",
38734         "be",
38735         "32"
38736       ],
38737       [
38738         "Belize",
38739         "bz",
38740         "501"
38741       ],
38742       [
38743         "Benin (Bénin)",
38744         "bj",
38745         "229"
38746       ],
38747       [
38748         "Bermuda",
38749         "bm",
38750         "1441"
38751       ],
38752       [
38753         "Bhutan (འབྲུག)",
38754         "bt",
38755         "975"
38756       ],
38757       [
38758         "Bolivia",
38759         "bo",
38760         "591"
38761       ],
38762       [
38763         "Bosnia and Herzegovina (Босна и Херцеговина)",
38764         "ba",
38765         "387"
38766       ],
38767       [
38768         "Botswana",
38769         "bw",
38770         "267"
38771       ],
38772       [
38773         "Brazil (Brasil)",
38774         "br",
38775         "55"
38776       ],
38777       [
38778         "British Indian Ocean Territory",
38779         "io",
38780         "246"
38781       ],
38782       [
38783         "British Virgin Islands",
38784         "vg",
38785         "1284"
38786       ],
38787       [
38788         "Brunei",
38789         "bn",
38790         "673"
38791       ],
38792       [
38793         "Bulgaria (България)",
38794         "bg",
38795         "359"
38796       ],
38797       [
38798         "Burkina Faso",
38799         "bf",
38800         "226"
38801       ],
38802       [
38803         "Burundi (Uburundi)",
38804         "bi",
38805         "257"
38806       ],
38807       [
38808         "Cambodia (កម្ពុជា)",
38809         "kh",
38810         "855"
38811       ],
38812       [
38813         "Cameroon (Cameroun)",
38814         "cm",
38815         "237"
38816       ],
38817       [
38818         "Canada",
38819         "ca",
38820         "1",
38821         1,
38822         ["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"]
38823       ],
38824       [
38825         "Cape Verde (Kabu Verdi)",
38826         "cv",
38827         "238"
38828       ],
38829       [
38830         "Caribbean Netherlands",
38831         "bq",
38832         "599",
38833         1
38834       ],
38835       [
38836         "Cayman Islands",
38837         "ky",
38838         "1345"
38839       ],
38840       [
38841         "Central African Republic (République centrafricaine)",
38842         "cf",
38843         "236"
38844       ],
38845       [
38846         "Chad (Tchad)",
38847         "td",
38848         "235"
38849       ],
38850       [
38851         "Chile",
38852         "cl",
38853         "56"
38854       ],
38855       [
38856         "China (中国)",
38857         "cn",
38858         "86"
38859       ],
38860       [
38861         "Christmas Island",
38862         "cx",
38863         "61",
38864         2
38865       ],
38866       [
38867         "Cocos (Keeling) Islands",
38868         "cc",
38869         "61",
38870         1
38871       ],
38872       [
38873         "Colombia",
38874         "co",
38875         "57"
38876       ],
38877       [
38878         "Comoros (‫جزر القمر‬‎)",
38879         "km",
38880         "269"
38881       ],
38882       [
38883         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38884         "cd",
38885         "243"
38886       ],
38887       [
38888         "Congo (Republic) (Congo-Brazzaville)",
38889         "cg",
38890         "242"
38891       ],
38892       [
38893         "Cook Islands",
38894         "ck",
38895         "682"
38896       ],
38897       [
38898         "Costa Rica",
38899         "cr",
38900         "506"
38901       ],
38902       [
38903         "Côte d’Ivoire",
38904         "ci",
38905         "225"
38906       ],
38907       [
38908         "Croatia (Hrvatska)",
38909         "hr",
38910         "385"
38911       ],
38912       [
38913         "Cuba",
38914         "cu",
38915         "53"
38916       ],
38917       [
38918         "Curaçao",
38919         "cw",
38920         "599",
38921         0
38922       ],
38923       [
38924         "Cyprus (Κύπρος)",
38925         "cy",
38926         "357"
38927       ],
38928       [
38929         "Czech Republic (Česká republika)",
38930         "cz",
38931         "420"
38932       ],
38933       [
38934         "Denmark (Danmark)",
38935         "dk",
38936         "45"
38937       ],
38938       [
38939         "Djibouti",
38940         "dj",
38941         "253"
38942       ],
38943       [
38944         "Dominica",
38945         "dm",
38946         "1767"
38947       ],
38948       [
38949         "Dominican Republic (República Dominicana)",
38950         "do",
38951         "1",
38952         2,
38953         ["809", "829", "849"]
38954       ],
38955       [
38956         "Ecuador",
38957         "ec",
38958         "593"
38959       ],
38960       [
38961         "Egypt (‫مصر‬‎)",
38962         "eg",
38963         "20"
38964       ],
38965       [
38966         "El Salvador",
38967         "sv",
38968         "503"
38969       ],
38970       [
38971         "Equatorial Guinea (Guinea Ecuatorial)",
38972         "gq",
38973         "240"
38974       ],
38975       [
38976         "Eritrea",
38977         "er",
38978         "291"
38979       ],
38980       [
38981         "Estonia (Eesti)",
38982         "ee",
38983         "372"
38984       ],
38985       [
38986         "Ethiopia",
38987         "et",
38988         "251"
38989       ],
38990       [
38991         "Falkland Islands (Islas Malvinas)",
38992         "fk",
38993         "500"
38994       ],
38995       [
38996         "Faroe Islands (Føroyar)",
38997         "fo",
38998         "298"
38999       ],
39000       [
39001         "Fiji",
39002         "fj",
39003         "679"
39004       ],
39005       [
39006         "Finland (Suomi)",
39007         "fi",
39008         "358",
39009         0
39010       ],
39011       [
39012         "France",
39013         "fr",
39014         "33"
39015       ],
39016       [
39017         "French Guiana (Guyane française)",
39018         "gf",
39019         "594"
39020       ],
39021       [
39022         "French Polynesia (Polynésie française)",
39023         "pf",
39024         "689"
39025       ],
39026       [
39027         "Gabon",
39028         "ga",
39029         "241"
39030       ],
39031       [
39032         "Gambia",
39033         "gm",
39034         "220"
39035       ],
39036       [
39037         "Georgia (საქართველო)",
39038         "ge",
39039         "995"
39040       ],
39041       [
39042         "Germany (Deutschland)",
39043         "de",
39044         "49"
39045       ],
39046       [
39047         "Ghana (Gaana)",
39048         "gh",
39049         "233"
39050       ],
39051       [
39052         "Gibraltar",
39053         "gi",
39054         "350"
39055       ],
39056       [
39057         "Greece (Ελλάδα)",
39058         "gr",
39059         "30"
39060       ],
39061       [
39062         "Greenland (Kalaallit Nunaat)",
39063         "gl",
39064         "299"
39065       ],
39066       [
39067         "Grenada",
39068         "gd",
39069         "1473"
39070       ],
39071       [
39072         "Guadeloupe",
39073         "gp",
39074         "590",
39075         0
39076       ],
39077       [
39078         "Guam",
39079         "gu",
39080         "1671"
39081       ],
39082       [
39083         "Guatemala",
39084         "gt",
39085         "502"
39086       ],
39087       [
39088         "Guernsey",
39089         "gg",
39090         "44",
39091         1
39092       ],
39093       [
39094         "Guinea (Guinée)",
39095         "gn",
39096         "224"
39097       ],
39098       [
39099         "Guinea-Bissau (Guiné Bissau)",
39100         "gw",
39101         "245"
39102       ],
39103       [
39104         "Guyana",
39105         "gy",
39106         "592"
39107       ],
39108       [
39109         "Haiti",
39110         "ht",
39111         "509"
39112       ],
39113       [
39114         "Honduras",
39115         "hn",
39116         "504"
39117       ],
39118       [
39119         "Hong Kong (香港)",
39120         "hk",
39121         "852"
39122       ],
39123       [
39124         "Hungary (Magyarország)",
39125         "hu",
39126         "36"
39127       ],
39128       [
39129         "Iceland (Ísland)",
39130         "is",
39131         "354"
39132       ],
39133       [
39134         "India (भारत)",
39135         "in",
39136         "91"
39137       ],
39138       [
39139         "Indonesia",
39140         "id",
39141         "62"
39142       ],
39143       [
39144         "Iran (‫ایران‬‎)",
39145         "ir",
39146         "98"
39147       ],
39148       [
39149         "Iraq (‫العراق‬‎)",
39150         "iq",
39151         "964"
39152       ],
39153       [
39154         "Ireland",
39155         "ie",
39156         "353"
39157       ],
39158       [
39159         "Isle of Man",
39160         "im",
39161         "44",
39162         2
39163       ],
39164       [
39165         "Israel (‫ישראל‬‎)",
39166         "il",
39167         "972"
39168       ],
39169       [
39170         "Italy (Italia)",
39171         "it",
39172         "39",
39173         0
39174       ],
39175       [
39176         "Jamaica",
39177         "jm",
39178         "1876"
39179       ],
39180       [
39181         "Japan (日本)",
39182         "jp",
39183         "81"
39184       ],
39185       [
39186         "Jersey",
39187         "je",
39188         "44",
39189         3
39190       ],
39191       [
39192         "Jordan (‫الأردن‬‎)",
39193         "jo",
39194         "962"
39195       ],
39196       [
39197         "Kazakhstan (Казахстан)",
39198         "kz",
39199         "7",
39200         1
39201       ],
39202       [
39203         "Kenya",
39204         "ke",
39205         "254"
39206       ],
39207       [
39208         "Kiribati",
39209         "ki",
39210         "686"
39211       ],
39212       [
39213         "Kosovo",
39214         "xk",
39215         "383"
39216       ],
39217       [
39218         "Kuwait (‫الكويت‬‎)",
39219         "kw",
39220         "965"
39221       ],
39222       [
39223         "Kyrgyzstan (Кыргызстан)",
39224         "kg",
39225         "996"
39226       ],
39227       [
39228         "Laos (ລາວ)",
39229         "la",
39230         "856"
39231       ],
39232       [
39233         "Latvia (Latvija)",
39234         "lv",
39235         "371"
39236       ],
39237       [
39238         "Lebanon (‫لبنان‬‎)",
39239         "lb",
39240         "961"
39241       ],
39242       [
39243         "Lesotho",
39244         "ls",
39245         "266"
39246       ],
39247       [
39248         "Liberia",
39249         "lr",
39250         "231"
39251       ],
39252       [
39253         "Libya (‫ليبيا‬‎)",
39254         "ly",
39255         "218"
39256       ],
39257       [
39258         "Liechtenstein",
39259         "li",
39260         "423"
39261       ],
39262       [
39263         "Lithuania (Lietuva)",
39264         "lt",
39265         "370"
39266       ],
39267       [
39268         "Luxembourg",
39269         "lu",
39270         "352"
39271       ],
39272       [
39273         "Macau (澳門)",
39274         "mo",
39275         "853"
39276       ],
39277       [
39278         "Macedonia (FYROM) (Македонија)",
39279         "mk",
39280         "389"
39281       ],
39282       [
39283         "Madagascar (Madagasikara)",
39284         "mg",
39285         "261"
39286       ],
39287       [
39288         "Malawi",
39289         "mw",
39290         "265"
39291       ],
39292       [
39293         "Malaysia",
39294         "my",
39295         "60"
39296       ],
39297       [
39298         "Maldives",
39299         "mv",
39300         "960"
39301       ],
39302       [
39303         "Mali",
39304         "ml",
39305         "223"
39306       ],
39307       [
39308         "Malta",
39309         "mt",
39310         "356"
39311       ],
39312       [
39313         "Marshall Islands",
39314         "mh",
39315         "692"
39316       ],
39317       [
39318         "Martinique",
39319         "mq",
39320         "596"
39321       ],
39322       [
39323         "Mauritania (‫موريتانيا‬‎)",
39324         "mr",
39325         "222"
39326       ],
39327       [
39328         "Mauritius (Moris)",
39329         "mu",
39330         "230"
39331       ],
39332       [
39333         "Mayotte",
39334         "yt",
39335         "262",
39336         1
39337       ],
39338       [
39339         "Mexico (México)",
39340         "mx",
39341         "52"
39342       ],
39343       [
39344         "Micronesia",
39345         "fm",
39346         "691"
39347       ],
39348       [
39349         "Moldova (Republica Moldova)",
39350         "md",
39351         "373"
39352       ],
39353       [
39354         "Monaco",
39355         "mc",
39356         "377"
39357       ],
39358       [
39359         "Mongolia (Монгол)",
39360         "mn",
39361         "976"
39362       ],
39363       [
39364         "Montenegro (Crna Gora)",
39365         "me",
39366         "382"
39367       ],
39368       [
39369         "Montserrat",
39370         "ms",
39371         "1664"
39372       ],
39373       [
39374         "Morocco (‫المغرب‬‎)",
39375         "ma",
39376         "212",
39377         0
39378       ],
39379       [
39380         "Mozambique (Moçambique)",
39381         "mz",
39382         "258"
39383       ],
39384       [
39385         "Myanmar (Burma) (မြန်မာ)",
39386         "mm",
39387         "95"
39388       ],
39389       [
39390         "Namibia (Namibië)",
39391         "na",
39392         "264"
39393       ],
39394       [
39395         "Nauru",
39396         "nr",
39397         "674"
39398       ],
39399       [
39400         "Nepal (नेपाल)",
39401         "np",
39402         "977"
39403       ],
39404       [
39405         "Netherlands (Nederland)",
39406         "nl",
39407         "31"
39408       ],
39409       [
39410         "New Caledonia (Nouvelle-Calédonie)",
39411         "nc",
39412         "687"
39413       ],
39414       [
39415         "New Zealand",
39416         "nz",
39417         "64"
39418       ],
39419       [
39420         "Nicaragua",
39421         "ni",
39422         "505"
39423       ],
39424       [
39425         "Niger (Nijar)",
39426         "ne",
39427         "227"
39428       ],
39429       [
39430         "Nigeria",
39431         "ng",
39432         "234"
39433       ],
39434       [
39435         "Niue",
39436         "nu",
39437         "683"
39438       ],
39439       [
39440         "Norfolk Island",
39441         "nf",
39442         "672"
39443       ],
39444       [
39445         "North Korea (조선 민주주의 인민 공화국)",
39446         "kp",
39447         "850"
39448       ],
39449       [
39450         "Northern Mariana Islands",
39451         "mp",
39452         "1670"
39453       ],
39454       [
39455         "Norway (Norge)",
39456         "no",
39457         "47",
39458         0
39459       ],
39460       [
39461         "Oman (‫عُمان‬‎)",
39462         "om",
39463         "968"
39464       ],
39465       [
39466         "Pakistan (‫پاکستان‬‎)",
39467         "pk",
39468         "92"
39469       ],
39470       [
39471         "Palau",
39472         "pw",
39473         "680"
39474       ],
39475       [
39476         "Palestine (‫فلسطين‬‎)",
39477         "ps",
39478         "970"
39479       ],
39480       [
39481         "Panama (Panamá)",
39482         "pa",
39483         "507"
39484       ],
39485       [
39486         "Papua New Guinea",
39487         "pg",
39488         "675"
39489       ],
39490       [
39491         "Paraguay",
39492         "py",
39493         "595"
39494       ],
39495       [
39496         "Peru (Perú)",
39497         "pe",
39498         "51"
39499       ],
39500       [
39501         "Philippines",
39502         "ph",
39503         "63"
39504       ],
39505       [
39506         "Poland (Polska)",
39507         "pl",
39508         "48"
39509       ],
39510       [
39511         "Portugal",
39512         "pt",
39513         "351"
39514       ],
39515       [
39516         "Puerto Rico",
39517         "pr",
39518         "1",
39519         3,
39520         ["787", "939"]
39521       ],
39522       [
39523         "Qatar (‫قطر‬‎)",
39524         "qa",
39525         "974"
39526       ],
39527       [
39528         "Réunion (La Réunion)",
39529         "re",
39530         "262",
39531         0
39532       ],
39533       [
39534         "Romania (România)",
39535         "ro",
39536         "40"
39537       ],
39538       [
39539         "Russia (Россия)",
39540         "ru",
39541         "7",
39542         0
39543       ],
39544       [
39545         "Rwanda",
39546         "rw",
39547         "250"
39548       ],
39549       [
39550         "Saint Barthélemy",
39551         "bl",
39552         "590",
39553         1
39554       ],
39555       [
39556         "Saint Helena",
39557         "sh",
39558         "290"
39559       ],
39560       [
39561         "Saint Kitts and Nevis",
39562         "kn",
39563         "1869"
39564       ],
39565       [
39566         "Saint Lucia",
39567         "lc",
39568         "1758"
39569       ],
39570       [
39571         "Saint Martin (Saint-Martin (partie française))",
39572         "mf",
39573         "590",
39574         2
39575       ],
39576       [
39577         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39578         "pm",
39579         "508"
39580       ],
39581       [
39582         "Saint Vincent and the Grenadines",
39583         "vc",
39584         "1784"
39585       ],
39586       [
39587         "Samoa",
39588         "ws",
39589         "685"
39590       ],
39591       [
39592         "San Marino",
39593         "sm",
39594         "378"
39595       ],
39596       [
39597         "São Tomé and Príncipe (São Tomé e Príncipe)",
39598         "st",
39599         "239"
39600       ],
39601       [
39602         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39603         "sa",
39604         "966"
39605       ],
39606       [
39607         "Senegal (Sénégal)",
39608         "sn",
39609         "221"
39610       ],
39611       [
39612         "Serbia (Србија)",
39613         "rs",
39614         "381"
39615       ],
39616       [
39617         "Seychelles",
39618         "sc",
39619         "248"
39620       ],
39621       [
39622         "Sierra Leone",
39623         "sl",
39624         "232"
39625       ],
39626       [
39627         "Singapore",
39628         "sg",
39629         "65"
39630       ],
39631       [
39632         "Sint Maarten",
39633         "sx",
39634         "1721"
39635       ],
39636       [
39637         "Slovakia (Slovensko)",
39638         "sk",
39639         "421"
39640       ],
39641       [
39642         "Slovenia (Slovenija)",
39643         "si",
39644         "386"
39645       ],
39646       [
39647         "Solomon Islands",
39648         "sb",
39649         "677"
39650       ],
39651       [
39652         "Somalia (Soomaaliya)",
39653         "so",
39654         "252"
39655       ],
39656       [
39657         "South Africa",
39658         "za",
39659         "27"
39660       ],
39661       [
39662         "South Korea (대한민국)",
39663         "kr",
39664         "82"
39665       ],
39666       [
39667         "South Sudan (‫جنوب السودان‬‎)",
39668         "ss",
39669         "211"
39670       ],
39671       [
39672         "Spain (España)",
39673         "es",
39674         "34"
39675       ],
39676       [
39677         "Sri Lanka (ශ්‍රී ලංකාව)",
39678         "lk",
39679         "94"
39680       ],
39681       [
39682         "Sudan (‫السودان‬‎)",
39683         "sd",
39684         "249"
39685       ],
39686       [
39687         "Suriname",
39688         "sr",
39689         "597"
39690       ],
39691       [
39692         "Svalbard and Jan Mayen",
39693         "sj",
39694         "47",
39695         1
39696       ],
39697       [
39698         "Swaziland",
39699         "sz",
39700         "268"
39701       ],
39702       [
39703         "Sweden (Sverige)",
39704         "se",
39705         "46"
39706       ],
39707       [
39708         "Switzerland (Schweiz)",
39709         "ch",
39710         "41"
39711       ],
39712       [
39713         "Syria (‫سوريا‬‎)",
39714         "sy",
39715         "963"
39716       ],
39717       [
39718         "Taiwan (台灣)",
39719         "tw",
39720         "886"
39721       ],
39722       [
39723         "Tajikistan",
39724         "tj",
39725         "992"
39726       ],
39727       [
39728         "Tanzania",
39729         "tz",
39730         "255"
39731       ],
39732       [
39733         "Thailand (ไทย)",
39734         "th",
39735         "66"
39736       ],
39737       [
39738         "Timor-Leste",
39739         "tl",
39740         "670"
39741       ],
39742       [
39743         "Togo",
39744         "tg",
39745         "228"
39746       ],
39747       [
39748         "Tokelau",
39749         "tk",
39750         "690"
39751       ],
39752       [
39753         "Tonga",
39754         "to",
39755         "676"
39756       ],
39757       [
39758         "Trinidad and Tobago",
39759         "tt",
39760         "1868"
39761       ],
39762       [
39763         "Tunisia (‫تونس‬‎)",
39764         "tn",
39765         "216"
39766       ],
39767       [
39768         "Turkey (Türkiye)",
39769         "tr",
39770         "90"
39771       ],
39772       [
39773         "Turkmenistan",
39774         "tm",
39775         "993"
39776       ],
39777       [
39778         "Turks and Caicos Islands",
39779         "tc",
39780         "1649"
39781       ],
39782       [
39783         "Tuvalu",
39784         "tv",
39785         "688"
39786       ],
39787       [
39788         "U.S. Virgin Islands",
39789         "vi",
39790         "1340"
39791       ],
39792       [
39793         "Uganda",
39794         "ug",
39795         "256"
39796       ],
39797       [
39798         "Ukraine (Україна)",
39799         "ua",
39800         "380"
39801       ],
39802       [
39803         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39804         "ae",
39805         "971"
39806       ],
39807       [
39808         "United Kingdom",
39809         "gb",
39810         "44",
39811         0
39812       ],
39813       [
39814         "United States",
39815         "us",
39816         "1",
39817         0
39818       ],
39819       [
39820         "Uruguay",
39821         "uy",
39822         "598"
39823       ],
39824       [
39825         "Uzbekistan (Oʻzbekiston)",
39826         "uz",
39827         "998"
39828       ],
39829       [
39830         "Vanuatu",
39831         "vu",
39832         "678"
39833       ],
39834       [
39835         "Vatican City (Città del Vaticano)",
39836         "va",
39837         "39",
39838         1
39839       ],
39840       [
39841         "Venezuela",
39842         "ve",
39843         "58"
39844       ],
39845       [
39846         "Vietnam (Việt Nam)",
39847         "vn",
39848         "84"
39849       ],
39850       [
39851         "Wallis and Futuna (Wallis-et-Futuna)",
39852         "wf",
39853         "681"
39854       ],
39855       [
39856         "Western Sahara (‫الصحراء الغربية‬‎)",
39857         "eh",
39858         "212",
39859         1
39860       ],
39861       [
39862         "Yemen (‫اليمن‬‎)",
39863         "ye",
39864         "967"
39865       ],
39866       [
39867         "Zambia",
39868         "zm",
39869         "260"
39870       ],
39871       [
39872         "Zimbabwe",
39873         "zw",
39874         "263"
39875       ],
39876       [
39877         "Åland Islands",
39878         "ax",
39879         "358",
39880         1
39881       ]
39882   ];
39883   
39884   return d;
39885 }/**
39886 *    This script refer to:
39887 *    Title: International Telephone Input
39888 *    Author: Jack O'Connor
39889 *    Code version:  v12.1.12
39890 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39891 **/
39892
39893 /**
39894  * @class Roo.bootstrap.PhoneInput
39895  * @extends Roo.bootstrap.TriggerField
39896  * An input with International dial-code selection
39897  
39898  * @cfg {String} defaultDialCode default '+852'
39899  * @cfg {Array} preferedCountries default []
39900   
39901  * @constructor
39902  * Create a new PhoneInput.
39903  * @param {Object} config Configuration options
39904  */
39905
39906 Roo.bootstrap.PhoneInput = function(config) {
39907     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39908 };
39909
39910 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39911         
39912         listWidth: undefined,
39913         
39914         selectedClass: 'active',
39915         
39916         invalidClass : "has-warning",
39917         
39918         validClass: 'has-success',
39919         
39920         allowed: '0123456789',
39921         
39922         max_length: 15,
39923         
39924         /**
39925          * @cfg {String} defaultDialCode The default dial code when initializing the input
39926          */
39927         defaultDialCode: '+852',
39928         
39929         /**
39930          * @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
39931          */
39932         preferedCountries: false,
39933         
39934         getAutoCreate : function()
39935         {
39936             var data = Roo.bootstrap.PhoneInputData();
39937             var align = this.labelAlign || this.parentLabelAlign();
39938             var id = Roo.id();
39939             
39940             this.allCountries = [];
39941             this.dialCodeMapping = [];
39942             
39943             for (var i = 0; i < data.length; i++) {
39944               var c = data[i];
39945               this.allCountries[i] = {
39946                 name: c[0],
39947                 iso2: c[1],
39948                 dialCode: c[2],
39949                 priority: c[3] || 0,
39950                 areaCodes: c[4] || null
39951               };
39952               this.dialCodeMapping[c[2]] = {
39953                   name: c[0],
39954                   iso2: c[1],
39955                   priority: c[3] || 0,
39956                   areaCodes: c[4] || null
39957               };
39958             }
39959             
39960             var cfg = {
39961                 cls: 'form-group',
39962                 cn: []
39963             };
39964             
39965             var input =  {
39966                 tag: 'input',
39967                 id : id,
39968                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39969                 maxlength: this.max_length,
39970                 cls : 'form-control tel-input',
39971                 autocomplete: 'new-password'
39972             };
39973             
39974             var hiddenInput = {
39975                 tag: 'input',
39976                 type: 'hidden',
39977                 cls: 'hidden-tel-input'
39978             };
39979             
39980             if (this.name) {
39981                 hiddenInput.name = this.name;
39982             }
39983             
39984             if (this.disabled) {
39985                 input.disabled = true;
39986             }
39987             
39988             var flag_container = {
39989                 tag: 'div',
39990                 cls: 'flag-box',
39991                 cn: [
39992                     {
39993                         tag: 'div',
39994                         cls: 'flag'
39995                     },
39996                     {
39997                         tag: 'div',
39998                         cls: 'caret'
39999                     }
40000                 ]
40001             };
40002             
40003             var box = {
40004                 tag: 'div',
40005                 cls: this.hasFeedback ? 'has-feedback' : '',
40006                 cn: [
40007                     hiddenInput,
40008                     input,
40009                     {
40010                         tag: 'input',
40011                         cls: 'dial-code-holder',
40012                         disabled: true
40013                     }
40014                 ]
40015             };
40016             
40017             var container = {
40018                 cls: 'roo-select2-container input-group',
40019                 cn: [
40020                     flag_container,
40021                     box
40022                 ]
40023             };
40024             
40025             if (this.fieldLabel.length) {
40026                 var indicator = {
40027                     tag: 'i',
40028                     tooltip: 'This field is required'
40029                 };
40030                 
40031                 var label = {
40032                     tag: 'label',
40033                     'for':  id,
40034                     cls: 'control-label',
40035                     cn: []
40036                 };
40037                 
40038                 var label_text = {
40039                     tag: 'span',
40040                     html: this.fieldLabel
40041                 };
40042                 
40043                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40044                 label.cn = [
40045                     indicator,
40046                     label_text
40047                 ];
40048                 
40049                 if(this.indicatorpos == 'right') {
40050                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40051                     label.cn = [
40052                         label_text,
40053                         indicator
40054                     ];
40055                 }
40056                 
40057                 if(align == 'left') {
40058                     container = {
40059                         tag: 'div',
40060                         cn: [
40061                             container
40062                         ]
40063                     };
40064                     
40065                     if(this.labelWidth > 12){
40066                         label.style = "width: " + this.labelWidth + 'px';
40067                     }
40068                     if(this.labelWidth < 13 && this.labelmd == 0){
40069                         this.labelmd = this.labelWidth;
40070                     }
40071                     if(this.labellg > 0){
40072                         label.cls += ' col-lg-' + this.labellg;
40073                         input.cls += ' col-lg-' + (12 - this.labellg);
40074                     }
40075                     if(this.labelmd > 0){
40076                         label.cls += ' col-md-' + this.labelmd;
40077                         container.cls += ' col-md-' + (12 - this.labelmd);
40078                     }
40079                     if(this.labelsm > 0){
40080                         label.cls += ' col-sm-' + this.labelsm;
40081                         container.cls += ' col-sm-' + (12 - this.labelsm);
40082                     }
40083                     if(this.labelxs > 0){
40084                         label.cls += ' col-xs-' + this.labelxs;
40085                         container.cls += ' col-xs-' + (12 - this.labelxs);
40086                     }
40087                 }
40088             }
40089             
40090             cfg.cn = [
40091                 label,
40092                 container
40093             ];
40094             
40095             var settings = this;
40096             
40097             ['xs','sm','md','lg'].map(function(size){
40098                 if (settings[size]) {
40099                     cfg.cls += ' col-' + size + '-' + settings[size];
40100                 }
40101             });
40102             
40103             this.store = new Roo.data.Store({
40104                 proxy : new Roo.data.MemoryProxy({}),
40105                 reader : new Roo.data.JsonReader({
40106                     fields : [
40107                         {
40108                             'name' : 'name',
40109                             'type' : 'string'
40110                         },
40111                         {
40112                             'name' : 'iso2',
40113                             'type' : 'string'
40114                         },
40115                         {
40116                             'name' : 'dialCode',
40117                             'type' : 'string'
40118                         },
40119                         {
40120                             'name' : 'priority',
40121                             'type' : 'string'
40122                         },
40123                         {
40124                             'name' : 'areaCodes',
40125                             'type' : 'string'
40126                         }
40127                     ]
40128                 })
40129             });
40130             
40131             if(!this.preferedCountries) {
40132                 this.preferedCountries = [
40133                     'hk',
40134                     'gb',
40135                     'us'
40136                 ];
40137             }
40138             
40139             var p = this.preferedCountries.reverse();
40140             
40141             if(p) {
40142                 for (var i = 0; i < p.length; i++) {
40143                     for (var j = 0; j < this.allCountries.length; j++) {
40144                         if(this.allCountries[j].iso2 == p[i]) {
40145                             var t = this.allCountries[j];
40146                             this.allCountries.splice(j,1);
40147                             this.allCountries.unshift(t);
40148                         }
40149                     } 
40150                 }
40151             }
40152             
40153             this.store.proxy.data = {
40154                 success: true,
40155                 data: this.allCountries
40156             };
40157             
40158             return cfg;
40159         },
40160         
40161         initEvents : function()
40162         {
40163             this.createList();
40164             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40165             
40166             this.indicator = this.indicatorEl();
40167             this.flag = this.flagEl();
40168             this.dialCodeHolder = this.dialCodeHolderEl();
40169             
40170             this.trigger = this.el.select('div.flag-box',true).first();
40171             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40172             
40173             var _this = this;
40174             
40175             (function(){
40176                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40177                 _this.list.setWidth(lw);
40178             }).defer(100);
40179             
40180             this.list.on('mouseover', this.onViewOver, this);
40181             this.list.on('mousemove', this.onViewMove, this);
40182             this.inputEl().on("keyup", this.onKeyUp, this);
40183             this.inputEl().on("keypress", this.onKeyPress, this);
40184             
40185             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40186
40187             this.view = new Roo.View(this.list, this.tpl, {
40188                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40189             });
40190             
40191             this.view.on('click', this.onViewClick, this);
40192             this.setValue(this.defaultDialCode);
40193         },
40194         
40195         onTriggerClick : function(e)
40196         {
40197             Roo.log('trigger click');
40198             if(this.disabled){
40199                 return;
40200             }
40201             
40202             if(this.isExpanded()){
40203                 this.collapse();
40204                 this.hasFocus = false;
40205             }else {
40206                 this.store.load({});
40207                 this.hasFocus = true;
40208                 this.expand();
40209             }
40210         },
40211         
40212         isExpanded : function()
40213         {
40214             return this.list.isVisible();
40215         },
40216         
40217         collapse : function()
40218         {
40219             if(!this.isExpanded()){
40220                 return;
40221             }
40222             this.list.hide();
40223             Roo.get(document).un('mousedown', this.collapseIf, this);
40224             Roo.get(document).un('mousewheel', this.collapseIf, this);
40225             this.fireEvent('collapse', this);
40226             this.validate();
40227         },
40228         
40229         expand : function()
40230         {
40231             Roo.log('expand');
40232
40233             if(this.isExpanded() || !this.hasFocus){
40234                 return;
40235             }
40236             
40237             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40238             this.list.setWidth(lw);
40239             
40240             this.list.show();
40241             this.restrictHeight();
40242             
40243             Roo.get(document).on('mousedown', this.collapseIf, this);
40244             Roo.get(document).on('mousewheel', this.collapseIf, this);
40245             
40246             this.fireEvent('expand', this);
40247         },
40248         
40249         restrictHeight : function()
40250         {
40251             this.list.alignTo(this.inputEl(), this.listAlign);
40252             this.list.alignTo(this.inputEl(), this.listAlign);
40253         },
40254         
40255         onViewOver : function(e, t)
40256         {
40257             if(this.inKeyMode){
40258                 return;
40259             }
40260             var item = this.view.findItemFromChild(t);
40261             
40262             if(item){
40263                 var index = this.view.indexOf(item);
40264                 this.select(index, false);
40265             }
40266         },
40267
40268         // private
40269         onViewClick : function(view, doFocus, el, e)
40270         {
40271             var index = this.view.getSelectedIndexes()[0];
40272             
40273             var r = this.store.getAt(index);
40274             
40275             if(r){
40276                 this.onSelect(r, index);
40277             }
40278             if(doFocus !== false && !this.blockFocus){
40279                 this.inputEl().focus();
40280             }
40281         },
40282         
40283         onViewMove : function(e, t)
40284         {
40285             this.inKeyMode = false;
40286         },
40287         
40288         select : function(index, scrollIntoView)
40289         {
40290             this.selectedIndex = index;
40291             this.view.select(index);
40292             if(scrollIntoView !== false){
40293                 var el = this.view.getNode(index);
40294                 if(el){
40295                     this.list.scrollChildIntoView(el, false);
40296                 }
40297             }
40298         },
40299         
40300         createList : function()
40301         {
40302             this.list = Roo.get(document.body).createChild({
40303                 tag: 'ul',
40304                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40305                 style: 'display:none'
40306             });
40307             
40308             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40309         },
40310         
40311         collapseIf : function(e)
40312         {
40313             var in_combo  = e.within(this.el);
40314             var in_list =  e.within(this.list);
40315             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40316             
40317             if (in_combo || in_list || is_list) {
40318                 return;
40319             }
40320             this.collapse();
40321         },
40322         
40323         onSelect : function(record, index)
40324         {
40325             if(this.fireEvent('beforeselect', this, record, index) !== false){
40326                 
40327                 this.setFlagClass(record.data.iso2);
40328                 this.setDialCode(record.data.dialCode);
40329                 this.hasFocus = false;
40330                 this.collapse();
40331                 this.fireEvent('select', this, record, index);
40332             }
40333         },
40334         
40335         flagEl : function()
40336         {
40337             var flag = this.el.select('div.flag',true).first();
40338             if(!flag){
40339                 return false;
40340             }
40341             return flag;
40342         },
40343         
40344         dialCodeHolderEl : function()
40345         {
40346             var d = this.el.select('input.dial-code-holder',true).first();
40347             if(!d){
40348                 return false;
40349             }
40350             return d;
40351         },
40352         
40353         setDialCode : function(v)
40354         {
40355             this.dialCodeHolder.dom.value = '+'+v;
40356         },
40357         
40358         setFlagClass : function(n)
40359         {
40360             this.flag.dom.className = 'flag '+n;
40361         },
40362         
40363         getValue : function()
40364         {
40365             var v = this.inputEl().getValue();
40366             if(this.dialCodeHolder) {
40367                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40368             }
40369             return v;
40370         },
40371         
40372         setValue : function(v)
40373         {
40374             var d = this.getDialCode(v);
40375             
40376             //invalid dial code
40377             if(v.length == 0 || !d || d.length == 0) {
40378                 if(this.rendered){
40379                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40380                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40381                 }
40382                 return;
40383             }
40384             
40385             //valid dial code
40386             this.setFlagClass(this.dialCodeMapping[d].iso2);
40387             this.setDialCode(d);
40388             this.inputEl().dom.value = v.replace('+'+d,'');
40389             this.hiddenEl().dom.value = this.getValue();
40390             
40391             this.validate();
40392         },
40393         
40394         getDialCode : function(v)
40395         {
40396             v = v ||  '';
40397             
40398             if (v.length == 0) {
40399                 return this.dialCodeHolder.dom.value;
40400             }
40401             
40402             var dialCode = "";
40403             if (v.charAt(0) != "+") {
40404                 return false;
40405             }
40406             var numericChars = "";
40407             for (var i = 1; i < v.length; i++) {
40408               var c = v.charAt(i);
40409               if (!isNaN(c)) {
40410                 numericChars += c;
40411                 if (this.dialCodeMapping[numericChars]) {
40412                   dialCode = v.substr(1, i);
40413                 }
40414                 if (numericChars.length == 4) {
40415                   break;
40416                 }
40417               }
40418             }
40419             return dialCode;
40420         },
40421         
40422         reset : function()
40423         {
40424             this.setValue(this.defaultDialCode);
40425             this.validate();
40426         },
40427         
40428         hiddenEl : function()
40429         {
40430             return this.el.select('input.hidden-tel-input',true).first();
40431         },
40432         
40433         // after setting val
40434         onKeyUp : function(e){
40435             this.setValue(this.getValue());
40436         },
40437         
40438         onKeyPress : function(e){
40439             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40440                 e.stopEvent();
40441             }
40442         }
40443         
40444 });
40445 /**
40446  * @class Roo.bootstrap.MoneyField
40447  * @extends Roo.bootstrap.ComboBox
40448  * Bootstrap MoneyField class
40449  * 
40450  * @constructor
40451  * Create a new MoneyField.
40452  * @param {Object} config Configuration options
40453  */
40454
40455 Roo.bootstrap.MoneyField = function(config) {
40456     
40457     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40458     
40459 };
40460
40461 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40462     
40463     /**
40464      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40465      */
40466     allowDecimals : true,
40467     /**
40468      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40469      */
40470     decimalSeparator : ".",
40471     /**
40472      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40473      */
40474     decimalPrecision : 0,
40475     /**
40476      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40477      */
40478     allowNegative : true,
40479     /**
40480      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40481      */
40482     allowZero: true,
40483     /**
40484      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40485      */
40486     minValue : Number.NEGATIVE_INFINITY,
40487     /**
40488      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40489      */
40490     maxValue : Number.MAX_VALUE,
40491     /**
40492      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40493      */
40494     minText : "The minimum value for this field is {0}",
40495     /**
40496      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40497      */
40498     maxText : "The maximum value for this field is {0}",
40499     /**
40500      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40501      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40502      */
40503     nanText : "{0} is not a valid number",
40504     /**
40505      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40506      */
40507     castInt : true,
40508     /**
40509      * @cfg {String} defaults currency of the MoneyField
40510      * value should be in lkey
40511      */
40512     defaultCurrency : false,
40513     /**
40514      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40515      */
40516     thousandsDelimiter : false,
40517     /**
40518      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40519      */
40520     max_length: false,
40521     
40522     inputlg : 9,
40523     inputmd : 9,
40524     inputsm : 9,
40525     inputxs : 6,
40526     
40527     store : false,
40528     
40529     getAutoCreate : function()
40530     {
40531         var align = this.labelAlign || this.parentLabelAlign();
40532         
40533         var id = Roo.id();
40534
40535         var cfg = {
40536             cls: 'form-group',
40537             cn: []
40538         };
40539
40540         var input =  {
40541             tag: 'input',
40542             id : id,
40543             cls : 'form-control roo-money-amount-input',
40544             autocomplete: 'new-password'
40545         };
40546         
40547         var hiddenInput = {
40548             tag: 'input',
40549             type: 'hidden',
40550             id: Roo.id(),
40551             cls: 'hidden-number-input'
40552         };
40553         
40554         if(this.max_length) {
40555             input.maxlength = this.max_length; 
40556         }
40557         
40558         if (this.name) {
40559             hiddenInput.name = this.name;
40560         }
40561
40562         if (this.disabled) {
40563             input.disabled = true;
40564         }
40565
40566         var clg = 12 - this.inputlg;
40567         var cmd = 12 - this.inputmd;
40568         var csm = 12 - this.inputsm;
40569         var cxs = 12 - this.inputxs;
40570         
40571         var container = {
40572             tag : 'div',
40573             cls : 'row roo-money-field',
40574             cn : [
40575                 {
40576                     tag : 'div',
40577                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40578                     cn : [
40579                         {
40580                             tag : 'div',
40581                             cls: 'roo-select2-container input-group',
40582                             cn: [
40583                                 {
40584                                     tag : 'input',
40585                                     cls : 'form-control roo-money-currency-input',
40586                                     autocomplete: 'new-password',
40587                                     readOnly : 1,
40588                                     name : this.currencyName
40589                                 },
40590                                 {
40591                                     tag :'span',
40592                                     cls : 'input-group-addon',
40593                                     cn : [
40594                                         {
40595                                             tag: 'span',
40596                                             cls: 'caret'
40597                                         }
40598                                     ]
40599                                 }
40600                             ]
40601                         }
40602                     ]
40603                 },
40604                 {
40605                     tag : 'div',
40606                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40607                     cn : [
40608                         {
40609                             tag: 'div',
40610                             cls: this.hasFeedback ? 'has-feedback' : '',
40611                             cn: [
40612                                 input
40613                             ]
40614                         }
40615                     ]
40616                 }
40617             ]
40618             
40619         };
40620         
40621         if (this.fieldLabel.length) {
40622             var indicator = {
40623                 tag: 'i',
40624                 tooltip: 'This field is required'
40625             };
40626
40627             var label = {
40628                 tag: 'label',
40629                 'for':  id,
40630                 cls: 'control-label',
40631                 cn: []
40632             };
40633
40634             var label_text = {
40635                 tag: 'span',
40636                 html: this.fieldLabel
40637             };
40638
40639             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40640             label.cn = [
40641                 indicator,
40642                 label_text
40643             ];
40644
40645             if(this.indicatorpos == 'right') {
40646                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40647                 label.cn = [
40648                     label_text,
40649                     indicator
40650                 ];
40651             }
40652
40653             if(align == 'left') {
40654                 container = {
40655                     tag: 'div',
40656                     cn: [
40657                         container
40658                     ]
40659                 };
40660
40661                 if(this.labelWidth > 12){
40662                     label.style = "width: " + this.labelWidth + 'px';
40663                 }
40664                 if(this.labelWidth < 13 && this.labelmd == 0){
40665                     this.labelmd = this.labelWidth;
40666                 }
40667                 if(this.labellg > 0){
40668                     label.cls += ' col-lg-' + this.labellg;
40669                     input.cls += ' col-lg-' + (12 - this.labellg);
40670                 }
40671                 if(this.labelmd > 0){
40672                     label.cls += ' col-md-' + this.labelmd;
40673                     container.cls += ' col-md-' + (12 - this.labelmd);
40674                 }
40675                 if(this.labelsm > 0){
40676                     label.cls += ' col-sm-' + this.labelsm;
40677                     container.cls += ' col-sm-' + (12 - this.labelsm);
40678                 }
40679                 if(this.labelxs > 0){
40680                     label.cls += ' col-xs-' + this.labelxs;
40681                     container.cls += ' col-xs-' + (12 - this.labelxs);
40682                 }
40683             }
40684         }
40685
40686         cfg.cn = [
40687             label,
40688             container,
40689             hiddenInput
40690         ];
40691         
40692         var settings = this;
40693
40694         ['xs','sm','md','lg'].map(function(size){
40695             if (settings[size]) {
40696                 cfg.cls += ' col-' + size + '-' + settings[size];
40697             }
40698         });
40699         
40700         return cfg;
40701     },
40702     
40703     initEvents : function()
40704     {
40705         this.indicator = this.indicatorEl();
40706         
40707         this.initCurrencyEvent();
40708         
40709         this.initNumberEvent();
40710     },
40711     
40712     initCurrencyEvent : function()
40713     {
40714         if (!this.store) {
40715             throw "can not find store for combo";
40716         }
40717         
40718         this.store = Roo.factory(this.store, Roo.data);
40719         this.store.parent = this;
40720         
40721         this.createList();
40722         
40723         this.triggerEl = this.el.select('.input-group-addon', true).first();
40724         
40725         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40726         
40727         var _this = this;
40728         
40729         (function(){
40730             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40731             _this.list.setWidth(lw);
40732         }).defer(100);
40733         
40734         this.list.on('mouseover', this.onViewOver, this);
40735         this.list.on('mousemove', this.onViewMove, this);
40736         this.list.on('scroll', this.onViewScroll, this);
40737         
40738         if(!this.tpl){
40739             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40740         }
40741         
40742         this.view = new Roo.View(this.list, this.tpl, {
40743             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40744         });
40745         
40746         this.view.on('click', this.onViewClick, this);
40747         
40748         this.store.on('beforeload', this.onBeforeLoad, this);
40749         this.store.on('load', this.onLoad, this);
40750         this.store.on('loadexception', this.onLoadException, this);
40751         
40752         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40753             "up" : function(e){
40754                 this.inKeyMode = true;
40755                 this.selectPrev();
40756             },
40757
40758             "down" : function(e){
40759                 if(!this.isExpanded()){
40760                     this.onTriggerClick();
40761                 }else{
40762                     this.inKeyMode = true;
40763                     this.selectNext();
40764                 }
40765             },
40766
40767             "enter" : function(e){
40768                 this.collapse();
40769                 
40770                 if(this.fireEvent("specialkey", this, e)){
40771                     this.onViewClick(false);
40772                 }
40773                 
40774                 return true;
40775             },
40776
40777             "esc" : function(e){
40778                 this.collapse();
40779             },
40780
40781             "tab" : function(e){
40782                 this.collapse();
40783                 
40784                 if(this.fireEvent("specialkey", this, e)){
40785                     this.onViewClick(false);
40786                 }
40787                 
40788                 return true;
40789             },
40790
40791             scope : this,
40792
40793             doRelay : function(foo, bar, hname){
40794                 if(hname == 'down' || this.scope.isExpanded()){
40795                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40796                 }
40797                 return true;
40798             },
40799
40800             forceKeyDown: true
40801         });
40802         
40803         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40804         
40805     },
40806     
40807     initNumberEvent : function(e)
40808     {
40809         this.inputEl().on("keydown" , this.fireKey,  this);
40810         this.inputEl().on("focus", this.onFocus,  this);
40811         this.inputEl().on("blur", this.onBlur,  this);
40812         
40813         this.inputEl().relayEvent('keyup', this);
40814         
40815         if(this.indicator){
40816             this.indicator.addClass('invisible');
40817         }
40818  
40819         this.originalValue = this.getValue();
40820         
40821         if(this.validationEvent == 'keyup'){
40822             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40823             this.inputEl().on('keyup', this.filterValidation, this);
40824         }
40825         else if(this.validationEvent !== false){
40826             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40827         }
40828         
40829         if(this.selectOnFocus){
40830             this.on("focus", this.preFocus, this);
40831             
40832         }
40833         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40834             this.inputEl().on("keypress", this.filterKeys, this);
40835         } else {
40836             this.inputEl().relayEvent('keypress', this);
40837         }
40838         
40839         var allowed = "0123456789";
40840         
40841         if(this.allowDecimals){
40842             allowed += this.decimalSeparator;
40843         }
40844         
40845         if(this.allowNegative){
40846             allowed += "-";
40847         }
40848         
40849         if(this.thousandsDelimiter) {
40850             allowed += ",";
40851         }
40852         
40853         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40854         
40855         var keyPress = function(e){
40856             
40857             var k = e.getKey();
40858             
40859             var c = e.getCharCode();
40860             
40861             if(
40862                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40863                     allowed.indexOf(String.fromCharCode(c)) === -1
40864             ){
40865                 e.stopEvent();
40866                 return;
40867             }
40868             
40869             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40870                 return;
40871             }
40872             
40873             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40874                 e.stopEvent();
40875             }
40876         };
40877         
40878         this.inputEl().on("keypress", keyPress, this);
40879         
40880     },
40881     
40882     onTriggerClick : function(e)
40883     {   
40884         if(this.disabled){
40885             return;
40886         }
40887         
40888         this.page = 0;
40889         this.loadNext = false;
40890         
40891         if(this.isExpanded()){
40892             this.collapse();
40893             return;
40894         }
40895         
40896         this.hasFocus = true;
40897         
40898         if(this.triggerAction == 'all') {
40899             this.doQuery(this.allQuery, true);
40900             return;
40901         }
40902         
40903         this.doQuery(this.getRawValue());
40904     },
40905     
40906     getCurrency : function()
40907     {   
40908         var v = this.currencyEl().getValue();
40909         
40910         return v;
40911     },
40912     
40913     restrictHeight : function()
40914     {
40915         this.list.alignTo(this.currencyEl(), this.listAlign);
40916         this.list.alignTo(this.currencyEl(), this.listAlign);
40917     },
40918     
40919     onViewClick : function(view, doFocus, el, e)
40920     {
40921         var index = this.view.getSelectedIndexes()[0];
40922         
40923         var r = this.store.getAt(index);
40924         
40925         if(r){
40926             this.onSelect(r, index);
40927         }
40928     },
40929     
40930     onSelect : function(record, index){
40931         
40932         if(this.fireEvent('beforeselect', this, record, index) !== false){
40933         
40934             this.setFromCurrencyData(index > -1 ? record.data : false);
40935             
40936             this.collapse();
40937             
40938             this.fireEvent('select', this, record, index);
40939         }
40940     },
40941     
40942     setFromCurrencyData : function(o)
40943     {
40944         var currency = '';
40945         
40946         this.lastCurrency = o;
40947         
40948         if (this.currencyField) {
40949             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40950         } else {
40951             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40952         }
40953         
40954         this.lastSelectionText = currency;
40955         
40956         //setting default currency
40957         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40958             this.setCurrency(this.defaultCurrency);
40959             return;
40960         }
40961         
40962         this.setCurrency(currency);
40963     },
40964     
40965     setFromData : function(o)
40966     {
40967         var c = {};
40968         
40969         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40970         
40971         this.setFromCurrencyData(c);
40972         
40973         var value = '';
40974         
40975         if (this.name) {
40976             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40977         } else {
40978             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40979         }
40980         
40981         this.setValue(value);
40982         
40983     },
40984     
40985     setCurrency : function(v)
40986     {   
40987         this.currencyValue = v;
40988         
40989         if(this.rendered){
40990             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40991             this.validate();
40992         }
40993     },
40994     
40995     setValue : function(v)
40996     {
40997         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40998         
40999         this.value = v;
41000         
41001         if(this.rendered){
41002             
41003             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41004             
41005             this.inputEl().dom.value = (v == '') ? '' :
41006                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41007             
41008             if(!this.allowZero && v === '0') {
41009                 this.hiddenEl().dom.value = '';
41010                 this.inputEl().dom.value = '';
41011             }
41012             
41013             this.validate();
41014         }
41015     },
41016     
41017     getRawValue : function()
41018     {
41019         var v = this.inputEl().getValue();
41020         
41021         return v;
41022     },
41023     
41024     getValue : function()
41025     {
41026         return this.fixPrecision(this.parseValue(this.getRawValue()));
41027     },
41028     
41029     parseValue : function(value)
41030     {
41031         if(this.thousandsDelimiter) {
41032             value += "";
41033             r = new RegExp(",", "g");
41034             value = value.replace(r, "");
41035         }
41036         
41037         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41038         return isNaN(value) ? '' : value;
41039         
41040     },
41041     
41042     fixPrecision : function(value)
41043     {
41044         if(this.thousandsDelimiter) {
41045             value += "";
41046             r = new RegExp(",", "g");
41047             value = value.replace(r, "");
41048         }
41049         
41050         var nan = isNaN(value);
41051         
41052         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41053             return nan ? '' : value;
41054         }
41055         return parseFloat(value).toFixed(this.decimalPrecision);
41056     },
41057     
41058     decimalPrecisionFcn : function(v)
41059     {
41060         return Math.floor(v);
41061     },
41062     
41063     validateValue : function(value)
41064     {
41065         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41066             return false;
41067         }
41068         
41069         var num = this.parseValue(value);
41070         
41071         if(isNaN(num)){
41072             this.markInvalid(String.format(this.nanText, value));
41073             return false;
41074         }
41075         
41076         if(num < this.minValue){
41077             this.markInvalid(String.format(this.minText, this.minValue));
41078             return false;
41079         }
41080         
41081         if(num > this.maxValue){
41082             this.markInvalid(String.format(this.maxText, this.maxValue));
41083             return false;
41084         }
41085         
41086         return true;
41087     },
41088     
41089     validate : function()
41090     {
41091         if(this.disabled || this.allowBlank){
41092             this.markValid();
41093             return true;
41094         }
41095         
41096         var currency = this.getCurrency();
41097         
41098         if(this.validateValue(this.getRawValue()) && currency.length){
41099             this.markValid();
41100             return true;
41101         }
41102         
41103         this.markInvalid();
41104         return false;
41105     },
41106     
41107     getName: function()
41108     {
41109         return this.name;
41110     },
41111     
41112     beforeBlur : function()
41113     {
41114         if(!this.castInt){
41115             return;
41116         }
41117         
41118         var v = this.parseValue(this.getRawValue());
41119         
41120         if(v || v == 0){
41121             this.setValue(v);
41122         }
41123     },
41124     
41125     onBlur : function()
41126     {
41127         this.beforeBlur();
41128         
41129         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41130             //this.el.removeClass(this.focusClass);
41131         }
41132         
41133         this.hasFocus = false;
41134         
41135         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41136             this.validate();
41137         }
41138         
41139         var v = this.getValue();
41140         
41141         if(String(v) !== String(this.startValue)){
41142             this.fireEvent('change', this, v, this.startValue);
41143         }
41144         
41145         this.fireEvent("blur", this);
41146     },
41147     
41148     inputEl : function()
41149     {
41150         return this.el.select('.roo-money-amount-input', true).first();
41151     },
41152     
41153     currencyEl : function()
41154     {
41155         return this.el.select('.roo-money-currency-input', true).first();
41156     },
41157     
41158     hiddenEl : function()
41159     {
41160         return this.el.select('input.hidden-number-input',true).first();
41161     }
41162     
41163 });