roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * 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 ml-md-auto';
4264             } else {
4265                 cfg.cls += ' navbar-left';
4266             }
4267         }
4268         
4269         if (this.align === 'right') {
4270             cfg.cls += ' navbar-right ml-md-auto';
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  btn-outline-secondary',
24796              disabled: true,
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             cls: "btn-outline-secondary",
24830             html : ' <i class="fa fa-refresh"></i>',
24831             preventDefault: true,
24832             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24833         });
24834         
24835     },
24836
24837     // private
24838     updateInfo : function(){
24839         if(this.displayEl){
24840             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24841             var msg = count == 0 ?
24842                 this.emptyMsg :
24843                 String.format(
24844                     this.displayMsg,
24845                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24846                 );
24847             this.displayEl.update(msg);
24848         }
24849     },
24850
24851     // private
24852     onLoad : function(ds, r, o)
24853     {
24854         this.cursor = o.params.start ? o.params.start : 0;
24855         
24856         var d = this.getPageData(),
24857             ap = d.activePage,
24858             ps = d.pages;
24859         
24860         
24861         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24862         this.field.dom.value = ap;
24863         this.first.setDisabled(ap == 1);
24864         this.prev.setDisabled(ap == 1);
24865         this.next.setDisabled(ap == ps);
24866         this.last.setDisabled(ap == ps);
24867         this.loading.enable();
24868         this.updateInfo();
24869     },
24870
24871     // private
24872     getPageData : function(){
24873         var total = this.ds.getTotalCount();
24874         return {
24875             total : total,
24876             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24877             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24878         };
24879     },
24880
24881     // private
24882     onLoadError : function(){
24883         this.loading.enable();
24884     },
24885
24886     // private
24887     onPagingKeydown : function(e){
24888         var k = e.getKey();
24889         var d = this.getPageData();
24890         if(k == e.RETURN){
24891             var v = this.field.dom.value, pageNum;
24892             if(!v || isNaN(pageNum = parseInt(v, 10))){
24893                 this.field.dom.value = d.activePage;
24894                 return;
24895             }
24896             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24897             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24898             e.stopEvent();
24899         }
24900         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))
24901         {
24902           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24903           this.field.dom.value = pageNum;
24904           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24905           e.stopEvent();
24906         }
24907         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24908         {
24909           var v = this.field.dom.value, pageNum; 
24910           var increment = (e.shiftKey) ? 10 : 1;
24911           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24912                 increment *= -1;
24913           }
24914           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24915             this.field.dom.value = d.activePage;
24916             return;
24917           }
24918           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24919           {
24920             this.field.dom.value = parseInt(v, 10) + increment;
24921             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24922             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24923           }
24924           e.stopEvent();
24925         }
24926     },
24927
24928     // private
24929     beforeLoad : function(){
24930         if(this.loading){
24931             this.loading.disable();
24932         }
24933     },
24934
24935     // private
24936     onClick : function(which){
24937         
24938         var ds = this.ds;
24939         if (!ds) {
24940             return;
24941         }
24942         
24943         switch(which){
24944             case "first":
24945                 ds.load({params:{start: 0, limit: this.pageSize}});
24946             break;
24947             case "prev":
24948                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24949             break;
24950             case "next":
24951                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24952             break;
24953             case "last":
24954                 var total = ds.getTotalCount();
24955                 var extra = total % this.pageSize;
24956                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24957                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24958             break;
24959             case "refresh":
24960                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24961             break;
24962         }
24963     },
24964
24965     /**
24966      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24967      * @param {Roo.data.Store} store The data store to unbind
24968      */
24969     unbind : function(ds){
24970         ds.un("beforeload", this.beforeLoad, this);
24971         ds.un("load", this.onLoad, this);
24972         ds.un("loadexception", this.onLoadError, this);
24973         ds.un("remove", this.updateInfo, this);
24974         ds.un("add", this.updateInfo, this);
24975         this.ds = undefined;
24976     },
24977
24978     /**
24979      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24980      * @param {Roo.data.Store} store The data store to bind
24981      */
24982     bind : function(ds){
24983         ds.on("beforeload", this.beforeLoad, this);
24984         ds.on("load", this.onLoad, this);
24985         ds.on("loadexception", this.onLoadError, this);
24986         ds.on("remove", this.updateInfo, this);
24987         ds.on("add", this.updateInfo, this);
24988         this.ds = ds;
24989     }
24990 });/*
24991  * - LGPL
24992  *
24993  * element
24994  * 
24995  */
24996
24997 /**
24998  * @class Roo.bootstrap.MessageBar
24999  * @extends Roo.bootstrap.Component
25000  * Bootstrap MessageBar class
25001  * @cfg {String} html contents of the MessageBar
25002  * @cfg {String} weight (info | success | warning | danger) default info
25003  * @cfg {String} beforeClass insert the bar before the given class
25004  * @cfg {Boolean} closable (true | false) default false
25005  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25006  * 
25007  * @constructor
25008  * Create a new Element
25009  * @param {Object} config The config object
25010  */
25011
25012 Roo.bootstrap.MessageBar = function(config){
25013     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25014 };
25015
25016 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25017     
25018     html: '',
25019     weight: 'info',
25020     closable: false,
25021     fixed: false,
25022     beforeClass: 'bootstrap-sticky-wrap',
25023     
25024     getAutoCreate : function(){
25025         
25026         var cfg = {
25027             tag: 'div',
25028             cls: 'alert alert-dismissable alert-' + this.weight,
25029             cn: [
25030                 {
25031                     tag: 'span',
25032                     cls: 'message',
25033                     html: this.html || ''
25034                 }
25035             ]
25036         };
25037         
25038         if(this.fixed){
25039             cfg.cls += ' alert-messages-fixed';
25040         }
25041         
25042         if(this.closable){
25043             cfg.cn.push({
25044                 tag: 'button',
25045                 cls: 'close',
25046                 html: 'x'
25047             });
25048         }
25049         
25050         return cfg;
25051     },
25052     
25053     onRender : function(ct, position)
25054     {
25055         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25056         
25057         if(!this.el){
25058             var cfg = Roo.apply({},  this.getAutoCreate());
25059             cfg.id = Roo.id();
25060             
25061             if (this.cls) {
25062                 cfg.cls += ' ' + this.cls;
25063             }
25064             if (this.style) {
25065                 cfg.style = this.style;
25066             }
25067             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25068             
25069             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25070         }
25071         
25072         this.el.select('>button.close').on('click', this.hide, this);
25073         
25074     },
25075     
25076     show : function()
25077     {
25078         if (!this.rendered) {
25079             this.render();
25080         }
25081         
25082         this.el.show();
25083         
25084         this.fireEvent('show', this);
25085         
25086     },
25087     
25088     hide : function()
25089     {
25090         if (!this.rendered) {
25091             this.render();
25092         }
25093         
25094         this.el.hide();
25095         
25096         this.fireEvent('hide', this);
25097     },
25098     
25099     update : function()
25100     {
25101 //        var e = this.el.dom.firstChild;
25102 //        
25103 //        if(this.closable){
25104 //            e = e.nextSibling;
25105 //        }
25106 //        
25107 //        e.data = this.html || '';
25108
25109         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25110     }
25111    
25112 });
25113
25114  
25115
25116      /*
25117  * - LGPL
25118  *
25119  * Graph
25120  * 
25121  */
25122
25123
25124 /**
25125  * @class Roo.bootstrap.Graph
25126  * @extends Roo.bootstrap.Component
25127  * Bootstrap Graph class
25128 > Prameters
25129  -sm {number} sm 4
25130  -md {number} md 5
25131  @cfg {String} graphtype  bar | vbar | pie
25132  @cfg {number} g_x coodinator | centre x (pie)
25133  @cfg {number} g_y coodinator | centre y (pie)
25134  @cfg {number} g_r radius (pie)
25135  @cfg {number} g_height height of the chart (respected by all elements in the set)
25136  @cfg {number} g_width width of the chart (respected by all elements in the set)
25137  @cfg {Object} title The title of the chart
25138     
25139  -{Array}  values
25140  -opts (object) options for the chart 
25141      o {
25142      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25143      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25144      o vgutter (number)
25145      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.
25146      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25147      o to
25148      o stretch (boolean)
25149      o }
25150  -opts (object) options for the pie
25151      o{
25152      o cut
25153      o startAngle (number)
25154      o endAngle (number)
25155      } 
25156  *
25157  * @constructor
25158  * Create a new Input
25159  * @param {Object} config The config object
25160  */
25161
25162 Roo.bootstrap.Graph = function(config){
25163     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25164     
25165     this.addEvents({
25166         // img events
25167         /**
25168          * @event click
25169          * The img click event for the img.
25170          * @param {Roo.EventObject} e
25171          */
25172         "click" : true
25173     });
25174 };
25175
25176 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25177     
25178     sm: 4,
25179     md: 5,
25180     graphtype: 'bar',
25181     g_height: 250,
25182     g_width: 400,
25183     g_x: 50,
25184     g_y: 50,
25185     g_r: 30,
25186     opts:{
25187         //g_colors: this.colors,
25188         g_type: 'soft',
25189         g_gutter: '20%'
25190
25191     },
25192     title : false,
25193
25194     getAutoCreate : function(){
25195         
25196         var cfg = {
25197             tag: 'div',
25198             html : null
25199         };
25200         
25201         
25202         return  cfg;
25203     },
25204
25205     onRender : function(ct,position){
25206         
25207         
25208         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25209         
25210         if (typeof(Raphael) == 'undefined') {
25211             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25212             return;
25213         }
25214         
25215         this.raphael = Raphael(this.el.dom);
25216         
25217                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25218                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25219                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25220                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25221                 /*
25222                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25223                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25224                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25225                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25226                 
25227                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25228                 r.barchart(330, 10, 300, 220, data1);
25229                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25230                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25231                 */
25232                 
25233                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25234                 // r.barchart(30, 30, 560, 250,  xdata, {
25235                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25236                 //     axis : "0 0 1 1",
25237                 //     axisxlabels :  xdata
25238                 //     //yvalues : cols,
25239                    
25240                 // });
25241 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25242 //        
25243 //        this.load(null,xdata,{
25244 //                axis : "0 0 1 1",
25245 //                axisxlabels :  xdata
25246 //                });
25247
25248     },
25249
25250     load : function(graphtype,xdata,opts)
25251     {
25252         this.raphael.clear();
25253         if(!graphtype) {
25254             graphtype = this.graphtype;
25255         }
25256         if(!opts){
25257             opts = this.opts;
25258         }
25259         var r = this.raphael,
25260             fin = function () {
25261                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25262             },
25263             fout = function () {
25264                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25265             },
25266             pfin = function() {
25267                 this.sector.stop();
25268                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25269
25270                 if (this.label) {
25271                     this.label[0].stop();
25272                     this.label[0].attr({ r: 7.5 });
25273                     this.label[1].attr({ "font-weight": 800 });
25274                 }
25275             },
25276             pfout = function() {
25277                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25278
25279                 if (this.label) {
25280                     this.label[0].animate({ r: 5 }, 500, "bounce");
25281                     this.label[1].attr({ "font-weight": 400 });
25282                 }
25283             };
25284
25285         switch(graphtype){
25286             case 'bar':
25287                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25288                 break;
25289             case 'hbar':
25290                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25291                 break;
25292             case 'pie':
25293 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25294 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25295 //            
25296                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25297                 
25298                 break;
25299
25300         }
25301         
25302         if(this.title){
25303             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25304         }
25305         
25306     },
25307     
25308     setTitle: function(o)
25309     {
25310         this.title = o;
25311     },
25312     
25313     initEvents: function() {
25314         
25315         if(!this.href){
25316             this.el.on('click', this.onClick, this);
25317         }
25318     },
25319     
25320     onClick : function(e)
25321     {
25322         Roo.log('img onclick');
25323         this.fireEvent('click', this, e);
25324     }
25325    
25326 });
25327
25328  
25329 /*
25330  * - LGPL
25331  *
25332  * numberBox
25333  * 
25334  */
25335 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25336
25337 /**
25338  * @class Roo.bootstrap.dash.NumberBox
25339  * @extends Roo.bootstrap.Component
25340  * Bootstrap NumberBox class
25341  * @cfg {String} headline Box headline
25342  * @cfg {String} content Box content
25343  * @cfg {String} icon Box icon
25344  * @cfg {String} footer Footer text
25345  * @cfg {String} fhref Footer href
25346  * 
25347  * @constructor
25348  * Create a new NumberBox
25349  * @param {Object} config The config object
25350  */
25351
25352
25353 Roo.bootstrap.dash.NumberBox = function(config){
25354     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25355     
25356 };
25357
25358 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25359     
25360     headline : '',
25361     content : '',
25362     icon : '',
25363     footer : '',
25364     fhref : '',
25365     ficon : '',
25366     
25367     getAutoCreate : function(){
25368         
25369         var cfg = {
25370             tag : 'div',
25371             cls : 'small-box ',
25372             cn : [
25373                 {
25374                     tag : 'div',
25375                     cls : 'inner',
25376                     cn :[
25377                         {
25378                             tag : 'h3',
25379                             cls : 'roo-headline',
25380                             html : this.headline
25381                         },
25382                         {
25383                             tag : 'p',
25384                             cls : 'roo-content',
25385                             html : this.content
25386                         }
25387                     ]
25388                 }
25389             ]
25390         };
25391         
25392         if(this.icon){
25393             cfg.cn.push({
25394                 tag : 'div',
25395                 cls : 'icon',
25396                 cn :[
25397                     {
25398                         tag : 'i',
25399                         cls : 'ion ' + this.icon
25400                     }
25401                 ]
25402             });
25403         }
25404         
25405         if(this.footer){
25406             var footer = {
25407                 tag : 'a',
25408                 cls : 'small-box-footer',
25409                 href : this.fhref || '#',
25410                 html : this.footer
25411             };
25412             
25413             cfg.cn.push(footer);
25414             
25415         }
25416         
25417         return  cfg;
25418     },
25419
25420     onRender : function(ct,position){
25421         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25422
25423
25424        
25425                 
25426     },
25427
25428     setHeadline: function (value)
25429     {
25430         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25431     },
25432     
25433     setFooter: function (value, href)
25434     {
25435         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25436         
25437         if(href){
25438             this.el.select('a.small-box-footer',true).first().attr('href', href);
25439         }
25440         
25441     },
25442
25443     setContent: function (value)
25444     {
25445         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25446     },
25447
25448     initEvents: function() 
25449     {   
25450         
25451     }
25452     
25453 });
25454
25455  
25456 /*
25457  * - LGPL
25458  *
25459  * TabBox
25460  * 
25461  */
25462 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25463
25464 /**
25465  * @class Roo.bootstrap.dash.TabBox
25466  * @extends Roo.bootstrap.Component
25467  * Bootstrap TabBox class
25468  * @cfg {String} title Title of the TabBox
25469  * @cfg {String} icon Icon of the TabBox
25470  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25471  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25472  * 
25473  * @constructor
25474  * Create a new TabBox
25475  * @param {Object} config The config object
25476  */
25477
25478
25479 Roo.bootstrap.dash.TabBox = function(config){
25480     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25481     this.addEvents({
25482         // raw events
25483         /**
25484          * @event addpane
25485          * When a pane is added
25486          * @param {Roo.bootstrap.dash.TabPane} pane
25487          */
25488         "addpane" : true,
25489         /**
25490          * @event activatepane
25491          * When a pane is activated
25492          * @param {Roo.bootstrap.dash.TabPane} pane
25493          */
25494         "activatepane" : true
25495         
25496          
25497     });
25498     
25499     this.panes = [];
25500 };
25501
25502 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25503
25504     title : '',
25505     icon : false,
25506     showtabs : true,
25507     tabScrollable : false,
25508     
25509     getChildContainer : function()
25510     {
25511         return this.el.select('.tab-content', true).first();
25512     },
25513     
25514     getAutoCreate : function(){
25515         
25516         var header = {
25517             tag: 'li',
25518             cls: 'pull-left header',
25519             html: this.title,
25520             cn : []
25521         };
25522         
25523         if(this.icon){
25524             header.cn.push({
25525                 tag: 'i',
25526                 cls: 'fa ' + this.icon
25527             });
25528         }
25529         
25530         var h = {
25531             tag: 'ul',
25532             cls: 'nav nav-tabs pull-right',
25533             cn: [
25534                 header
25535             ]
25536         };
25537         
25538         if(this.tabScrollable){
25539             h = {
25540                 tag: 'div',
25541                 cls: 'tab-header',
25542                 cn: [
25543                     {
25544                         tag: 'ul',
25545                         cls: 'nav nav-tabs pull-right',
25546                         cn: [
25547                             header
25548                         ]
25549                     }
25550                 ]
25551             };
25552         }
25553         
25554         var cfg = {
25555             tag: 'div',
25556             cls: 'nav-tabs-custom',
25557             cn: [
25558                 h,
25559                 {
25560                     tag: 'div',
25561                     cls: 'tab-content no-padding',
25562                     cn: []
25563                 }
25564             ]
25565         };
25566
25567         return  cfg;
25568     },
25569     initEvents : function()
25570     {
25571         //Roo.log('add add pane handler');
25572         this.on('addpane', this.onAddPane, this);
25573     },
25574      /**
25575      * Updates the box title
25576      * @param {String} html to set the title to.
25577      */
25578     setTitle : function(value)
25579     {
25580         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25581     },
25582     onAddPane : function(pane)
25583     {
25584         this.panes.push(pane);
25585         //Roo.log('addpane');
25586         //Roo.log(pane);
25587         // tabs are rendere left to right..
25588         if(!this.showtabs){
25589             return;
25590         }
25591         
25592         var ctr = this.el.select('.nav-tabs', true).first();
25593          
25594          
25595         var existing = ctr.select('.nav-tab',true);
25596         var qty = existing.getCount();;
25597         
25598         
25599         var tab = ctr.createChild({
25600             tag : 'li',
25601             cls : 'nav-tab' + (qty ? '' : ' active'),
25602             cn : [
25603                 {
25604                     tag : 'a',
25605                     href:'#',
25606                     html : pane.title
25607                 }
25608             ]
25609         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25610         pane.tab = tab;
25611         
25612         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25613         if (!qty) {
25614             pane.el.addClass('active');
25615         }
25616         
25617                 
25618     },
25619     onTabClick : function(ev,un,ob,pane)
25620     {
25621         //Roo.log('tab - prev default');
25622         ev.preventDefault();
25623         
25624         
25625         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25626         pane.tab.addClass('active');
25627         //Roo.log(pane.title);
25628         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25629         // technically we should have a deactivate event.. but maybe add later.
25630         // and it should not de-activate the selected tab...
25631         this.fireEvent('activatepane', pane);
25632         pane.el.addClass('active');
25633         pane.fireEvent('activate');
25634         
25635         
25636     },
25637     
25638     getActivePane : function()
25639     {
25640         var r = false;
25641         Roo.each(this.panes, function(p) {
25642             if(p.el.hasClass('active')){
25643                 r = p;
25644                 return false;
25645             }
25646             
25647             return;
25648         });
25649         
25650         return r;
25651     }
25652     
25653     
25654 });
25655
25656  
25657 /*
25658  * - LGPL
25659  *
25660  * Tab pane
25661  * 
25662  */
25663 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25664 /**
25665  * @class Roo.bootstrap.TabPane
25666  * @extends Roo.bootstrap.Component
25667  * Bootstrap TabPane class
25668  * @cfg {Boolean} active (false | true) Default false
25669  * @cfg {String} title title of panel
25670
25671  * 
25672  * @constructor
25673  * Create a new TabPane
25674  * @param {Object} config The config object
25675  */
25676
25677 Roo.bootstrap.dash.TabPane = function(config){
25678     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25679     
25680     this.addEvents({
25681         // raw events
25682         /**
25683          * @event activate
25684          * When a pane is activated
25685          * @param {Roo.bootstrap.dash.TabPane} pane
25686          */
25687         "activate" : true
25688          
25689     });
25690 };
25691
25692 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25693     
25694     active : false,
25695     title : '',
25696     
25697     // the tabBox that this is attached to.
25698     tab : false,
25699      
25700     getAutoCreate : function() 
25701     {
25702         var cfg = {
25703             tag: 'div',
25704             cls: 'tab-pane'
25705         };
25706         
25707         if(this.active){
25708             cfg.cls += ' active';
25709         }
25710         
25711         return cfg;
25712     },
25713     initEvents  : function()
25714     {
25715         //Roo.log('trigger add pane handler');
25716         this.parent().fireEvent('addpane', this)
25717     },
25718     
25719      /**
25720      * Updates the tab title 
25721      * @param {String} html to set the title to.
25722      */
25723     setTitle: function(str)
25724     {
25725         if (!this.tab) {
25726             return;
25727         }
25728         this.title = str;
25729         this.tab.select('a', true).first().dom.innerHTML = str;
25730         
25731     }
25732     
25733     
25734     
25735 });
25736
25737  
25738
25739
25740  /*
25741  * - LGPL
25742  *
25743  * menu
25744  * 
25745  */
25746 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25747
25748 /**
25749  * @class Roo.bootstrap.menu.Menu
25750  * @extends Roo.bootstrap.Component
25751  * Bootstrap Menu class - container for Menu
25752  * @cfg {String} html Text of the menu
25753  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25754  * @cfg {String} icon Font awesome icon
25755  * @cfg {String} pos Menu align to (top | bottom) default bottom
25756  * 
25757  * 
25758  * @constructor
25759  * Create a new Menu
25760  * @param {Object} config The config object
25761  */
25762
25763
25764 Roo.bootstrap.menu.Menu = function(config){
25765     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25766     
25767     this.addEvents({
25768         /**
25769          * @event beforeshow
25770          * Fires before this menu is displayed
25771          * @param {Roo.bootstrap.menu.Menu} this
25772          */
25773         beforeshow : true,
25774         /**
25775          * @event beforehide
25776          * Fires before this menu is hidden
25777          * @param {Roo.bootstrap.menu.Menu} this
25778          */
25779         beforehide : true,
25780         /**
25781          * @event show
25782          * Fires after this menu is displayed
25783          * @param {Roo.bootstrap.menu.Menu} this
25784          */
25785         show : true,
25786         /**
25787          * @event hide
25788          * Fires after this menu is hidden
25789          * @param {Roo.bootstrap.menu.Menu} this
25790          */
25791         hide : true,
25792         /**
25793          * @event click
25794          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25795          * @param {Roo.bootstrap.menu.Menu} this
25796          * @param {Roo.EventObject} e
25797          */
25798         click : true
25799     });
25800     
25801 };
25802
25803 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25804     
25805     submenu : false,
25806     html : '',
25807     weight : 'default',
25808     icon : false,
25809     pos : 'bottom',
25810     
25811     
25812     getChildContainer : function() {
25813         if(this.isSubMenu){
25814             return this.el;
25815         }
25816         
25817         return this.el.select('ul.dropdown-menu', true).first();  
25818     },
25819     
25820     getAutoCreate : function()
25821     {
25822         var text = [
25823             {
25824                 tag : 'span',
25825                 cls : 'roo-menu-text',
25826                 html : this.html
25827             }
25828         ];
25829         
25830         if(this.icon){
25831             text.unshift({
25832                 tag : 'i',
25833                 cls : 'fa ' + this.icon
25834             })
25835         }
25836         
25837         
25838         var cfg = {
25839             tag : 'div',
25840             cls : 'btn-group',
25841             cn : [
25842                 {
25843                     tag : 'button',
25844                     cls : 'dropdown-button btn btn-' + this.weight,
25845                     cn : text
25846                 },
25847                 {
25848                     tag : 'button',
25849                     cls : 'dropdown-toggle btn btn-' + this.weight,
25850                     cn : [
25851                         {
25852                             tag : 'span',
25853                             cls : 'caret'
25854                         }
25855                     ]
25856                 },
25857                 {
25858                     tag : 'ul',
25859                     cls : 'dropdown-menu'
25860                 }
25861             ]
25862             
25863         };
25864         
25865         if(this.pos == 'top'){
25866             cfg.cls += ' dropup';
25867         }
25868         
25869         if(this.isSubMenu){
25870             cfg = {
25871                 tag : 'ul',
25872                 cls : 'dropdown-menu'
25873             }
25874         }
25875         
25876         return cfg;
25877     },
25878     
25879     onRender : function(ct, position)
25880     {
25881         this.isSubMenu = ct.hasClass('dropdown-submenu');
25882         
25883         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25884     },
25885     
25886     initEvents : function() 
25887     {
25888         if(this.isSubMenu){
25889             return;
25890         }
25891         
25892         this.hidden = true;
25893         
25894         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25895         this.triggerEl.on('click', this.onTriggerPress, this);
25896         
25897         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25898         this.buttonEl.on('click', this.onClick, this);
25899         
25900     },
25901     
25902     list : function()
25903     {
25904         if(this.isSubMenu){
25905             return this.el;
25906         }
25907         
25908         return this.el.select('ul.dropdown-menu', true).first();
25909     },
25910     
25911     onClick : function(e)
25912     {
25913         this.fireEvent("click", this, e);
25914     },
25915     
25916     onTriggerPress  : function(e)
25917     {   
25918         if (this.isVisible()) {
25919             this.hide();
25920         } else {
25921             this.show();
25922         }
25923     },
25924     
25925     isVisible : function(){
25926         return !this.hidden;
25927     },
25928     
25929     show : function()
25930     {
25931         this.fireEvent("beforeshow", this);
25932         
25933         this.hidden = false;
25934         this.el.addClass('open');
25935         
25936         Roo.get(document).on("mouseup", this.onMouseUp, this);
25937         
25938         this.fireEvent("show", this);
25939         
25940         
25941     },
25942     
25943     hide : function()
25944     {
25945         this.fireEvent("beforehide", this);
25946         
25947         this.hidden = true;
25948         this.el.removeClass('open');
25949         
25950         Roo.get(document).un("mouseup", this.onMouseUp);
25951         
25952         this.fireEvent("hide", this);
25953     },
25954     
25955     onMouseUp : function()
25956     {
25957         this.hide();
25958     }
25959     
25960 });
25961
25962  
25963  /*
25964  * - LGPL
25965  *
25966  * menu item
25967  * 
25968  */
25969 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25970
25971 /**
25972  * @class Roo.bootstrap.menu.Item
25973  * @extends Roo.bootstrap.Component
25974  * Bootstrap MenuItem class
25975  * @cfg {Boolean} submenu (true | false) default false
25976  * @cfg {String} html text of the item
25977  * @cfg {String} href the link
25978  * @cfg {Boolean} disable (true | false) default false
25979  * @cfg {Boolean} preventDefault (true | false) default true
25980  * @cfg {String} icon Font awesome icon
25981  * @cfg {String} pos Submenu align to (left | right) default right 
25982  * 
25983  * 
25984  * @constructor
25985  * Create a new Item
25986  * @param {Object} config The config object
25987  */
25988
25989
25990 Roo.bootstrap.menu.Item = function(config){
25991     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25992     this.addEvents({
25993         /**
25994          * @event mouseover
25995          * Fires when the mouse is hovering over this menu
25996          * @param {Roo.bootstrap.menu.Item} this
25997          * @param {Roo.EventObject} e
25998          */
25999         mouseover : true,
26000         /**
26001          * @event mouseout
26002          * Fires when the mouse exits this menu
26003          * @param {Roo.bootstrap.menu.Item} this
26004          * @param {Roo.EventObject} e
26005          */
26006         mouseout : true,
26007         // raw events
26008         /**
26009          * @event click
26010          * The raw click event for the entire grid.
26011          * @param {Roo.EventObject} e
26012          */
26013         click : true
26014     });
26015 };
26016
26017 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26018     
26019     submenu : false,
26020     href : '',
26021     html : '',
26022     preventDefault: true,
26023     disable : false,
26024     icon : false,
26025     pos : 'right',
26026     
26027     getAutoCreate : function()
26028     {
26029         var text = [
26030             {
26031                 tag : 'span',
26032                 cls : 'roo-menu-item-text',
26033                 html : this.html
26034             }
26035         ];
26036         
26037         if(this.icon){
26038             text.unshift({
26039                 tag : 'i',
26040                 cls : 'fa ' + this.icon
26041             })
26042         }
26043         
26044         var cfg = {
26045             tag : 'li',
26046             cn : [
26047                 {
26048                     tag : 'a',
26049                     href : this.href || '#',
26050                     cn : text
26051                 }
26052             ]
26053         };
26054         
26055         if(this.disable){
26056             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26057         }
26058         
26059         if(this.submenu){
26060             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26061             
26062             if(this.pos == 'left'){
26063                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26064             }
26065         }
26066         
26067         return cfg;
26068     },
26069     
26070     initEvents : function() 
26071     {
26072         this.el.on('mouseover', this.onMouseOver, this);
26073         this.el.on('mouseout', this.onMouseOut, this);
26074         
26075         this.el.select('a', true).first().on('click', this.onClick, this);
26076         
26077     },
26078     
26079     onClick : function(e)
26080     {
26081         if(this.preventDefault){
26082             e.preventDefault();
26083         }
26084         
26085         this.fireEvent("click", this, e);
26086     },
26087     
26088     onMouseOver : function(e)
26089     {
26090         if(this.submenu && this.pos == 'left'){
26091             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26092         }
26093         
26094         this.fireEvent("mouseover", this, e);
26095     },
26096     
26097     onMouseOut : function(e)
26098     {
26099         this.fireEvent("mouseout", this, e);
26100     }
26101 });
26102
26103  
26104
26105  /*
26106  * - LGPL
26107  *
26108  * menu separator
26109  * 
26110  */
26111 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26112
26113 /**
26114  * @class Roo.bootstrap.menu.Separator
26115  * @extends Roo.bootstrap.Component
26116  * Bootstrap Separator class
26117  * 
26118  * @constructor
26119  * Create a new Separator
26120  * @param {Object} config The config object
26121  */
26122
26123
26124 Roo.bootstrap.menu.Separator = function(config){
26125     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26126 };
26127
26128 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26129     
26130     getAutoCreate : function(){
26131         var cfg = {
26132             tag : 'li',
26133             cls: 'divider'
26134         };
26135         
26136         return cfg;
26137     }
26138    
26139 });
26140
26141  
26142
26143  /*
26144  * - LGPL
26145  *
26146  * Tooltip
26147  * 
26148  */
26149
26150 /**
26151  * @class Roo.bootstrap.Tooltip
26152  * Bootstrap Tooltip class
26153  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26154  * to determine which dom element triggers the tooltip.
26155  * 
26156  * It needs to add support for additional attributes like tooltip-position
26157  * 
26158  * @constructor
26159  * Create a new Toolti
26160  * @param {Object} config The config object
26161  */
26162
26163 Roo.bootstrap.Tooltip = function(config){
26164     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26165     
26166     this.alignment = Roo.bootstrap.Tooltip.alignment;
26167     
26168     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26169         this.alignment = config.alignment;
26170     }
26171     
26172 };
26173
26174 Roo.apply(Roo.bootstrap.Tooltip, {
26175     /**
26176      * @function init initialize tooltip monitoring.
26177      * @static
26178      */
26179     currentEl : false,
26180     currentTip : false,
26181     currentRegion : false,
26182     
26183     //  init : delay?
26184     
26185     init : function()
26186     {
26187         Roo.get(document).on('mouseover', this.enter ,this);
26188         Roo.get(document).on('mouseout', this.leave, this);
26189          
26190         
26191         this.currentTip = new Roo.bootstrap.Tooltip();
26192     },
26193     
26194     enter : function(ev)
26195     {
26196         var dom = ev.getTarget();
26197         
26198         //Roo.log(['enter',dom]);
26199         var el = Roo.fly(dom);
26200         if (this.currentEl) {
26201             //Roo.log(dom);
26202             //Roo.log(this.currentEl);
26203             //Roo.log(this.currentEl.contains(dom));
26204             if (this.currentEl == el) {
26205                 return;
26206             }
26207             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26208                 return;
26209             }
26210
26211         }
26212         
26213         if (this.currentTip.el) {
26214             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26215         }    
26216         //Roo.log(ev);
26217         
26218         if(!el || el.dom == document){
26219             return;
26220         }
26221         
26222         var bindEl = el;
26223         
26224         // you can not look for children, as if el is the body.. then everythign is the child..
26225         if (!el.attr('tooltip')) { //
26226             if (!el.select("[tooltip]").elements.length) {
26227                 return;
26228             }
26229             // is the mouse over this child...?
26230             bindEl = el.select("[tooltip]").first();
26231             var xy = ev.getXY();
26232             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26233                 //Roo.log("not in region.");
26234                 return;
26235             }
26236             //Roo.log("child element over..");
26237             
26238         }
26239         this.currentEl = bindEl;
26240         this.currentTip.bind(bindEl);
26241         this.currentRegion = Roo.lib.Region.getRegion(dom);
26242         this.currentTip.enter();
26243         
26244     },
26245     leave : function(ev)
26246     {
26247         var dom = ev.getTarget();
26248         //Roo.log(['leave',dom]);
26249         if (!this.currentEl) {
26250             return;
26251         }
26252         
26253         
26254         if (dom != this.currentEl.dom) {
26255             return;
26256         }
26257         var xy = ev.getXY();
26258         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26259             return;
26260         }
26261         // only activate leave if mouse cursor is outside... bounding box..
26262         
26263         
26264         
26265         
26266         if (this.currentTip) {
26267             this.currentTip.leave();
26268         }
26269         //Roo.log('clear currentEl');
26270         this.currentEl = false;
26271         
26272         
26273     },
26274     alignment : {
26275         'left' : ['r-l', [-2,0], 'right'],
26276         'right' : ['l-r', [2,0], 'left'],
26277         'bottom' : ['t-b', [0,2], 'top'],
26278         'top' : [ 'b-t', [0,-2], 'bottom']
26279     }
26280     
26281 });
26282
26283
26284 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26285     
26286     
26287     bindEl : false,
26288     
26289     delay : null, // can be { show : 300 , hide: 500}
26290     
26291     timeout : null,
26292     
26293     hoverState : null, //???
26294     
26295     placement : 'bottom', 
26296     
26297     alignment : false,
26298     
26299     getAutoCreate : function(){
26300     
26301         var cfg = {
26302            cls : 'tooltip',
26303            role : 'tooltip',
26304            cn : [
26305                 {
26306                     cls : 'tooltip-arrow'
26307                 },
26308                 {
26309                     cls : 'tooltip-inner'
26310                 }
26311            ]
26312         };
26313         
26314         return cfg;
26315     },
26316     bind : function(el)
26317     {
26318         this.bindEl = el;
26319     },
26320       
26321     
26322     enter : function () {
26323        
26324         if (this.timeout != null) {
26325             clearTimeout(this.timeout);
26326         }
26327         
26328         this.hoverState = 'in';
26329          //Roo.log("enter - show");
26330         if (!this.delay || !this.delay.show) {
26331             this.show();
26332             return;
26333         }
26334         var _t = this;
26335         this.timeout = setTimeout(function () {
26336             if (_t.hoverState == 'in') {
26337                 _t.show();
26338             }
26339         }, this.delay.show);
26340     },
26341     leave : function()
26342     {
26343         clearTimeout(this.timeout);
26344     
26345         this.hoverState = 'out';
26346          if (!this.delay || !this.delay.hide) {
26347             this.hide();
26348             return;
26349         }
26350        
26351         var _t = this;
26352         this.timeout = setTimeout(function () {
26353             //Roo.log("leave - timeout");
26354             
26355             if (_t.hoverState == 'out') {
26356                 _t.hide();
26357                 Roo.bootstrap.Tooltip.currentEl = false;
26358             }
26359         }, delay);
26360     },
26361     
26362     show : function (msg)
26363     {
26364         if (!this.el) {
26365             this.render(document.body);
26366         }
26367         // set content.
26368         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26369         
26370         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26371         
26372         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26373         
26374         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26375         
26376         var placement = typeof this.placement == 'function' ?
26377             this.placement.call(this, this.el, on_el) :
26378             this.placement;
26379             
26380         var autoToken = /\s?auto?\s?/i;
26381         var autoPlace = autoToken.test(placement);
26382         if (autoPlace) {
26383             placement = placement.replace(autoToken, '') || 'top';
26384         }
26385         
26386         //this.el.detach()
26387         //this.el.setXY([0,0]);
26388         this.el.show();
26389         //this.el.dom.style.display='block';
26390         
26391         //this.el.appendTo(on_el);
26392         
26393         var p = this.getPosition();
26394         var box = this.el.getBox();
26395         
26396         if (autoPlace) {
26397             // fixme..
26398         }
26399         
26400         var align = this.alignment[placement];
26401         
26402         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26403         
26404         if(placement == 'top' || placement == 'bottom'){
26405             if(xy[0] < 0){
26406                 placement = 'right';
26407             }
26408             
26409             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26410                 placement = 'left';
26411             }
26412             
26413             var scroll = Roo.select('body', true).first().getScroll();
26414             
26415             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26416                 placement = 'top';
26417             }
26418             
26419             align = this.alignment[placement];
26420         }
26421         
26422         this.el.alignTo(this.bindEl, align[0],align[1]);
26423         //var arrow = this.el.select('.arrow',true).first();
26424         //arrow.set(align[2], 
26425         
26426         this.el.addClass(placement);
26427         
26428         this.el.addClass('in fade');
26429         
26430         this.hoverState = null;
26431         
26432         if (this.el.hasClass('fade')) {
26433             // fade it?
26434         }
26435         
26436     },
26437     hide : function()
26438     {
26439          
26440         if (!this.el) {
26441             return;
26442         }
26443         //this.el.setXY([0,0]);
26444         this.el.removeClass('in');
26445         //this.el.hide();
26446         
26447     }
26448     
26449 });
26450  
26451
26452  /*
26453  * - LGPL
26454  *
26455  * Location Picker
26456  * 
26457  */
26458
26459 /**
26460  * @class Roo.bootstrap.LocationPicker
26461  * @extends Roo.bootstrap.Component
26462  * Bootstrap LocationPicker class
26463  * @cfg {Number} latitude Position when init default 0
26464  * @cfg {Number} longitude Position when init default 0
26465  * @cfg {Number} zoom default 15
26466  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26467  * @cfg {Boolean} mapTypeControl default false
26468  * @cfg {Boolean} disableDoubleClickZoom default false
26469  * @cfg {Boolean} scrollwheel default true
26470  * @cfg {Boolean} streetViewControl default false
26471  * @cfg {Number} radius default 0
26472  * @cfg {String} locationName
26473  * @cfg {Boolean} draggable default true
26474  * @cfg {Boolean} enableAutocomplete default false
26475  * @cfg {Boolean} enableReverseGeocode default true
26476  * @cfg {String} markerTitle
26477  * 
26478  * @constructor
26479  * Create a new LocationPicker
26480  * @param {Object} config The config object
26481  */
26482
26483
26484 Roo.bootstrap.LocationPicker = function(config){
26485     
26486     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26487     
26488     this.addEvents({
26489         /**
26490          * @event initial
26491          * Fires when the picker initialized.
26492          * @param {Roo.bootstrap.LocationPicker} this
26493          * @param {Google Location} location
26494          */
26495         initial : true,
26496         /**
26497          * @event positionchanged
26498          * Fires when the picker position changed.
26499          * @param {Roo.bootstrap.LocationPicker} this
26500          * @param {Google Location} location
26501          */
26502         positionchanged : true,
26503         /**
26504          * @event resize
26505          * Fires when the map resize.
26506          * @param {Roo.bootstrap.LocationPicker} this
26507          */
26508         resize : true,
26509         /**
26510          * @event show
26511          * Fires when the map show.
26512          * @param {Roo.bootstrap.LocationPicker} this
26513          */
26514         show : true,
26515         /**
26516          * @event hide
26517          * Fires when the map hide.
26518          * @param {Roo.bootstrap.LocationPicker} this
26519          */
26520         hide : true,
26521         /**
26522          * @event mapClick
26523          * Fires when click the map.
26524          * @param {Roo.bootstrap.LocationPicker} this
26525          * @param {Map event} e
26526          */
26527         mapClick : true,
26528         /**
26529          * @event mapRightClick
26530          * Fires when right click the map.
26531          * @param {Roo.bootstrap.LocationPicker} this
26532          * @param {Map event} e
26533          */
26534         mapRightClick : true,
26535         /**
26536          * @event markerClick
26537          * Fires when click the marker.
26538          * @param {Roo.bootstrap.LocationPicker} this
26539          * @param {Map event} e
26540          */
26541         markerClick : true,
26542         /**
26543          * @event markerRightClick
26544          * Fires when right click the marker.
26545          * @param {Roo.bootstrap.LocationPicker} this
26546          * @param {Map event} e
26547          */
26548         markerRightClick : true,
26549         /**
26550          * @event OverlayViewDraw
26551          * Fires when OverlayView Draw
26552          * @param {Roo.bootstrap.LocationPicker} this
26553          */
26554         OverlayViewDraw : true,
26555         /**
26556          * @event OverlayViewOnAdd
26557          * Fires when OverlayView Draw
26558          * @param {Roo.bootstrap.LocationPicker} this
26559          */
26560         OverlayViewOnAdd : true,
26561         /**
26562          * @event OverlayViewOnRemove
26563          * Fires when OverlayView Draw
26564          * @param {Roo.bootstrap.LocationPicker} this
26565          */
26566         OverlayViewOnRemove : true,
26567         /**
26568          * @event OverlayViewShow
26569          * Fires when OverlayView Draw
26570          * @param {Roo.bootstrap.LocationPicker} this
26571          * @param {Pixel} cpx
26572          */
26573         OverlayViewShow : true,
26574         /**
26575          * @event OverlayViewHide
26576          * Fires when OverlayView Draw
26577          * @param {Roo.bootstrap.LocationPicker} this
26578          */
26579         OverlayViewHide : true,
26580         /**
26581          * @event loadexception
26582          * Fires when load google lib failed.
26583          * @param {Roo.bootstrap.LocationPicker} this
26584          */
26585         loadexception : true
26586     });
26587         
26588 };
26589
26590 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26591     
26592     gMapContext: false,
26593     
26594     latitude: 0,
26595     longitude: 0,
26596     zoom: 15,
26597     mapTypeId: false,
26598     mapTypeControl: false,
26599     disableDoubleClickZoom: false,
26600     scrollwheel: true,
26601     streetViewControl: false,
26602     radius: 0,
26603     locationName: '',
26604     draggable: true,
26605     enableAutocomplete: false,
26606     enableReverseGeocode: true,
26607     markerTitle: '',
26608     
26609     getAutoCreate: function()
26610     {
26611
26612         var cfg = {
26613             tag: 'div',
26614             cls: 'roo-location-picker'
26615         };
26616         
26617         return cfg
26618     },
26619     
26620     initEvents: function(ct, position)
26621     {       
26622         if(!this.el.getWidth() || this.isApplied()){
26623             return;
26624         }
26625         
26626         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26627         
26628         this.initial();
26629     },
26630     
26631     initial: function()
26632     {
26633         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26634             this.fireEvent('loadexception', this);
26635             return;
26636         }
26637         
26638         if(!this.mapTypeId){
26639             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26640         }
26641         
26642         this.gMapContext = this.GMapContext();
26643         
26644         this.initOverlayView();
26645         
26646         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26647         
26648         var _this = this;
26649                 
26650         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26651             _this.setPosition(_this.gMapContext.marker.position);
26652         });
26653         
26654         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26655             _this.fireEvent('mapClick', this, event);
26656             
26657         });
26658
26659         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26660             _this.fireEvent('mapRightClick', this, event);
26661             
26662         });
26663         
26664         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26665             _this.fireEvent('markerClick', this, event);
26666             
26667         });
26668
26669         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26670             _this.fireEvent('markerRightClick', this, event);
26671             
26672         });
26673         
26674         this.setPosition(this.gMapContext.location);
26675         
26676         this.fireEvent('initial', this, this.gMapContext.location);
26677     },
26678     
26679     initOverlayView: function()
26680     {
26681         var _this = this;
26682         
26683         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26684             
26685             draw: function()
26686             {
26687                 _this.fireEvent('OverlayViewDraw', _this);
26688             },
26689             
26690             onAdd: function()
26691             {
26692                 _this.fireEvent('OverlayViewOnAdd', _this);
26693             },
26694             
26695             onRemove: function()
26696             {
26697                 _this.fireEvent('OverlayViewOnRemove', _this);
26698             },
26699             
26700             show: function(cpx)
26701             {
26702                 _this.fireEvent('OverlayViewShow', _this, cpx);
26703             },
26704             
26705             hide: function()
26706             {
26707                 _this.fireEvent('OverlayViewHide', _this);
26708             }
26709             
26710         });
26711     },
26712     
26713     fromLatLngToContainerPixel: function(event)
26714     {
26715         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26716     },
26717     
26718     isApplied: function() 
26719     {
26720         return this.getGmapContext() == false ? false : true;
26721     },
26722     
26723     getGmapContext: function() 
26724     {
26725         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26726     },
26727     
26728     GMapContext: function() 
26729     {
26730         var position = new google.maps.LatLng(this.latitude, this.longitude);
26731         
26732         var _map = new google.maps.Map(this.el.dom, {
26733             center: position,
26734             zoom: this.zoom,
26735             mapTypeId: this.mapTypeId,
26736             mapTypeControl: this.mapTypeControl,
26737             disableDoubleClickZoom: this.disableDoubleClickZoom,
26738             scrollwheel: this.scrollwheel,
26739             streetViewControl: this.streetViewControl,
26740             locationName: this.locationName,
26741             draggable: this.draggable,
26742             enableAutocomplete: this.enableAutocomplete,
26743             enableReverseGeocode: this.enableReverseGeocode
26744         });
26745         
26746         var _marker = new google.maps.Marker({
26747             position: position,
26748             map: _map,
26749             title: this.markerTitle,
26750             draggable: this.draggable
26751         });
26752         
26753         return {
26754             map: _map,
26755             marker: _marker,
26756             circle: null,
26757             location: position,
26758             radius: this.radius,
26759             locationName: this.locationName,
26760             addressComponents: {
26761                 formatted_address: null,
26762                 addressLine1: null,
26763                 addressLine2: null,
26764                 streetName: null,
26765                 streetNumber: null,
26766                 city: null,
26767                 district: null,
26768                 state: null,
26769                 stateOrProvince: null
26770             },
26771             settings: this,
26772             domContainer: this.el.dom,
26773             geodecoder: new google.maps.Geocoder()
26774         };
26775     },
26776     
26777     drawCircle: function(center, radius, options) 
26778     {
26779         if (this.gMapContext.circle != null) {
26780             this.gMapContext.circle.setMap(null);
26781         }
26782         if (radius > 0) {
26783             radius *= 1;
26784             options = Roo.apply({}, options, {
26785                 strokeColor: "#0000FF",
26786                 strokeOpacity: .35,
26787                 strokeWeight: 2,
26788                 fillColor: "#0000FF",
26789                 fillOpacity: .2
26790             });
26791             
26792             options.map = this.gMapContext.map;
26793             options.radius = radius;
26794             options.center = center;
26795             this.gMapContext.circle = new google.maps.Circle(options);
26796             return this.gMapContext.circle;
26797         }
26798         
26799         return null;
26800     },
26801     
26802     setPosition: function(location) 
26803     {
26804         this.gMapContext.location = location;
26805         this.gMapContext.marker.setPosition(location);
26806         this.gMapContext.map.panTo(location);
26807         this.drawCircle(location, this.gMapContext.radius, {});
26808         
26809         var _this = this;
26810         
26811         if (this.gMapContext.settings.enableReverseGeocode) {
26812             this.gMapContext.geodecoder.geocode({
26813                 latLng: this.gMapContext.location
26814             }, function(results, status) {
26815                 
26816                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26817                     _this.gMapContext.locationName = results[0].formatted_address;
26818                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26819                     
26820                     _this.fireEvent('positionchanged', this, location);
26821                 }
26822             });
26823             
26824             return;
26825         }
26826         
26827         this.fireEvent('positionchanged', this, location);
26828     },
26829     
26830     resize: function()
26831     {
26832         google.maps.event.trigger(this.gMapContext.map, "resize");
26833         
26834         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26835         
26836         this.fireEvent('resize', this);
26837     },
26838     
26839     setPositionByLatLng: function(latitude, longitude)
26840     {
26841         this.setPosition(new google.maps.LatLng(latitude, longitude));
26842     },
26843     
26844     getCurrentPosition: function() 
26845     {
26846         return {
26847             latitude: this.gMapContext.location.lat(),
26848             longitude: this.gMapContext.location.lng()
26849         };
26850     },
26851     
26852     getAddressName: function() 
26853     {
26854         return this.gMapContext.locationName;
26855     },
26856     
26857     getAddressComponents: function() 
26858     {
26859         return this.gMapContext.addressComponents;
26860     },
26861     
26862     address_component_from_google_geocode: function(address_components) 
26863     {
26864         var result = {};
26865         
26866         for (var i = 0; i < address_components.length; i++) {
26867             var component = address_components[i];
26868             if (component.types.indexOf("postal_code") >= 0) {
26869                 result.postalCode = component.short_name;
26870             } else if (component.types.indexOf("street_number") >= 0) {
26871                 result.streetNumber = component.short_name;
26872             } else if (component.types.indexOf("route") >= 0) {
26873                 result.streetName = component.short_name;
26874             } else if (component.types.indexOf("neighborhood") >= 0) {
26875                 result.city = component.short_name;
26876             } else if (component.types.indexOf("locality") >= 0) {
26877                 result.city = component.short_name;
26878             } else if (component.types.indexOf("sublocality") >= 0) {
26879                 result.district = component.short_name;
26880             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26881                 result.stateOrProvince = component.short_name;
26882             } else if (component.types.indexOf("country") >= 0) {
26883                 result.country = component.short_name;
26884             }
26885         }
26886         
26887         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26888         result.addressLine2 = "";
26889         return result;
26890     },
26891     
26892     setZoomLevel: function(zoom)
26893     {
26894         this.gMapContext.map.setZoom(zoom);
26895     },
26896     
26897     show: function()
26898     {
26899         if(!this.el){
26900             return;
26901         }
26902         
26903         this.el.show();
26904         
26905         this.resize();
26906         
26907         this.fireEvent('show', this);
26908     },
26909     
26910     hide: function()
26911     {
26912         if(!this.el){
26913             return;
26914         }
26915         
26916         this.el.hide();
26917         
26918         this.fireEvent('hide', this);
26919     }
26920     
26921 });
26922
26923 Roo.apply(Roo.bootstrap.LocationPicker, {
26924     
26925     OverlayView : function(map, options)
26926     {
26927         options = options || {};
26928         
26929         this.setMap(map);
26930     }
26931     
26932     
26933 });/*
26934  * - LGPL
26935  *
26936  * Alert
26937  * 
26938  */
26939
26940 /**
26941  * @class Roo.bootstrap.Alert
26942  * @extends Roo.bootstrap.Component
26943  * Bootstrap Alert class
26944  * @cfg {String} title The title of alert
26945  * @cfg {String} html The content of alert
26946  * @cfg {String} weight (  success | info | warning | danger )
26947  * @cfg {String} faicon font-awesomeicon
26948  * 
26949  * @constructor
26950  * Create a new alert
26951  * @param {Object} config The config object
26952  */
26953
26954
26955 Roo.bootstrap.Alert = function(config){
26956     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26957     
26958 };
26959
26960 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26961     
26962     title: '',
26963     html: '',
26964     weight: false,
26965     faicon: false,
26966     
26967     getAutoCreate : function()
26968     {
26969         
26970         var cfg = {
26971             tag : 'div',
26972             cls : 'alert',
26973             cn : [
26974                 {
26975                     tag : 'i',
26976                     cls : 'roo-alert-icon'
26977                     
26978                 },
26979                 {
26980                     tag : 'b',
26981                     cls : 'roo-alert-title',
26982                     html : this.title
26983                 },
26984                 {
26985                     tag : 'span',
26986                     cls : 'roo-alert-text',
26987                     html : this.html
26988                 }
26989             ]
26990         };
26991         
26992         if(this.faicon){
26993             cfg.cn[0].cls += ' fa ' + this.faicon;
26994         }
26995         
26996         if(this.weight){
26997             cfg.cls += ' alert-' + this.weight;
26998         }
26999         
27000         return cfg;
27001     },
27002     
27003     initEvents: function() 
27004     {
27005         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27006     },
27007     
27008     setTitle : function(str)
27009     {
27010         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27011     },
27012     
27013     setText : function(str)
27014     {
27015         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27016     },
27017     
27018     setWeight : function(weight)
27019     {
27020         if(this.weight){
27021             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27022         }
27023         
27024         this.weight = weight;
27025         
27026         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27027     },
27028     
27029     setIcon : function(icon)
27030     {
27031         if(this.faicon){
27032             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27033         }
27034         
27035         this.faicon = icon;
27036         
27037         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27038     },
27039     
27040     hide: function() 
27041     {
27042         this.el.hide();   
27043     },
27044     
27045     show: function() 
27046     {  
27047         this.el.show();   
27048     }
27049     
27050 });
27051
27052  
27053 /*
27054 * Licence: LGPL
27055 */
27056
27057 /**
27058  * @class Roo.bootstrap.UploadCropbox
27059  * @extends Roo.bootstrap.Component
27060  * Bootstrap UploadCropbox class
27061  * @cfg {String} emptyText show when image has been loaded
27062  * @cfg {String} rotateNotify show when image too small to rotate
27063  * @cfg {Number} errorTimeout default 3000
27064  * @cfg {Number} minWidth default 300
27065  * @cfg {Number} minHeight default 300
27066  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27067  * @cfg {Boolean} isDocument (true|false) default false
27068  * @cfg {String} url action url
27069  * @cfg {String} paramName default 'imageUpload'
27070  * @cfg {String} method default POST
27071  * @cfg {Boolean} loadMask (true|false) default true
27072  * @cfg {Boolean} loadingText default 'Loading...'
27073  * 
27074  * @constructor
27075  * Create a new UploadCropbox
27076  * @param {Object} config The config object
27077  */
27078
27079 Roo.bootstrap.UploadCropbox = function(config){
27080     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27081     
27082     this.addEvents({
27083         /**
27084          * @event beforeselectfile
27085          * Fire before select file
27086          * @param {Roo.bootstrap.UploadCropbox} this
27087          */
27088         "beforeselectfile" : true,
27089         /**
27090          * @event initial
27091          * Fire after initEvent
27092          * @param {Roo.bootstrap.UploadCropbox} this
27093          */
27094         "initial" : true,
27095         /**
27096          * @event crop
27097          * Fire after initEvent
27098          * @param {Roo.bootstrap.UploadCropbox} this
27099          * @param {String} data
27100          */
27101         "crop" : true,
27102         /**
27103          * @event prepare
27104          * Fire when preparing the file data
27105          * @param {Roo.bootstrap.UploadCropbox} this
27106          * @param {Object} file
27107          */
27108         "prepare" : true,
27109         /**
27110          * @event exception
27111          * Fire when get exception
27112          * @param {Roo.bootstrap.UploadCropbox} this
27113          * @param {XMLHttpRequest} xhr
27114          */
27115         "exception" : true,
27116         /**
27117          * @event beforeloadcanvas
27118          * Fire before load the canvas
27119          * @param {Roo.bootstrap.UploadCropbox} this
27120          * @param {String} src
27121          */
27122         "beforeloadcanvas" : true,
27123         /**
27124          * @event trash
27125          * Fire when trash image
27126          * @param {Roo.bootstrap.UploadCropbox} this
27127          */
27128         "trash" : true,
27129         /**
27130          * @event download
27131          * Fire when download the image
27132          * @param {Roo.bootstrap.UploadCropbox} this
27133          */
27134         "download" : true,
27135         /**
27136          * @event footerbuttonclick
27137          * Fire when footerbuttonclick
27138          * @param {Roo.bootstrap.UploadCropbox} this
27139          * @param {String} type
27140          */
27141         "footerbuttonclick" : true,
27142         /**
27143          * @event resize
27144          * Fire when resize
27145          * @param {Roo.bootstrap.UploadCropbox} this
27146          */
27147         "resize" : true,
27148         /**
27149          * @event rotate
27150          * Fire when rotate the image
27151          * @param {Roo.bootstrap.UploadCropbox} this
27152          * @param {String} pos
27153          */
27154         "rotate" : true,
27155         /**
27156          * @event inspect
27157          * Fire when inspect the file
27158          * @param {Roo.bootstrap.UploadCropbox} this
27159          * @param {Object} file
27160          */
27161         "inspect" : true,
27162         /**
27163          * @event upload
27164          * Fire when xhr upload the file
27165          * @param {Roo.bootstrap.UploadCropbox} this
27166          * @param {Object} data
27167          */
27168         "upload" : true,
27169         /**
27170          * @event arrange
27171          * Fire when arrange the file data
27172          * @param {Roo.bootstrap.UploadCropbox} this
27173          * @param {Object} formData
27174          */
27175         "arrange" : true
27176     });
27177     
27178     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27179 };
27180
27181 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27182     
27183     emptyText : 'Click to upload image',
27184     rotateNotify : 'Image is too small to rotate',
27185     errorTimeout : 3000,
27186     scale : 0,
27187     baseScale : 1,
27188     rotate : 0,
27189     dragable : false,
27190     pinching : false,
27191     mouseX : 0,
27192     mouseY : 0,
27193     cropData : false,
27194     minWidth : 300,
27195     minHeight : 300,
27196     file : false,
27197     exif : {},
27198     baseRotate : 1,
27199     cropType : 'image/jpeg',
27200     buttons : false,
27201     canvasLoaded : false,
27202     isDocument : false,
27203     method : 'POST',
27204     paramName : 'imageUpload',
27205     loadMask : true,
27206     loadingText : 'Loading...',
27207     maskEl : false,
27208     
27209     getAutoCreate : function()
27210     {
27211         var cfg = {
27212             tag : 'div',
27213             cls : 'roo-upload-cropbox',
27214             cn : [
27215                 {
27216                     tag : 'input',
27217                     cls : 'roo-upload-cropbox-selector',
27218                     type : 'file'
27219                 },
27220                 {
27221                     tag : 'div',
27222                     cls : 'roo-upload-cropbox-body',
27223                     style : 'cursor:pointer',
27224                     cn : [
27225                         {
27226                             tag : 'div',
27227                             cls : 'roo-upload-cropbox-preview'
27228                         },
27229                         {
27230                             tag : 'div',
27231                             cls : 'roo-upload-cropbox-thumb'
27232                         },
27233                         {
27234                             tag : 'div',
27235                             cls : 'roo-upload-cropbox-empty-notify',
27236                             html : this.emptyText
27237                         },
27238                         {
27239                             tag : 'div',
27240                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27241                             html : this.rotateNotify
27242                         }
27243                     ]
27244                 },
27245                 {
27246                     tag : 'div',
27247                     cls : 'roo-upload-cropbox-footer',
27248                     cn : {
27249                         tag : 'div',
27250                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27251                         cn : []
27252                     }
27253                 }
27254             ]
27255         };
27256         
27257         return cfg;
27258     },
27259     
27260     onRender : function(ct, position)
27261     {
27262         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27263         
27264         if (this.buttons.length) {
27265             
27266             Roo.each(this.buttons, function(bb) {
27267                 
27268                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27269                 
27270                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27271                 
27272             }, this);
27273         }
27274         
27275         if(this.loadMask){
27276             this.maskEl = this.el;
27277         }
27278     },
27279     
27280     initEvents : function()
27281     {
27282         this.urlAPI = (window.createObjectURL && window) || 
27283                                 (window.URL && URL.revokeObjectURL && URL) || 
27284                                 (window.webkitURL && webkitURL);
27285                         
27286         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27287         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27288         
27289         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27290         this.selectorEl.hide();
27291         
27292         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27293         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27294         
27295         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27296         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27297         this.thumbEl.hide();
27298         
27299         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27300         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27301         
27302         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27303         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27304         this.errorEl.hide();
27305         
27306         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27307         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27308         this.footerEl.hide();
27309         
27310         this.setThumbBoxSize();
27311         
27312         this.bind();
27313         
27314         this.resize();
27315         
27316         this.fireEvent('initial', this);
27317     },
27318
27319     bind : function()
27320     {
27321         var _this = this;
27322         
27323         window.addEventListener("resize", function() { _this.resize(); } );
27324         
27325         this.bodyEl.on('click', this.beforeSelectFile, this);
27326         
27327         if(Roo.isTouch){
27328             this.bodyEl.on('touchstart', this.onTouchStart, this);
27329             this.bodyEl.on('touchmove', this.onTouchMove, this);
27330             this.bodyEl.on('touchend', this.onTouchEnd, this);
27331         }
27332         
27333         if(!Roo.isTouch){
27334             this.bodyEl.on('mousedown', this.onMouseDown, this);
27335             this.bodyEl.on('mousemove', this.onMouseMove, this);
27336             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27337             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27338             Roo.get(document).on('mouseup', this.onMouseUp, this);
27339         }
27340         
27341         this.selectorEl.on('change', this.onFileSelected, this);
27342     },
27343     
27344     reset : function()
27345     {    
27346         this.scale = 0;
27347         this.baseScale = 1;
27348         this.rotate = 0;
27349         this.baseRotate = 1;
27350         this.dragable = false;
27351         this.pinching = false;
27352         this.mouseX = 0;
27353         this.mouseY = 0;
27354         this.cropData = false;
27355         this.notifyEl.dom.innerHTML = this.emptyText;
27356         
27357         this.selectorEl.dom.value = '';
27358         
27359     },
27360     
27361     resize : function()
27362     {
27363         if(this.fireEvent('resize', this) != false){
27364             this.setThumbBoxPosition();
27365             this.setCanvasPosition();
27366         }
27367     },
27368     
27369     onFooterButtonClick : function(e, el, o, type)
27370     {
27371         switch (type) {
27372             case 'rotate-left' :
27373                 this.onRotateLeft(e);
27374                 break;
27375             case 'rotate-right' :
27376                 this.onRotateRight(e);
27377                 break;
27378             case 'picture' :
27379                 this.beforeSelectFile(e);
27380                 break;
27381             case 'trash' :
27382                 this.trash(e);
27383                 break;
27384             case 'crop' :
27385                 this.crop(e);
27386                 break;
27387             case 'download' :
27388                 this.download(e);
27389                 break;
27390             default :
27391                 break;
27392         }
27393         
27394         this.fireEvent('footerbuttonclick', this, type);
27395     },
27396     
27397     beforeSelectFile : function(e)
27398     {
27399         e.preventDefault();
27400         
27401         if(this.fireEvent('beforeselectfile', this) != false){
27402             this.selectorEl.dom.click();
27403         }
27404     },
27405     
27406     onFileSelected : function(e)
27407     {
27408         e.preventDefault();
27409         
27410         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27411             return;
27412         }
27413         
27414         var file = this.selectorEl.dom.files[0];
27415         
27416         if(this.fireEvent('inspect', this, file) != false){
27417             this.prepare(file);
27418         }
27419         
27420     },
27421     
27422     trash : function(e)
27423     {
27424         this.fireEvent('trash', this);
27425     },
27426     
27427     download : function(e)
27428     {
27429         this.fireEvent('download', this);
27430     },
27431     
27432     loadCanvas : function(src)
27433     {   
27434         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27435             
27436             this.reset();
27437             
27438             this.imageEl = document.createElement('img');
27439             
27440             var _this = this;
27441             
27442             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27443             
27444             this.imageEl.src = src;
27445         }
27446     },
27447     
27448     onLoadCanvas : function()
27449     {   
27450         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27451         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27452         
27453         this.bodyEl.un('click', this.beforeSelectFile, this);
27454         
27455         this.notifyEl.hide();
27456         this.thumbEl.show();
27457         this.footerEl.show();
27458         
27459         this.baseRotateLevel();
27460         
27461         if(this.isDocument){
27462             this.setThumbBoxSize();
27463         }
27464         
27465         this.setThumbBoxPosition();
27466         
27467         this.baseScaleLevel();
27468         
27469         this.draw();
27470         
27471         this.resize();
27472         
27473         this.canvasLoaded = true;
27474         
27475         if(this.loadMask){
27476             this.maskEl.unmask();
27477         }
27478         
27479     },
27480     
27481     setCanvasPosition : function()
27482     {   
27483         if(!this.canvasEl){
27484             return;
27485         }
27486         
27487         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27488         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27489         
27490         this.previewEl.setLeft(pw);
27491         this.previewEl.setTop(ph);
27492         
27493     },
27494     
27495     onMouseDown : function(e)
27496     {   
27497         e.stopEvent();
27498         
27499         this.dragable = true;
27500         this.pinching = false;
27501         
27502         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27503             this.dragable = false;
27504             return;
27505         }
27506         
27507         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27508         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27509         
27510     },
27511     
27512     onMouseMove : function(e)
27513     {   
27514         e.stopEvent();
27515         
27516         if(!this.canvasLoaded){
27517             return;
27518         }
27519         
27520         if (!this.dragable){
27521             return;
27522         }
27523         
27524         var minX = Math.ceil(this.thumbEl.getLeft(true));
27525         var minY = Math.ceil(this.thumbEl.getTop(true));
27526         
27527         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27528         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27529         
27530         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27531         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27532         
27533         x = x - this.mouseX;
27534         y = y - this.mouseY;
27535         
27536         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27537         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27538         
27539         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27540         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27541         
27542         this.previewEl.setLeft(bgX);
27543         this.previewEl.setTop(bgY);
27544         
27545         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27546         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27547     },
27548     
27549     onMouseUp : function(e)
27550     {   
27551         e.stopEvent();
27552         
27553         this.dragable = false;
27554     },
27555     
27556     onMouseWheel : function(e)
27557     {   
27558         e.stopEvent();
27559         
27560         this.startScale = this.scale;
27561         
27562         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27563         
27564         if(!this.zoomable()){
27565             this.scale = this.startScale;
27566             return;
27567         }
27568         
27569         this.draw();
27570         
27571         return;
27572     },
27573     
27574     zoomable : function()
27575     {
27576         var minScale = this.thumbEl.getWidth() / this.minWidth;
27577         
27578         if(this.minWidth < this.minHeight){
27579             minScale = this.thumbEl.getHeight() / this.minHeight;
27580         }
27581         
27582         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27583         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27584         
27585         if(
27586                 this.isDocument &&
27587                 (this.rotate == 0 || this.rotate == 180) && 
27588                 (
27589                     width > this.imageEl.OriginWidth || 
27590                     height > this.imageEl.OriginHeight ||
27591                     (width < this.minWidth && height < this.minHeight)
27592                 )
27593         ){
27594             return false;
27595         }
27596         
27597         if(
27598                 this.isDocument &&
27599                 (this.rotate == 90 || this.rotate == 270) && 
27600                 (
27601                     width > this.imageEl.OriginWidth || 
27602                     height > this.imageEl.OriginHeight ||
27603                     (width < this.minHeight && height < this.minWidth)
27604                 )
27605         ){
27606             return false;
27607         }
27608         
27609         if(
27610                 !this.isDocument &&
27611                 (this.rotate == 0 || this.rotate == 180) && 
27612                 (
27613                     width < this.minWidth || 
27614                     width > this.imageEl.OriginWidth || 
27615                     height < this.minHeight || 
27616                     height > this.imageEl.OriginHeight
27617                 )
27618         ){
27619             return false;
27620         }
27621         
27622         if(
27623                 !this.isDocument &&
27624                 (this.rotate == 90 || this.rotate == 270) && 
27625                 (
27626                     width < this.minHeight || 
27627                     width > this.imageEl.OriginWidth || 
27628                     height < this.minWidth || 
27629                     height > this.imageEl.OriginHeight
27630                 )
27631         ){
27632             return false;
27633         }
27634         
27635         return true;
27636         
27637     },
27638     
27639     onRotateLeft : function(e)
27640     {   
27641         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27642             
27643             var minScale = this.thumbEl.getWidth() / this.minWidth;
27644             
27645             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27646             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27647             
27648             this.startScale = this.scale;
27649             
27650             while (this.getScaleLevel() < minScale){
27651             
27652                 this.scale = this.scale + 1;
27653                 
27654                 if(!this.zoomable()){
27655                     break;
27656                 }
27657                 
27658                 if(
27659                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27660                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27661                 ){
27662                     continue;
27663                 }
27664                 
27665                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27666
27667                 this.draw();
27668                 
27669                 return;
27670             }
27671             
27672             this.scale = this.startScale;
27673             
27674             this.onRotateFail();
27675             
27676             return false;
27677         }
27678         
27679         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27680
27681         if(this.isDocument){
27682             this.setThumbBoxSize();
27683             this.setThumbBoxPosition();
27684             this.setCanvasPosition();
27685         }
27686         
27687         this.draw();
27688         
27689         this.fireEvent('rotate', this, 'left');
27690         
27691     },
27692     
27693     onRotateRight : function(e)
27694     {
27695         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27696             
27697             var minScale = this.thumbEl.getWidth() / this.minWidth;
27698         
27699             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27700             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27701             
27702             this.startScale = this.scale;
27703             
27704             while (this.getScaleLevel() < minScale){
27705             
27706                 this.scale = this.scale + 1;
27707                 
27708                 if(!this.zoomable()){
27709                     break;
27710                 }
27711                 
27712                 if(
27713                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27714                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27715                 ){
27716                     continue;
27717                 }
27718                 
27719                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27720
27721                 this.draw();
27722                 
27723                 return;
27724             }
27725             
27726             this.scale = this.startScale;
27727             
27728             this.onRotateFail();
27729             
27730             return false;
27731         }
27732         
27733         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27734
27735         if(this.isDocument){
27736             this.setThumbBoxSize();
27737             this.setThumbBoxPosition();
27738             this.setCanvasPosition();
27739         }
27740         
27741         this.draw();
27742         
27743         this.fireEvent('rotate', this, 'right');
27744     },
27745     
27746     onRotateFail : function()
27747     {
27748         this.errorEl.show(true);
27749         
27750         var _this = this;
27751         
27752         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27753     },
27754     
27755     draw : function()
27756     {
27757         this.previewEl.dom.innerHTML = '';
27758         
27759         var canvasEl = document.createElement("canvas");
27760         
27761         var contextEl = canvasEl.getContext("2d");
27762         
27763         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27764         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27765         var center = this.imageEl.OriginWidth / 2;
27766         
27767         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27768             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27769             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27770             center = this.imageEl.OriginHeight / 2;
27771         }
27772         
27773         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27774         
27775         contextEl.translate(center, center);
27776         contextEl.rotate(this.rotate * Math.PI / 180);
27777
27778         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27779         
27780         this.canvasEl = document.createElement("canvas");
27781         
27782         this.contextEl = this.canvasEl.getContext("2d");
27783         
27784         switch (this.rotate) {
27785             case 0 :
27786                 
27787                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27788                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27789                 
27790                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27791                 
27792                 break;
27793             case 90 : 
27794                 
27795                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27796                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27797                 
27798                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27799                     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);
27800                     break;
27801                 }
27802                 
27803                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27804                 
27805                 break;
27806             case 180 :
27807                 
27808                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27809                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27810                 
27811                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27812                     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);
27813                     break;
27814                 }
27815                 
27816                 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);
27817                 
27818                 break;
27819             case 270 :
27820                 
27821                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27822                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27823         
27824                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27825                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27826                     break;
27827                 }
27828                 
27829                 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);
27830                 
27831                 break;
27832             default : 
27833                 break;
27834         }
27835         
27836         this.previewEl.appendChild(this.canvasEl);
27837         
27838         this.setCanvasPosition();
27839     },
27840     
27841     crop : function()
27842     {
27843         if(!this.canvasLoaded){
27844             return;
27845         }
27846         
27847         var imageCanvas = document.createElement("canvas");
27848         
27849         var imageContext = imageCanvas.getContext("2d");
27850         
27851         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27852         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27853         
27854         var center = imageCanvas.width / 2;
27855         
27856         imageContext.translate(center, center);
27857         
27858         imageContext.rotate(this.rotate * Math.PI / 180);
27859         
27860         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27861         
27862         var canvas = document.createElement("canvas");
27863         
27864         var context = canvas.getContext("2d");
27865                 
27866         canvas.width = this.minWidth;
27867         canvas.height = this.minHeight;
27868
27869         switch (this.rotate) {
27870             case 0 :
27871                 
27872                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27873                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27874                 
27875                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27876                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27877                 
27878                 var targetWidth = this.minWidth - 2 * x;
27879                 var targetHeight = this.minHeight - 2 * y;
27880                 
27881                 var scale = 1;
27882                 
27883                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27884                     scale = targetWidth / width;
27885                 }
27886                 
27887                 if(x > 0 && y == 0){
27888                     scale = targetHeight / height;
27889                 }
27890                 
27891                 if(x > 0 && y > 0){
27892                     scale = targetWidth / width;
27893                     
27894                     if(width < height){
27895                         scale = targetHeight / height;
27896                     }
27897                 }
27898                 
27899                 context.scale(scale, scale);
27900                 
27901                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27902                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27903
27904                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27905                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27906
27907                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27908                 
27909                 break;
27910             case 90 : 
27911                 
27912                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27913                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27914                 
27915                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27916                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27917                 
27918                 var targetWidth = this.minWidth - 2 * x;
27919                 var targetHeight = this.minHeight - 2 * y;
27920                 
27921                 var scale = 1;
27922                 
27923                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27924                     scale = targetWidth / width;
27925                 }
27926                 
27927                 if(x > 0 && y == 0){
27928                     scale = targetHeight / height;
27929                 }
27930                 
27931                 if(x > 0 && y > 0){
27932                     scale = targetWidth / width;
27933                     
27934                     if(width < height){
27935                         scale = targetHeight / height;
27936                     }
27937                 }
27938                 
27939                 context.scale(scale, scale);
27940                 
27941                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27942                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27943
27944                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27945                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27946                 
27947                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27948                 
27949                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27950                 
27951                 break;
27952             case 180 :
27953                 
27954                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27955                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27956                 
27957                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27958                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27959                 
27960                 var targetWidth = this.minWidth - 2 * x;
27961                 var targetHeight = this.minHeight - 2 * y;
27962                 
27963                 var scale = 1;
27964                 
27965                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27966                     scale = targetWidth / width;
27967                 }
27968                 
27969                 if(x > 0 && y == 0){
27970                     scale = targetHeight / height;
27971                 }
27972                 
27973                 if(x > 0 && y > 0){
27974                     scale = targetWidth / width;
27975                     
27976                     if(width < height){
27977                         scale = targetHeight / height;
27978                     }
27979                 }
27980                 
27981                 context.scale(scale, scale);
27982                 
27983                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27984                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27985
27986                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27987                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27988
27989                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27990                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27991                 
27992                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27993                 
27994                 break;
27995             case 270 :
27996                 
27997                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27998                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27999                 
28000                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28001                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28002                 
28003                 var targetWidth = this.minWidth - 2 * x;
28004                 var targetHeight = this.minHeight - 2 * y;
28005                 
28006                 var scale = 1;
28007                 
28008                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28009                     scale = targetWidth / width;
28010                 }
28011                 
28012                 if(x > 0 && y == 0){
28013                     scale = targetHeight / height;
28014                 }
28015                 
28016                 if(x > 0 && y > 0){
28017                     scale = targetWidth / width;
28018                     
28019                     if(width < height){
28020                         scale = targetHeight / height;
28021                     }
28022                 }
28023                 
28024                 context.scale(scale, scale);
28025                 
28026                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28027                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28028
28029                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28030                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28031                 
28032                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28033                 
28034                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28035                 
28036                 break;
28037             default : 
28038                 break;
28039         }
28040         
28041         this.cropData = canvas.toDataURL(this.cropType);
28042         
28043         if(this.fireEvent('crop', this, this.cropData) !== false){
28044             this.process(this.file, this.cropData);
28045         }
28046         
28047         return;
28048         
28049     },
28050     
28051     setThumbBoxSize : function()
28052     {
28053         var width, height;
28054         
28055         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28056             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28057             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28058             
28059             this.minWidth = width;
28060             this.minHeight = height;
28061             
28062             if(this.rotate == 90 || this.rotate == 270){
28063                 this.minWidth = height;
28064                 this.minHeight = width;
28065             }
28066         }
28067         
28068         height = 300;
28069         width = Math.ceil(this.minWidth * height / this.minHeight);
28070         
28071         if(this.minWidth > this.minHeight){
28072             width = 300;
28073             height = Math.ceil(this.minHeight * width / this.minWidth);
28074         }
28075         
28076         this.thumbEl.setStyle({
28077             width : width + 'px',
28078             height : height + 'px'
28079         });
28080
28081         return;
28082             
28083     },
28084     
28085     setThumbBoxPosition : function()
28086     {
28087         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28088         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28089         
28090         this.thumbEl.setLeft(x);
28091         this.thumbEl.setTop(y);
28092         
28093     },
28094     
28095     baseRotateLevel : function()
28096     {
28097         this.baseRotate = 1;
28098         
28099         if(
28100                 typeof(this.exif) != 'undefined' &&
28101                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28102                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28103         ){
28104             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28105         }
28106         
28107         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28108         
28109     },
28110     
28111     baseScaleLevel : function()
28112     {
28113         var width, height;
28114         
28115         if(this.isDocument){
28116             
28117             if(this.baseRotate == 6 || this.baseRotate == 8){
28118             
28119                 height = this.thumbEl.getHeight();
28120                 this.baseScale = height / this.imageEl.OriginWidth;
28121
28122                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28123                     width = this.thumbEl.getWidth();
28124                     this.baseScale = width / this.imageEl.OriginHeight;
28125                 }
28126
28127                 return;
28128             }
28129
28130             height = this.thumbEl.getHeight();
28131             this.baseScale = height / this.imageEl.OriginHeight;
28132
28133             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28134                 width = this.thumbEl.getWidth();
28135                 this.baseScale = width / this.imageEl.OriginWidth;
28136             }
28137
28138             return;
28139         }
28140         
28141         if(this.baseRotate == 6 || this.baseRotate == 8){
28142             
28143             width = this.thumbEl.getHeight();
28144             this.baseScale = width / this.imageEl.OriginHeight;
28145             
28146             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28147                 height = this.thumbEl.getWidth();
28148                 this.baseScale = height / this.imageEl.OriginHeight;
28149             }
28150             
28151             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28152                 height = this.thumbEl.getWidth();
28153                 this.baseScale = height / this.imageEl.OriginHeight;
28154                 
28155                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28156                     width = this.thumbEl.getHeight();
28157                     this.baseScale = width / this.imageEl.OriginWidth;
28158                 }
28159             }
28160             
28161             return;
28162         }
28163         
28164         width = this.thumbEl.getWidth();
28165         this.baseScale = width / this.imageEl.OriginWidth;
28166         
28167         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28168             height = this.thumbEl.getHeight();
28169             this.baseScale = height / this.imageEl.OriginHeight;
28170         }
28171         
28172         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28173             
28174             height = this.thumbEl.getHeight();
28175             this.baseScale = height / this.imageEl.OriginHeight;
28176             
28177             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28178                 width = this.thumbEl.getWidth();
28179                 this.baseScale = width / this.imageEl.OriginWidth;
28180             }
28181             
28182         }
28183         
28184         return;
28185     },
28186     
28187     getScaleLevel : function()
28188     {
28189         return this.baseScale * Math.pow(1.1, this.scale);
28190     },
28191     
28192     onTouchStart : function(e)
28193     {
28194         if(!this.canvasLoaded){
28195             this.beforeSelectFile(e);
28196             return;
28197         }
28198         
28199         var touches = e.browserEvent.touches;
28200         
28201         if(!touches){
28202             return;
28203         }
28204         
28205         if(touches.length == 1){
28206             this.onMouseDown(e);
28207             return;
28208         }
28209         
28210         if(touches.length != 2){
28211             return;
28212         }
28213         
28214         var coords = [];
28215         
28216         for(var i = 0, finger; finger = touches[i]; i++){
28217             coords.push(finger.pageX, finger.pageY);
28218         }
28219         
28220         var x = Math.pow(coords[0] - coords[2], 2);
28221         var y = Math.pow(coords[1] - coords[3], 2);
28222         
28223         this.startDistance = Math.sqrt(x + y);
28224         
28225         this.startScale = this.scale;
28226         
28227         this.pinching = true;
28228         this.dragable = false;
28229         
28230     },
28231     
28232     onTouchMove : function(e)
28233     {
28234         if(!this.pinching && !this.dragable){
28235             return;
28236         }
28237         
28238         var touches = e.browserEvent.touches;
28239         
28240         if(!touches){
28241             return;
28242         }
28243         
28244         if(this.dragable){
28245             this.onMouseMove(e);
28246             return;
28247         }
28248         
28249         var coords = [];
28250         
28251         for(var i = 0, finger; finger = touches[i]; i++){
28252             coords.push(finger.pageX, finger.pageY);
28253         }
28254         
28255         var x = Math.pow(coords[0] - coords[2], 2);
28256         var y = Math.pow(coords[1] - coords[3], 2);
28257         
28258         this.endDistance = Math.sqrt(x + y);
28259         
28260         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28261         
28262         if(!this.zoomable()){
28263             this.scale = this.startScale;
28264             return;
28265         }
28266         
28267         this.draw();
28268         
28269     },
28270     
28271     onTouchEnd : function(e)
28272     {
28273         this.pinching = false;
28274         this.dragable = false;
28275         
28276     },
28277     
28278     process : function(file, crop)
28279     {
28280         if(this.loadMask){
28281             this.maskEl.mask(this.loadingText);
28282         }
28283         
28284         this.xhr = new XMLHttpRequest();
28285         
28286         file.xhr = this.xhr;
28287
28288         this.xhr.open(this.method, this.url, true);
28289         
28290         var headers = {
28291             "Accept": "application/json",
28292             "Cache-Control": "no-cache",
28293             "X-Requested-With": "XMLHttpRequest"
28294         };
28295         
28296         for (var headerName in headers) {
28297             var headerValue = headers[headerName];
28298             if (headerValue) {
28299                 this.xhr.setRequestHeader(headerName, headerValue);
28300             }
28301         }
28302         
28303         var _this = this;
28304         
28305         this.xhr.onload = function()
28306         {
28307             _this.xhrOnLoad(_this.xhr);
28308         }
28309         
28310         this.xhr.onerror = function()
28311         {
28312             _this.xhrOnError(_this.xhr);
28313         }
28314         
28315         var formData = new FormData();
28316
28317         formData.append('returnHTML', 'NO');
28318         
28319         if(crop){
28320             formData.append('crop', crop);
28321         }
28322         
28323         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28324             formData.append(this.paramName, file, file.name);
28325         }
28326         
28327         if(typeof(file.filename) != 'undefined'){
28328             formData.append('filename', file.filename);
28329         }
28330         
28331         if(typeof(file.mimetype) != 'undefined'){
28332             formData.append('mimetype', file.mimetype);
28333         }
28334         
28335         if(this.fireEvent('arrange', this, formData) != false){
28336             this.xhr.send(formData);
28337         };
28338     },
28339     
28340     xhrOnLoad : function(xhr)
28341     {
28342         if(this.loadMask){
28343             this.maskEl.unmask();
28344         }
28345         
28346         if (xhr.readyState !== 4) {
28347             this.fireEvent('exception', this, xhr);
28348             return;
28349         }
28350
28351         var response = Roo.decode(xhr.responseText);
28352         
28353         if(!response.success){
28354             this.fireEvent('exception', this, xhr);
28355             return;
28356         }
28357         
28358         var response = Roo.decode(xhr.responseText);
28359         
28360         this.fireEvent('upload', this, response);
28361         
28362     },
28363     
28364     xhrOnError : function()
28365     {
28366         if(this.loadMask){
28367             this.maskEl.unmask();
28368         }
28369         
28370         Roo.log('xhr on error');
28371         
28372         var response = Roo.decode(xhr.responseText);
28373           
28374         Roo.log(response);
28375         
28376     },
28377     
28378     prepare : function(file)
28379     {   
28380         if(this.loadMask){
28381             this.maskEl.mask(this.loadingText);
28382         }
28383         
28384         this.file = false;
28385         this.exif = {};
28386         
28387         if(typeof(file) === 'string'){
28388             this.loadCanvas(file);
28389             return;
28390         }
28391         
28392         if(!file || !this.urlAPI){
28393             return;
28394         }
28395         
28396         this.file = file;
28397         this.cropType = file.type;
28398         
28399         var _this = this;
28400         
28401         if(this.fireEvent('prepare', this, this.file) != false){
28402             
28403             var reader = new FileReader();
28404             
28405             reader.onload = function (e) {
28406                 if (e.target.error) {
28407                     Roo.log(e.target.error);
28408                     return;
28409                 }
28410                 
28411                 var buffer = e.target.result,
28412                     dataView = new DataView(buffer),
28413                     offset = 2,
28414                     maxOffset = dataView.byteLength - 4,
28415                     markerBytes,
28416                     markerLength;
28417                 
28418                 if (dataView.getUint16(0) === 0xffd8) {
28419                     while (offset < maxOffset) {
28420                         markerBytes = dataView.getUint16(offset);
28421                         
28422                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28423                             markerLength = dataView.getUint16(offset + 2) + 2;
28424                             if (offset + markerLength > dataView.byteLength) {
28425                                 Roo.log('Invalid meta data: Invalid segment size.');
28426                                 break;
28427                             }
28428                             
28429                             if(markerBytes == 0xffe1){
28430                                 _this.parseExifData(
28431                                     dataView,
28432                                     offset,
28433                                     markerLength
28434                                 );
28435                             }
28436                             
28437                             offset += markerLength;
28438                             
28439                             continue;
28440                         }
28441                         
28442                         break;
28443                     }
28444                     
28445                 }
28446                 
28447                 var url = _this.urlAPI.createObjectURL(_this.file);
28448                 
28449                 _this.loadCanvas(url);
28450                 
28451                 return;
28452             }
28453             
28454             reader.readAsArrayBuffer(this.file);
28455             
28456         }
28457         
28458     },
28459     
28460     parseExifData : function(dataView, offset, length)
28461     {
28462         var tiffOffset = offset + 10,
28463             littleEndian,
28464             dirOffset;
28465     
28466         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28467             // No Exif data, might be XMP data instead
28468             return;
28469         }
28470         
28471         // Check for the ASCII code for "Exif" (0x45786966):
28472         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28473             // No Exif data, might be XMP data instead
28474             return;
28475         }
28476         if (tiffOffset + 8 > dataView.byteLength) {
28477             Roo.log('Invalid Exif data: Invalid segment size.');
28478             return;
28479         }
28480         // Check for the two null bytes:
28481         if (dataView.getUint16(offset + 8) !== 0x0000) {
28482             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28483             return;
28484         }
28485         // Check the byte alignment:
28486         switch (dataView.getUint16(tiffOffset)) {
28487         case 0x4949:
28488             littleEndian = true;
28489             break;
28490         case 0x4D4D:
28491             littleEndian = false;
28492             break;
28493         default:
28494             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28495             return;
28496         }
28497         // Check for the TIFF tag marker (0x002A):
28498         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28499             Roo.log('Invalid Exif data: Missing TIFF marker.');
28500             return;
28501         }
28502         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28503         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28504         
28505         this.parseExifTags(
28506             dataView,
28507             tiffOffset,
28508             tiffOffset + dirOffset,
28509             littleEndian
28510         );
28511     },
28512     
28513     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28514     {
28515         var tagsNumber,
28516             dirEndOffset,
28517             i;
28518         if (dirOffset + 6 > dataView.byteLength) {
28519             Roo.log('Invalid Exif data: Invalid directory offset.');
28520             return;
28521         }
28522         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28523         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28524         if (dirEndOffset + 4 > dataView.byteLength) {
28525             Roo.log('Invalid Exif data: Invalid directory size.');
28526             return;
28527         }
28528         for (i = 0; i < tagsNumber; i += 1) {
28529             this.parseExifTag(
28530                 dataView,
28531                 tiffOffset,
28532                 dirOffset + 2 + 12 * i, // tag offset
28533                 littleEndian
28534             );
28535         }
28536         // Return the offset to the next directory:
28537         return dataView.getUint32(dirEndOffset, littleEndian);
28538     },
28539     
28540     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28541     {
28542         var tag = dataView.getUint16(offset, littleEndian);
28543         
28544         this.exif[tag] = this.getExifValue(
28545             dataView,
28546             tiffOffset,
28547             offset,
28548             dataView.getUint16(offset + 2, littleEndian), // tag type
28549             dataView.getUint32(offset + 4, littleEndian), // tag length
28550             littleEndian
28551         );
28552     },
28553     
28554     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28555     {
28556         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28557             tagSize,
28558             dataOffset,
28559             values,
28560             i,
28561             str,
28562             c;
28563     
28564         if (!tagType) {
28565             Roo.log('Invalid Exif data: Invalid tag type.');
28566             return;
28567         }
28568         
28569         tagSize = tagType.size * length;
28570         // Determine if the value is contained in the dataOffset bytes,
28571         // or if the value at the dataOffset is a pointer to the actual data:
28572         dataOffset = tagSize > 4 ?
28573                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28574         if (dataOffset + tagSize > dataView.byteLength) {
28575             Roo.log('Invalid Exif data: Invalid data offset.');
28576             return;
28577         }
28578         if (length === 1) {
28579             return tagType.getValue(dataView, dataOffset, littleEndian);
28580         }
28581         values = [];
28582         for (i = 0; i < length; i += 1) {
28583             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28584         }
28585         
28586         if (tagType.ascii) {
28587             str = '';
28588             // Concatenate the chars:
28589             for (i = 0; i < values.length; i += 1) {
28590                 c = values[i];
28591                 // Ignore the terminating NULL byte(s):
28592                 if (c === '\u0000') {
28593                     break;
28594                 }
28595                 str += c;
28596             }
28597             return str;
28598         }
28599         return values;
28600     }
28601     
28602 });
28603
28604 Roo.apply(Roo.bootstrap.UploadCropbox, {
28605     tags : {
28606         'Orientation': 0x0112
28607     },
28608     
28609     Orientation: {
28610             1: 0, //'top-left',
28611 //            2: 'top-right',
28612             3: 180, //'bottom-right',
28613 //            4: 'bottom-left',
28614 //            5: 'left-top',
28615             6: 90, //'right-top',
28616 //            7: 'right-bottom',
28617             8: 270 //'left-bottom'
28618     },
28619     
28620     exifTagTypes : {
28621         // byte, 8-bit unsigned int:
28622         1: {
28623             getValue: function (dataView, dataOffset) {
28624                 return dataView.getUint8(dataOffset);
28625             },
28626             size: 1
28627         },
28628         // ascii, 8-bit byte:
28629         2: {
28630             getValue: function (dataView, dataOffset) {
28631                 return String.fromCharCode(dataView.getUint8(dataOffset));
28632             },
28633             size: 1,
28634             ascii: true
28635         },
28636         // short, 16 bit int:
28637         3: {
28638             getValue: function (dataView, dataOffset, littleEndian) {
28639                 return dataView.getUint16(dataOffset, littleEndian);
28640             },
28641             size: 2
28642         },
28643         // long, 32 bit int:
28644         4: {
28645             getValue: function (dataView, dataOffset, littleEndian) {
28646                 return dataView.getUint32(dataOffset, littleEndian);
28647             },
28648             size: 4
28649         },
28650         // rational = two long values, first is numerator, second is denominator:
28651         5: {
28652             getValue: function (dataView, dataOffset, littleEndian) {
28653                 return dataView.getUint32(dataOffset, littleEndian) /
28654                     dataView.getUint32(dataOffset + 4, littleEndian);
28655             },
28656             size: 8
28657         },
28658         // slong, 32 bit signed int:
28659         9: {
28660             getValue: function (dataView, dataOffset, littleEndian) {
28661                 return dataView.getInt32(dataOffset, littleEndian);
28662             },
28663             size: 4
28664         },
28665         // srational, two slongs, first is numerator, second is denominator:
28666         10: {
28667             getValue: function (dataView, dataOffset, littleEndian) {
28668                 return dataView.getInt32(dataOffset, littleEndian) /
28669                     dataView.getInt32(dataOffset + 4, littleEndian);
28670             },
28671             size: 8
28672         }
28673     },
28674     
28675     footer : {
28676         STANDARD : [
28677             {
28678                 tag : 'div',
28679                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28680                 action : 'rotate-left',
28681                 cn : [
28682                     {
28683                         tag : 'button',
28684                         cls : 'btn btn-default',
28685                         html : '<i class="fa fa-undo"></i>'
28686                     }
28687                 ]
28688             },
28689             {
28690                 tag : 'div',
28691                 cls : 'btn-group roo-upload-cropbox-picture',
28692                 action : 'picture',
28693                 cn : [
28694                     {
28695                         tag : 'button',
28696                         cls : 'btn btn-default',
28697                         html : '<i class="fa fa-picture-o"></i>'
28698                     }
28699                 ]
28700             },
28701             {
28702                 tag : 'div',
28703                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28704                 action : 'rotate-right',
28705                 cn : [
28706                     {
28707                         tag : 'button',
28708                         cls : 'btn btn-default',
28709                         html : '<i class="fa fa-repeat"></i>'
28710                     }
28711                 ]
28712             }
28713         ],
28714         DOCUMENT : [
28715             {
28716                 tag : 'div',
28717                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28718                 action : 'rotate-left',
28719                 cn : [
28720                     {
28721                         tag : 'button',
28722                         cls : 'btn btn-default',
28723                         html : '<i class="fa fa-undo"></i>'
28724                     }
28725                 ]
28726             },
28727             {
28728                 tag : 'div',
28729                 cls : 'btn-group roo-upload-cropbox-download',
28730                 action : 'download',
28731                 cn : [
28732                     {
28733                         tag : 'button',
28734                         cls : 'btn btn-default',
28735                         html : '<i class="fa fa-download"></i>'
28736                     }
28737                 ]
28738             },
28739             {
28740                 tag : 'div',
28741                 cls : 'btn-group roo-upload-cropbox-crop',
28742                 action : 'crop',
28743                 cn : [
28744                     {
28745                         tag : 'button',
28746                         cls : 'btn btn-default',
28747                         html : '<i class="fa fa-crop"></i>'
28748                     }
28749                 ]
28750             },
28751             {
28752                 tag : 'div',
28753                 cls : 'btn-group roo-upload-cropbox-trash',
28754                 action : 'trash',
28755                 cn : [
28756                     {
28757                         tag : 'button',
28758                         cls : 'btn btn-default',
28759                         html : '<i class="fa fa-trash"></i>'
28760                     }
28761                 ]
28762             },
28763             {
28764                 tag : 'div',
28765                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28766                 action : 'rotate-right',
28767                 cn : [
28768                     {
28769                         tag : 'button',
28770                         cls : 'btn btn-default',
28771                         html : '<i class="fa fa-repeat"></i>'
28772                     }
28773                 ]
28774             }
28775         ],
28776         ROTATOR : [
28777             {
28778                 tag : 'div',
28779                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28780                 action : 'rotate-left',
28781                 cn : [
28782                     {
28783                         tag : 'button',
28784                         cls : 'btn btn-default',
28785                         html : '<i class="fa fa-undo"></i>'
28786                     }
28787                 ]
28788             },
28789             {
28790                 tag : 'div',
28791                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28792                 action : 'rotate-right',
28793                 cn : [
28794                     {
28795                         tag : 'button',
28796                         cls : 'btn btn-default',
28797                         html : '<i class="fa fa-repeat"></i>'
28798                     }
28799                 ]
28800             }
28801         ]
28802     }
28803 });
28804
28805 /*
28806 * Licence: LGPL
28807 */
28808
28809 /**
28810  * @class Roo.bootstrap.DocumentManager
28811  * @extends Roo.bootstrap.Component
28812  * Bootstrap DocumentManager class
28813  * @cfg {String} paramName default 'imageUpload'
28814  * @cfg {String} toolTipName default 'filename'
28815  * @cfg {String} method default POST
28816  * @cfg {String} url action url
28817  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28818  * @cfg {Boolean} multiple multiple upload default true
28819  * @cfg {Number} thumbSize default 300
28820  * @cfg {String} fieldLabel
28821  * @cfg {Number} labelWidth default 4
28822  * @cfg {String} labelAlign (left|top) default left
28823  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28824 * @cfg {Number} labellg set the width of label (1-12)
28825  * @cfg {Number} labelmd set the width of label (1-12)
28826  * @cfg {Number} labelsm set the width of label (1-12)
28827  * @cfg {Number} labelxs set the width of label (1-12)
28828  * 
28829  * @constructor
28830  * Create a new DocumentManager
28831  * @param {Object} config The config object
28832  */
28833
28834 Roo.bootstrap.DocumentManager = function(config){
28835     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28836     
28837     this.files = [];
28838     this.delegates = [];
28839     
28840     this.addEvents({
28841         /**
28842          * @event initial
28843          * Fire when initial the DocumentManager
28844          * @param {Roo.bootstrap.DocumentManager} this
28845          */
28846         "initial" : true,
28847         /**
28848          * @event inspect
28849          * inspect selected file
28850          * @param {Roo.bootstrap.DocumentManager} this
28851          * @param {File} file
28852          */
28853         "inspect" : true,
28854         /**
28855          * @event exception
28856          * Fire when xhr load exception
28857          * @param {Roo.bootstrap.DocumentManager} this
28858          * @param {XMLHttpRequest} xhr
28859          */
28860         "exception" : true,
28861         /**
28862          * @event afterupload
28863          * Fire when xhr load exception
28864          * @param {Roo.bootstrap.DocumentManager} this
28865          * @param {XMLHttpRequest} xhr
28866          */
28867         "afterupload" : true,
28868         /**
28869          * @event prepare
28870          * prepare the form data
28871          * @param {Roo.bootstrap.DocumentManager} this
28872          * @param {Object} formData
28873          */
28874         "prepare" : true,
28875         /**
28876          * @event remove
28877          * Fire when remove the file
28878          * @param {Roo.bootstrap.DocumentManager} this
28879          * @param {Object} file
28880          */
28881         "remove" : true,
28882         /**
28883          * @event refresh
28884          * Fire after refresh the file
28885          * @param {Roo.bootstrap.DocumentManager} this
28886          */
28887         "refresh" : true,
28888         /**
28889          * @event click
28890          * Fire after click the image
28891          * @param {Roo.bootstrap.DocumentManager} this
28892          * @param {Object} file
28893          */
28894         "click" : true,
28895         /**
28896          * @event edit
28897          * Fire when upload a image and editable set to true
28898          * @param {Roo.bootstrap.DocumentManager} this
28899          * @param {Object} file
28900          */
28901         "edit" : true,
28902         /**
28903          * @event beforeselectfile
28904          * Fire before select file
28905          * @param {Roo.bootstrap.DocumentManager} this
28906          */
28907         "beforeselectfile" : true,
28908         /**
28909          * @event process
28910          * Fire before process file
28911          * @param {Roo.bootstrap.DocumentManager} this
28912          * @param {Object} file
28913          */
28914         "process" : true,
28915         /**
28916          * @event previewrendered
28917          * Fire when preview rendered
28918          * @param {Roo.bootstrap.DocumentManager} this
28919          * @param {Object} file
28920          */
28921         "previewrendered" : true,
28922         /**
28923          */
28924         "previewResize" : true
28925         
28926     });
28927 };
28928
28929 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28930     
28931     boxes : 0,
28932     inputName : '',
28933     thumbSize : 300,
28934     multiple : true,
28935     files : false,
28936     method : 'POST',
28937     url : '',
28938     paramName : 'imageUpload',
28939     toolTipName : 'filename',
28940     fieldLabel : '',
28941     labelWidth : 4,
28942     labelAlign : 'left',
28943     editable : true,
28944     delegates : false,
28945     xhr : false, 
28946     
28947     labellg : 0,
28948     labelmd : 0,
28949     labelsm : 0,
28950     labelxs : 0,
28951     
28952     getAutoCreate : function()
28953     {   
28954         var managerWidget = {
28955             tag : 'div',
28956             cls : 'roo-document-manager',
28957             cn : [
28958                 {
28959                     tag : 'input',
28960                     cls : 'roo-document-manager-selector',
28961                     type : 'file'
28962                 },
28963                 {
28964                     tag : 'div',
28965                     cls : 'roo-document-manager-uploader',
28966                     cn : [
28967                         {
28968                             tag : 'div',
28969                             cls : 'roo-document-manager-upload-btn',
28970                             html : '<i class="fa fa-plus"></i>'
28971                         }
28972                     ]
28973                     
28974                 }
28975             ]
28976         };
28977         
28978         var content = [
28979             {
28980                 tag : 'div',
28981                 cls : 'column col-md-12',
28982                 cn : managerWidget
28983             }
28984         ];
28985         
28986         if(this.fieldLabel.length){
28987             
28988             content = [
28989                 {
28990                     tag : 'div',
28991                     cls : 'column col-md-12',
28992                     html : this.fieldLabel
28993                 },
28994                 {
28995                     tag : 'div',
28996                     cls : 'column col-md-12',
28997                     cn : managerWidget
28998                 }
28999             ];
29000
29001             if(this.labelAlign == 'left'){
29002                 content = [
29003                     {
29004                         tag : 'div',
29005                         cls : 'column',
29006                         html : this.fieldLabel
29007                     },
29008                     {
29009                         tag : 'div',
29010                         cls : 'column',
29011                         cn : managerWidget
29012                     }
29013                 ];
29014                 
29015                 if(this.labelWidth > 12){
29016                     content[0].style = "width: " + this.labelWidth + 'px';
29017                 }
29018
29019                 if(this.labelWidth < 13 && this.labelmd == 0){
29020                     this.labelmd = this.labelWidth;
29021                 }
29022
29023                 if(this.labellg > 0){
29024                     content[0].cls += ' col-lg-' + this.labellg;
29025                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29026                 }
29027
29028                 if(this.labelmd > 0){
29029                     content[0].cls += ' col-md-' + this.labelmd;
29030                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29031                 }
29032
29033                 if(this.labelsm > 0){
29034                     content[0].cls += ' col-sm-' + this.labelsm;
29035                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29036                 }
29037
29038                 if(this.labelxs > 0){
29039                     content[0].cls += ' col-xs-' + this.labelxs;
29040                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29041                 }
29042                 
29043             }
29044         }
29045         
29046         var cfg = {
29047             tag : 'div',
29048             cls : 'row clearfix',
29049             cn : content
29050         };
29051         
29052         return cfg;
29053         
29054     },
29055     
29056     initEvents : function()
29057     {
29058         this.managerEl = this.el.select('.roo-document-manager', true).first();
29059         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29060         
29061         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29062         this.selectorEl.hide();
29063         
29064         if(this.multiple){
29065             this.selectorEl.attr('multiple', 'multiple');
29066         }
29067         
29068         this.selectorEl.on('change', this.onFileSelected, this);
29069         
29070         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29071         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29072         
29073         this.uploader.on('click', this.onUploaderClick, this);
29074         
29075         this.renderProgressDialog();
29076         
29077         var _this = this;
29078         
29079         window.addEventListener("resize", function() { _this.refresh(); } );
29080         
29081         this.fireEvent('initial', this);
29082     },
29083     
29084     renderProgressDialog : function()
29085     {
29086         var _this = this;
29087         
29088         this.progressDialog = new Roo.bootstrap.Modal({
29089             cls : 'roo-document-manager-progress-dialog',
29090             allow_close : false,
29091             title : '',
29092             buttons : [
29093                 {
29094                     name  :'cancel',
29095                     weight : 'danger',
29096                     html : 'Cancel'
29097                 }
29098             ], 
29099             listeners : { 
29100                 btnclick : function() {
29101                     _this.uploadCancel();
29102                     this.hide();
29103                 }
29104             }
29105         });
29106          
29107         this.progressDialog.render(Roo.get(document.body));
29108          
29109         this.progress = new Roo.bootstrap.Progress({
29110             cls : 'roo-document-manager-progress',
29111             active : true,
29112             striped : true
29113         });
29114         
29115         this.progress.render(this.progressDialog.getChildContainer());
29116         
29117         this.progressBar = new Roo.bootstrap.ProgressBar({
29118             cls : 'roo-document-manager-progress-bar',
29119             aria_valuenow : 0,
29120             aria_valuemin : 0,
29121             aria_valuemax : 12,
29122             panel : 'success'
29123         });
29124         
29125         this.progressBar.render(this.progress.getChildContainer());
29126     },
29127     
29128     onUploaderClick : function(e)
29129     {
29130         e.preventDefault();
29131      
29132         if(this.fireEvent('beforeselectfile', this) != false){
29133             this.selectorEl.dom.click();
29134         }
29135         
29136     },
29137     
29138     onFileSelected : function(e)
29139     {
29140         e.preventDefault();
29141         
29142         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29143             return;
29144         }
29145         
29146         Roo.each(this.selectorEl.dom.files, function(file){
29147             if(this.fireEvent('inspect', this, file) != false){
29148                 this.files.push(file);
29149             }
29150         }, this);
29151         
29152         this.queue();
29153         
29154     },
29155     
29156     queue : function()
29157     {
29158         this.selectorEl.dom.value = '';
29159         
29160         if(!this.files || !this.files.length){
29161             return;
29162         }
29163         
29164         if(this.boxes > 0 && this.files.length > this.boxes){
29165             this.files = this.files.slice(0, this.boxes);
29166         }
29167         
29168         this.uploader.show();
29169         
29170         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29171             this.uploader.hide();
29172         }
29173         
29174         var _this = this;
29175         
29176         var files = [];
29177         
29178         var docs = [];
29179         
29180         Roo.each(this.files, function(file){
29181             
29182             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29183                 var f = this.renderPreview(file);
29184                 files.push(f);
29185                 return;
29186             }
29187             
29188             if(file.type.indexOf('image') != -1){
29189                 this.delegates.push(
29190                     (function(){
29191                         _this.process(file);
29192                     }).createDelegate(this)
29193                 );
29194         
29195                 return;
29196             }
29197             
29198             docs.push(
29199                 (function(){
29200                     _this.process(file);
29201                 }).createDelegate(this)
29202             );
29203             
29204         }, this);
29205         
29206         this.files = files;
29207         
29208         this.delegates = this.delegates.concat(docs);
29209         
29210         if(!this.delegates.length){
29211             this.refresh();
29212             return;
29213         }
29214         
29215         this.progressBar.aria_valuemax = this.delegates.length;
29216         
29217         this.arrange();
29218         
29219         return;
29220     },
29221     
29222     arrange : function()
29223     {
29224         if(!this.delegates.length){
29225             this.progressDialog.hide();
29226             this.refresh();
29227             return;
29228         }
29229         
29230         var delegate = this.delegates.shift();
29231         
29232         this.progressDialog.show();
29233         
29234         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29235         
29236         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29237         
29238         delegate();
29239     },
29240     
29241     refresh : function()
29242     {
29243         this.uploader.show();
29244         
29245         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29246             this.uploader.hide();
29247         }
29248         
29249         Roo.isTouch ? this.closable(false) : this.closable(true);
29250         
29251         this.fireEvent('refresh', this);
29252     },
29253     
29254     onRemove : function(e, el, o)
29255     {
29256         e.preventDefault();
29257         
29258         this.fireEvent('remove', this, o);
29259         
29260     },
29261     
29262     remove : function(o)
29263     {
29264         var files = [];
29265         
29266         Roo.each(this.files, function(file){
29267             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29268                 files.push(file);
29269                 return;
29270             }
29271
29272             o.target.remove();
29273
29274         }, this);
29275         
29276         this.files = files;
29277         
29278         this.refresh();
29279     },
29280     
29281     clear : function()
29282     {
29283         Roo.each(this.files, function(file){
29284             if(!file.target){
29285                 return;
29286             }
29287             
29288             file.target.remove();
29289
29290         }, this);
29291         
29292         this.files = [];
29293         
29294         this.refresh();
29295     },
29296     
29297     onClick : function(e, el, o)
29298     {
29299         e.preventDefault();
29300         
29301         this.fireEvent('click', this, o);
29302         
29303     },
29304     
29305     closable : function(closable)
29306     {
29307         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29308             
29309             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29310             
29311             if(closable){
29312                 el.show();
29313                 return;
29314             }
29315             
29316             el.hide();
29317             
29318         }, this);
29319     },
29320     
29321     xhrOnLoad : function(xhr)
29322     {
29323         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29324             el.remove();
29325         }, this);
29326         
29327         if (xhr.readyState !== 4) {
29328             this.arrange();
29329             this.fireEvent('exception', this, xhr);
29330             return;
29331         }
29332
29333         var response = Roo.decode(xhr.responseText);
29334         
29335         if(!response.success){
29336             this.arrange();
29337             this.fireEvent('exception', this, xhr);
29338             return;
29339         }
29340         
29341         var file = this.renderPreview(response.data);
29342         
29343         this.files.push(file);
29344         
29345         this.arrange();
29346         
29347         this.fireEvent('afterupload', this, xhr);
29348         
29349     },
29350     
29351     xhrOnError : function(xhr)
29352     {
29353         Roo.log('xhr on error');
29354         
29355         var response = Roo.decode(xhr.responseText);
29356           
29357         Roo.log(response);
29358         
29359         this.arrange();
29360     },
29361     
29362     process : function(file)
29363     {
29364         if(this.fireEvent('process', this, file) !== false){
29365             if(this.editable && file.type.indexOf('image') != -1){
29366                 this.fireEvent('edit', this, file);
29367                 return;
29368             }
29369
29370             this.uploadStart(file, false);
29371
29372             return;
29373         }
29374         
29375     },
29376     
29377     uploadStart : function(file, crop)
29378     {
29379         this.xhr = new XMLHttpRequest();
29380         
29381         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29382             this.arrange();
29383             return;
29384         }
29385         
29386         file.xhr = this.xhr;
29387             
29388         this.managerEl.createChild({
29389             tag : 'div',
29390             cls : 'roo-document-manager-loading',
29391             cn : [
29392                 {
29393                     tag : 'div',
29394                     tooltip : file.name,
29395                     cls : 'roo-document-manager-thumb',
29396                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29397                 }
29398             ]
29399
29400         });
29401
29402         this.xhr.open(this.method, this.url, true);
29403         
29404         var headers = {
29405             "Accept": "application/json",
29406             "Cache-Control": "no-cache",
29407             "X-Requested-With": "XMLHttpRequest"
29408         };
29409         
29410         for (var headerName in headers) {
29411             var headerValue = headers[headerName];
29412             if (headerValue) {
29413                 this.xhr.setRequestHeader(headerName, headerValue);
29414             }
29415         }
29416         
29417         var _this = this;
29418         
29419         this.xhr.onload = function()
29420         {
29421             _this.xhrOnLoad(_this.xhr);
29422         }
29423         
29424         this.xhr.onerror = function()
29425         {
29426             _this.xhrOnError(_this.xhr);
29427         }
29428         
29429         var formData = new FormData();
29430
29431         formData.append('returnHTML', 'NO');
29432         
29433         if(crop){
29434             formData.append('crop', crop);
29435         }
29436         
29437         formData.append(this.paramName, file, file.name);
29438         
29439         var options = {
29440             file : file, 
29441             manually : false
29442         };
29443         
29444         if(this.fireEvent('prepare', this, formData, options) != false){
29445             
29446             if(options.manually){
29447                 return;
29448             }
29449             
29450             this.xhr.send(formData);
29451             return;
29452         };
29453         
29454         this.uploadCancel();
29455     },
29456     
29457     uploadCancel : function()
29458     {
29459         if (this.xhr) {
29460             this.xhr.abort();
29461         }
29462         
29463         this.delegates = [];
29464         
29465         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29466             el.remove();
29467         }, this);
29468         
29469         this.arrange();
29470     },
29471     
29472     renderPreview : function(file)
29473     {
29474         if(typeof(file.target) != 'undefined' && file.target){
29475             return file;
29476         }
29477         
29478         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29479         
29480         var previewEl = this.managerEl.createChild({
29481             tag : 'div',
29482             cls : 'roo-document-manager-preview',
29483             cn : [
29484                 {
29485                     tag : 'div',
29486                     tooltip : file[this.toolTipName],
29487                     cls : 'roo-document-manager-thumb',
29488                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29489                 },
29490                 {
29491                     tag : 'button',
29492                     cls : 'close',
29493                     html : '<i class="fa fa-times-circle"></i>'
29494                 }
29495             ]
29496         });
29497
29498         var close = previewEl.select('button.close', true).first();
29499
29500         close.on('click', this.onRemove, this, file);
29501
29502         file.target = previewEl;
29503
29504         var image = previewEl.select('img', true).first();
29505         
29506         var _this = this;
29507         
29508         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29509         
29510         image.on('click', this.onClick, this, file);
29511         
29512         this.fireEvent('previewrendered', this, file);
29513         
29514         return file;
29515         
29516     },
29517     
29518     onPreviewLoad : function(file, image)
29519     {
29520         if(typeof(file.target) == 'undefined' || !file.target){
29521             return;
29522         }
29523         
29524         var width = image.dom.naturalWidth || image.dom.width;
29525         var height = image.dom.naturalHeight || image.dom.height;
29526         
29527         if(!this.previewResize) {
29528             return;
29529         }
29530         
29531         if(width > height){
29532             file.target.addClass('wide');
29533             return;
29534         }
29535         
29536         file.target.addClass('tall');
29537         return;
29538         
29539     },
29540     
29541     uploadFromSource : function(file, crop)
29542     {
29543         this.xhr = new XMLHttpRequest();
29544         
29545         this.managerEl.createChild({
29546             tag : 'div',
29547             cls : 'roo-document-manager-loading',
29548             cn : [
29549                 {
29550                     tag : 'div',
29551                     tooltip : file.name,
29552                     cls : 'roo-document-manager-thumb',
29553                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29554                 }
29555             ]
29556
29557         });
29558
29559         this.xhr.open(this.method, this.url, true);
29560         
29561         var headers = {
29562             "Accept": "application/json",
29563             "Cache-Control": "no-cache",
29564             "X-Requested-With": "XMLHttpRequest"
29565         };
29566         
29567         for (var headerName in headers) {
29568             var headerValue = headers[headerName];
29569             if (headerValue) {
29570                 this.xhr.setRequestHeader(headerName, headerValue);
29571             }
29572         }
29573         
29574         var _this = this;
29575         
29576         this.xhr.onload = function()
29577         {
29578             _this.xhrOnLoad(_this.xhr);
29579         }
29580         
29581         this.xhr.onerror = function()
29582         {
29583             _this.xhrOnError(_this.xhr);
29584         }
29585         
29586         var formData = new FormData();
29587
29588         formData.append('returnHTML', 'NO');
29589         
29590         formData.append('crop', crop);
29591         
29592         if(typeof(file.filename) != 'undefined'){
29593             formData.append('filename', file.filename);
29594         }
29595         
29596         if(typeof(file.mimetype) != 'undefined'){
29597             formData.append('mimetype', file.mimetype);
29598         }
29599         
29600         Roo.log(formData);
29601         
29602         if(this.fireEvent('prepare', this, formData) != false){
29603             this.xhr.send(formData);
29604         };
29605     }
29606 });
29607
29608 /*
29609 * Licence: LGPL
29610 */
29611
29612 /**
29613  * @class Roo.bootstrap.DocumentViewer
29614  * @extends Roo.bootstrap.Component
29615  * Bootstrap DocumentViewer class
29616  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29617  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29618  * 
29619  * @constructor
29620  * Create a new DocumentViewer
29621  * @param {Object} config The config object
29622  */
29623
29624 Roo.bootstrap.DocumentViewer = function(config){
29625     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29626     
29627     this.addEvents({
29628         /**
29629          * @event initial
29630          * Fire after initEvent
29631          * @param {Roo.bootstrap.DocumentViewer} this
29632          */
29633         "initial" : true,
29634         /**
29635          * @event click
29636          * Fire after click
29637          * @param {Roo.bootstrap.DocumentViewer} this
29638          */
29639         "click" : true,
29640         /**
29641          * @event download
29642          * Fire after download button
29643          * @param {Roo.bootstrap.DocumentViewer} this
29644          */
29645         "download" : true,
29646         /**
29647          * @event trash
29648          * Fire after trash button
29649          * @param {Roo.bootstrap.DocumentViewer} this
29650          */
29651         "trash" : true
29652         
29653     });
29654 };
29655
29656 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29657     
29658     showDownload : true,
29659     
29660     showTrash : true,
29661     
29662     getAutoCreate : function()
29663     {
29664         var cfg = {
29665             tag : 'div',
29666             cls : 'roo-document-viewer',
29667             cn : [
29668                 {
29669                     tag : 'div',
29670                     cls : 'roo-document-viewer-body',
29671                     cn : [
29672                         {
29673                             tag : 'div',
29674                             cls : 'roo-document-viewer-thumb',
29675                             cn : [
29676                                 {
29677                                     tag : 'img',
29678                                     cls : 'roo-document-viewer-image'
29679                                 }
29680                             ]
29681                         }
29682                     ]
29683                 },
29684                 {
29685                     tag : 'div',
29686                     cls : 'roo-document-viewer-footer',
29687                     cn : {
29688                         tag : 'div',
29689                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29690                         cn : [
29691                             {
29692                                 tag : 'div',
29693                                 cls : 'btn-group roo-document-viewer-download',
29694                                 cn : [
29695                                     {
29696                                         tag : 'button',
29697                                         cls : 'btn btn-default',
29698                                         html : '<i class="fa fa-download"></i>'
29699                                     }
29700                                 ]
29701                             },
29702                             {
29703                                 tag : 'div',
29704                                 cls : 'btn-group roo-document-viewer-trash',
29705                                 cn : [
29706                                     {
29707                                         tag : 'button',
29708                                         cls : 'btn btn-default',
29709                                         html : '<i class="fa fa-trash"></i>'
29710                                     }
29711                                 ]
29712                             }
29713                         ]
29714                     }
29715                 }
29716             ]
29717         };
29718         
29719         return cfg;
29720     },
29721     
29722     initEvents : function()
29723     {
29724         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29725         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29726         
29727         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29728         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29729         
29730         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29731         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29732         
29733         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29734         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29735         
29736         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29737         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29738         
29739         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29740         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29741         
29742         this.bodyEl.on('click', this.onClick, this);
29743         this.downloadBtn.on('click', this.onDownload, this);
29744         this.trashBtn.on('click', this.onTrash, this);
29745         
29746         this.downloadBtn.hide();
29747         this.trashBtn.hide();
29748         
29749         if(this.showDownload){
29750             this.downloadBtn.show();
29751         }
29752         
29753         if(this.showTrash){
29754             this.trashBtn.show();
29755         }
29756         
29757         if(!this.showDownload && !this.showTrash) {
29758             this.footerEl.hide();
29759         }
29760         
29761     },
29762     
29763     initial : function()
29764     {
29765         this.fireEvent('initial', this);
29766         
29767     },
29768     
29769     onClick : function(e)
29770     {
29771         e.preventDefault();
29772         
29773         this.fireEvent('click', this);
29774     },
29775     
29776     onDownload : function(e)
29777     {
29778         e.preventDefault();
29779         
29780         this.fireEvent('download', this);
29781     },
29782     
29783     onTrash : function(e)
29784     {
29785         e.preventDefault();
29786         
29787         this.fireEvent('trash', this);
29788     }
29789     
29790 });
29791 /*
29792  * - LGPL
29793  *
29794  * nav progress bar
29795  * 
29796  */
29797
29798 /**
29799  * @class Roo.bootstrap.NavProgressBar
29800  * @extends Roo.bootstrap.Component
29801  * Bootstrap NavProgressBar class
29802  * 
29803  * @constructor
29804  * Create a new nav progress bar
29805  * @param {Object} config The config object
29806  */
29807
29808 Roo.bootstrap.NavProgressBar = function(config){
29809     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29810
29811     this.bullets = this.bullets || [];
29812    
29813 //    Roo.bootstrap.NavProgressBar.register(this);
29814      this.addEvents({
29815         /**
29816              * @event changed
29817              * Fires when the active item changes
29818              * @param {Roo.bootstrap.NavProgressBar} this
29819              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29820              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29821          */
29822         'changed': true
29823      });
29824     
29825 };
29826
29827 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29828     
29829     bullets : [],
29830     barItems : [],
29831     
29832     getAutoCreate : function()
29833     {
29834         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29835         
29836         cfg = {
29837             tag : 'div',
29838             cls : 'roo-navigation-bar-group',
29839             cn : [
29840                 {
29841                     tag : 'div',
29842                     cls : 'roo-navigation-top-bar'
29843                 },
29844                 {
29845                     tag : 'div',
29846                     cls : 'roo-navigation-bullets-bar',
29847                     cn : [
29848                         {
29849                             tag : 'ul',
29850                             cls : 'roo-navigation-bar'
29851                         }
29852                     ]
29853                 },
29854                 
29855                 {
29856                     tag : 'div',
29857                     cls : 'roo-navigation-bottom-bar'
29858                 }
29859             ]
29860             
29861         };
29862         
29863         return cfg;
29864         
29865     },
29866     
29867     initEvents: function() 
29868     {
29869         
29870     },
29871     
29872     onRender : function(ct, position) 
29873     {
29874         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29875         
29876         if(this.bullets.length){
29877             Roo.each(this.bullets, function(b){
29878                this.addItem(b);
29879             }, this);
29880         }
29881         
29882         this.format();
29883         
29884     },
29885     
29886     addItem : function(cfg)
29887     {
29888         var item = new Roo.bootstrap.NavProgressItem(cfg);
29889         
29890         item.parentId = this.id;
29891         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29892         
29893         if(cfg.html){
29894             var top = new Roo.bootstrap.Element({
29895                 tag : 'div',
29896                 cls : 'roo-navigation-bar-text'
29897             });
29898             
29899             var bottom = new Roo.bootstrap.Element({
29900                 tag : 'div',
29901                 cls : 'roo-navigation-bar-text'
29902             });
29903             
29904             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29905             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29906             
29907             var topText = new Roo.bootstrap.Element({
29908                 tag : 'span',
29909                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29910             });
29911             
29912             var bottomText = new Roo.bootstrap.Element({
29913                 tag : 'span',
29914                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29915             });
29916             
29917             topText.onRender(top.el, null);
29918             bottomText.onRender(bottom.el, null);
29919             
29920             item.topEl = top;
29921             item.bottomEl = bottom;
29922         }
29923         
29924         this.barItems.push(item);
29925         
29926         return item;
29927     },
29928     
29929     getActive : function()
29930     {
29931         var active = false;
29932         
29933         Roo.each(this.barItems, function(v){
29934             
29935             if (!v.isActive()) {
29936                 return;
29937             }
29938             
29939             active = v;
29940             return false;
29941             
29942         });
29943         
29944         return active;
29945     },
29946     
29947     setActiveItem : function(item)
29948     {
29949         var prev = false;
29950         
29951         Roo.each(this.barItems, function(v){
29952             if (v.rid == item.rid) {
29953                 return ;
29954             }
29955             
29956             if (v.isActive()) {
29957                 v.setActive(false);
29958                 prev = v;
29959             }
29960         });
29961
29962         item.setActive(true);
29963         
29964         this.fireEvent('changed', this, item, prev);
29965     },
29966     
29967     getBarItem: function(rid)
29968     {
29969         var ret = false;
29970         
29971         Roo.each(this.barItems, function(e) {
29972             if (e.rid != rid) {
29973                 return;
29974             }
29975             
29976             ret =  e;
29977             return false;
29978         });
29979         
29980         return ret;
29981     },
29982     
29983     indexOfItem : function(item)
29984     {
29985         var index = false;
29986         
29987         Roo.each(this.barItems, function(v, i){
29988             
29989             if (v.rid != item.rid) {
29990                 return;
29991             }
29992             
29993             index = i;
29994             return false
29995         });
29996         
29997         return index;
29998     },
29999     
30000     setActiveNext : function()
30001     {
30002         var i = this.indexOfItem(this.getActive());
30003         
30004         if (i > this.barItems.length) {
30005             return;
30006         }
30007         
30008         this.setActiveItem(this.barItems[i+1]);
30009     },
30010     
30011     setActivePrev : function()
30012     {
30013         var i = this.indexOfItem(this.getActive());
30014         
30015         if (i  < 1) {
30016             return;
30017         }
30018         
30019         this.setActiveItem(this.barItems[i-1]);
30020     },
30021     
30022     format : function()
30023     {
30024         if(!this.barItems.length){
30025             return;
30026         }
30027      
30028         var width = 100 / this.barItems.length;
30029         
30030         Roo.each(this.barItems, function(i){
30031             i.el.setStyle('width', width + '%');
30032             i.topEl.el.setStyle('width', width + '%');
30033             i.bottomEl.el.setStyle('width', width + '%');
30034         }, this);
30035         
30036     }
30037     
30038 });
30039 /*
30040  * - LGPL
30041  *
30042  * Nav Progress Item
30043  * 
30044  */
30045
30046 /**
30047  * @class Roo.bootstrap.NavProgressItem
30048  * @extends Roo.bootstrap.Component
30049  * Bootstrap NavProgressItem class
30050  * @cfg {String} rid the reference id
30051  * @cfg {Boolean} active (true|false) Is item active default false
30052  * @cfg {Boolean} disabled (true|false) Is item active default false
30053  * @cfg {String} html
30054  * @cfg {String} position (top|bottom) text position default bottom
30055  * @cfg {String} icon show icon instead of number
30056  * 
30057  * @constructor
30058  * Create a new NavProgressItem
30059  * @param {Object} config The config object
30060  */
30061 Roo.bootstrap.NavProgressItem = function(config){
30062     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30063     this.addEvents({
30064         // raw events
30065         /**
30066          * @event click
30067          * The raw click event for the entire grid.
30068          * @param {Roo.bootstrap.NavProgressItem} this
30069          * @param {Roo.EventObject} e
30070          */
30071         "click" : true
30072     });
30073    
30074 };
30075
30076 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30077     
30078     rid : '',
30079     active : false,
30080     disabled : false,
30081     html : '',
30082     position : 'bottom',
30083     icon : false,
30084     
30085     getAutoCreate : function()
30086     {
30087         var iconCls = 'roo-navigation-bar-item-icon';
30088         
30089         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30090         
30091         var cfg = {
30092             tag: 'li',
30093             cls: 'roo-navigation-bar-item',
30094             cn : [
30095                 {
30096                     tag : 'i',
30097                     cls : iconCls
30098                 }
30099             ]
30100         };
30101         
30102         if(this.active){
30103             cfg.cls += ' active';
30104         }
30105         if(this.disabled){
30106             cfg.cls += ' disabled';
30107         }
30108         
30109         return cfg;
30110     },
30111     
30112     disable : function()
30113     {
30114         this.setDisabled(true);
30115     },
30116     
30117     enable : function()
30118     {
30119         this.setDisabled(false);
30120     },
30121     
30122     initEvents: function() 
30123     {
30124         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30125         
30126         this.iconEl.on('click', this.onClick, this);
30127     },
30128     
30129     onClick : function(e)
30130     {
30131         e.preventDefault();
30132         
30133         if(this.disabled){
30134             return;
30135         }
30136         
30137         if(this.fireEvent('click', this, e) === false){
30138             return;
30139         };
30140         
30141         this.parent().setActiveItem(this);
30142     },
30143     
30144     isActive: function () 
30145     {
30146         return this.active;
30147     },
30148     
30149     setActive : function(state)
30150     {
30151         if(this.active == state){
30152             return;
30153         }
30154         
30155         this.active = state;
30156         
30157         if (state) {
30158             this.el.addClass('active');
30159             return;
30160         }
30161         
30162         this.el.removeClass('active');
30163         
30164         return;
30165     },
30166     
30167     setDisabled : function(state)
30168     {
30169         if(this.disabled == state){
30170             return;
30171         }
30172         
30173         this.disabled = state;
30174         
30175         if (state) {
30176             this.el.addClass('disabled');
30177             return;
30178         }
30179         
30180         this.el.removeClass('disabled');
30181     },
30182     
30183     tooltipEl : function()
30184     {
30185         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30186     }
30187 });
30188  
30189
30190  /*
30191  * - LGPL
30192  *
30193  * FieldLabel
30194  * 
30195  */
30196
30197 /**
30198  * @class Roo.bootstrap.FieldLabel
30199  * @extends Roo.bootstrap.Component
30200  * Bootstrap FieldLabel class
30201  * @cfg {String} html contents of the element
30202  * @cfg {String} tag tag of the element default label
30203  * @cfg {String} cls class of the element
30204  * @cfg {String} target label target 
30205  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30206  * @cfg {String} invalidClass default "text-warning"
30207  * @cfg {String} validClass default "text-success"
30208  * @cfg {String} iconTooltip default "This field is required"
30209  * @cfg {String} indicatorpos (left|right) default left
30210  * 
30211  * @constructor
30212  * Create a new FieldLabel
30213  * @param {Object} config The config object
30214  */
30215
30216 Roo.bootstrap.FieldLabel = function(config){
30217     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30218     
30219     this.addEvents({
30220             /**
30221              * @event invalid
30222              * Fires after the field has been marked as invalid.
30223              * @param {Roo.form.FieldLabel} this
30224              * @param {String} msg The validation message
30225              */
30226             invalid : true,
30227             /**
30228              * @event valid
30229              * Fires after the field has been validated with no errors.
30230              * @param {Roo.form.FieldLabel} this
30231              */
30232             valid : true
30233         });
30234 };
30235
30236 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30237     
30238     tag: 'label',
30239     cls: '',
30240     html: '',
30241     target: '',
30242     allowBlank : true,
30243     invalidClass : 'has-warning',
30244     validClass : 'has-success',
30245     iconTooltip : 'This field is required',
30246     indicatorpos : 'left',
30247     
30248     getAutoCreate : function(){
30249         
30250         var cls = "";
30251         if (!this.allowBlank) {
30252             cls  = "visible";
30253         }
30254         
30255         var cfg = {
30256             tag : this.tag,
30257             cls : 'roo-bootstrap-field-label ' + this.cls,
30258             for : this.target,
30259             cn : [
30260                 {
30261                     tag : 'i',
30262                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30263                     tooltip : this.iconTooltip
30264                 },
30265                 {
30266                     tag : 'span',
30267                     html : this.html
30268                 }
30269             ] 
30270         };
30271         
30272         if(this.indicatorpos == 'right'){
30273             var cfg = {
30274                 tag : this.tag,
30275                 cls : 'roo-bootstrap-field-label ' + this.cls,
30276                 for : this.target,
30277                 cn : [
30278                     {
30279                         tag : 'span',
30280                         html : this.html
30281                     },
30282                     {
30283                         tag : 'i',
30284                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30285                         tooltip : this.iconTooltip
30286                     }
30287                 ] 
30288             };
30289         }
30290         
30291         return cfg;
30292     },
30293     
30294     initEvents: function() 
30295     {
30296         Roo.bootstrap.Element.superclass.initEvents.call(this);
30297         
30298         this.indicator = this.indicatorEl();
30299         
30300         if(this.indicator){
30301             this.indicator.removeClass('visible');
30302             this.indicator.addClass('invisible');
30303         }
30304         
30305         Roo.bootstrap.FieldLabel.register(this);
30306     },
30307     
30308     indicatorEl : function()
30309     {
30310         var indicator = this.el.select('i.roo-required-indicator',true).first();
30311         
30312         if(!indicator){
30313             return false;
30314         }
30315         
30316         return indicator;
30317         
30318     },
30319     
30320     /**
30321      * Mark this field as valid
30322      */
30323     markValid : function()
30324     {
30325         if(this.indicator){
30326             this.indicator.removeClass('visible');
30327             this.indicator.addClass('invisible');
30328         }
30329         
30330         this.el.removeClass(this.invalidClass);
30331         
30332         this.el.addClass(this.validClass);
30333         
30334         this.fireEvent('valid', this);
30335     },
30336     
30337     /**
30338      * Mark this field as invalid
30339      * @param {String} msg The validation message
30340      */
30341     markInvalid : function(msg)
30342     {
30343         if(this.indicator){
30344             this.indicator.removeClass('invisible');
30345             this.indicator.addClass('visible');
30346         }
30347         
30348         this.el.removeClass(this.validClass);
30349         
30350         this.el.addClass(this.invalidClass);
30351         
30352         this.fireEvent('invalid', this, msg);
30353     }
30354     
30355    
30356 });
30357
30358 Roo.apply(Roo.bootstrap.FieldLabel, {
30359     
30360     groups: {},
30361     
30362      /**
30363     * register a FieldLabel Group
30364     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30365     */
30366     register : function(label)
30367     {
30368         if(this.groups.hasOwnProperty(label.target)){
30369             return;
30370         }
30371      
30372         this.groups[label.target] = label;
30373         
30374     },
30375     /**
30376     * fetch a FieldLabel Group based on the target
30377     * @param {string} target
30378     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30379     */
30380     get: function(target) {
30381         if (typeof(this.groups[target]) == 'undefined') {
30382             return false;
30383         }
30384         
30385         return this.groups[target] ;
30386     }
30387 });
30388
30389  
30390
30391  /*
30392  * - LGPL
30393  *
30394  * page DateSplitField.
30395  * 
30396  */
30397
30398
30399 /**
30400  * @class Roo.bootstrap.DateSplitField
30401  * @extends Roo.bootstrap.Component
30402  * Bootstrap DateSplitField class
30403  * @cfg {string} fieldLabel - the label associated
30404  * @cfg {Number} labelWidth set the width of label (0-12)
30405  * @cfg {String} labelAlign (top|left)
30406  * @cfg {Boolean} dayAllowBlank (true|false) default false
30407  * @cfg {Boolean} monthAllowBlank (true|false) default false
30408  * @cfg {Boolean} yearAllowBlank (true|false) default false
30409  * @cfg {string} dayPlaceholder 
30410  * @cfg {string} monthPlaceholder
30411  * @cfg {string} yearPlaceholder
30412  * @cfg {string} dayFormat default 'd'
30413  * @cfg {string} monthFormat default 'm'
30414  * @cfg {string} yearFormat default 'Y'
30415  * @cfg {Number} labellg set the width of label (1-12)
30416  * @cfg {Number} labelmd set the width of label (1-12)
30417  * @cfg {Number} labelsm set the width of label (1-12)
30418  * @cfg {Number} labelxs set the width of label (1-12)
30419
30420  *     
30421  * @constructor
30422  * Create a new DateSplitField
30423  * @param {Object} config The config object
30424  */
30425
30426 Roo.bootstrap.DateSplitField = function(config){
30427     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30428     
30429     this.addEvents({
30430         // raw events
30431          /**
30432          * @event years
30433          * getting the data of years
30434          * @param {Roo.bootstrap.DateSplitField} this
30435          * @param {Object} years
30436          */
30437         "years" : true,
30438         /**
30439          * @event days
30440          * getting the data of days
30441          * @param {Roo.bootstrap.DateSplitField} this
30442          * @param {Object} days
30443          */
30444         "days" : true,
30445         /**
30446          * @event invalid
30447          * Fires after the field has been marked as invalid.
30448          * @param {Roo.form.Field} this
30449          * @param {String} msg The validation message
30450          */
30451         invalid : true,
30452        /**
30453          * @event valid
30454          * Fires after the field has been validated with no errors.
30455          * @param {Roo.form.Field} this
30456          */
30457         valid : true
30458     });
30459 };
30460
30461 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30462     
30463     fieldLabel : '',
30464     labelAlign : 'top',
30465     labelWidth : 3,
30466     dayAllowBlank : false,
30467     monthAllowBlank : false,
30468     yearAllowBlank : false,
30469     dayPlaceholder : '',
30470     monthPlaceholder : '',
30471     yearPlaceholder : '',
30472     dayFormat : 'd',
30473     monthFormat : 'm',
30474     yearFormat : 'Y',
30475     isFormField : true,
30476     labellg : 0,
30477     labelmd : 0,
30478     labelsm : 0,
30479     labelxs : 0,
30480     
30481     getAutoCreate : function()
30482     {
30483         var cfg = {
30484             tag : 'div',
30485             cls : 'row roo-date-split-field-group',
30486             cn : [
30487                 {
30488                     tag : 'input',
30489                     type : 'hidden',
30490                     cls : 'form-hidden-field roo-date-split-field-group-value',
30491                     name : this.name
30492                 }
30493             ]
30494         };
30495         
30496         var labelCls = 'col-md-12';
30497         var contentCls = 'col-md-4';
30498         
30499         if(this.fieldLabel){
30500             
30501             var label = {
30502                 tag : 'div',
30503                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30504                 cn : [
30505                     {
30506                         tag : 'label',
30507                         html : this.fieldLabel
30508                     }
30509                 ]
30510             };
30511             
30512             if(this.labelAlign == 'left'){
30513             
30514                 if(this.labelWidth > 12){
30515                     label.style = "width: " + this.labelWidth + 'px';
30516                 }
30517
30518                 if(this.labelWidth < 13 && this.labelmd == 0){
30519                     this.labelmd = this.labelWidth;
30520                 }
30521
30522                 if(this.labellg > 0){
30523                     labelCls = ' col-lg-' + this.labellg;
30524                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30525                 }
30526
30527                 if(this.labelmd > 0){
30528                     labelCls = ' col-md-' + this.labelmd;
30529                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30530                 }
30531
30532                 if(this.labelsm > 0){
30533                     labelCls = ' col-sm-' + this.labelsm;
30534                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30535                 }
30536
30537                 if(this.labelxs > 0){
30538                     labelCls = ' col-xs-' + this.labelxs;
30539                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30540                 }
30541             }
30542             
30543             label.cls += ' ' + labelCls;
30544             
30545             cfg.cn.push(label);
30546         }
30547         
30548         Roo.each(['day', 'month', 'year'], function(t){
30549             cfg.cn.push({
30550                 tag : 'div',
30551                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30552             });
30553         }, this);
30554         
30555         return cfg;
30556     },
30557     
30558     inputEl: function ()
30559     {
30560         return this.el.select('.roo-date-split-field-group-value', true).first();
30561     },
30562     
30563     onRender : function(ct, position) 
30564     {
30565         var _this = this;
30566         
30567         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30568         
30569         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30570         
30571         this.dayField = new Roo.bootstrap.ComboBox({
30572             allowBlank : this.dayAllowBlank,
30573             alwaysQuery : true,
30574             displayField : 'value',
30575             editable : false,
30576             fieldLabel : '',
30577             forceSelection : true,
30578             mode : 'local',
30579             placeholder : this.dayPlaceholder,
30580             selectOnFocus : true,
30581             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30582             triggerAction : 'all',
30583             typeAhead : true,
30584             valueField : 'value',
30585             store : new Roo.data.SimpleStore({
30586                 data : (function() {    
30587                     var days = [];
30588                     _this.fireEvent('days', _this, days);
30589                     return days;
30590                 })(),
30591                 fields : [ 'value' ]
30592             }),
30593             listeners : {
30594                 select : function (_self, record, index)
30595                 {
30596                     _this.setValue(_this.getValue());
30597                 }
30598             }
30599         });
30600
30601         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30602         
30603         this.monthField = new Roo.bootstrap.MonthField({
30604             after : '<i class=\"fa fa-calendar\"></i>',
30605             allowBlank : this.monthAllowBlank,
30606             placeholder : this.monthPlaceholder,
30607             readOnly : true,
30608             listeners : {
30609                 render : function (_self)
30610                 {
30611                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30612                         e.preventDefault();
30613                         _self.focus();
30614                     });
30615                 },
30616                 select : function (_self, oldvalue, newvalue)
30617                 {
30618                     _this.setValue(_this.getValue());
30619                 }
30620             }
30621         });
30622         
30623         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30624         
30625         this.yearField = new Roo.bootstrap.ComboBox({
30626             allowBlank : this.yearAllowBlank,
30627             alwaysQuery : true,
30628             displayField : 'value',
30629             editable : false,
30630             fieldLabel : '',
30631             forceSelection : true,
30632             mode : 'local',
30633             placeholder : this.yearPlaceholder,
30634             selectOnFocus : true,
30635             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30636             triggerAction : 'all',
30637             typeAhead : true,
30638             valueField : 'value',
30639             store : new Roo.data.SimpleStore({
30640                 data : (function() {
30641                     var years = [];
30642                     _this.fireEvent('years', _this, years);
30643                     return years;
30644                 })(),
30645                 fields : [ 'value' ]
30646             }),
30647             listeners : {
30648                 select : function (_self, record, index)
30649                 {
30650                     _this.setValue(_this.getValue());
30651                 }
30652             }
30653         });
30654
30655         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30656     },
30657     
30658     setValue : function(v, format)
30659     {
30660         this.inputEl.dom.value = v;
30661         
30662         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30663         
30664         var d = Date.parseDate(v, f);
30665         
30666         if(!d){
30667             this.validate();
30668             return;
30669         }
30670         
30671         this.setDay(d.format(this.dayFormat));
30672         this.setMonth(d.format(this.monthFormat));
30673         this.setYear(d.format(this.yearFormat));
30674         
30675         this.validate();
30676         
30677         return;
30678     },
30679     
30680     setDay : function(v)
30681     {
30682         this.dayField.setValue(v);
30683         this.inputEl.dom.value = this.getValue();
30684         this.validate();
30685         return;
30686     },
30687     
30688     setMonth : function(v)
30689     {
30690         this.monthField.setValue(v, true);
30691         this.inputEl.dom.value = this.getValue();
30692         this.validate();
30693         return;
30694     },
30695     
30696     setYear : function(v)
30697     {
30698         this.yearField.setValue(v);
30699         this.inputEl.dom.value = this.getValue();
30700         this.validate();
30701         return;
30702     },
30703     
30704     getDay : function()
30705     {
30706         return this.dayField.getValue();
30707     },
30708     
30709     getMonth : function()
30710     {
30711         return this.monthField.getValue();
30712     },
30713     
30714     getYear : function()
30715     {
30716         return this.yearField.getValue();
30717     },
30718     
30719     getValue : function()
30720     {
30721         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30722         
30723         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30724         
30725         return date;
30726     },
30727     
30728     reset : function()
30729     {
30730         this.setDay('');
30731         this.setMonth('');
30732         this.setYear('');
30733         this.inputEl.dom.value = '';
30734         this.validate();
30735         return;
30736     },
30737     
30738     validate : function()
30739     {
30740         var d = this.dayField.validate();
30741         var m = this.monthField.validate();
30742         var y = this.yearField.validate();
30743         
30744         var valid = true;
30745         
30746         if(
30747                 (!this.dayAllowBlank && !d) ||
30748                 (!this.monthAllowBlank && !m) ||
30749                 (!this.yearAllowBlank && !y)
30750         ){
30751             valid = false;
30752         }
30753         
30754         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30755             return valid;
30756         }
30757         
30758         if(valid){
30759             this.markValid();
30760             return valid;
30761         }
30762         
30763         this.markInvalid();
30764         
30765         return valid;
30766     },
30767     
30768     markValid : function()
30769     {
30770         
30771         var label = this.el.select('label', true).first();
30772         var icon = this.el.select('i.fa-star', true).first();
30773
30774         if(label && icon){
30775             icon.remove();
30776         }
30777         
30778         this.fireEvent('valid', this);
30779     },
30780     
30781      /**
30782      * Mark this field as invalid
30783      * @param {String} msg The validation message
30784      */
30785     markInvalid : function(msg)
30786     {
30787         
30788         var label = this.el.select('label', true).first();
30789         var icon = this.el.select('i.fa-star', true).first();
30790
30791         if(label && !icon){
30792             this.el.select('.roo-date-split-field-label', true).createChild({
30793                 tag : 'i',
30794                 cls : 'text-danger fa fa-lg fa-star',
30795                 tooltip : 'This field is required',
30796                 style : 'margin-right:5px;'
30797             }, label, true);
30798         }
30799         
30800         this.fireEvent('invalid', this, msg);
30801     },
30802     
30803     clearInvalid : function()
30804     {
30805         var label = this.el.select('label', true).first();
30806         var icon = this.el.select('i.fa-star', true).first();
30807
30808         if(label && icon){
30809             icon.remove();
30810         }
30811         
30812         this.fireEvent('valid', this);
30813     },
30814     
30815     getName: function()
30816     {
30817         return this.name;
30818     }
30819     
30820 });
30821
30822  /**
30823  *
30824  * This is based on 
30825  * http://masonry.desandro.com
30826  *
30827  * The idea is to render all the bricks based on vertical width...
30828  *
30829  * The original code extends 'outlayer' - we might need to use that....
30830  * 
30831  */
30832
30833
30834 /**
30835  * @class Roo.bootstrap.LayoutMasonry
30836  * @extends Roo.bootstrap.Component
30837  * Bootstrap Layout Masonry class
30838  * 
30839  * @constructor
30840  * Create a new Element
30841  * @param {Object} config The config object
30842  */
30843
30844 Roo.bootstrap.LayoutMasonry = function(config){
30845     
30846     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30847     
30848     this.bricks = [];
30849     
30850     Roo.bootstrap.LayoutMasonry.register(this);
30851     
30852     this.addEvents({
30853         // raw events
30854         /**
30855          * @event layout
30856          * Fire after layout the items
30857          * @param {Roo.bootstrap.LayoutMasonry} this
30858          * @param {Roo.EventObject} e
30859          */
30860         "layout" : true
30861     });
30862     
30863 };
30864
30865 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30866     
30867     /**
30868      * @cfg {Boolean} isLayoutInstant = no animation?
30869      */   
30870     isLayoutInstant : false, // needed?
30871    
30872     /**
30873      * @cfg {Number} boxWidth  width of the columns
30874      */   
30875     boxWidth : 450,
30876     
30877       /**
30878      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30879      */   
30880     boxHeight : 0,
30881     
30882     /**
30883      * @cfg {Number} padWidth padding below box..
30884      */   
30885     padWidth : 10, 
30886     
30887     /**
30888      * @cfg {Number} gutter gutter width..
30889      */   
30890     gutter : 10,
30891     
30892      /**
30893      * @cfg {Number} maxCols maximum number of columns
30894      */   
30895     
30896     maxCols: 0,
30897     
30898     /**
30899      * @cfg {Boolean} isAutoInitial defalut true
30900      */   
30901     isAutoInitial : true, 
30902     
30903     containerWidth: 0,
30904     
30905     /**
30906      * @cfg {Boolean} isHorizontal defalut false
30907      */   
30908     isHorizontal : false, 
30909
30910     currentSize : null,
30911     
30912     tag: 'div',
30913     
30914     cls: '',
30915     
30916     bricks: null, //CompositeElement
30917     
30918     cols : 1,
30919     
30920     _isLayoutInited : false,
30921     
30922 //    isAlternative : false, // only use for vertical layout...
30923     
30924     /**
30925      * @cfg {Number} alternativePadWidth padding below box..
30926      */   
30927     alternativePadWidth : 50,
30928     
30929     selectedBrick : [],
30930     
30931     getAutoCreate : function(){
30932         
30933         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30934         
30935         var cfg = {
30936             tag: this.tag,
30937             cls: 'blog-masonary-wrapper ' + this.cls,
30938             cn : {
30939                 cls : 'mas-boxes masonary'
30940             }
30941         };
30942         
30943         return cfg;
30944     },
30945     
30946     getChildContainer: function( )
30947     {
30948         if (this.boxesEl) {
30949             return this.boxesEl;
30950         }
30951         
30952         this.boxesEl = this.el.select('.mas-boxes').first();
30953         
30954         return this.boxesEl;
30955     },
30956     
30957     
30958     initEvents : function()
30959     {
30960         var _this = this;
30961         
30962         if(this.isAutoInitial){
30963             Roo.log('hook children rendered');
30964             this.on('childrenrendered', function() {
30965                 Roo.log('children rendered');
30966                 _this.initial();
30967             } ,this);
30968         }
30969     },
30970     
30971     initial : function()
30972     {
30973         this.selectedBrick = [];
30974         
30975         this.currentSize = this.el.getBox(true);
30976         
30977         Roo.EventManager.onWindowResize(this.resize, this); 
30978
30979         if(!this.isAutoInitial){
30980             this.layout();
30981             return;
30982         }
30983         
30984         this.layout();
30985         
30986         return;
30987         //this.layout.defer(500,this);
30988         
30989     },
30990     
30991     resize : function()
30992     {
30993         var cs = this.el.getBox(true);
30994         
30995         if (
30996                 this.currentSize.width == cs.width && 
30997                 this.currentSize.x == cs.x && 
30998                 this.currentSize.height == cs.height && 
30999                 this.currentSize.y == cs.y 
31000         ) {
31001             Roo.log("no change in with or X or Y");
31002             return;
31003         }
31004         
31005         this.currentSize = cs;
31006         
31007         this.layout();
31008         
31009     },
31010     
31011     layout : function()
31012     {   
31013         this._resetLayout();
31014         
31015         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31016         
31017         this.layoutItems( isInstant );
31018       
31019         this._isLayoutInited = true;
31020         
31021         this.fireEvent('layout', this);
31022         
31023     },
31024     
31025     _resetLayout : function()
31026     {
31027         if(this.isHorizontal){
31028             this.horizontalMeasureColumns();
31029             return;
31030         }
31031         
31032         this.verticalMeasureColumns();
31033         
31034     },
31035     
31036     verticalMeasureColumns : function()
31037     {
31038         this.getContainerWidth();
31039         
31040 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31041 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31042 //            return;
31043 //        }
31044         
31045         var boxWidth = this.boxWidth + this.padWidth;
31046         
31047         if(this.containerWidth < this.boxWidth){
31048             boxWidth = this.containerWidth
31049         }
31050         
31051         var containerWidth = this.containerWidth;
31052         
31053         var cols = Math.floor(containerWidth / boxWidth);
31054         
31055         this.cols = Math.max( cols, 1 );
31056         
31057         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31058         
31059         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31060         
31061         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31062         
31063         this.colWidth = boxWidth + avail - this.padWidth;
31064         
31065         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31066         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31067     },
31068     
31069     horizontalMeasureColumns : function()
31070     {
31071         this.getContainerWidth();
31072         
31073         var boxWidth = this.boxWidth;
31074         
31075         if(this.containerWidth < boxWidth){
31076             boxWidth = this.containerWidth;
31077         }
31078         
31079         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31080         
31081         this.el.setHeight(boxWidth);
31082         
31083     },
31084     
31085     getContainerWidth : function()
31086     {
31087         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31088     },
31089     
31090     layoutItems : function( isInstant )
31091     {
31092         Roo.log(this.bricks);
31093         
31094         var items = Roo.apply([], this.bricks);
31095         
31096         if(this.isHorizontal){
31097             this._horizontalLayoutItems( items , isInstant );
31098             return;
31099         }
31100         
31101 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31102 //            this._verticalAlternativeLayoutItems( items , isInstant );
31103 //            return;
31104 //        }
31105         
31106         this._verticalLayoutItems( items , isInstant );
31107         
31108     },
31109     
31110     _verticalLayoutItems : function ( items , isInstant)
31111     {
31112         if ( !items || !items.length ) {
31113             return;
31114         }
31115         
31116         var standard = [
31117             ['xs', 'xs', 'xs', 'tall'],
31118             ['xs', 'xs', 'tall'],
31119             ['xs', 'xs', 'sm'],
31120             ['xs', 'xs', 'xs'],
31121             ['xs', 'tall'],
31122             ['xs', 'sm'],
31123             ['xs', 'xs'],
31124             ['xs'],
31125             
31126             ['sm', 'xs', 'xs'],
31127             ['sm', 'xs'],
31128             ['sm'],
31129             
31130             ['tall', 'xs', 'xs', 'xs'],
31131             ['tall', 'xs', 'xs'],
31132             ['tall', 'xs'],
31133             ['tall']
31134             
31135         ];
31136         
31137         var queue = [];
31138         
31139         var boxes = [];
31140         
31141         var box = [];
31142         
31143         Roo.each(items, function(item, k){
31144             
31145             switch (item.size) {
31146                 // these layouts take up a full box,
31147                 case 'md' :
31148                 case 'md-left' :
31149                 case 'md-right' :
31150                 case 'wide' :
31151                     
31152                     if(box.length){
31153                         boxes.push(box);
31154                         box = [];
31155                     }
31156                     
31157                     boxes.push([item]);
31158                     
31159                     break;
31160                     
31161                 case 'xs' :
31162                 case 'sm' :
31163                 case 'tall' :
31164                     
31165                     box.push(item);
31166                     
31167                     break;
31168                 default :
31169                     break;
31170                     
31171             }
31172             
31173         }, this);
31174         
31175         if(box.length){
31176             boxes.push(box);
31177             box = [];
31178         }
31179         
31180         var filterPattern = function(box, length)
31181         {
31182             if(!box.length){
31183                 return;
31184             }
31185             
31186             var match = false;
31187             
31188             var pattern = box.slice(0, length);
31189             
31190             var format = [];
31191             
31192             Roo.each(pattern, function(i){
31193                 format.push(i.size);
31194             }, this);
31195             
31196             Roo.each(standard, function(s){
31197                 
31198                 if(String(s) != String(format)){
31199                     return;
31200                 }
31201                 
31202                 match = true;
31203                 return false;
31204                 
31205             }, this);
31206             
31207             if(!match && length == 1){
31208                 return;
31209             }
31210             
31211             if(!match){
31212                 filterPattern(box, length - 1);
31213                 return;
31214             }
31215                 
31216             queue.push(pattern);
31217
31218             box = box.slice(length, box.length);
31219
31220             filterPattern(box, 4);
31221
31222             return;
31223             
31224         }
31225         
31226         Roo.each(boxes, function(box, k){
31227             
31228             if(!box.length){
31229                 return;
31230             }
31231             
31232             if(box.length == 1){
31233                 queue.push(box);
31234                 return;
31235             }
31236             
31237             filterPattern(box, 4);
31238             
31239         }, this);
31240         
31241         this._processVerticalLayoutQueue( queue, isInstant );
31242         
31243     },
31244     
31245 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31246 //    {
31247 //        if ( !items || !items.length ) {
31248 //            return;
31249 //        }
31250 //
31251 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31252 //        
31253 //    },
31254     
31255     _horizontalLayoutItems : function ( items , isInstant)
31256     {
31257         if ( !items || !items.length || items.length < 3) {
31258             return;
31259         }
31260         
31261         items.reverse();
31262         
31263         var eItems = items.slice(0, 3);
31264         
31265         items = items.slice(3, items.length);
31266         
31267         var standard = [
31268             ['xs', 'xs', 'xs', 'wide'],
31269             ['xs', 'xs', 'wide'],
31270             ['xs', 'xs', 'sm'],
31271             ['xs', 'xs', 'xs'],
31272             ['xs', 'wide'],
31273             ['xs', 'sm'],
31274             ['xs', 'xs'],
31275             ['xs'],
31276             
31277             ['sm', 'xs', 'xs'],
31278             ['sm', 'xs'],
31279             ['sm'],
31280             
31281             ['wide', 'xs', 'xs', 'xs'],
31282             ['wide', 'xs', 'xs'],
31283             ['wide', 'xs'],
31284             ['wide'],
31285             
31286             ['wide-thin']
31287         ];
31288         
31289         var queue = [];
31290         
31291         var boxes = [];
31292         
31293         var box = [];
31294         
31295         Roo.each(items, function(item, k){
31296             
31297             switch (item.size) {
31298                 case 'md' :
31299                 case 'md-left' :
31300                 case 'md-right' :
31301                 case 'tall' :
31302                     
31303                     if(box.length){
31304                         boxes.push(box);
31305                         box = [];
31306                     }
31307                     
31308                     boxes.push([item]);
31309                     
31310                     break;
31311                     
31312                 case 'xs' :
31313                 case 'sm' :
31314                 case 'wide' :
31315                 case 'wide-thin' :
31316                     
31317                     box.push(item);
31318                     
31319                     break;
31320                 default :
31321                     break;
31322                     
31323             }
31324             
31325         }, this);
31326         
31327         if(box.length){
31328             boxes.push(box);
31329             box = [];
31330         }
31331         
31332         var filterPattern = function(box, length)
31333         {
31334             if(!box.length){
31335                 return;
31336             }
31337             
31338             var match = false;
31339             
31340             var pattern = box.slice(0, length);
31341             
31342             var format = [];
31343             
31344             Roo.each(pattern, function(i){
31345                 format.push(i.size);
31346             }, this);
31347             
31348             Roo.each(standard, function(s){
31349                 
31350                 if(String(s) != String(format)){
31351                     return;
31352                 }
31353                 
31354                 match = true;
31355                 return false;
31356                 
31357             }, this);
31358             
31359             if(!match && length == 1){
31360                 return;
31361             }
31362             
31363             if(!match){
31364                 filterPattern(box, length - 1);
31365                 return;
31366             }
31367                 
31368             queue.push(pattern);
31369
31370             box = box.slice(length, box.length);
31371
31372             filterPattern(box, 4);
31373
31374             return;
31375             
31376         }
31377         
31378         Roo.each(boxes, function(box, k){
31379             
31380             if(!box.length){
31381                 return;
31382             }
31383             
31384             if(box.length == 1){
31385                 queue.push(box);
31386                 return;
31387             }
31388             
31389             filterPattern(box, 4);
31390             
31391         }, this);
31392         
31393         
31394         var prune = [];
31395         
31396         var pos = this.el.getBox(true);
31397         
31398         var minX = pos.x;
31399         
31400         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31401         
31402         var hit_end = false;
31403         
31404         Roo.each(queue, function(box){
31405             
31406             if(hit_end){
31407                 
31408                 Roo.each(box, function(b){
31409                 
31410                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31411                     b.el.hide();
31412
31413                 }, this);
31414
31415                 return;
31416             }
31417             
31418             var mx = 0;
31419             
31420             Roo.each(box, function(b){
31421                 
31422                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31423                 b.el.show();
31424
31425                 mx = Math.max(mx, b.x);
31426                 
31427             }, this);
31428             
31429             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31430             
31431             if(maxX < minX){
31432                 
31433                 Roo.each(box, function(b){
31434                 
31435                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31436                     b.el.hide();
31437                     
31438                 }, this);
31439                 
31440                 hit_end = true;
31441                 
31442                 return;
31443             }
31444             
31445             prune.push(box);
31446             
31447         }, this);
31448         
31449         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31450     },
31451     
31452     /** Sets position of item in DOM
31453     * @param {Element} item
31454     * @param {Number} x - horizontal position
31455     * @param {Number} y - vertical position
31456     * @param {Boolean} isInstant - disables transitions
31457     */
31458     _processVerticalLayoutQueue : function( queue, isInstant )
31459     {
31460         var pos = this.el.getBox(true);
31461         var x = pos.x;
31462         var y = pos.y;
31463         var maxY = [];
31464         
31465         for (var i = 0; i < this.cols; i++){
31466             maxY[i] = pos.y;
31467         }
31468         
31469         Roo.each(queue, function(box, k){
31470             
31471             var col = k % this.cols;
31472             
31473             Roo.each(box, function(b,kk){
31474                 
31475                 b.el.position('absolute');
31476                 
31477                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31478                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31479                 
31480                 if(b.size == 'md-left' || b.size == 'md-right'){
31481                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31482                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31483                 }
31484                 
31485                 b.el.setWidth(width);
31486                 b.el.setHeight(height);
31487                 // iframe?
31488                 b.el.select('iframe',true).setSize(width,height);
31489                 
31490             }, this);
31491             
31492             for (var i = 0; i < this.cols; i++){
31493                 
31494                 if(maxY[i] < maxY[col]){
31495                     col = i;
31496                     continue;
31497                 }
31498                 
31499                 col = Math.min(col, i);
31500                 
31501             }
31502             
31503             x = pos.x + col * (this.colWidth + this.padWidth);
31504             
31505             y = maxY[col];
31506             
31507             var positions = [];
31508             
31509             switch (box.length){
31510                 case 1 :
31511                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31512                     break;
31513                 case 2 :
31514                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31515                     break;
31516                 case 3 :
31517                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31518                     break;
31519                 case 4 :
31520                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31521                     break;
31522                 default :
31523                     break;
31524             }
31525             
31526             Roo.each(box, function(b,kk){
31527                 
31528                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31529                 
31530                 var sz = b.el.getSize();
31531                 
31532                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31533                 
31534             }, this);
31535             
31536         }, this);
31537         
31538         var mY = 0;
31539         
31540         for (var i = 0; i < this.cols; i++){
31541             mY = Math.max(mY, maxY[i]);
31542         }
31543         
31544         this.el.setHeight(mY - pos.y);
31545         
31546     },
31547     
31548 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31549 //    {
31550 //        var pos = this.el.getBox(true);
31551 //        var x = pos.x;
31552 //        var y = pos.y;
31553 //        var maxX = pos.right;
31554 //        
31555 //        var maxHeight = 0;
31556 //        
31557 //        Roo.each(items, function(item, k){
31558 //            
31559 //            var c = k % 2;
31560 //            
31561 //            item.el.position('absolute');
31562 //                
31563 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31564 //
31565 //            item.el.setWidth(width);
31566 //
31567 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31568 //
31569 //            item.el.setHeight(height);
31570 //            
31571 //            if(c == 0){
31572 //                item.el.setXY([x, y], isInstant ? false : true);
31573 //            } else {
31574 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31575 //            }
31576 //            
31577 //            y = y + height + this.alternativePadWidth;
31578 //            
31579 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31580 //            
31581 //        }, this);
31582 //        
31583 //        this.el.setHeight(maxHeight);
31584 //        
31585 //    },
31586     
31587     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31588     {
31589         var pos = this.el.getBox(true);
31590         
31591         var minX = pos.x;
31592         var minY = pos.y;
31593         
31594         var maxX = pos.right;
31595         
31596         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31597         
31598         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31599         
31600         Roo.each(queue, function(box, k){
31601             
31602             Roo.each(box, function(b, kk){
31603                 
31604                 b.el.position('absolute');
31605                 
31606                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31607                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31608                 
31609                 if(b.size == 'md-left' || b.size == 'md-right'){
31610                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31611                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31612                 }
31613                 
31614                 b.el.setWidth(width);
31615                 b.el.setHeight(height);
31616                 
31617             }, this);
31618             
31619             if(!box.length){
31620                 return;
31621             }
31622             
31623             var positions = [];
31624             
31625             switch (box.length){
31626                 case 1 :
31627                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31628                     break;
31629                 case 2 :
31630                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31631                     break;
31632                 case 3 :
31633                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31634                     break;
31635                 case 4 :
31636                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31637                     break;
31638                 default :
31639                     break;
31640             }
31641             
31642             Roo.each(box, function(b,kk){
31643                 
31644                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31645                 
31646                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31647                 
31648             }, this);
31649             
31650         }, this);
31651         
31652     },
31653     
31654     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31655     {
31656         Roo.each(eItems, function(b,k){
31657             
31658             b.size = (k == 0) ? 'sm' : 'xs';
31659             b.x = (k == 0) ? 2 : 1;
31660             b.y = (k == 0) ? 2 : 1;
31661             
31662             b.el.position('absolute');
31663             
31664             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31665                 
31666             b.el.setWidth(width);
31667             
31668             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31669             
31670             b.el.setHeight(height);
31671             
31672         }, this);
31673
31674         var positions = [];
31675         
31676         positions.push({
31677             x : maxX - this.unitWidth * 2 - this.gutter,
31678             y : minY
31679         });
31680         
31681         positions.push({
31682             x : maxX - this.unitWidth,
31683             y : minY + (this.unitWidth + this.gutter) * 2
31684         });
31685         
31686         positions.push({
31687             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31688             y : minY
31689         });
31690         
31691         Roo.each(eItems, function(b,k){
31692             
31693             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31694
31695         }, this);
31696         
31697     },
31698     
31699     getVerticalOneBoxColPositions : function(x, y, box)
31700     {
31701         var pos = [];
31702         
31703         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31704         
31705         if(box[0].size == 'md-left'){
31706             rand = 0;
31707         }
31708         
31709         if(box[0].size == 'md-right'){
31710             rand = 1;
31711         }
31712         
31713         pos.push({
31714             x : x + (this.unitWidth + this.gutter) * rand,
31715             y : y
31716         });
31717         
31718         return pos;
31719     },
31720     
31721     getVerticalTwoBoxColPositions : function(x, y, box)
31722     {
31723         var pos = [];
31724         
31725         if(box[0].size == 'xs'){
31726             
31727             pos.push({
31728                 x : x,
31729                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31730             });
31731
31732             pos.push({
31733                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31734                 y : y
31735             });
31736             
31737             return pos;
31738             
31739         }
31740         
31741         pos.push({
31742             x : x,
31743             y : y
31744         });
31745
31746         pos.push({
31747             x : x + (this.unitWidth + this.gutter) * 2,
31748             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31749         });
31750         
31751         return pos;
31752         
31753     },
31754     
31755     getVerticalThreeBoxColPositions : function(x, y, box)
31756     {
31757         var pos = [];
31758         
31759         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31760             
31761             pos.push({
31762                 x : x,
31763                 y : y
31764             });
31765
31766             pos.push({
31767                 x : x + (this.unitWidth + this.gutter) * 1,
31768                 y : y
31769             });
31770             
31771             pos.push({
31772                 x : x + (this.unitWidth + this.gutter) * 2,
31773                 y : y
31774             });
31775             
31776             return pos;
31777             
31778         }
31779         
31780         if(box[0].size == 'xs' && box[1].size == 'xs'){
31781             
31782             pos.push({
31783                 x : x,
31784                 y : y
31785             });
31786
31787             pos.push({
31788                 x : x,
31789                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31790             });
31791             
31792             pos.push({
31793                 x : x + (this.unitWidth + this.gutter) * 1,
31794                 y : y
31795             });
31796             
31797             return pos;
31798             
31799         }
31800         
31801         pos.push({
31802             x : x,
31803             y : y
31804         });
31805
31806         pos.push({
31807             x : x + (this.unitWidth + this.gutter) * 2,
31808             y : y
31809         });
31810
31811         pos.push({
31812             x : x + (this.unitWidth + this.gutter) * 2,
31813             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31814         });
31815             
31816         return pos;
31817         
31818     },
31819     
31820     getVerticalFourBoxColPositions : function(x, y, box)
31821     {
31822         var pos = [];
31823         
31824         if(box[0].size == 'xs'){
31825             
31826             pos.push({
31827                 x : x,
31828                 y : y
31829             });
31830
31831             pos.push({
31832                 x : x,
31833                 y : y + (this.unitHeight + this.gutter) * 1
31834             });
31835             
31836             pos.push({
31837                 x : x,
31838                 y : y + (this.unitHeight + this.gutter) * 2
31839             });
31840             
31841             pos.push({
31842                 x : x + (this.unitWidth + this.gutter) * 1,
31843                 y : y
31844             });
31845             
31846             return pos;
31847             
31848         }
31849         
31850         pos.push({
31851             x : x,
31852             y : y
31853         });
31854
31855         pos.push({
31856             x : x + (this.unitWidth + this.gutter) * 2,
31857             y : y
31858         });
31859
31860         pos.push({
31861             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31862             y : y + (this.unitHeight + this.gutter) * 1
31863         });
31864
31865         pos.push({
31866             x : x + (this.unitWidth + this.gutter) * 2,
31867             y : y + (this.unitWidth + this.gutter) * 2
31868         });
31869
31870         return pos;
31871         
31872     },
31873     
31874     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31875     {
31876         var pos = [];
31877         
31878         if(box[0].size == 'md-left'){
31879             pos.push({
31880                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31881                 y : minY
31882             });
31883             
31884             return pos;
31885         }
31886         
31887         if(box[0].size == 'md-right'){
31888             pos.push({
31889                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31890                 y : minY + (this.unitWidth + this.gutter) * 1
31891             });
31892             
31893             return pos;
31894         }
31895         
31896         var rand = Math.floor(Math.random() * (4 - box[0].y));
31897         
31898         pos.push({
31899             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31900             y : minY + (this.unitWidth + this.gutter) * rand
31901         });
31902         
31903         return pos;
31904         
31905     },
31906     
31907     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31908     {
31909         var pos = [];
31910         
31911         if(box[0].size == 'xs'){
31912             
31913             pos.push({
31914                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31915                 y : minY
31916             });
31917
31918             pos.push({
31919                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31920                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31921             });
31922             
31923             return pos;
31924             
31925         }
31926         
31927         pos.push({
31928             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31929             y : minY
31930         });
31931
31932         pos.push({
31933             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31934             y : minY + (this.unitWidth + this.gutter) * 2
31935         });
31936         
31937         return pos;
31938         
31939     },
31940     
31941     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31942     {
31943         var pos = [];
31944         
31945         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31946             
31947             pos.push({
31948                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31949                 y : minY
31950             });
31951
31952             pos.push({
31953                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31954                 y : minY + (this.unitWidth + this.gutter) * 1
31955             });
31956             
31957             pos.push({
31958                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31959                 y : minY + (this.unitWidth + this.gutter) * 2
31960             });
31961             
31962             return pos;
31963             
31964         }
31965         
31966         if(box[0].size == 'xs' && box[1].size == 'xs'){
31967             
31968             pos.push({
31969                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31970                 y : minY
31971             });
31972
31973             pos.push({
31974                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31975                 y : minY
31976             });
31977             
31978             pos.push({
31979                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31980                 y : minY + (this.unitWidth + this.gutter) * 1
31981             });
31982             
31983             return pos;
31984             
31985         }
31986         
31987         pos.push({
31988             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31989             y : minY
31990         });
31991
31992         pos.push({
31993             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31994             y : minY + (this.unitWidth + this.gutter) * 2
31995         });
31996
31997         pos.push({
31998             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31999             y : minY + (this.unitWidth + this.gutter) * 2
32000         });
32001             
32002         return pos;
32003         
32004     },
32005     
32006     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32007     {
32008         var pos = [];
32009         
32010         if(box[0].size == 'xs'){
32011             
32012             pos.push({
32013                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32014                 y : minY
32015             });
32016
32017             pos.push({
32018                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32019                 y : minY
32020             });
32021             
32022             pos.push({
32023                 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),
32024                 y : minY
32025             });
32026             
32027             pos.push({
32028                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32029                 y : minY + (this.unitWidth + this.gutter) * 1
32030             });
32031             
32032             return pos;
32033             
32034         }
32035         
32036         pos.push({
32037             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32038             y : minY
32039         });
32040         
32041         pos.push({
32042             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32043             y : minY + (this.unitWidth + this.gutter) * 2
32044         });
32045         
32046         pos.push({
32047             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32048             y : minY + (this.unitWidth + this.gutter) * 2
32049         });
32050         
32051         pos.push({
32052             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),
32053             y : minY + (this.unitWidth + this.gutter) * 2
32054         });
32055
32056         return pos;
32057         
32058     },
32059     
32060     /**
32061     * remove a Masonry Brick
32062     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32063     */
32064     removeBrick : function(brick_id)
32065     {
32066         if (!brick_id) {
32067             return;
32068         }
32069         
32070         for (var i = 0; i<this.bricks.length; i++) {
32071             if (this.bricks[i].id == brick_id) {
32072                 this.bricks.splice(i,1);
32073                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32074                 this.initial();
32075             }
32076         }
32077     },
32078     
32079     /**
32080     * adds a Masonry Brick
32081     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32082     */
32083     addBrick : function(cfg)
32084     {
32085         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32086         //this.register(cn);
32087         cn.parentId = this.id;
32088         cn.render(this.el);
32089         return cn;
32090     },
32091     
32092     /**
32093     * register a Masonry Brick
32094     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32095     */
32096     
32097     register : function(brick)
32098     {
32099         this.bricks.push(brick);
32100         brick.masonryId = this.id;
32101     },
32102     
32103     /**
32104     * clear all the Masonry Brick
32105     */
32106     clearAll : function()
32107     {
32108         this.bricks = [];
32109         //this.getChildContainer().dom.innerHTML = "";
32110         this.el.dom.innerHTML = '';
32111     },
32112     
32113     getSelected : function()
32114     {
32115         if (!this.selectedBrick) {
32116             return false;
32117         }
32118         
32119         return this.selectedBrick;
32120     }
32121 });
32122
32123 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32124     
32125     groups: {},
32126      /**
32127     * register a Masonry Layout
32128     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32129     */
32130     
32131     register : function(layout)
32132     {
32133         this.groups[layout.id] = layout;
32134     },
32135     /**
32136     * fetch a  Masonry Layout based on the masonry layout ID
32137     * @param {string} the masonry layout to add
32138     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32139     */
32140     
32141     get: function(layout_id) {
32142         if (typeof(this.groups[layout_id]) == 'undefined') {
32143             return false;
32144         }
32145         return this.groups[layout_id] ;
32146     }
32147     
32148     
32149     
32150 });
32151
32152  
32153
32154  /**
32155  *
32156  * This is based on 
32157  * http://masonry.desandro.com
32158  *
32159  * The idea is to render all the bricks based on vertical width...
32160  *
32161  * The original code extends 'outlayer' - we might need to use that....
32162  * 
32163  */
32164
32165
32166 /**
32167  * @class Roo.bootstrap.LayoutMasonryAuto
32168  * @extends Roo.bootstrap.Component
32169  * Bootstrap Layout Masonry class
32170  * 
32171  * @constructor
32172  * Create a new Element
32173  * @param {Object} config The config object
32174  */
32175
32176 Roo.bootstrap.LayoutMasonryAuto = function(config){
32177     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32178 };
32179
32180 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32181     
32182       /**
32183      * @cfg {Boolean} isFitWidth  - resize the width..
32184      */   
32185     isFitWidth : false,  // options..
32186     /**
32187      * @cfg {Boolean} isOriginLeft = left align?
32188      */   
32189     isOriginLeft : true,
32190     /**
32191      * @cfg {Boolean} isOriginTop = top align?
32192      */   
32193     isOriginTop : false,
32194     /**
32195      * @cfg {Boolean} isLayoutInstant = no animation?
32196      */   
32197     isLayoutInstant : false, // needed?
32198     /**
32199      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32200      */   
32201     isResizingContainer : true,
32202     /**
32203      * @cfg {Number} columnWidth  width of the columns 
32204      */   
32205     
32206     columnWidth : 0,
32207     
32208     /**
32209      * @cfg {Number} maxCols maximum number of columns
32210      */   
32211     
32212     maxCols: 0,
32213     /**
32214      * @cfg {Number} padHeight padding below box..
32215      */   
32216     
32217     padHeight : 10, 
32218     
32219     /**
32220      * @cfg {Boolean} isAutoInitial defalut true
32221      */   
32222     
32223     isAutoInitial : true, 
32224     
32225     // private?
32226     gutter : 0,
32227     
32228     containerWidth: 0,
32229     initialColumnWidth : 0,
32230     currentSize : null,
32231     
32232     colYs : null, // array.
32233     maxY : 0,
32234     padWidth: 10,
32235     
32236     
32237     tag: 'div',
32238     cls: '',
32239     bricks: null, //CompositeElement
32240     cols : 0, // array?
32241     // element : null, // wrapped now this.el
32242     _isLayoutInited : null, 
32243     
32244     
32245     getAutoCreate : function(){
32246         
32247         var cfg = {
32248             tag: this.tag,
32249             cls: 'blog-masonary-wrapper ' + this.cls,
32250             cn : {
32251                 cls : 'mas-boxes masonary'
32252             }
32253         };
32254         
32255         return cfg;
32256     },
32257     
32258     getChildContainer: function( )
32259     {
32260         if (this.boxesEl) {
32261             return this.boxesEl;
32262         }
32263         
32264         this.boxesEl = this.el.select('.mas-boxes').first();
32265         
32266         return this.boxesEl;
32267     },
32268     
32269     
32270     initEvents : function()
32271     {
32272         var _this = this;
32273         
32274         if(this.isAutoInitial){
32275             Roo.log('hook children rendered');
32276             this.on('childrenrendered', function() {
32277                 Roo.log('children rendered');
32278                 _this.initial();
32279             } ,this);
32280         }
32281         
32282     },
32283     
32284     initial : function()
32285     {
32286         this.reloadItems();
32287
32288         this.currentSize = this.el.getBox(true);
32289
32290         /// was window resize... - let's see if this works..
32291         Roo.EventManager.onWindowResize(this.resize, this); 
32292
32293         if(!this.isAutoInitial){
32294             this.layout();
32295             return;
32296         }
32297         
32298         this.layout.defer(500,this);
32299     },
32300     
32301     reloadItems: function()
32302     {
32303         this.bricks = this.el.select('.masonry-brick', true);
32304         
32305         this.bricks.each(function(b) {
32306             //Roo.log(b.getSize());
32307             if (!b.attr('originalwidth')) {
32308                 b.attr('originalwidth',  b.getSize().width);
32309             }
32310             
32311         });
32312         
32313         Roo.log(this.bricks.elements.length);
32314     },
32315     
32316     resize : function()
32317     {
32318         Roo.log('resize');
32319         var cs = this.el.getBox(true);
32320         
32321         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32322             Roo.log("no change in with or X");
32323             return;
32324         }
32325         this.currentSize = cs;
32326         this.layout();
32327     },
32328     
32329     layout : function()
32330     {
32331          Roo.log('layout');
32332         this._resetLayout();
32333         //this._manageStamps();
32334       
32335         // don't animate first layout
32336         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32337         this.layoutItems( isInstant );
32338       
32339         // flag for initalized
32340         this._isLayoutInited = true;
32341     },
32342     
32343     layoutItems : function( isInstant )
32344     {
32345         //var items = this._getItemsForLayout( this.items );
32346         // original code supports filtering layout items.. we just ignore it..
32347         
32348         this._layoutItems( this.bricks , isInstant );
32349       
32350         this._postLayout();
32351     },
32352     _layoutItems : function ( items , isInstant)
32353     {
32354        //this.fireEvent( 'layout', this, items );
32355     
32356
32357         if ( !items || !items.elements.length ) {
32358           // no items, emit event with empty array
32359             return;
32360         }
32361
32362         var queue = [];
32363         items.each(function(item) {
32364             Roo.log("layout item");
32365             Roo.log(item);
32366             // get x/y object from method
32367             var position = this._getItemLayoutPosition( item );
32368             // enqueue
32369             position.item = item;
32370             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32371             queue.push( position );
32372         }, this);
32373       
32374         this._processLayoutQueue( queue );
32375     },
32376     /** Sets position of item in DOM
32377     * @param {Element} item
32378     * @param {Number} x - horizontal position
32379     * @param {Number} y - vertical position
32380     * @param {Boolean} isInstant - disables transitions
32381     */
32382     _processLayoutQueue : function( queue )
32383     {
32384         for ( var i=0, len = queue.length; i < len; i++ ) {
32385             var obj = queue[i];
32386             obj.item.position('absolute');
32387             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32388         }
32389     },
32390       
32391     
32392     /**
32393     * Any logic you want to do after each layout,
32394     * i.e. size the container
32395     */
32396     _postLayout : function()
32397     {
32398         this.resizeContainer();
32399     },
32400     
32401     resizeContainer : function()
32402     {
32403         if ( !this.isResizingContainer ) {
32404             return;
32405         }
32406         var size = this._getContainerSize();
32407         if ( size ) {
32408             this.el.setSize(size.width,size.height);
32409             this.boxesEl.setSize(size.width,size.height);
32410         }
32411     },
32412     
32413     
32414     
32415     _resetLayout : function()
32416     {
32417         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32418         this.colWidth = this.el.getWidth();
32419         //this.gutter = this.el.getWidth(); 
32420         
32421         this.measureColumns();
32422
32423         // reset column Y
32424         var i = this.cols;
32425         this.colYs = [];
32426         while (i--) {
32427             this.colYs.push( 0 );
32428         }
32429     
32430         this.maxY = 0;
32431     },
32432
32433     measureColumns : function()
32434     {
32435         this.getContainerWidth();
32436       // if columnWidth is 0, default to outerWidth of first item
32437         if ( !this.columnWidth ) {
32438             var firstItem = this.bricks.first();
32439             Roo.log(firstItem);
32440             this.columnWidth  = this.containerWidth;
32441             if (firstItem && firstItem.attr('originalwidth') ) {
32442                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32443             }
32444             // columnWidth fall back to item of first element
32445             Roo.log("set column width?");
32446                         this.initialColumnWidth = this.columnWidth  ;
32447
32448             // if first elem has no width, default to size of container
32449             
32450         }
32451         
32452         
32453         if (this.initialColumnWidth) {
32454             this.columnWidth = this.initialColumnWidth;
32455         }
32456         
32457         
32458             
32459         // column width is fixed at the top - however if container width get's smaller we should
32460         // reduce it...
32461         
32462         // this bit calcs how man columns..
32463             
32464         var columnWidth = this.columnWidth += this.gutter;
32465       
32466         // calculate columns
32467         var containerWidth = this.containerWidth + this.gutter;
32468         
32469         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32470         // fix rounding errors, typically with gutters
32471         var excess = columnWidth - containerWidth % columnWidth;
32472         
32473         
32474         // if overshoot is less than a pixel, round up, otherwise floor it
32475         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32476         cols = Math[ mathMethod ]( cols );
32477         this.cols = Math.max( cols, 1 );
32478         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32479         
32480          // padding positioning..
32481         var totalColWidth = this.cols * this.columnWidth;
32482         var padavail = this.containerWidth - totalColWidth;
32483         // so for 2 columns - we need 3 'pads'
32484         
32485         var padNeeded = (1+this.cols) * this.padWidth;
32486         
32487         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32488         
32489         this.columnWidth += padExtra
32490         //this.padWidth = Math.floor(padavail /  ( this.cols));
32491         
32492         // adjust colum width so that padding is fixed??
32493         
32494         // we have 3 columns ... total = width * 3
32495         // we have X left over... that should be used by 
32496         
32497         //if (this.expandC) {
32498             
32499         //}
32500         
32501         
32502         
32503     },
32504     
32505     getContainerWidth : function()
32506     {
32507        /* // container is parent if fit width
32508         var container = this.isFitWidth ? this.element.parentNode : this.element;
32509         // check that this.size and size are there
32510         // IE8 triggers resize on body size change, so they might not be
32511         
32512         var size = getSize( container );  //FIXME
32513         this.containerWidth = size && size.innerWidth; //FIXME
32514         */
32515          
32516         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32517         
32518     },
32519     
32520     _getItemLayoutPosition : function( item )  // what is item?
32521     {
32522         // we resize the item to our columnWidth..
32523       
32524         item.setWidth(this.columnWidth);
32525         item.autoBoxAdjust  = false;
32526         
32527         var sz = item.getSize();
32528  
32529         // how many columns does this brick span
32530         var remainder = this.containerWidth % this.columnWidth;
32531         
32532         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32533         // round if off by 1 pixel, otherwise use ceil
32534         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32535         colSpan = Math.min( colSpan, this.cols );
32536         
32537         // normally this should be '1' as we dont' currently allow multi width columns..
32538         
32539         var colGroup = this._getColGroup( colSpan );
32540         // get the minimum Y value from the columns
32541         var minimumY = Math.min.apply( Math, colGroup );
32542         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32543         
32544         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32545          
32546         // position the brick
32547         var position = {
32548             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32549             y: this.currentSize.y + minimumY + this.padHeight
32550         };
32551         
32552         Roo.log(position);
32553         // apply setHeight to necessary columns
32554         var setHeight = minimumY + sz.height + this.padHeight;
32555         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32556         
32557         var setSpan = this.cols + 1 - colGroup.length;
32558         for ( var i = 0; i < setSpan; i++ ) {
32559           this.colYs[ shortColIndex + i ] = setHeight ;
32560         }
32561       
32562         return position;
32563     },
32564     
32565     /**
32566      * @param {Number} colSpan - number of columns the element spans
32567      * @returns {Array} colGroup
32568      */
32569     _getColGroup : function( colSpan )
32570     {
32571         if ( colSpan < 2 ) {
32572           // if brick spans only one column, use all the column Ys
32573           return this.colYs;
32574         }
32575       
32576         var colGroup = [];
32577         // how many different places could this brick fit horizontally
32578         var groupCount = this.cols + 1 - colSpan;
32579         // for each group potential horizontal position
32580         for ( var i = 0; i < groupCount; i++ ) {
32581           // make an array of colY values for that one group
32582           var groupColYs = this.colYs.slice( i, i + colSpan );
32583           // and get the max value of the array
32584           colGroup[i] = Math.max.apply( Math, groupColYs );
32585         }
32586         return colGroup;
32587     },
32588     /*
32589     _manageStamp : function( stamp )
32590     {
32591         var stampSize =  stamp.getSize();
32592         var offset = stamp.getBox();
32593         // get the columns that this stamp affects
32594         var firstX = this.isOriginLeft ? offset.x : offset.right;
32595         var lastX = firstX + stampSize.width;
32596         var firstCol = Math.floor( firstX / this.columnWidth );
32597         firstCol = Math.max( 0, firstCol );
32598         
32599         var lastCol = Math.floor( lastX / this.columnWidth );
32600         // lastCol should not go over if multiple of columnWidth #425
32601         lastCol -= lastX % this.columnWidth ? 0 : 1;
32602         lastCol = Math.min( this.cols - 1, lastCol );
32603         
32604         // set colYs to bottom of the stamp
32605         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32606             stampSize.height;
32607             
32608         for ( var i = firstCol; i <= lastCol; i++ ) {
32609           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32610         }
32611     },
32612     */
32613     
32614     _getContainerSize : function()
32615     {
32616         this.maxY = Math.max.apply( Math, this.colYs );
32617         var size = {
32618             height: this.maxY
32619         };
32620       
32621         if ( this.isFitWidth ) {
32622             size.width = this._getContainerFitWidth();
32623         }
32624       
32625         return size;
32626     },
32627     
32628     _getContainerFitWidth : function()
32629     {
32630         var unusedCols = 0;
32631         // count unused columns
32632         var i = this.cols;
32633         while ( --i ) {
32634           if ( this.colYs[i] !== 0 ) {
32635             break;
32636           }
32637           unusedCols++;
32638         }
32639         // fit container to columns that have been used
32640         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32641     },
32642     
32643     needsResizeLayout : function()
32644     {
32645         var previousWidth = this.containerWidth;
32646         this.getContainerWidth();
32647         return previousWidth !== this.containerWidth;
32648     }
32649  
32650 });
32651
32652  
32653
32654  /*
32655  * - LGPL
32656  *
32657  * element
32658  * 
32659  */
32660
32661 /**
32662  * @class Roo.bootstrap.MasonryBrick
32663  * @extends Roo.bootstrap.Component
32664  * Bootstrap MasonryBrick class
32665  * 
32666  * @constructor
32667  * Create a new MasonryBrick
32668  * @param {Object} config The config object
32669  */
32670
32671 Roo.bootstrap.MasonryBrick = function(config){
32672     
32673     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32674     
32675     Roo.bootstrap.MasonryBrick.register(this);
32676     
32677     this.addEvents({
32678         // raw events
32679         /**
32680          * @event click
32681          * When a MasonryBrick is clcik
32682          * @param {Roo.bootstrap.MasonryBrick} this
32683          * @param {Roo.EventObject} e
32684          */
32685         "click" : true
32686     });
32687 };
32688
32689 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32690     
32691     /**
32692      * @cfg {String} title
32693      */   
32694     title : '',
32695     /**
32696      * @cfg {String} html
32697      */   
32698     html : '',
32699     /**
32700      * @cfg {String} bgimage
32701      */   
32702     bgimage : '',
32703     /**
32704      * @cfg {String} videourl
32705      */   
32706     videourl : '',
32707     /**
32708      * @cfg {String} cls
32709      */   
32710     cls : '',
32711     /**
32712      * @cfg {String} href
32713      */   
32714     href : '',
32715     /**
32716      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32717      */   
32718     size : 'xs',
32719     
32720     /**
32721      * @cfg {String} placetitle (center|bottom)
32722      */   
32723     placetitle : '',
32724     
32725     /**
32726      * @cfg {Boolean} isFitContainer defalut true
32727      */   
32728     isFitContainer : true, 
32729     
32730     /**
32731      * @cfg {Boolean} preventDefault defalut false
32732      */   
32733     preventDefault : false, 
32734     
32735     /**
32736      * @cfg {Boolean} inverse defalut false
32737      */   
32738     maskInverse : false, 
32739     
32740     getAutoCreate : function()
32741     {
32742         if(!this.isFitContainer){
32743             return this.getSplitAutoCreate();
32744         }
32745         
32746         var cls = 'masonry-brick masonry-brick-full';
32747         
32748         if(this.href.length){
32749             cls += ' masonry-brick-link';
32750         }
32751         
32752         if(this.bgimage.length){
32753             cls += ' masonry-brick-image';
32754         }
32755         
32756         if(this.maskInverse){
32757             cls += ' mask-inverse';
32758         }
32759         
32760         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32761             cls += ' enable-mask';
32762         }
32763         
32764         if(this.size){
32765             cls += ' masonry-' + this.size + '-brick';
32766         }
32767         
32768         if(this.placetitle.length){
32769             
32770             switch (this.placetitle) {
32771                 case 'center' :
32772                     cls += ' masonry-center-title';
32773                     break;
32774                 case 'bottom' :
32775                     cls += ' masonry-bottom-title';
32776                     break;
32777                 default:
32778                     break;
32779             }
32780             
32781         } else {
32782             if(!this.html.length && !this.bgimage.length){
32783                 cls += ' masonry-center-title';
32784             }
32785
32786             if(!this.html.length && this.bgimage.length){
32787                 cls += ' masonry-bottom-title';
32788             }
32789         }
32790         
32791         if(this.cls){
32792             cls += ' ' + this.cls;
32793         }
32794         
32795         var cfg = {
32796             tag: (this.href.length) ? 'a' : 'div',
32797             cls: cls,
32798             cn: [
32799                 {
32800                     tag: 'div',
32801                     cls: 'masonry-brick-mask'
32802                 },
32803                 {
32804                     tag: 'div',
32805                     cls: 'masonry-brick-paragraph',
32806                     cn: []
32807                 }
32808             ]
32809         };
32810         
32811         if(this.href.length){
32812             cfg.href = this.href;
32813         }
32814         
32815         var cn = cfg.cn[1].cn;
32816         
32817         if(this.title.length){
32818             cn.push({
32819                 tag: 'h4',
32820                 cls: 'masonry-brick-title',
32821                 html: this.title
32822             });
32823         }
32824         
32825         if(this.html.length){
32826             cn.push({
32827                 tag: 'p',
32828                 cls: 'masonry-brick-text',
32829                 html: this.html
32830             });
32831         }
32832         
32833         if (!this.title.length && !this.html.length) {
32834             cfg.cn[1].cls += ' hide';
32835         }
32836         
32837         if(this.bgimage.length){
32838             cfg.cn.push({
32839                 tag: 'img',
32840                 cls: 'masonry-brick-image-view',
32841                 src: this.bgimage
32842             });
32843         }
32844         
32845         if(this.videourl.length){
32846             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32847             // youtube support only?
32848             cfg.cn.push({
32849                 tag: 'iframe',
32850                 cls: 'masonry-brick-image-view',
32851                 src: vurl,
32852                 frameborder : 0,
32853                 allowfullscreen : true
32854             });
32855         }
32856         
32857         return cfg;
32858         
32859     },
32860     
32861     getSplitAutoCreate : function()
32862     {
32863         var cls = 'masonry-brick masonry-brick-split';
32864         
32865         if(this.href.length){
32866             cls += ' masonry-brick-link';
32867         }
32868         
32869         if(this.bgimage.length){
32870             cls += ' masonry-brick-image';
32871         }
32872         
32873         if(this.size){
32874             cls += ' masonry-' + this.size + '-brick';
32875         }
32876         
32877         switch (this.placetitle) {
32878             case 'center' :
32879                 cls += ' masonry-center-title';
32880                 break;
32881             case 'bottom' :
32882                 cls += ' masonry-bottom-title';
32883                 break;
32884             default:
32885                 if(!this.bgimage.length){
32886                     cls += ' masonry-center-title';
32887                 }
32888
32889                 if(this.bgimage.length){
32890                     cls += ' masonry-bottom-title';
32891                 }
32892                 break;
32893         }
32894         
32895         if(this.cls){
32896             cls += ' ' + this.cls;
32897         }
32898         
32899         var cfg = {
32900             tag: (this.href.length) ? 'a' : 'div',
32901             cls: cls,
32902             cn: [
32903                 {
32904                     tag: 'div',
32905                     cls: 'masonry-brick-split-head',
32906                     cn: [
32907                         {
32908                             tag: 'div',
32909                             cls: 'masonry-brick-paragraph',
32910                             cn: []
32911                         }
32912                     ]
32913                 },
32914                 {
32915                     tag: 'div',
32916                     cls: 'masonry-brick-split-body',
32917                     cn: []
32918                 }
32919             ]
32920         };
32921         
32922         if(this.href.length){
32923             cfg.href = this.href;
32924         }
32925         
32926         if(this.title.length){
32927             cfg.cn[0].cn[0].cn.push({
32928                 tag: 'h4',
32929                 cls: 'masonry-brick-title',
32930                 html: this.title
32931             });
32932         }
32933         
32934         if(this.html.length){
32935             cfg.cn[1].cn.push({
32936                 tag: 'p',
32937                 cls: 'masonry-brick-text',
32938                 html: this.html
32939             });
32940         }
32941
32942         if(this.bgimage.length){
32943             cfg.cn[0].cn.push({
32944                 tag: 'img',
32945                 cls: 'masonry-brick-image-view',
32946                 src: this.bgimage
32947             });
32948         }
32949         
32950         if(this.videourl.length){
32951             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32952             // youtube support only?
32953             cfg.cn[0].cn.cn.push({
32954                 tag: 'iframe',
32955                 cls: 'masonry-brick-image-view',
32956                 src: vurl,
32957                 frameborder : 0,
32958                 allowfullscreen : true
32959             });
32960         }
32961         
32962         return cfg;
32963     },
32964     
32965     initEvents: function() 
32966     {
32967         switch (this.size) {
32968             case 'xs' :
32969                 this.x = 1;
32970                 this.y = 1;
32971                 break;
32972             case 'sm' :
32973                 this.x = 2;
32974                 this.y = 2;
32975                 break;
32976             case 'md' :
32977             case 'md-left' :
32978             case 'md-right' :
32979                 this.x = 3;
32980                 this.y = 3;
32981                 break;
32982             case 'tall' :
32983                 this.x = 2;
32984                 this.y = 3;
32985                 break;
32986             case 'wide' :
32987                 this.x = 3;
32988                 this.y = 2;
32989                 break;
32990             case 'wide-thin' :
32991                 this.x = 3;
32992                 this.y = 1;
32993                 break;
32994                         
32995             default :
32996                 break;
32997         }
32998         
32999         if(Roo.isTouch){
33000             this.el.on('touchstart', this.onTouchStart, this);
33001             this.el.on('touchmove', this.onTouchMove, this);
33002             this.el.on('touchend', this.onTouchEnd, this);
33003             this.el.on('contextmenu', this.onContextMenu, this);
33004         } else {
33005             this.el.on('mouseenter'  ,this.enter, this);
33006             this.el.on('mouseleave', this.leave, this);
33007             this.el.on('click', this.onClick, this);
33008         }
33009         
33010         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33011             this.parent().bricks.push(this);   
33012         }
33013         
33014     },
33015     
33016     onClick: function(e, el)
33017     {
33018         var time = this.endTimer - this.startTimer;
33019         // Roo.log(e.preventDefault());
33020         if(Roo.isTouch){
33021             if(time > 1000){
33022                 e.preventDefault();
33023                 return;
33024             }
33025         }
33026         
33027         if(!this.preventDefault){
33028             return;
33029         }
33030         
33031         e.preventDefault();
33032         
33033         if (this.activeClass != '') {
33034             this.selectBrick();
33035         }
33036         
33037         this.fireEvent('click', this, e);
33038     },
33039     
33040     enter: function(e, el)
33041     {
33042         e.preventDefault();
33043         
33044         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33045             return;
33046         }
33047         
33048         if(this.bgimage.length && this.html.length){
33049             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33050         }
33051     },
33052     
33053     leave: function(e, el)
33054     {
33055         e.preventDefault();
33056         
33057         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33058             return;
33059         }
33060         
33061         if(this.bgimage.length && this.html.length){
33062             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33063         }
33064     },
33065     
33066     onTouchStart: function(e, el)
33067     {
33068 //        e.preventDefault();
33069         
33070         this.touchmoved = false;
33071         
33072         if(!this.isFitContainer){
33073             return;
33074         }
33075         
33076         if(!this.bgimage.length || !this.html.length){
33077             return;
33078         }
33079         
33080         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33081         
33082         this.timer = new Date().getTime();
33083         
33084     },
33085     
33086     onTouchMove: function(e, el)
33087     {
33088         this.touchmoved = true;
33089     },
33090     
33091     onContextMenu : function(e,el)
33092     {
33093         e.preventDefault();
33094         e.stopPropagation();
33095         return false;
33096     },
33097     
33098     onTouchEnd: function(e, el)
33099     {
33100 //        e.preventDefault();
33101         
33102         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33103         
33104             this.leave(e,el);
33105             
33106             return;
33107         }
33108         
33109         if(!this.bgimage.length || !this.html.length){
33110             
33111             if(this.href.length){
33112                 window.location.href = this.href;
33113             }
33114             
33115             return;
33116         }
33117         
33118         if(!this.isFitContainer){
33119             return;
33120         }
33121         
33122         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33123         
33124         window.location.href = this.href;
33125     },
33126     
33127     //selection on single brick only
33128     selectBrick : function() {
33129         
33130         if (!this.parentId) {
33131             return;
33132         }
33133         
33134         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33135         var index = m.selectedBrick.indexOf(this.id);
33136         
33137         if ( index > -1) {
33138             m.selectedBrick.splice(index,1);
33139             this.el.removeClass(this.activeClass);
33140             return;
33141         }
33142         
33143         for(var i = 0; i < m.selectedBrick.length; i++) {
33144             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33145             b.el.removeClass(b.activeClass);
33146         }
33147         
33148         m.selectedBrick = [];
33149         
33150         m.selectedBrick.push(this.id);
33151         this.el.addClass(this.activeClass);
33152         return;
33153     },
33154     
33155     isSelected : function(){
33156         return this.el.hasClass(this.activeClass);
33157         
33158     }
33159 });
33160
33161 Roo.apply(Roo.bootstrap.MasonryBrick, {
33162     
33163     //groups: {},
33164     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33165      /**
33166     * register a Masonry Brick
33167     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33168     */
33169     
33170     register : function(brick)
33171     {
33172         //this.groups[brick.id] = brick;
33173         this.groups.add(brick.id, brick);
33174     },
33175     /**
33176     * fetch a  masonry brick based on the masonry brick ID
33177     * @param {string} the masonry brick to add
33178     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33179     */
33180     
33181     get: function(brick_id) 
33182     {
33183         // if (typeof(this.groups[brick_id]) == 'undefined') {
33184         //     return false;
33185         // }
33186         // return this.groups[brick_id] ;
33187         
33188         if(this.groups.key(brick_id)) {
33189             return this.groups.key(brick_id);
33190         }
33191         
33192         return false;
33193     }
33194     
33195     
33196     
33197 });
33198
33199  /*
33200  * - LGPL
33201  *
33202  * element
33203  * 
33204  */
33205
33206 /**
33207  * @class Roo.bootstrap.Brick
33208  * @extends Roo.bootstrap.Component
33209  * Bootstrap Brick class
33210  * 
33211  * @constructor
33212  * Create a new Brick
33213  * @param {Object} config The config object
33214  */
33215
33216 Roo.bootstrap.Brick = function(config){
33217     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33218     
33219     this.addEvents({
33220         // raw events
33221         /**
33222          * @event click
33223          * When a Brick is click
33224          * @param {Roo.bootstrap.Brick} this
33225          * @param {Roo.EventObject} e
33226          */
33227         "click" : true
33228     });
33229 };
33230
33231 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33232     
33233     /**
33234      * @cfg {String} title
33235      */   
33236     title : '',
33237     /**
33238      * @cfg {String} html
33239      */   
33240     html : '',
33241     /**
33242      * @cfg {String} bgimage
33243      */   
33244     bgimage : '',
33245     /**
33246      * @cfg {String} cls
33247      */   
33248     cls : '',
33249     /**
33250      * @cfg {String} href
33251      */   
33252     href : '',
33253     /**
33254      * @cfg {String} video
33255      */   
33256     video : '',
33257     /**
33258      * @cfg {Boolean} square
33259      */   
33260     square : true,
33261     
33262     getAutoCreate : function()
33263     {
33264         var cls = 'roo-brick';
33265         
33266         if(this.href.length){
33267             cls += ' roo-brick-link';
33268         }
33269         
33270         if(this.bgimage.length){
33271             cls += ' roo-brick-image';
33272         }
33273         
33274         if(!this.html.length && !this.bgimage.length){
33275             cls += ' roo-brick-center-title';
33276         }
33277         
33278         if(!this.html.length && this.bgimage.length){
33279             cls += ' roo-brick-bottom-title';
33280         }
33281         
33282         if(this.cls){
33283             cls += ' ' + this.cls;
33284         }
33285         
33286         var cfg = {
33287             tag: (this.href.length) ? 'a' : 'div',
33288             cls: cls,
33289             cn: [
33290                 {
33291                     tag: 'div',
33292                     cls: 'roo-brick-paragraph',
33293                     cn: []
33294                 }
33295             ]
33296         };
33297         
33298         if(this.href.length){
33299             cfg.href = this.href;
33300         }
33301         
33302         var cn = cfg.cn[0].cn;
33303         
33304         if(this.title.length){
33305             cn.push({
33306                 tag: 'h4',
33307                 cls: 'roo-brick-title',
33308                 html: this.title
33309             });
33310         }
33311         
33312         if(this.html.length){
33313             cn.push({
33314                 tag: 'p',
33315                 cls: 'roo-brick-text',
33316                 html: this.html
33317             });
33318         } else {
33319             cn.cls += ' hide';
33320         }
33321         
33322         if(this.bgimage.length){
33323             cfg.cn.push({
33324                 tag: 'img',
33325                 cls: 'roo-brick-image-view',
33326                 src: this.bgimage
33327             });
33328         }
33329         
33330         return cfg;
33331     },
33332     
33333     initEvents: function() 
33334     {
33335         if(this.title.length || this.html.length){
33336             this.el.on('mouseenter'  ,this.enter, this);
33337             this.el.on('mouseleave', this.leave, this);
33338         }
33339         
33340         Roo.EventManager.onWindowResize(this.resize, this); 
33341         
33342         if(this.bgimage.length){
33343             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33344             this.imageEl.on('load', this.onImageLoad, this);
33345             return;
33346         }
33347         
33348         this.resize();
33349     },
33350     
33351     onImageLoad : function()
33352     {
33353         this.resize();
33354     },
33355     
33356     resize : function()
33357     {
33358         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33359         
33360         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33361         
33362         if(this.bgimage.length){
33363             var image = this.el.select('.roo-brick-image-view', true).first();
33364             
33365             image.setWidth(paragraph.getWidth());
33366             
33367             if(this.square){
33368                 image.setHeight(paragraph.getWidth());
33369             }
33370             
33371             this.el.setHeight(image.getHeight());
33372             paragraph.setHeight(image.getHeight());
33373             
33374         }
33375         
33376     },
33377     
33378     enter: function(e, el)
33379     {
33380         e.preventDefault();
33381         
33382         if(this.bgimage.length){
33383             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33384             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33385         }
33386     },
33387     
33388     leave: function(e, el)
33389     {
33390         e.preventDefault();
33391         
33392         if(this.bgimage.length){
33393             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33394             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33395         }
33396     }
33397     
33398 });
33399
33400  
33401
33402  /*
33403  * - LGPL
33404  *
33405  * Number field 
33406  */
33407
33408 /**
33409  * @class Roo.bootstrap.NumberField
33410  * @extends Roo.bootstrap.Input
33411  * Bootstrap NumberField class
33412  * 
33413  * 
33414  * 
33415  * 
33416  * @constructor
33417  * Create a new NumberField
33418  * @param {Object} config The config object
33419  */
33420
33421 Roo.bootstrap.NumberField = function(config){
33422     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33423 };
33424
33425 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33426     
33427     /**
33428      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33429      */
33430     allowDecimals : true,
33431     /**
33432      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33433      */
33434     decimalSeparator : ".",
33435     /**
33436      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33437      */
33438     decimalPrecision : 2,
33439     /**
33440      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33441      */
33442     allowNegative : true,
33443     
33444     /**
33445      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33446      */
33447     allowZero: true,
33448     /**
33449      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33450      */
33451     minValue : Number.NEGATIVE_INFINITY,
33452     /**
33453      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33454      */
33455     maxValue : Number.MAX_VALUE,
33456     /**
33457      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33458      */
33459     minText : "The minimum value for this field is {0}",
33460     /**
33461      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33462      */
33463     maxText : "The maximum value for this field is {0}",
33464     /**
33465      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33466      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33467      */
33468     nanText : "{0} is not a valid number",
33469     /**
33470      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33471      */
33472     thousandsDelimiter : false,
33473     /**
33474      * @cfg {String} valueAlign alignment of value
33475      */
33476     valueAlign : "left",
33477
33478     getAutoCreate : function()
33479     {
33480         var hiddenInput = {
33481             tag: 'input',
33482             type: 'hidden',
33483             id: Roo.id(),
33484             cls: 'hidden-number-input'
33485         };
33486         
33487         if (this.name) {
33488             hiddenInput.name = this.name;
33489         }
33490         
33491         this.name = '';
33492         
33493         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33494         
33495         this.name = hiddenInput.name;
33496         
33497         if(cfg.cn.length > 0) {
33498             cfg.cn.push(hiddenInput);
33499         }
33500         
33501         return cfg;
33502     },
33503
33504     // private
33505     initEvents : function()
33506     {   
33507         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33508         
33509         var allowed = "0123456789";
33510         
33511         if(this.allowDecimals){
33512             allowed += this.decimalSeparator;
33513         }
33514         
33515         if(this.allowNegative){
33516             allowed += "-";
33517         }
33518         
33519         if(this.thousandsDelimiter) {
33520             allowed += ",";
33521         }
33522         
33523         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33524         
33525         var keyPress = function(e){
33526             
33527             var k = e.getKey();
33528             
33529             var c = e.getCharCode();
33530             
33531             if(
33532                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33533                     allowed.indexOf(String.fromCharCode(c)) === -1
33534             ){
33535                 e.stopEvent();
33536                 return;
33537             }
33538             
33539             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33540                 return;
33541             }
33542             
33543             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33544                 e.stopEvent();
33545             }
33546         };
33547         
33548         this.el.on("keypress", keyPress, this);
33549     },
33550     
33551     validateValue : function(value)
33552     {
33553         
33554         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33555             return false;
33556         }
33557         
33558         var num = this.parseValue(value);
33559         
33560         if(isNaN(num)){
33561             this.markInvalid(String.format(this.nanText, value));
33562             return false;
33563         }
33564         
33565         if(num < this.minValue){
33566             this.markInvalid(String.format(this.minText, this.minValue));
33567             return false;
33568         }
33569         
33570         if(num > this.maxValue){
33571             this.markInvalid(String.format(this.maxText, this.maxValue));
33572             return false;
33573         }
33574         
33575         return true;
33576     },
33577
33578     getValue : function()
33579     {
33580         var v = this.hiddenEl().getValue();
33581         
33582         return this.fixPrecision(this.parseValue(v));
33583     },
33584
33585     parseValue : function(value)
33586     {
33587         if(this.thousandsDelimiter) {
33588             value += "";
33589             r = new RegExp(",", "g");
33590             value = value.replace(r, "");
33591         }
33592         
33593         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33594         return isNaN(value) ? '' : value;
33595     },
33596
33597     fixPrecision : function(value)
33598     {
33599         if(this.thousandsDelimiter) {
33600             value += "";
33601             r = new RegExp(",", "g");
33602             value = value.replace(r, "");
33603         }
33604         
33605         var nan = isNaN(value);
33606         
33607         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33608             return nan ? '' : value;
33609         }
33610         return parseFloat(value).toFixed(this.decimalPrecision);
33611     },
33612
33613     setValue : function(v)
33614     {
33615         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33616         
33617         this.value = v;
33618         
33619         if(this.rendered){
33620             
33621             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33622             
33623             this.inputEl().dom.value = (v == '') ? '' :
33624                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33625             
33626             if(!this.allowZero && v === '0') {
33627                 this.hiddenEl().dom.value = '';
33628                 this.inputEl().dom.value = '';
33629             }
33630             
33631             this.validate();
33632         }
33633     },
33634
33635     decimalPrecisionFcn : function(v)
33636     {
33637         return Math.floor(v);
33638     },
33639
33640     beforeBlur : function()
33641     {
33642         var v = this.parseValue(this.getRawValue());
33643         
33644         if(v || v === 0 || v === ''){
33645             this.setValue(v);
33646         }
33647     },
33648     
33649     hiddenEl : function()
33650     {
33651         return this.el.select('input.hidden-number-input',true).first();
33652     }
33653     
33654 });
33655
33656  
33657
33658 /*
33659 * Licence: LGPL
33660 */
33661
33662 /**
33663  * @class Roo.bootstrap.DocumentSlider
33664  * @extends Roo.bootstrap.Component
33665  * Bootstrap DocumentSlider class
33666  * 
33667  * @constructor
33668  * Create a new DocumentViewer
33669  * @param {Object} config The config object
33670  */
33671
33672 Roo.bootstrap.DocumentSlider = function(config){
33673     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33674     
33675     this.files = [];
33676     
33677     this.addEvents({
33678         /**
33679          * @event initial
33680          * Fire after initEvent
33681          * @param {Roo.bootstrap.DocumentSlider} this
33682          */
33683         "initial" : true,
33684         /**
33685          * @event update
33686          * Fire after update
33687          * @param {Roo.bootstrap.DocumentSlider} this
33688          */
33689         "update" : true,
33690         /**
33691          * @event click
33692          * Fire after click
33693          * @param {Roo.bootstrap.DocumentSlider} this
33694          */
33695         "click" : true
33696     });
33697 };
33698
33699 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33700     
33701     files : false,
33702     
33703     indicator : 0,
33704     
33705     getAutoCreate : function()
33706     {
33707         var cfg = {
33708             tag : 'div',
33709             cls : 'roo-document-slider',
33710             cn : [
33711                 {
33712                     tag : 'div',
33713                     cls : 'roo-document-slider-header',
33714                     cn : [
33715                         {
33716                             tag : 'div',
33717                             cls : 'roo-document-slider-header-title'
33718                         }
33719                     ]
33720                 },
33721                 {
33722                     tag : 'div',
33723                     cls : 'roo-document-slider-body',
33724                     cn : [
33725                         {
33726                             tag : 'div',
33727                             cls : 'roo-document-slider-prev',
33728                             cn : [
33729                                 {
33730                                     tag : 'i',
33731                                     cls : 'fa fa-chevron-left'
33732                                 }
33733                             ]
33734                         },
33735                         {
33736                             tag : 'div',
33737                             cls : 'roo-document-slider-thumb',
33738                             cn : [
33739                                 {
33740                                     tag : 'img',
33741                                     cls : 'roo-document-slider-image'
33742                                 }
33743                             ]
33744                         },
33745                         {
33746                             tag : 'div',
33747                             cls : 'roo-document-slider-next',
33748                             cn : [
33749                                 {
33750                                     tag : 'i',
33751                                     cls : 'fa fa-chevron-right'
33752                                 }
33753                             ]
33754                         }
33755                     ]
33756                 }
33757             ]
33758         };
33759         
33760         return cfg;
33761     },
33762     
33763     initEvents : function()
33764     {
33765         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33766         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33767         
33768         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33769         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33770         
33771         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33772         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33773         
33774         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33775         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33776         
33777         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33778         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33779         
33780         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33781         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33782         
33783         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33784         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33785         
33786         this.thumbEl.on('click', this.onClick, this);
33787         
33788         this.prevIndicator.on('click', this.prev, this);
33789         
33790         this.nextIndicator.on('click', this.next, this);
33791         
33792     },
33793     
33794     initial : function()
33795     {
33796         if(this.files.length){
33797             this.indicator = 1;
33798             this.update()
33799         }
33800         
33801         this.fireEvent('initial', this);
33802     },
33803     
33804     update : function()
33805     {
33806         this.imageEl.attr('src', this.files[this.indicator - 1]);
33807         
33808         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33809         
33810         this.prevIndicator.show();
33811         
33812         if(this.indicator == 1){
33813             this.prevIndicator.hide();
33814         }
33815         
33816         this.nextIndicator.show();
33817         
33818         if(this.indicator == this.files.length){
33819             this.nextIndicator.hide();
33820         }
33821         
33822         this.thumbEl.scrollTo('top');
33823         
33824         this.fireEvent('update', this);
33825     },
33826     
33827     onClick : function(e)
33828     {
33829         e.preventDefault();
33830         
33831         this.fireEvent('click', this);
33832     },
33833     
33834     prev : function(e)
33835     {
33836         e.preventDefault();
33837         
33838         this.indicator = Math.max(1, this.indicator - 1);
33839         
33840         this.update();
33841     },
33842     
33843     next : function(e)
33844     {
33845         e.preventDefault();
33846         
33847         this.indicator = Math.min(this.files.length, this.indicator + 1);
33848         
33849         this.update();
33850     }
33851 });
33852 /*
33853  * - LGPL
33854  *
33855  * RadioSet
33856  *
33857  *
33858  */
33859
33860 /**
33861  * @class Roo.bootstrap.RadioSet
33862  * @extends Roo.bootstrap.Input
33863  * Bootstrap RadioSet class
33864  * @cfg {String} indicatorpos (left|right) default left
33865  * @cfg {Boolean} inline (true|false) inline the element (default true)
33866  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33867  * @constructor
33868  * Create a new RadioSet
33869  * @param {Object} config The config object
33870  */
33871
33872 Roo.bootstrap.RadioSet = function(config){
33873     
33874     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33875     
33876     this.radioes = [];
33877     
33878     Roo.bootstrap.RadioSet.register(this);
33879     
33880     this.addEvents({
33881         /**
33882         * @event check
33883         * Fires when the element is checked or unchecked.
33884         * @param {Roo.bootstrap.RadioSet} this This radio
33885         * @param {Roo.bootstrap.Radio} item The checked item
33886         */
33887        check : true,
33888        /**
33889         * @event click
33890         * Fires when the element is click.
33891         * @param {Roo.bootstrap.RadioSet} this This radio set
33892         * @param {Roo.bootstrap.Radio} item The checked item
33893         * @param {Roo.EventObject} e The event object
33894         */
33895        click : true
33896     });
33897     
33898 };
33899
33900 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33901
33902     radioes : false,
33903     
33904     inline : true,
33905     
33906     weight : '',
33907     
33908     indicatorpos : 'left',
33909     
33910     getAutoCreate : function()
33911     {
33912         var label = {
33913             tag : 'label',
33914             cls : 'roo-radio-set-label',
33915             cn : [
33916                 {
33917                     tag : 'span',
33918                     html : this.fieldLabel
33919                 }
33920             ]
33921         };
33922         
33923         if(this.indicatorpos == 'left'){
33924             label.cn.unshift({
33925                 tag : 'i',
33926                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33927                 tooltip : 'This field is required'
33928             });
33929         } else {
33930             label.cn.push({
33931                 tag : 'i',
33932                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33933                 tooltip : 'This field is required'
33934             });
33935         }
33936         
33937         var items = {
33938             tag : 'div',
33939             cls : 'roo-radio-set-items'
33940         };
33941         
33942         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33943         
33944         if (align === 'left' && this.fieldLabel.length) {
33945             
33946             items = {
33947                 cls : "roo-radio-set-right", 
33948                 cn: [
33949                     items
33950                 ]
33951             };
33952             
33953             if(this.labelWidth > 12){
33954                 label.style = "width: " + this.labelWidth + 'px';
33955             }
33956             
33957             if(this.labelWidth < 13 && this.labelmd == 0){
33958                 this.labelmd = this.labelWidth;
33959             }
33960             
33961             if(this.labellg > 0){
33962                 label.cls += ' col-lg-' + this.labellg;
33963                 items.cls += ' col-lg-' + (12 - this.labellg);
33964             }
33965             
33966             if(this.labelmd > 0){
33967                 label.cls += ' col-md-' + this.labelmd;
33968                 items.cls += ' col-md-' + (12 - this.labelmd);
33969             }
33970             
33971             if(this.labelsm > 0){
33972                 label.cls += ' col-sm-' + this.labelsm;
33973                 items.cls += ' col-sm-' + (12 - this.labelsm);
33974             }
33975             
33976             if(this.labelxs > 0){
33977                 label.cls += ' col-xs-' + this.labelxs;
33978                 items.cls += ' col-xs-' + (12 - this.labelxs);
33979             }
33980         }
33981         
33982         var cfg = {
33983             tag : 'div',
33984             cls : 'roo-radio-set',
33985             cn : [
33986                 {
33987                     tag : 'input',
33988                     cls : 'roo-radio-set-input',
33989                     type : 'hidden',
33990                     name : this.name,
33991                     value : this.value ? this.value :  ''
33992                 },
33993                 label,
33994                 items
33995             ]
33996         };
33997         
33998         if(this.weight.length){
33999             cfg.cls += ' roo-radio-' + this.weight;
34000         }
34001         
34002         if(this.inline) {
34003             cfg.cls += ' roo-radio-set-inline';
34004         }
34005         
34006         var settings=this;
34007         ['xs','sm','md','lg'].map(function(size){
34008             if (settings[size]) {
34009                 cfg.cls += ' col-' + size + '-' + settings[size];
34010             }
34011         });
34012         
34013         return cfg;
34014         
34015     },
34016
34017     initEvents : function()
34018     {
34019         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34020         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34021         
34022         if(!this.fieldLabel.length){
34023             this.labelEl.hide();
34024         }
34025         
34026         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34027         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34028         
34029         this.indicator = this.indicatorEl();
34030         
34031         if(this.indicator){
34032             this.indicator.addClass('invisible');
34033         }
34034         
34035         this.originalValue = this.getValue();
34036         
34037     },
34038     
34039     inputEl: function ()
34040     {
34041         return this.el.select('.roo-radio-set-input', true).first();
34042     },
34043     
34044     getChildContainer : function()
34045     {
34046         return this.itemsEl;
34047     },
34048     
34049     register : function(item)
34050     {
34051         this.radioes.push(item);
34052         
34053     },
34054     
34055     validate : function()
34056     {   
34057         if(this.getVisibilityEl().hasClass('hidden')){
34058             return true;
34059         }
34060         
34061         var valid = false;
34062         
34063         Roo.each(this.radioes, function(i){
34064             if(!i.checked){
34065                 return;
34066             }
34067             
34068             valid = true;
34069             return false;
34070         });
34071         
34072         if(this.allowBlank) {
34073             return true;
34074         }
34075         
34076         if(this.disabled || valid){
34077             this.markValid();
34078             return true;
34079         }
34080         
34081         this.markInvalid();
34082         return false;
34083         
34084     },
34085     
34086     markValid : function()
34087     {
34088         if(this.labelEl.isVisible(true)){
34089             this.indicatorEl().removeClass('visible');
34090             this.indicatorEl().addClass('invisible');
34091         }
34092         
34093         this.el.removeClass([this.invalidClass, this.validClass]);
34094         this.el.addClass(this.validClass);
34095         
34096         this.fireEvent('valid', this);
34097     },
34098     
34099     markInvalid : function(msg)
34100     {
34101         if(this.allowBlank || this.disabled){
34102             return;
34103         }
34104         
34105         if(this.labelEl.isVisible(true)){
34106             this.indicatorEl().removeClass('invisible');
34107             this.indicatorEl().addClass('visible');
34108         }
34109         
34110         this.el.removeClass([this.invalidClass, this.validClass]);
34111         this.el.addClass(this.invalidClass);
34112         
34113         this.fireEvent('invalid', this, msg);
34114         
34115     },
34116     
34117     setValue : function(v, suppressEvent)
34118     {   
34119         if(this.value === v){
34120             return;
34121         }
34122         
34123         this.value = v;
34124         
34125         if(this.rendered){
34126             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34127         }
34128         
34129         Roo.each(this.radioes, function(i){
34130             i.checked = false;
34131             i.el.removeClass('checked');
34132         });
34133         
34134         Roo.each(this.radioes, function(i){
34135             
34136             if(i.value === v || i.value.toString() === v.toString()){
34137                 i.checked = true;
34138                 i.el.addClass('checked');
34139                 
34140                 if(suppressEvent !== true){
34141                     this.fireEvent('check', this, i);
34142                 }
34143                 
34144                 return false;
34145             }
34146             
34147         }, this);
34148         
34149         this.validate();
34150     },
34151     
34152     clearInvalid : function(){
34153         
34154         if(!this.el || this.preventMark){
34155             return;
34156         }
34157         
34158         this.el.removeClass([this.invalidClass]);
34159         
34160         this.fireEvent('valid', this);
34161     }
34162     
34163 });
34164
34165 Roo.apply(Roo.bootstrap.RadioSet, {
34166     
34167     groups: {},
34168     
34169     register : function(set)
34170     {
34171         this.groups[set.name] = set;
34172     },
34173     
34174     get: function(name) 
34175     {
34176         if (typeof(this.groups[name]) == 'undefined') {
34177             return false;
34178         }
34179         
34180         return this.groups[name] ;
34181     }
34182     
34183 });
34184 /*
34185  * Based on:
34186  * Ext JS Library 1.1.1
34187  * Copyright(c) 2006-2007, Ext JS, LLC.
34188  *
34189  * Originally Released Under LGPL - original licence link has changed is not relivant.
34190  *
34191  * Fork - LGPL
34192  * <script type="text/javascript">
34193  */
34194
34195
34196 /**
34197  * @class Roo.bootstrap.SplitBar
34198  * @extends Roo.util.Observable
34199  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34200  * <br><br>
34201  * Usage:
34202  * <pre><code>
34203 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34204                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34205 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34206 split.minSize = 100;
34207 split.maxSize = 600;
34208 split.animate = true;
34209 split.on('moved', splitterMoved);
34210 </code></pre>
34211  * @constructor
34212  * Create a new SplitBar
34213  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34214  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34215  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34216  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34217                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34218                         position of the SplitBar).
34219  */
34220 Roo.bootstrap.SplitBar = function(cfg){
34221     
34222     /** @private */
34223     
34224     //{
34225     //  dragElement : elm
34226     //  resizingElement: el,
34227         // optional..
34228     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34229     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34230         // existingProxy ???
34231     //}
34232     
34233     this.el = Roo.get(cfg.dragElement, true);
34234     this.el.dom.unselectable = "on";
34235     /** @private */
34236     this.resizingEl = Roo.get(cfg.resizingElement, true);
34237
34238     /**
34239      * @private
34240      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34241      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34242      * @type Number
34243      */
34244     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34245     
34246     /**
34247      * The minimum size of the resizing element. (Defaults to 0)
34248      * @type Number
34249      */
34250     this.minSize = 0;
34251     
34252     /**
34253      * The maximum size of the resizing element. (Defaults to 2000)
34254      * @type Number
34255      */
34256     this.maxSize = 2000;
34257     
34258     /**
34259      * Whether to animate the transition to the new size
34260      * @type Boolean
34261      */
34262     this.animate = false;
34263     
34264     /**
34265      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34266      * @type Boolean
34267      */
34268     this.useShim = false;
34269     
34270     /** @private */
34271     this.shim = null;
34272     
34273     if(!cfg.existingProxy){
34274         /** @private */
34275         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34276     }else{
34277         this.proxy = Roo.get(cfg.existingProxy).dom;
34278     }
34279     /** @private */
34280     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34281     
34282     /** @private */
34283     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34284     
34285     /** @private */
34286     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34287     
34288     /** @private */
34289     this.dragSpecs = {};
34290     
34291     /**
34292      * @private The adapter to use to positon and resize elements
34293      */
34294     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34295     this.adapter.init(this);
34296     
34297     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34298         /** @private */
34299         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34300         this.el.addClass("roo-splitbar-h");
34301     }else{
34302         /** @private */
34303         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34304         this.el.addClass("roo-splitbar-v");
34305     }
34306     
34307     this.addEvents({
34308         /**
34309          * @event resize
34310          * Fires when the splitter is moved (alias for {@link #event-moved})
34311          * @param {Roo.bootstrap.SplitBar} this
34312          * @param {Number} newSize the new width or height
34313          */
34314         "resize" : true,
34315         /**
34316          * @event moved
34317          * Fires when the splitter is moved
34318          * @param {Roo.bootstrap.SplitBar} this
34319          * @param {Number} newSize the new width or height
34320          */
34321         "moved" : true,
34322         /**
34323          * @event beforeresize
34324          * Fires before the splitter is dragged
34325          * @param {Roo.bootstrap.SplitBar} this
34326          */
34327         "beforeresize" : true,
34328
34329         "beforeapply" : true
34330     });
34331
34332     Roo.util.Observable.call(this);
34333 };
34334
34335 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34336     onStartProxyDrag : function(x, y){
34337         this.fireEvent("beforeresize", this);
34338         if(!this.overlay){
34339             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34340             o.unselectable();
34341             o.enableDisplayMode("block");
34342             // all splitbars share the same overlay
34343             Roo.bootstrap.SplitBar.prototype.overlay = o;
34344         }
34345         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34346         this.overlay.show();
34347         Roo.get(this.proxy).setDisplayed("block");
34348         var size = this.adapter.getElementSize(this);
34349         this.activeMinSize = this.getMinimumSize();;
34350         this.activeMaxSize = this.getMaximumSize();;
34351         var c1 = size - this.activeMinSize;
34352         var c2 = Math.max(this.activeMaxSize - size, 0);
34353         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34354             this.dd.resetConstraints();
34355             this.dd.setXConstraint(
34356                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34357                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34358             );
34359             this.dd.setYConstraint(0, 0);
34360         }else{
34361             this.dd.resetConstraints();
34362             this.dd.setXConstraint(0, 0);
34363             this.dd.setYConstraint(
34364                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34365                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34366             );
34367          }
34368         this.dragSpecs.startSize = size;
34369         this.dragSpecs.startPoint = [x, y];
34370         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34371     },
34372     
34373     /** 
34374      * @private Called after the drag operation by the DDProxy
34375      */
34376     onEndProxyDrag : function(e){
34377         Roo.get(this.proxy).setDisplayed(false);
34378         var endPoint = Roo.lib.Event.getXY(e);
34379         if(this.overlay){
34380             this.overlay.hide();
34381         }
34382         var newSize;
34383         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34384             newSize = this.dragSpecs.startSize + 
34385                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34386                     endPoint[0] - this.dragSpecs.startPoint[0] :
34387                     this.dragSpecs.startPoint[0] - endPoint[0]
34388                 );
34389         }else{
34390             newSize = this.dragSpecs.startSize + 
34391                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34392                     endPoint[1] - this.dragSpecs.startPoint[1] :
34393                     this.dragSpecs.startPoint[1] - endPoint[1]
34394                 );
34395         }
34396         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34397         if(newSize != this.dragSpecs.startSize){
34398             if(this.fireEvent('beforeapply', this, newSize) !== false){
34399                 this.adapter.setElementSize(this, newSize);
34400                 this.fireEvent("moved", this, newSize);
34401                 this.fireEvent("resize", this, newSize);
34402             }
34403         }
34404     },
34405     
34406     /**
34407      * Get the adapter this SplitBar uses
34408      * @return The adapter object
34409      */
34410     getAdapter : function(){
34411         return this.adapter;
34412     },
34413     
34414     /**
34415      * Set the adapter this SplitBar uses
34416      * @param {Object} adapter A SplitBar adapter object
34417      */
34418     setAdapter : function(adapter){
34419         this.adapter = adapter;
34420         this.adapter.init(this);
34421     },
34422     
34423     /**
34424      * Gets the minimum size for the resizing element
34425      * @return {Number} The minimum size
34426      */
34427     getMinimumSize : function(){
34428         return this.minSize;
34429     },
34430     
34431     /**
34432      * Sets the minimum size for the resizing element
34433      * @param {Number} minSize The minimum size
34434      */
34435     setMinimumSize : function(minSize){
34436         this.minSize = minSize;
34437     },
34438     
34439     /**
34440      * Gets the maximum size for the resizing element
34441      * @return {Number} The maximum size
34442      */
34443     getMaximumSize : function(){
34444         return this.maxSize;
34445     },
34446     
34447     /**
34448      * Sets the maximum size for the resizing element
34449      * @param {Number} maxSize The maximum size
34450      */
34451     setMaximumSize : function(maxSize){
34452         this.maxSize = maxSize;
34453     },
34454     
34455     /**
34456      * Sets the initialize size for the resizing element
34457      * @param {Number} size The initial size
34458      */
34459     setCurrentSize : function(size){
34460         var oldAnimate = this.animate;
34461         this.animate = false;
34462         this.adapter.setElementSize(this, size);
34463         this.animate = oldAnimate;
34464     },
34465     
34466     /**
34467      * Destroy this splitbar. 
34468      * @param {Boolean} removeEl True to remove the element
34469      */
34470     destroy : function(removeEl){
34471         if(this.shim){
34472             this.shim.remove();
34473         }
34474         this.dd.unreg();
34475         this.proxy.parentNode.removeChild(this.proxy);
34476         if(removeEl){
34477             this.el.remove();
34478         }
34479     }
34480 });
34481
34482 /**
34483  * @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.
34484  */
34485 Roo.bootstrap.SplitBar.createProxy = function(dir){
34486     var proxy = new Roo.Element(document.createElement("div"));
34487     proxy.unselectable();
34488     var cls = 'roo-splitbar-proxy';
34489     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34490     document.body.appendChild(proxy.dom);
34491     return proxy.dom;
34492 };
34493
34494 /** 
34495  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34496  * Default Adapter. It assumes the splitter and resizing element are not positioned
34497  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34498  */
34499 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34500 };
34501
34502 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34503     // do nothing for now
34504     init : function(s){
34505     
34506     },
34507     /**
34508      * Called before drag operations to get the current size of the resizing element. 
34509      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34510      */
34511      getElementSize : function(s){
34512         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34513             return s.resizingEl.getWidth();
34514         }else{
34515             return s.resizingEl.getHeight();
34516         }
34517     },
34518     
34519     /**
34520      * Called after drag operations to set the size of the resizing element.
34521      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34522      * @param {Number} newSize The new size to set
34523      * @param {Function} onComplete A function to be invoked when resizing is complete
34524      */
34525     setElementSize : function(s, newSize, onComplete){
34526         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34527             if(!s.animate){
34528                 s.resizingEl.setWidth(newSize);
34529                 if(onComplete){
34530                     onComplete(s, newSize);
34531                 }
34532             }else{
34533                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34534             }
34535         }else{
34536             
34537             if(!s.animate){
34538                 s.resizingEl.setHeight(newSize);
34539                 if(onComplete){
34540                     onComplete(s, newSize);
34541                 }
34542             }else{
34543                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34544             }
34545         }
34546     }
34547 };
34548
34549 /** 
34550  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34551  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34552  * Adapter that  moves the splitter element to align with the resized sizing element. 
34553  * Used with an absolute positioned SplitBar.
34554  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34555  * document.body, make sure you assign an id to the body element.
34556  */
34557 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34558     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34559     this.container = Roo.get(container);
34560 };
34561
34562 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34563     init : function(s){
34564         this.basic.init(s);
34565     },
34566     
34567     getElementSize : function(s){
34568         return this.basic.getElementSize(s);
34569     },
34570     
34571     setElementSize : function(s, newSize, onComplete){
34572         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34573     },
34574     
34575     moveSplitter : function(s){
34576         var yes = Roo.bootstrap.SplitBar;
34577         switch(s.placement){
34578             case yes.LEFT:
34579                 s.el.setX(s.resizingEl.getRight());
34580                 break;
34581             case yes.RIGHT:
34582                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34583                 break;
34584             case yes.TOP:
34585                 s.el.setY(s.resizingEl.getBottom());
34586                 break;
34587             case yes.BOTTOM:
34588                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34589                 break;
34590         }
34591     }
34592 };
34593
34594 /**
34595  * Orientation constant - Create a vertical SplitBar
34596  * @static
34597  * @type Number
34598  */
34599 Roo.bootstrap.SplitBar.VERTICAL = 1;
34600
34601 /**
34602  * Orientation constant - Create a horizontal SplitBar
34603  * @static
34604  * @type Number
34605  */
34606 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34607
34608 /**
34609  * Placement constant - The resizing element is to the left of the splitter element
34610  * @static
34611  * @type Number
34612  */
34613 Roo.bootstrap.SplitBar.LEFT = 1;
34614
34615 /**
34616  * Placement constant - The resizing element is to the right of the splitter element
34617  * @static
34618  * @type Number
34619  */
34620 Roo.bootstrap.SplitBar.RIGHT = 2;
34621
34622 /**
34623  * Placement constant - The resizing element is positioned above the splitter element
34624  * @static
34625  * @type Number
34626  */
34627 Roo.bootstrap.SplitBar.TOP = 3;
34628
34629 /**
34630  * Placement constant - The resizing element is positioned under splitter element
34631  * @static
34632  * @type Number
34633  */
34634 Roo.bootstrap.SplitBar.BOTTOM = 4;
34635 Roo.namespace("Roo.bootstrap.layout");/*
34636  * Based on:
34637  * Ext JS Library 1.1.1
34638  * Copyright(c) 2006-2007, Ext JS, LLC.
34639  *
34640  * Originally Released Under LGPL - original licence link has changed is not relivant.
34641  *
34642  * Fork - LGPL
34643  * <script type="text/javascript">
34644  */
34645
34646 /**
34647  * @class Roo.bootstrap.layout.Manager
34648  * @extends Roo.bootstrap.Component
34649  * Base class for layout managers.
34650  */
34651 Roo.bootstrap.layout.Manager = function(config)
34652 {
34653     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34654
34655
34656
34657
34658
34659     /** false to disable window resize monitoring @type Boolean */
34660     this.monitorWindowResize = true;
34661     this.regions = {};
34662     this.addEvents({
34663         /**
34664          * @event layout
34665          * Fires when a layout is performed.
34666          * @param {Roo.LayoutManager} this
34667          */
34668         "layout" : true,
34669         /**
34670          * @event regionresized
34671          * Fires when the user resizes a region.
34672          * @param {Roo.LayoutRegion} region The resized region
34673          * @param {Number} newSize The new size (width for east/west, height for north/south)
34674          */
34675         "regionresized" : true,
34676         /**
34677          * @event regioncollapsed
34678          * Fires when a region is collapsed.
34679          * @param {Roo.LayoutRegion} region The collapsed region
34680          */
34681         "regioncollapsed" : true,
34682         /**
34683          * @event regionexpanded
34684          * Fires when a region is expanded.
34685          * @param {Roo.LayoutRegion} region The expanded region
34686          */
34687         "regionexpanded" : true
34688     });
34689     this.updating = false;
34690
34691     if (config.el) {
34692         this.el = Roo.get(config.el);
34693         this.initEvents();
34694     }
34695
34696 };
34697
34698 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34699
34700
34701     regions : null,
34702
34703     monitorWindowResize : true,
34704
34705
34706     updating : false,
34707
34708
34709     onRender : function(ct, position)
34710     {
34711         if(!this.el){
34712             this.el = Roo.get(ct);
34713             this.initEvents();
34714         }
34715         //this.fireEvent('render',this);
34716     },
34717
34718
34719     initEvents: function()
34720     {
34721
34722
34723         // ie scrollbar fix
34724         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34725             document.body.scroll = "no";
34726         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34727             this.el.position('relative');
34728         }
34729         this.id = this.el.id;
34730         this.el.addClass("roo-layout-container");
34731         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34732         if(this.el.dom != document.body ) {
34733             this.el.on('resize', this.layout,this);
34734             this.el.on('show', this.layout,this);
34735         }
34736
34737     },
34738
34739     /**
34740      * Returns true if this layout is currently being updated
34741      * @return {Boolean}
34742      */
34743     isUpdating : function(){
34744         return this.updating;
34745     },
34746
34747     /**
34748      * Suspend the LayoutManager from doing auto-layouts while
34749      * making multiple add or remove calls
34750      */
34751     beginUpdate : function(){
34752         this.updating = true;
34753     },
34754
34755     /**
34756      * Restore auto-layouts and optionally disable the manager from performing a layout
34757      * @param {Boolean} noLayout true to disable a layout update
34758      */
34759     endUpdate : function(noLayout){
34760         this.updating = false;
34761         if(!noLayout){
34762             this.layout();
34763         }
34764     },
34765
34766     layout: function(){
34767         // abstract...
34768     },
34769
34770     onRegionResized : function(region, newSize){
34771         this.fireEvent("regionresized", region, newSize);
34772         this.layout();
34773     },
34774
34775     onRegionCollapsed : function(region){
34776         this.fireEvent("regioncollapsed", region);
34777     },
34778
34779     onRegionExpanded : function(region){
34780         this.fireEvent("regionexpanded", region);
34781     },
34782
34783     /**
34784      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34785      * performs box-model adjustments.
34786      * @return {Object} The size as an object {width: (the width), height: (the height)}
34787      */
34788     getViewSize : function()
34789     {
34790         var size;
34791         if(this.el.dom != document.body){
34792             size = this.el.getSize();
34793         }else{
34794             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34795         }
34796         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34797         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34798         return size;
34799     },
34800
34801     /**
34802      * Returns the Element this layout is bound to.
34803      * @return {Roo.Element}
34804      */
34805     getEl : function(){
34806         return this.el;
34807     },
34808
34809     /**
34810      * Returns the specified region.
34811      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34812      * @return {Roo.LayoutRegion}
34813      */
34814     getRegion : function(target){
34815         return this.regions[target.toLowerCase()];
34816     },
34817
34818     onWindowResize : function(){
34819         if(this.monitorWindowResize){
34820             this.layout();
34821         }
34822     }
34823 });
34824 /*
34825  * Based on:
34826  * Ext JS Library 1.1.1
34827  * Copyright(c) 2006-2007, Ext JS, LLC.
34828  *
34829  * Originally Released Under LGPL - original licence link has changed is not relivant.
34830  *
34831  * Fork - LGPL
34832  * <script type="text/javascript">
34833  */
34834 /**
34835  * @class Roo.bootstrap.layout.Border
34836  * @extends Roo.bootstrap.layout.Manager
34837  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34838  * please see: examples/bootstrap/nested.html<br><br>
34839  
34840 <b>The container the layout is rendered into can be either the body element or any other element.
34841 If it is not the body element, the container needs to either be an absolute positioned element,
34842 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34843 the container size if it is not the body element.</b>
34844
34845 * @constructor
34846 * Create a new Border
34847 * @param {Object} config Configuration options
34848  */
34849 Roo.bootstrap.layout.Border = function(config){
34850     config = config || {};
34851     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34852     
34853     
34854     
34855     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34856         if(config[region]){
34857             config[region].region = region;
34858             this.addRegion(config[region]);
34859         }
34860     },this);
34861     
34862 };
34863
34864 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34865
34866 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34867     /**
34868      * Creates and adds a new region if it doesn't already exist.
34869      * @param {String} target The target region key (north, south, east, west or center).
34870      * @param {Object} config The regions config object
34871      * @return {BorderLayoutRegion} The new region
34872      */
34873     addRegion : function(config)
34874     {
34875         if(!this.regions[config.region]){
34876             var r = this.factory(config);
34877             this.bindRegion(r);
34878         }
34879         return this.regions[config.region];
34880     },
34881
34882     // private (kinda)
34883     bindRegion : function(r){
34884         this.regions[r.config.region] = r;
34885         
34886         r.on("visibilitychange",    this.layout, this);
34887         r.on("paneladded",          this.layout, this);
34888         r.on("panelremoved",        this.layout, this);
34889         r.on("invalidated",         this.layout, this);
34890         r.on("resized",             this.onRegionResized, this);
34891         r.on("collapsed",           this.onRegionCollapsed, this);
34892         r.on("expanded",            this.onRegionExpanded, this);
34893     },
34894
34895     /**
34896      * Performs a layout update.
34897      */
34898     layout : function()
34899     {
34900         if(this.updating) {
34901             return;
34902         }
34903         
34904         // render all the rebions if they have not been done alreayd?
34905         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34906             if(this.regions[region] && !this.regions[region].bodyEl){
34907                 this.regions[region].onRender(this.el)
34908             }
34909         },this);
34910         
34911         var size = this.getViewSize();
34912         var w = size.width;
34913         var h = size.height;
34914         var centerW = w;
34915         var centerH = h;
34916         var centerY = 0;
34917         var centerX = 0;
34918         //var x = 0, y = 0;
34919
34920         var rs = this.regions;
34921         var north = rs["north"];
34922         var south = rs["south"]; 
34923         var west = rs["west"];
34924         var east = rs["east"];
34925         var center = rs["center"];
34926         //if(this.hideOnLayout){ // not supported anymore
34927             //c.el.setStyle("display", "none");
34928         //}
34929         if(north && north.isVisible()){
34930             var b = north.getBox();
34931             var m = north.getMargins();
34932             b.width = w - (m.left+m.right);
34933             b.x = m.left;
34934             b.y = m.top;
34935             centerY = b.height + b.y + m.bottom;
34936             centerH -= centerY;
34937             north.updateBox(this.safeBox(b));
34938         }
34939         if(south && south.isVisible()){
34940             var b = south.getBox();
34941             var m = south.getMargins();
34942             b.width = w - (m.left+m.right);
34943             b.x = m.left;
34944             var totalHeight = (b.height + m.top + m.bottom);
34945             b.y = h - totalHeight + m.top;
34946             centerH -= totalHeight;
34947             south.updateBox(this.safeBox(b));
34948         }
34949         if(west && west.isVisible()){
34950             var b = west.getBox();
34951             var m = west.getMargins();
34952             b.height = centerH - (m.top+m.bottom);
34953             b.x = m.left;
34954             b.y = centerY + m.top;
34955             var totalWidth = (b.width + m.left + m.right);
34956             centerX += totalWidth;
34957             centerW -= totalWidth;
34958             west.updateBox(this.safeBox(b));
34959         }
34960         if(east && east.isVisible()){
34961             var b = east.getBox();
34962             var m = east.getMargins();
34963             b.height = centerH - (m.top+m.bottom);
34964             var totalWidth = (b.width + m.left + m.right);
34965             b.x = w - totalWidth + m.left;
34966             b.y = centerY + m.top;
34967             centerW -= totalWidth;
34968             east.updateBox(this.safeBox(b));
34969         }
34970         if(center){
34971             var m = center.getMargins();
34972             var centerBox = {
34973                 x: centerX + m.left,
34974                 y: centerY + m.top,
34975                 width: centerW - (m.left+m.right),
34976                 height: centerH - (m.top+m.bottom)
34977             };
34978             //if(this.hideOnLayout){
34979                 //center.el.setStyle("display", "block");
34980             //}
34981             center.updateBox(this.safeBox(centerBox));
34982         }
34983         this.el.repaint();
34984         this.fireEvent("layout", this);
34985     },
34986
34987     // private
34988     safeBox : function(box){
34989         box.width = Math.max(0, box.width);
34990         box.height = Math.max(0, box.height);
34991         return box;
34992     },
34993
34994     /**
34995      * Adds a ContentPanel (or subclass) to this layout.
34996      * @param {String} target The target region key (north, south, east, west or center).
34997      * @param {Roo.ContentPanel} panel The panel to add
34998      * @return {Roo.ContentPanel} The added panel
34999      */
35000     add : function(target, panel){
35001          
35002         target = target.toLowerCase();
35003         return this.regions[target].add(panel);
35004     },
35005
35006     /**
35007      * Remove a ContentPanel (or subclass) to this layout.
35008      * @param {String} target The target region key (north, south, east, west or center).
35009      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35010      * @return {Roo.ContentPanel} The removed panel
35011      */
35012     remove : function(target, panel){
35013         target = target.toLowerCase();
35014         return this.regions[target].remove(panel);
35015     },
35016
35017     /**
35018      * Searches all regions for a panel with the specified id
35019      * @param {String} panelId
35020      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35021      */
35022     findPanel : function(panelId){
35023         var rs = this.regions;
35024         for(var target in rs){
35025             if(typeof rs[target] != "function"){
35026                 var p = rs[target].getPanel(panelId);
35027                 if(p){
35028                     return p;
35029                 }
35030             }
35031         }
35032         return null;
35033     },
35034
35035     /**
35036      * Searches all regions for a panel with the specified id and activates (shows) it.
35037      * @param {String/ContentPanel} panelId The panels id or the panel itself
35038      * @return {Roo.ContentPanel} The shown panel or null
35039      */
35040     showPanel : function(panelId) {
35041       var rs = this.regions;
35042       for(var target in rs){
35043          var r = rs[target];
35044          if(typeof r != "function"){
35045             if(r.hasPanel(panelId)){
35046                return r.showPanel(panelId);
35047             }
35048          }
35049       }
35050       return null;
35051    },
35052
35053    /**
35054      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35055      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35056      */
35057    /*
35058     restoreState : function(provider){
35059         if(!provider){
35060             provider = Roo.state.Manager;
35061         }
35062         var sm = new Roo.LayoutStateManager();
35063         sm.init(this, provider);
35064     },
35065 */
35066  
35067  
35068     /**
35069      * Adds a xtype elements to the layout.
35070      * <pre><code>
35071
35072 layout.addxtype({
35073        xtype : 'ContentPanel',
35074        region: 'west',
35075        items: [ .... ]
35076    }
35077 );
35078
35079 layout.addxtype({
35080         xtype : 'NestedLayoutPanel',
35081         region: 'west',
35082         layout: {
35083            center: { },
35084            west: { }   
35085         },
35086         items : [ ... list of content panels or nested layout panels.. ]
35087    }
35088 );
35089 </code></pre>
35090      * @param {Object} cfg Xtype definition of item to add.
35091      */
35092     addxtype : function(cfg)
35093     {
35094         // basically accepts a pannel...
35095         // can accept a layout region..!?!?
35096         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35097         
35098         
35099         // theory?  children can only be panels??
35100         
35101         //if (!cfg.xtype.match(/Panel$/)) {
35102         //    return false;
35103         //}
35104         var ret = false;
35105         
35106         if (typeof(cfg.region) == 'undefined') {
35107             Roo.log("Failed to add Panel, region was not set");
35108             Roo.log(cfg);
35109             return false;
35110         }
35111         var region = cfg.region;
35112         delete cfg.region;
35113         
35114           
35115         var xitems = [];
35116         if (cfg.items) {
35117             xitems = cfg.items;
35118             delete cfg.items;
35119         }
35120         var nb = false;
35121         
35122         switch(cfg.xtype) 
35123         {
35124             case 'Content':  // ContentPanel (el, cfg)
35125             case 'Scroll':  // ContentPanel (el, cfg)
35126             case 'View': 
35127                 cfg.autoCreate = true;
35128                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35129                 //} else {
35130                 //    var el = this.el.createChild();
35131                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35132                 //}
35133                 
35134                 this.add(region, ret);
35135                 break;
35136             
35137             /*
35138             case 'TreePanel': // our new panel!
35139                 cfg.el = this.el.createChild();
35140                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35141                 this.add(region, ret);
35142                 break;
35143             */
35144             
35145             case 'Nest': 
35146                 // create a new Layout (which is  a Border Layout...
35147                 
35148                 var clayout = cfg.layout;
35149                 clayout.el  = this.el.createChild();
35150                 clayout.items   = clayout.items  || [];
35151                 
35152                 delete cfg.layout;
35153                 
35154                 // replace this exitems with the clayout ones..
35155                 xitems = clayout.items;
35156                  
35157                 // force background off if it's in center...
35158                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35159                     cfg.background = false;
35160                 }
35161                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35162                 
35163                 
35164                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35165                 //console.log('adding nested layout panel '  + cfg.toSource());
35166                 this.add(region, ret);
35167                 nb = {}; /// find first...
35168                 break;
35169             
35170             case 'Grid':
35171                 
35172                 // needs grid and region
35173                 
35174                 //var el = this.getRegion(region).el.createChild();
35175                 /*
35176                  *var el = this.el.createChild();
35177                 // create the grid first...
35178                 cfg.grid.container = el;
35179                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35180                 */
35181                 
35182                 if (region == 'center' && this.active ) {
35183                     cfg.background = false;
35184                 }
35185                 
35186                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35187                 
35188                 this.add(region, ret);
35189                 /*
35190                 if (cfg.background) {
35191                     // render grid on panel activation (if panel background)
35192                     ret.on('activate', function(gp) {
35193                         if (!gp.grid.rendered) {
35194                     //        gp.grid.render(el);
35195                         }
35196                     });
35197                 } else {
35198                   //  cfg.grid.render(el);
35199                 }
35200                 */
35201                 break;
35202            
35203            
35204             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35205                 // it was the old xcomponent building that caused this before.
35206                 // espeically if border is the top element in the tree.
35207                 ret = this;
35208                 break; 
35209                 
35210                     
35211                 
35212                 
35213                 
35214             default:
35215                 /*
35216                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35217                     
35218                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35219                     this.add(region, ret);
35220                 } else {
35221                 */
35222                     Roo.log(cfg);
35223                     throw "Can not add '" + cfg.xtype + "' to Border";
35224                     return null;
35225              
35226                                 
35227              
35228         }
35229         this.beginUpdate();
35230         // add children..
35231         var region = '';
35232         var abn = {};
35233         Roo.each(xitems, function(i)  {
35234             region = nb && i.region ? i.region : false;
35235             
35236             var add = ret.addxtype(i);
35237            
35238             if (region) {
35239                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35240                 if (!i.background) {
35241                     abn[region] = nb[region] ;
35242                 }
35243             }
35244             
35245         });
35246         this.endUpdate();
35247
35248         // make the last non-background panel active..
35249         //if (nb) { Roo.log(abn); }
35250         if (nb) {
35251             
35252             for(var r in abn) {
35253                 region = this.getRegion(r);
35254                 if (region) {
35255                     // tried using nb[r], but it does not work..
35256                      
35257                     region.showPanel(abn[r]);
35258                    
35259                 }
35260             }
35261         }
35262         return ret;
35263         
35264     },
35265     
35266     
35267 // private
35268     factory : function(cfg)
35269     {
35270         
35271         var validRegions = Roo.bootstrap.layout.Border.regions;
35272
35273         var target = cfg.region;
35274         cfg.mgr = this;
35275         
35276         var r = Roo.bootstrap.layout;
35277         Roo.log(target);
35278         switch(target){
35279             case "north":
35280                 return new r.North(cfg);
35281             case "south":
35282                 return new r.South(cfg);
35283             case "east":
35284                 return new r.East(cfg);
35285             case "west":
35286                 return new r.West(cfg);
35287             case "center":
35288                 return new r.Center(cfg);
35289         }
35290         throw 'Layout region "'+target+'" not supported.';
35291     }
35292     
35293     
35294 });
35295  /*
35296  * Based on:
35297  * Ext JS Library 1.1.1
35298  * Copyright(c) 2006-2007, Ext JS, LLC.
35299  *
35300  * Originally Released Under LGPL - original licence link has changed is not relivant.
35301  *
35302  * Fork - LGPL
35303  * <script type="text/javascript">
35304  */
35305  
35306 /**
35307  * @class Roo.bootstrap.layout.Basic
35308  * @extends Roo.util.Observable
35309  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35310  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35311  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35312  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35313  * @cfg {string}   region  the region that it inhabits..
35314  * @cfg {bool}   skipConfig skip config?
35315  * 
35316
35317  */
35318 Roo.bootstrap.layout.Basic = function(config){
35319     
35320     this.mgr = config.mgr;
35321     
35322     this.position = config.region;
35323     
35324     var skipConfig = config.skipConfig;
35325     
35326     this.events = {
35327         /**
35328          * @scope Roo.BasicLayoutRegion
35329          */
35330         
35331         /**
35332          * @event beforeremove
35333          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35334          * @param {Roo.LayoutRegion} this
35335          * @param {Roo.ContentPanel} panel The panel
35336          * @param {Object} e The cancel event object
35337          */
35338         "beforeremove" : true,
35339         /**
35340          * @event invalidated
35341          * Fires when the layout for this region is changed.
35342          * @param {Roo.LayoutRegion} this
35343          */
35344         "invalidated" : true,
35345         /**
35346          * @event visibilitychange
35347          * Fires when this region is shown or hidden 
35348          * @param {Roo.LayoutRegion} this
35349          * @param {Boolean} visibility true or false
35350          */
35351         "visibilitychange" : true,
35352         /**
35353          * @event paneladded
35354          * Fires when a panel is added. 
35355          * @param {Roo.LayoutRegion} this
35356          * @param {Roo.ContentPanel} panel The panel
35357          */
35358         "paneladded" : true,
35359         /**
35360          * @event panelremoved
35361          * Fires when a panel is removed. 
35362          * @param {Roo.LayoutRegion} this
35363          * @param {Roo.ContentPanel} panel The panel
35364          */
35365         "panelremoved" : true,
35366         /**
35367          * @event beforecollapse
35368          * Fires when this region before collapse.
35369          * @param {Roo.LayoutRegion} this
35370          */
35371         "beforecollapse" : true,
35372         /**
35373          * @event collapsed
35374          * Fires when this region is collapsed.
35375          * @param {Roo.LayoutRegion} this
35376          */
35377         "collapsed" : true,
35378         /**
35379          * @event expanded
35380          * Fires when this region is expanded.
35381          * @param {Roo.LayoutRegion} this
35382          */
35383         "expanded" : true,
35384         /**
35385          * @event slideshow
35386          * Fires when this region is slid into view.
35387          * @param {Roo.LayoutRegion} this
35388          */
35389         "slideshow" : true,
35390         /**
35391          * @event slidehide
35392          * Fires when this region slides out of view. 
35393          * @param {Roo.LayoutRegion} this
35394          */
35395         "slidehide" : true,
35396         /**
35397          * @event panelactivated
35398          * Fires when a panel is activated. 
35399          * @param {Roo.LayoutRegion} this
35400          * @param {Roo.ContentPanel} panel The activated panel
35401          */
35402         "panelactivated" : true,
35403         /**
35404          * @event resized
35405          * Fires when the user resizes this region. 
35406          * @param {Roo.LayoutRegion} this
35407          * @param {Number} newSize The new size (width for east/west, height for north/south)
35408          */
35409         "resized" : true
35410     };
35411     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35412     this.panels = new Roo.util.MixedCollection();
35413     this.panels.getKey = this.getPanelId.createDelegate(this);
35414     this.box = null;
35415     this.activePanel = null;
35416     // ensure listeners are added...
35417     
35418     if (config.listeners || config.events) {
35419         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35420             listeners : config.listeners || {},
35421             events : config.events || {}
35422         });
35423     }
35424     
35425     if(skipConfig !== true){
35426         this.applyConfig(config);
35427     }
35428 };
35429
35430 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35431 {
35432     getPanelId : function(p){
35433         return p.getId();
35434     },
35435     
35436     applyConfig : function(config){
35437         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35438         this.config = config;
35439         
35440     },
35441     
35442     /**
35443      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35444      * the width, for horizontal (north, south) the height.
35445      * @param {Number} newSize The new width or height
35446      */
35447     resizeTo : function(newSize){
35448         var el = this.el ? this.el :
35449                  (this.activePanel ? this.activePanel.getEl() : null);
35450         if(el){
35451             switch(this.position){
35452                 case "east":
35453                 case "west":
35454                     el.setWidth(newSize);
35455                     this.fireEvent("resized", this, newSize);
35456                 break;
35457                 case "north":
35458                 case "south":
35459                     el.setHeight(newSize);
35460                     this.fireEvent("resized", this, newSize);
35461                 break;                
35462             }
35463         }
35464     },
35465     
35466     getBox : function(){
35467         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35468     },
35469     
35470     getMargins : function(){
35471         return this.margins;
35472     },
35473     
35474     updateBox : function(box){
35475         this.box = box;
35476         var el = this.activePanel.getEl();
35477         el.dom.style.left = box.x + "px";
35478         el.dom.style.top = box.y + "px";
35479         this.activePanel.setSize(box.width, box.height);
35480     },
35481     
35482     /**
35483      * Returns the container element for this region.
35484      * @return {Roo.Element}
35485      */
35486     getEl : function(){
35487         return this.activePanel;
35488     },
35489     
35490     /**
35491      * Returns true if this region is currently visible.
35492      * @return {Boolean}
35493      */
35494     isVisible : function(){
35495         return this.activePanel ? true : false;
35496     },
35497     
35498     setActivePanel : function(panel){
35499         panel = this.getPanel(panel);
35500         if(this.activePanel && this.activePanel != panel){
35501             this.activePanel.setActiveState(false);
35502             this.activePanel.getEl().setLeftTop(-10000,-10000);
35503         }
35504         this.activePanel = panel;
35505         panel.setActiveState(true);
35506         if(this.box){
35507             panel.setSize(this.box.width, this.box.height);
35508         }
35509         this.fireEvent("panelactivated", this, panel);
35510         this.fireEvent("invalidated");
35511     },
35512     
35513     /**
35514      * Show the specified panel.
35515      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35516      * @return {Roo.ContentPanel} The shown panel or null
35517      */
35518     showPanel : function(panel){
35519         panel = this.getPanel(panel);
35520         if(panel){
35521             this.setActivePanel(panel);
35522         }
35523         return panel;
35524     },
35525     
35526     /**
35527      * Get the active panel for this region.
35528      * @return {Roo.ContentPanel} The active panel or null
35529      */
35530     getActivePanel : function(){
35531         return this.activePanel;
35532     },
35533     
35534     /**
35535      * Add the passed ContentPanel(s)
35536      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35537      * @return {Roo.ContentPanel} The panel added (if only one was added)
35538      */
35539     add : function(panel){
35540         if(arguments.length > 1){
35541             for(var i = 0, len = arguments.length; i < len; i++) {
35542                 this.add(arguments[i]);
35543             }
35544             return null;
35545         }
35546         if(this.hasPanel(panel)){
35547             this.showPanel(panel);
35548             return panel;
35549         }
35550         var el = panel.getEl();
35551         if(el.dom.parentNode != this.mgr.el.dom){
35552             this.mgr.el.dom.appendChild(el.dom);
35553         }
35554         if(panel.setRegion){
35555             panel.setRegion(this);
35556         }
35557         this.panels.add(panel);
35558         el.setStyle("position", "absolute");
35559         if(!panel.background){
35560             this.setActivePanel(panel);
35561             if(this.config.initialSize && this.panels.getCount()==1){
35562                 this.resizeTo(this.config.initialSize);
35563             }
35564         }
35565         this.fireEvent("paneladded", this, panel);
35566         return panel;
35567     },
35568     
35569     /**
35570      * Returns true if the panel is in this region.
35571      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35572      * @return {Boolean}
35573      */
35574     hasPanel : function(panel){
35575         if(typeof panel == "object"){ // must be panel obj
35576             panel = panel.getId();
35577         }
35578         return this.getPanel(panel) ? true : false;
35579     },
35580     
35581     /**
35582      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35583      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35584      * @param {Boolean} preservePanel Overrides the config preservePanel option
35585      * @return {Roo.ContentPanel} The panel that was removed
35586      */
35587     remove : function(panel, preservePanel){
35588         panel = this.getPanel(panel);
35589         if(!panel){
35590             return null;
35591         }
35592         var e = {};
35593         this.fireEvent("beforeremove", this, panel, e);
35594         if(e.cancel === true){
35595             return null;
35596         }
35597         var panelId = panel.getId();
35598         this.panels.removeKey(panelId);
35599         return panel;
35600     },
35601     
35602     /**
35603      * Returns the panel specified or null if it's not in this region.
35604      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35605      * @return {Roo.ContentPanel}
35606      */
35607     getPanel : function(id){
35608         if(typeof id == "object"){ // must be panel obj
35609             return id;
35610         }
35611         return this.panels.get(id);
35612     },
35613     
35614     /**
35615      * Returns this regions position (north/south/east/west/center).
35616      * @return {String} 
35617      */
35618     getPosition: function(){
35619         return this.position;    
35620     }
35621 });/*
35622  * Based on:
35623  * Ext JS Library 1.1.1
35624  * Copyright(c) 2006-2007, Ext JS, LLC.
35625  *
35626  * Originally Released Under LGPL - original licence link has changed is not relivant.
35627  *
35628  * Fork - LGPL
35629  * <script type="text/javascript">
35630  */
35631  
35632 /**
35633  * @class Roo.bootstrap.layout.Region
35634  * @extends Roo.bootstrap.layout.Basic
35635  * This class represents a region in a layout manager.
35636  
35637  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35638  * @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})
35639  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35640  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35641  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35642  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35643  * @cfg {String}    title           The title for the region (overrides panel titles)
35644  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35645  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35646  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35647  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35648  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35649  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35650  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35651  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35652  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35653  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35654
35655  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35656  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35657  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35658  * @cfg {Number}    width           For East/West panels
35659  * @cfg {Number}    height          For North/South panels
35660  * @cfg {Boolean}   split           To show the splitter
35661  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35662  * 
35663  * @cfg {string}   cls             Extra CSS classes to add to region
35664  * 
35665  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35666  * @cfg {string}   region  the region that it inhabits..
35667  *
35668
35669  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35670  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35671
35672  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35673  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35674  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35675  */
35676 Roo.bootstrap.layout.Region = function(config)
35677 {
35678     this.applyConfig(config);
35679
35680     var mgr = config.mgr;
35681     var pos = config.region;
35682     config.skipConfig = true;
35683     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35684     
35685     if (mgr.el) {
35686         this.onRender(mgr.el);   
35687     }
35688      
35689     this.visible = true;
35690     this.collapsed = false;
35691     this.unrendered_panels = [];
35692 };
35693
35694 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35695
35696     position: '', // set by wrapper (eg. north/south etc..)
35697     unrendered_panels : null,  // unrendered panels.
35698     createBody : function(){
35699         /** This region's body element 
35700         * @type Roo.Element */
35701         this.bodyEl = this.el.createChild({
35702                 tag: "div",
35703                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35704         });
35705     },
35706
35707     onRender: function(ctr, pos)
35708     {
35709         var dh = Roo.DomHelper;
35710         /** This region's container element 
35711         * @type Roo.Element */
35712         this.el = dh.append(ctr.dom, {
35713                 tag: "div",
35714                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35715             }, true);
35716         /** This region's title element 
35717         * @type Roo.Element */
35718     
35719         this.titleEl = dh.append(this.el.dom,
35720             {
35721                     tag: "div",
35722                     unselectable: "on",
35723                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35724                     children:[
35725                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35726                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35727                     ]}, true);
35728         
35729         this.titleEl.enableDisplayMode();
35730         /** This region's title text element 
35731         * @type HTMLElement */
35732         this.titleTextEl = this.titleEl.dom.firstChild;
35733         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35734         /*
35735         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35736         this.closeBtn.enableDisplayMode();
35737         this.closeBtn.on("click", this.closeClicked, this);
35738         this.closeBtn.hide();
35739     */
35740         this.createBody(this.config);
35741         if(this.config.hideWhenEmpty){
35742             this.hide();
35743             this.on("paneladded", this.validateVisibility, this);
35744             this.on("panelremoved", this.validateVisibility, this);
35745         }
35746         if(this.autoScroll){
35747             this.bodyEl.setStyle("overflow", "auto");
35748         }else{
35749             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35750         }
35751         //if(c.titlebar !== false){
35752             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35753                 this.titleEl.hide();
35754             }else{
35755                 this.titleEl.show();
35756                 if(this.config.title){
35757                     this.titleTextEl.innerHTML = this.config.title;
35758                 }
35759             }
35760         //}
35761         if(this.config.collapsed){
35762             this.collapse(true);
35763         }
35764         if(this.config.hidden){
35765             this.hide();
35766         }
35767         
35768         if (this.unrendered_panels && this.unrendered_panels.length) {
35769             for (var i =0;i< this.unrendered_panels.length; i++) {
35770                 this.add(this.unrendered_panels[i]);
35771             }
35772             this.unrendered_panels = null;
35773             
35774         }
35775         
35776     },
35777     
35778     applyConfig : function(c)
35779     {
35780         /*
35781          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35782             var dh = Roo.DomHelper;
35783             if(c.titlebar !== false){
35784                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35785                 this.collapseBtn.on("click", this.collapse, this);
35786                 this.collapseBtn.enableDisplayMode();
35787                 /*
35788                 if(c.showPin === true || this.showPin){
35789                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35790                     this.stickBtn.enableDisplayMode();
35791                     this.stickBtn.on("click", this.expand, this);
35792                     this.stickBtn.hide();
35793                 }
35794                 
35795             }
35796             */
35797             /** This region's collapsed element
35798             * @type Roo.Element */
35799             /*
35800              *
35801             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35802                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35803             ]}, true);
35804             
35805             if(c.floatable !== false){
35806                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35807                this.collapsedEl.on("click", this.collapseClick, this);
35808             }
35809
35810             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35811                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35812                    id: "message", unselectable: "on", style:{"float":"left"}});
35813                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35814              }
35815             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35816             this.expandBtn.on("click", this.expand, this);
35817             
35818         }
35819         
35820         if(this.collapseBtn){
35821             this.collapseBtn.setVisible(c.collapsible == true);
35822         }
35823         
35824         this.cmargins = c.cmargins || this.cmargins ||
35825                          (this.position == "west" || this.position == "east" ?
35826                              {top: 0, left: 2, right:2, bottom: 0} :
35827                              {top: 2, left: 0, right:0, bottom: 2});
35828         */
35829         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35830         
35831         
35832         this.bottomTabs = c.tabPosition != "top";
35833         
35834         this.autoScroll = c.autoScroll || false;
35835         
35836         
35837        
35838         
35839         this.duration = c.duration || .30;
35840         this.slideDuration = c.slideDuration || .45;
35841         this.config = c;
35842        
35843     },
35844     /**
35845      * Returns true if this region is currently visible.
35846      * @return {Boolean}
35847      */
35848     isVisible : function(){
35849         return this.visible;
35850     },
35851
35852     /**
35853      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35854      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35855      */
35856     //setCollapsedTitle : function(title){
35857     //    title = title || "&#160;";
35858      //   if(this.collapsedTitleTextEl){
35859       //      this.collapsedTitleTextEl.innerHTML = title;
35860        // }
35861     //},
35862
35863     getBox : function(){
35864         var b;
35865       //  if(!this.collapsed){
35866             b = this.el.getBox(false, true);
35867        // }else{
35868           //  b = this.collapsedEl.getBox(false, true);
35869         //}
35870         return b;
35871     },
35872
35873     getMargins : function(){
35874         return this.margins;
35875         //return this.collapsed ? this.cmargins : this.margins;
35876     },
35877 /*
35878     highlight : function(){
35879         this.el.addClass("x-layout-panel-dragover");
35880     },
35881
35882     unhighlight : function(){
35883         this.el.removeClass("x-layout-panel-dragover");
35884     },
35885 */
35886     updateBox : function(box)
35887     {
35888         if (!this.bodyEl) {
35889             return; // not rendered yet..
35890         }
35891         
35892         this.box = box;
35893         if(!this.collapsed){
35894             this.el.dom.style.left = box.x + "px";
35895             this.el.dom.style.top = box.y + "px";
35896             this.updateBody(box.width, box.height);
35897         }else{
35898             this.collapsedEl.dom.style.left = box.x + "px";
35899             this.collapsedEl.dom.style.top = box.y + "px";
35900             this.collapsedEl.setSize(box.width, box.height);
35901         }
35902         if(this.tabs){
35903             this.tabs.autoSizeTabs();
35904         }
35905     },
35906
35907     updateBody : function(w, h)
35908     {
35909         if(w !== null){
35910             this.el.setWidth(w);
35911             w -= this.el.getBorderWidth("rl");
35912             if(this.config.adjustments){
35913                 w += this.config.adjustments[0];
35914             }
35915         }
35916         if(h !== null && h > 0){
35917             this.el.setHeight(h);
35918             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35919             h -= this.el.getBorderWidth("tb");
35920             if(this.config.adjustments){
35921                 h += this.config.adjustments[1];
35922             }
35923             this.bodyEl.setHeight(h);
35924             if(this.tabs){
35925                 h = this.tabs.syncHeight(h);
35926             }
35927         }
35928         if(this.panelSize){
35929             w = w !== null ? w : this.panelSize.width;
35930             h = h !== null ? h : this.panelSize.height;
35931         }
35932         if(this.activePanel){
35933             var el = this.activePanel.getEl();
35934             w = w !== null ? w : el.getWidth();
35935             h = h !== null ? h : el.getHeight();
35936             this.panelSize = {width: w, height: h};
35937             this.activePanel.setSize(w, h);
35938         }
35939         if(Roo.isIE && this.tabs){
35940             this.tabs.el.repaint();
35941         }
35942     },
35943
35944     /**
35945      * Returns the container element for this region.
35946      * @return {Roo.Element}
35947      */
35948     getEl : function(){
35949         return this.el;
35950     },
35951
35952     /**
35953      * Hides this region.
35954      */
35955     hide : function(){
35956         //if(!this.collapsed){
35957             this.el.dom.style.left = "-2000px";
35958             this.el.hide();
35959         //}else{
35960          //   this.collapsedEl.dom.style.left = "-2000px";
35961          //   this.collapsedEl.hide();
35962        // }
35963         this.visible = false;
35964         this.fireEvent("visibilitychange", this, false);
35965     },
35966
35967     /**
35968      * Shows this region if it was previously hidden.
35969      */
35970     show : function(){
35971         //if(!this.collapsed){
35972             this.el.show();
35973         //}else{
35974         //    this.collapsedEl.show();
35975        // }
35976         this.visible = true;
35977         this.fireEvent("visibilitychange", this, true);
35978     },
35979 /*
35980     closeClicked : function(){
35981         if(this.activePanel){
35982             this.remove(this.activePanel);
35983         }
35984     },
35985
35986     collapseClick : function(e){
35987         if(this.isSlid){
35988            e.stopPropagation();
35989            this.slideIn();
35990         }else{
35991            e.stopPropagation();
35992            this.slideOut();
35993         }
35994     },
35995 */
35996     /**
35997      * Collapses this region.
35998      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35999      */
36000     /*
36001     collapse : function(skipAnim, skipCheck = false){
36002         if(this.collapsed) {
36003             return;
36004         }
36005         
36006         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36007             
36008             this.collapsed = true;
36009             if(this.split){
36010                 this.split.el.hide();
36011             }
36012             if(this.config.animate && skipAnim !== true){
36013                 this.fireEvent("invalidated", this);
36014                 this.animateCollapse();
36015             }else{
36016                 this.el.setLocation(-20000,-20000);
36017                 this.el.hide();
36018                 this.collapsedEl.show();
36019                 this.fireEvent("collapsed", this);
36020                 this.fireEvent("invalidated", this);
36021             }
36022         }
36023         
36024     },
36025 */
36026     animateCollapse : function(){
36027         // overridden
36028     },
36029
36030     /**
36031      * Expands this region if it was previously collapsed.
36032      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36033      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36034      */
36035     /*
36036     expand : function(e, skipAnim){
36037         if(e) {
36038             e.stopPropagation();
36039         }
36040         if(!this.collapsed || this.el.hasActiveFx()) {
36041             return;
36042         }
36043         if(this.isSlid){
36044             this.afterSlideIn();
36045             skipAnim = true;
36046         }
36047         this.collapsed = false;
36048         if(this.config.animate && skipAnim !== true){
36049             this.animateExpand();
36050         }else{
36051             this.el.show();
36052             if(this.split){
36053                 this.split.el.show();
36054             }
36055             this.collapsedEl.setLocation(-2000,-2000);
36056             this.collapsedEl.hide();
36057             this.fireEvent("invalidated", this);
36058             this.fireEvent("expanded", this);
36059         }
36060     },
36061 */
36062     animateExpand : function(){
36063         // overridden
36064     },
36065
36066     initTabs : function()
36067     {
36068         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36069         
36070         var ts = new Roo.bootstrap.panel.Tabs({
36071                 el: this.bodyEl.dom,
36072                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36073                 disableTooltips: this.config.disableTabTips,
36074                 toolbar : this.config.toolbar
36075             });
36076         
36077         if(this.config.hideTabs){
36078             ts.stripWrap.setDisplayed(false);
36079         }
36080         this.tabs = ts;
36081         ts.resizeTabs = this.config.resizeTabs === true;
36082         ts.minTabWidth = this.config.minTabWidth || 40;
36083         ts.maxTabWidth = this.config.maxTabWidth || 250;
36084         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36085         ts.monitorResize = false;
36086         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36087         ts.bodyEl.addClass('roo-layout-tabs-body');
36088         this.panels.each(this.initPanelAsTab, this);
36089     },
36090
36091     initPanelAsTab : function(panel){
36092         var ti = this.tabs.addTab(
36093             panel.getEl().id,
36094             panel.getTitle(),
36095             null,
36096             this.config.closeOnTab && panel.isClosable(),
36097             panel.tpl
36098         );
36099         if(panel.tabTip !== undefined){
36100             ti.setTooltip(panel.tabTip);
36101         }
36102         ti.on("activate", function(){
36103               this.setActivePanel(panel);
36104         }, this);
36105         
36106         if(this.config.closeOnTab){
36107             ti.on("beforeclose", function(t, e){
36108                 e.cancel = true;
36109                 this.remove(panel);
36110             }, this);
36111         }
36112         
36113         panel.tabItem = ti;
36114         
36115         return ti;
36116     },
36117
36118     updatePanelTitle : function(panel, title)
36119     {
36120         if(this.activePanel == panel){
36121             this.updateTitle(title);
36122         }
36123         if(this.tabs){
36124             var ti = this.tabs.getTab(panel.getEl().id);
36125             ti.setText(title);
36126             if(panel.tabTip !== undefined){
36127                 ti.setTooltip(panel.tabTip);
36128             }
36129         }
36130     },
36131
36132     updateTitle : function(title){
36133         if(this.titleTextEl && !this.config.title){
36134             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36135         }
36136     },
36137
36138     setActivePanel : function(panel)
36139     {
36140         panel = this.getPanel(panel);
36141         if(this.activePanel && this.activePanel != panel){
36142             if(this.activePanel.setActiveState(false) === false){
36143                 return;
36144             }
36145         }
36146         this.activePanel = panel;
36147         panel.setActiveState(true);
36148         if(this.panelSize){
36149             panel.setSize(this.panelSize.width, this.panelSize.height);
36150         }
36151         if(this.closeBtn){
36152             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36153         }
36154         this.updateTitle(panel.getTitle());
36155         if(this.tabs){
36156             this.fireEvent("invalidated", this);
36157         }
36158         this.fireEvent("panelactivated", this, panel);
36159     },
36160
36161     /**
36162      * Shows the specified panel.
36163      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36164      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36165      */
36166     showPanel : function(panel)
36167     {
36168         panel = this.getPanel(panel);
36169         if(panel){
36170             if(this.tabs){
36171                 var tab = this.tabs.getTab(panel.getEl().id);
36172                 if(tab.isHidden()){
36173                     this.tabs.unhideTab(tab.id);
36174                 }
36175                 tab.activate();
36176             }else{
36177                 this.setActivePanel(panel);
36178             }
36179         }
36180         return panel;
36181     },
36182
36183     /**
36184      * Get the active panel for this region.
36185      * @return {Roo.ContentPanel} The active panel or null
36186      */
36187     getActivePanel : function(){
36188         return this.activePanel;
36189     },
36190
36191     validateVisibility : function(){
36192         if(this.panels.getCount() < 1){
36193             this.updateTitle("&#160;");
36194             this.closeBtn.hide();
36195             this.hide();
36196         }else{
36197             if(!this.isVisible()){
36198                 this.show();
36199             }
36200         }
36201     },
36202
36203     /**
36204      * Adds the passed ContentPanel(s) to this region.
36205      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36206      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36207      */
36208     add : function(panel)
36209     {
36210         if(arguments.length > 1){
36211             for(var i = 0, len = arguments.length; i < len; i++) {
36212                 this.add(arguments[i]);
36213             }
36214             return null;
36215         }
36216         
36217         // if we have not been rendered yet, then we can not really do much of this..
36218         if (!this.bodyEl) {
36219             this.unrendered_panels.push(panel);
36220             return panel;
36221         }
36222         
36223         
36224         
36225         
36226         if(this.hasPanel(panel)){
36227             this.showPanel(panel);
36228             return panel;
36229         }
36230         panel.setRegion(this);
36231         this.panels.add(panel);
36232        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36233             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36234             // and hide them... ???
36235             this.bodyEl.dom.appendChild(panel.getEl().dom);
36236             if(panel.background !== true){
36237                 this.setActivePanel(panel);
36238             }
36239             this.fireEvent("paneladded", this, panel);
36240             return panel;
36241         }
36242         */
36243         if(!this.tabs){
36244             this.initTabs();
36245         }else{
36246             this.initPanelAsTab(panel);
36247         }
36248         
36249         
36250         if(panel.background !== true){
36251             this.tabs.activate(panel.getEl().id);
36252         }
36253         this.fireEvent("paneladded", this, panel);
36254         return panel;
36255     },
36256
36257     /**
36258      * Hides the tab for the specified panel.
36259      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36260      */
36261     hidePanel : function(panel){
36262         if(this.tabs && (panel = this.getPanel(panel))){
36263             this.tabs.hideTab(panel.getEl().id);
36264         }
36265     },
36266
36267     /**
36268      * Unhides the tab for a previously hidden panel.
36269      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36270      */
36271     unhidePanel : function(panel){
36272         if(this.tabs && (panel = this.getPanel(panel))){
36273             this.tabs.unhideTab(panel.getEl().id);
36274         }
36275     },
36276
36277     clearPanels : function(){
36278         while(this.panels.getCount() > 0){
36279              this.remove(this.panels.first());
36280         }
36281     },
36282
36283     /**
36284      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36285      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36286      * @param {Boolean} preservePanel Overrides the config preservePanel option
36287      * @return {Roo.ContentPanel} The panel that was removed
36288      */
36289     remove : function(panel, preservePanel)
36290     {
36291         panel = this.getPanel(panel);
36292         if(!panel){
36293             return null;
36294         }
36295         var e = {};
36296         this.fireEvent("beforeremove", this, panel, e);
36297         if(e.cancel === true){
36298             return null;
36299         }
36300         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36301         var panelId = panel.getId();
36302         this.panels.removeKey(panelId);
36303         if(preservePanel){
36304             document.body.appendChild(panel.getEl().dom);
36305         }
36306         if(this.tabs){
36307             this.tabs.removeTab(panel.getEl().id);
36308         }else if (!preservePanel){
36309             this.bodyEl.dom.removeChild(panel.getEl().dom);
36310         }
36311         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36312             var p = this.panels.first();
36313             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36314             tempEl.appendChild(p.getEl().dom);
36315             this.bodyEl.update("");
36316             this.bodyEl.dom.appendChild(p.getEl().dom);
36317             tempEl = null;
36318             this.updateTitle(p.getTitle());
36319             this.tabs = null;
36320             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36321             this.setActivePanel(p);
36322         }
36323         panel.setRegion(null);
36324         if(this.activePanel == panel){
36325             this.activePanel = null;
36326         }
36327         if(this.config.autoDestroy !== false && preservePanel !== true){
36328             try{panel.destroy();}catch(e){}
36329         }
36330         this.fireEvent("panelremoved", this, panel);
36331         return panel;
36332     },
36333
36334     /**
36335      * Returns the TabPanel component used by this region
36336      * @return {Roo.TabPanel}
36337      */
36338     getTabs : function(){
36339         return this.tabs;
36340     },
36341
36342     createTool : function(parentEl, className){
36343         var btn = Roo.DomHelper.append(parentEl, {
36344             tag: "div",
36345             cls: "x-layout-tools-button",
36346             children: [ {
36347                 tag: "div",
36348                 cls: "roo-layout-tools-button-inner " + className,
36349                 html: "&#160;"
36350             }]
36351         }, true);
36352         btn.addClassOnOver("roo-layout-tools-button-over");
36353         return btn;
36354     }
36355 });/*
36356  * Based on:
36357  * Ext JS Library 1.1.1
36358  * Copyright(c) 2006-2007, Ext JS, LLC.
36359  *
36360  * Originally Released Under LGPL - original licence link has changed is not relivant.
36361  *
36362  * Fork - LGPL
36363  * <script type="text/javascript">
36364  */
36365  
36366
36367
36368 /**
36369  * @class Roo.SplitLayoutRegion
36370  * @extends Roo.LayoutRegion
36371  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36372  */
36373 Roo.bootstrap.layout.Split = function(config){
36374     this.cursor = config.cursor;
36375     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36376 };
36377
36378 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36379 {
36380     splitTip : "Drag to resize.",
36381     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36382     useSplitTips : false,
36383
36384     applyConfig : function(config){
36385         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36386     },
36387     
36388     onRender : function(ctr,pos) {
36389         
36390         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36391         if(!this.config.split){
36392             return;
36393         }
36394         if(!this.split){
36395             
36396             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36397                             tag: "div",
36398                             id: this.el.id + "-split",
36399                             cls: "roo-layout-split roo-layout-split-"+this.position,
36400                             html: "&#160;"
36401             });
36402             /** The SplitBar for this region 
36403             * @type Roo.SplitBar */
36404             // does not exist yet...
36405             Roo.log([this.position, this.orientation]);
36406             
36407             this.split = new Roo.bootstrap.SplitBar({
36408                 dragElement : splitEl,
36409                 resizingElement: this.el,
36410                 orientation : this.orientation
36411             });
36412             
36413             this.split.on("moved", this.onSplitMove, this);
36414             this.split.useShim = this.config.useShim === true;
36415             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36416             if(this.useSplitTips){
36417                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36418             }
36419             //if(config.collapsible){
36420             //    this.split.el.on("dblclick", this.collapse,  this);
36421             //}
36422         }
36423         if(typeof this.config.minSize != "undefined"){
36424             this.split.minSize = this.config.minSize;
36425         }
36426         if(typeof this.config.maxSize != "undefined"){
36427             this.split.maxSize = this.config.maxSize;
36428         }
36429         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36430             this.hideSplitter();
36431         }
36432         
36433     },
36434
36435     getHMaxSize : function(){
36436          var cmax = this.config.maxSize || 10000;
36437          var center = this.mgr.getRegion("center");
36438          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36439     },
36440
36441     getVMaxSize : function(){
36442          var cmax = this.config.maxSize || 10000;
36443          var center = this.mgr.getRegion("center");
36444          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36445     },
36446
36447     onSplitMove : function(split, newSize){
36448         this.fireEvent("resized", this, newSize);
36449     },
36450     
36451     /** 
36452      * Returns the {@link Roo.SplitBar} for this region.
36453      * @return {Roo.SplitBar}
36454      */
36455     getSplitBar : function(){
36456         return this.split;
36457     },
36458     
36459     hide : function(){
36460         this.hideSplitter();
36461         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36462     },
36463
36464     hideSplitter : function(){
36465         if(this.split){
36466             this.split.el.setLocation(-2000,-2000);
36467             this.split.el.hide();
36468         }
36469     },
36470
36471     show : function(){
36472         if(this.split){
36473             this.split.el.show();
36474         }
36475         Roo.bootstrap.layout.Split.superclass.show.call(this);
36476     },
36477     
36478     beforeSlide: function(){
36479         if(Roo.isGecko){// firefox overflow auto bug workaround
36480             this.bodyEl.clip();
36481             if(this.tabs) {
36482                 this.tabs.bodyEl.clip();
36483             }
36484             if(this.activePanel){
36485                 this.activePanel.getEl().clip();
36486                 
36487                 if(this.activePanel.beforeSlide){
36488                     this.activePanel.beforeSlide();
36489                 }
36490             }
36491         }
36492     },
36493     
36494     afterSlide : function(){
36495         if(Roo.isGecko){// firefox overflow auto bug workaround
36496             this.bodyEl.unclip();
36497             if(this.tabs) {
36498                 this.tabs.bodyEl.unclip();
36499             }
36500             if(this.activePanel){
36501                 this.activePanel.getEl().unclip();
36502                 if(this.activePanel.afterSlide){
36503                     this.activePanel.afterSlide();
36504                 }
36505             }
36506         }
36507     },
36508
36509     initAutoHide : function(){
36510         if(this.autoHide !== false){
36511             if(!this.autoHideHd){
36512                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36513                 this.autoHideHd = {
36514                     "mouseout": function(e){
36515                         if(!e.within(this.el, true)){
36516                             st.delay(500);
36517                         }
36518                     },
36519                     "mouseover" : function(e){
36520                         st.cancel();
36521                     },
36522                     scope : this
36523                 };
36524             }
36525             this.el.on(this.autoHideHd);
36526         }
36527     },
36528
36529     clearAutoHide : function(){
36530         if(this.autoHide !== false){
36531             this.el.un("mouseout", this.autoHideHd.mouseout);
36532             this.el.un("mouseover", this.autoHideHd.mouseover);
36533         }
36534     },
36535
36536     clearMonitor : function(){
36537         Roo.get(document).un("click", this.slideInIf, this);
36538     },
36539
36540     // these names are backwards but not changed for compat
36541     slideOut : function(){
36542         if(this.isSlid || this.el.hasActiveFx()){
36543             return;
36544         }
36545         this.isSlid = true;
36546         if(this.collapseBtn){
36547             this.collapseBtn.hide();
36548         }
36549         this.closeBtnState = this.closeBtn.getStyle('display');
36550         this.closeBtn.hide();
36551         if(this.stickBtn){
36552             this.stickBtn.show();
36553         }
36554         this.el.show();
36555         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36556         this.beforeSlide();
36557         this.el.setStyle("z-index", 10001);
36558         this.el.slideIn(this.getSlideAnchor(), {
36559             callback: function(){
36560                 this.afterSlide();
36561                 this.initAutoHide();
36562                 Roo.get(document).on("click", this.slideInIf, this);
36563                 this.fireEvent("slideshow", this);
36564             },
36565             scope: this,
36566             block: true
36567         });
36568     },
36569
36570     afterSlideIn : function(){
36571         this.clearAutoHide();
36572         this.isSlid = false;
36573         this.clearMonitor();
36574         this.el.setStyle("z-index", "");
36575         if(this.collapseBtn){
36576             this.collapseBtn.show();
36577         }
36578         this.closeBtn.setStyle('display', this.closeBtnState);
36579         if(this.stickBtn){
36580             this.stickBtn.hide();
36581         }
36582         this.fireEvent("slidehide", this);
36583     },
36584
36585     slideIn : function(cb){
36586         if(!this.isSlid || this.el.hasActiveFx()){
36587             Roo.callback(cb);
36588             return;
36589         }
36590         this.isSlid = false;
36591         this.beforeSlide();
36592         this.el.slideOut(this.getSlideAnchor(), {
36593             callback: function(){
36594                 this.el.setLeftTop(-10000, -10000);
36595                 this.afterSlide();
36596                 this.afterSlideIn();
36597                 Roo.callback(cb);
36598             },
36599             scope: this,
36600             block: true
36601         });
36602     },
36603     
36604     slideInIf : function(e){
36605         if(!e.within(this.el)){
36606             this.slideIn();
36607         }
36608     },
36609
36610     animateCollapse : function(){
36611         this.beforeSlide();
36612         this.el.setStyle("z-index", 20000);
36613         var anchor = this.getSlideAnchor();
36614         this.el.slideOut(anchor, {
36615             callback : function(){
36616                 this.el.setStyle("z-index", "");
36617                 this.collapsedEl.slideIn(anchor, {duration:.3});
36618                 this.afterSlide();
36619                 this.el.setLocation(-10000,-10000);
36620                 this.el.hide();
36621                 this.fireEvent("collapsed", this);
36622             },
36623             scope: this,
36624             block: true
36625         });
36626     },
36627
36628     animateExpand : function(){
36629         this.beforeSlide();
36630         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36631         this.el.setStyle("z-index", 20000);
36632         this.collapsedEl.hide({
36633             duration:.1
36634         });
36635         this.el.slideIn(this.getSlideAnchor(), {
36636             callback : function(){
36637                 this.el.setStyle("z-index", "");
36638                 this.afterSlide();
36639                 if(this.split){
36640                     this.split.el.show();
36641                 }
36642                 this.fireEvent("invalidated", this);
36643                 this.fireEvent("expanded", this);
36644             },
36645             scope: this,
36646             block: true
36647         });
36648     },
36649
36650     anchors : {
36651         "west" : "left",
36652         "east" : "right",
36653         "north" : "top",
36654         "south" : "bottom"
36655     },
36656
36657     sanchors : {
36658         "west" : "l",
36659         "east" : "r",
36660         "north" : "t",
36661         "south" : "b"
36662     },
36663
36664     canchors : {
36665         "west" : "tl-tr",
36666         "east" : "tr-tl",
36667         "north" : "tl-bl",
36668         "south" : "bl-tl"
36669     },
36670
36671     getAnchor : function(){
36672         return this.anchors[this.position];
36673     },
36674
36675     getCollapseAnchor : function(){
36676         return this.canchors[this.position];
36677     },
36678
36679     getSlideAnchor : function(){
36680         return this.sanchors[this.position];
36681     },
36682
36683     getAlignAdj : function(){
36684         var cm = this.cmargins;
36685         switch(this.position){
36686             case "west":
36687                 return [0, 0];
36688             break;
36689             case "east":
36690                 return [0, 0];
36691             break;
36692             case "north":
36693                 return [0, 0];
36694             break;
36695             case "south":
36696                 return [0, 0];
36697             break;
36698         }
36699     },
36700
36701     getExpandAdj : function(){
36702         var c = this.collapsedEl, cm = this.cmargins;
36703         switch(this.position){
36704             case "west":
36705                 return [-(cm.right+c.getWidth()+cm.left), 0];
36706             break;
36707             case "east":
36708                 return [cm.right+c.getWidth()+cm.left, 0];
36709             break;
36710             case "north":
36711                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36712             break;
36713             case "south":
36714                 return [0, cm.top+cm.bottom+c.getHeight()];
36715             break;
36716         }
36717     }
36718 });/*
36719  * Based on:
36720  * Ext JS Library 1.1.1
36721  * Copyright(c) 2006-2007, Ext JS, LLC.
36722  *
36723  * Originally Released Under LGPL - original licence link has changed is not relivant.
36724  *
36725  * Fork - LGPL
36726  * <script type="text/javascript">
36727  */
36728 /*
36729  * These classes are private internal classes
36730  */
36731 Roo.bootstrap.layout.Center = function(config){
36732     config.region = "center";
36733     Roo.bootstrap.layout.Region.call(this, config);
36734     this.visible = true;
36735     this.minWidth = config.minWidth || 20;
36736     this.minHeight = config.minHeight || 20;
36737 };
36738
36739 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36740     hide : function(){
36741         // center panel can't be hidden
36742     },
36743     
36744     show : function(){
36745         // center panel can't be hidden
36746     },
36747     
36748     getMinWidth: function(){
36749         return this.minWidth;
36750     },
36751     
36752     getMinHeight: function(){
36753         return this.minHeight;
36754     }
36755 });
36756
36757
36758
36759
36760  
36761
36762
36763
36764
36765
36766 Roo.bootstrap.layout.North = function(config)
36767 {
36768     config.region = 'north';
36769     config.cursor = 'n-resize';
36770     
36771     Roo.bootstrap.layout.Split.call(this, config);
36772     
36773     
36774     if(this.split){
36775         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36776         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36777         this.split.el.addClass("roo-layout-split-v");
36778     }
36779     var size = config.initialSize || config.height;
36780     if(typeof size != "undefined"){
36781         this.el.setHeight(size);
36782     }
36783 };
36784 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36785 {
36786     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36787     
36788     
36789     
36790     getBox : function(){
36791         if(this.collapsed){
36792             return this.collapsedEl.getBox();
36793         }
36794         var box = this.el.getBox();
36795         if(this.split){
36796             box.height += this.split.el.getHeight();
36797         }
36798         return box;
36799     },
36800     
36801     updateBox : function(box){
36802         if(this.split && !this.collapsed){
36803             box.height -= this.split.el.getHeight();
36804             this.split.el.setLeft(box.x);
36805             this.split.el.setTop(box.y+box.height);
36806             this.split.el.setWidth(box.width);
36807         }
36808         if(this.collapsed){
36809             this.updateBody(box.width, null);
36810         }
36811         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36812     }
36813 });
36814
36815
36816
36817
36818
36819 Roo.bootstrap.layout.South = function(config){
36820     config.region = 'south';
36821     config.cursor = 's-resize';
36822     Roo.bootstrap.layout.Split.call(this, config);
36823     if(this.split){
36824         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36825         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36826         this.split.el.addClass("roo-layout-split-v");
36827     }
36828     var size = config.initialSize || config.height;
36829     if(typeof size != "undefined"){
36830         this.el.setHeight(size);
36831     }
36832 };
36833
36834 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36835     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36836     getBox : function(){
36837         if(this.collapsed){
36838             return this.collapsedEl.getBox();
36839         }
36840         var box = this.el.getBox();
36841         if(this.split){
36842             var sh = this.split.el.getHeight();
36843             box.height += sh;
36844             box.y -= sh;
36845         }
36846         return box;
36847     },
36848     
36849     updateBox : function(box){
36850         if(this.split && !this.collapsed){
36851             var sh = this.split.el.getHeight();
36852             box.height -= sh;
36853             box.y += sh;
36854             this.split.el.setLeft(box.x);
36855             this.split.el.setTop(box.y-sh);
36856             this.split.el.setWidth(box.width);
36857         }
36858         if(this.collapsed){
36859             this.updateBody(box.width, null);
36860         }
36861         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36862     }
36863 });
36864
36865 Roo.bootstrap.layout.East = function(config){
36866     config.region = "east";
36867     config.cursor = "e-resize";
36868     Roo.bootstrap.layout.Split.call(this, config);
36869     if(this.split){
36870         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36871         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36872         this.split.el.addClass("roo-layout-split-h");
36873     }
36874     var size = config.initialSize || config.width;
36875     if(typeof size != "undefined"){
36876         this.el.setWidth(size);
36877     }
36878 };
36879 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36880     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36881     getBox : function(){
36882         if(this.collapsed){
36883             return this.collapsedEl.getBox();
36884         }
36885         var box = this.el.getBox();
36886         if(this.split){
36887             var sw = this.split.el.getWidth();
36888             box.width += sw;
36889             box.x -= sw;
36890         }
36891         return box;
36892     },
36893
36894     updateBox : function(box){
36895         if(this.split && !this.collapsed){
36896             var sw = this.split.el.getWidth();
36897             box.width -= sw;
36898             this.split.el.setLeft(box.x);
36899             this.split.el.setTop(box.y);
36900             this.split.el.setHeight(box.height);
36901             box.x += sw;
36902         }
36903         if(this.collapsed){
36904             this.updateBody(null, box.height);
36905         }
36906         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36907     }
36908 });
36909
36910 Roo.bootstrap.layout.West = function(config){
36911     config.region = "west";
36912     config.cursor = "w-resize";
36913     
36914     Roo.bootstrap.layout.Split.call(this, config);
36915     if(this.split){
36916         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36917         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36918         this.split.el.addClass("roo-layout-split-h");
36919     }
36920     
36921 };
36922 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36923     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36924     
36925     onRender: function(ctr, pos)
36926     {
36927         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36928         var size = this.config.initialSize || this.config.width;
36929         if(typeof size != "undefined"){
36930             this.el.setWidth(size);
36931         }
36932     },
36933     
36934     getBox : function(){
36935         if(this.collapsed){
36936             return this.collapsedEl.getBox();
36937         }
36938         var box = this.el.getBox();
36939         if(this.split){
36940             box.width += this.split.el.getWidth();
36941         }
36942         return box;
36943     },
36944     
36945     updateBox : function(box){
36946         if(this.split && !this.collapsed){
36947             var sw = this.split.el.getWidth();
36948             box.width -= sw;
36949             this.split.el.setLeft(box.x+box.width);
36950             this.split.el.setTop(box.y);
36951             this.split.el.setHeight(box.height);
36952         }
36953         if(this.collapsed){
36954             this.updateBody(null, box.height);
36955         }
36956         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36957     }
36958 });
36959 Roo.namespace("Roo.bootstrap.panel");/*
36960  * Based on:
36961  * Ext JS Library 1.1.1
36962  * Copyright(c) 2006-2007, Ext JS, LLC.
36963  *
36964  * Originally Released Under LGPL - original licence link has changed is not relivant.
36965  *
36966  * Fork - LGPL
36967  * <script type="text/javascript">
36968  */
36969 /**
36970  * @class Roo.ContentPanel
36971  * @extends Roo.util.Observable
36972  * A basic ContentPanel element.
36973  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36974  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36975  * @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
36976  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36977  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36978  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36979  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36980  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36981  * @cfg {String} title          The title for this panel
36982  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36983  * @cfg {String} url            Calls {@link #setUrl} with this value
36984  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36985  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36986  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36987  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36988  * @cfg {Boolean} badges render the badges
36989
36990  * @constructor
36991  * Create a new ContentPanel.
36992  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36993  * @param {String/Object} config A string to set only the title or a config object
36994  * @param {String} content (optional) Set the HTML content for this panel
36995  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36996  */
36997 Roo.bootstrap.panel.Content = function( config){
36998     
36999     this.tpl = config.tpl || false;
37000     
37001     var el = config.el;
37002     var content = config.content;
37003
37004     if(config.autoCreate){ // xtype is available if this is called from factory
37005         el = Roo.id();
37006     }
37007     this.el = Roo.get(el);
37008     if(!this.el && config && config.autoCreate){
37009         if(typeof config.autoCreate == "object"){
37010             if(!config.autoCreate.id){
37011                 config.autoCreate.id = config.id||el;
37012             }
37013             this.el = Roo.DomHelper.append(document.body,
37014                         config.autoCreate, true);
37015         }else{
37016             var elcfg =  {   tag: "div",
37017                             cls: "roo-layout-inactive-content",
37018                             id: config.id||el
37019                             };
37020             if (config.html) {
37021                 elcfg.html = config.html;
37022                 
37023             }
37024                         
37025             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37026         }
37027     } 
37028     this.closable = false;
37029     this.loaded = false;
37030     this.active = false;
37031    
37032       
37033     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37034         
37035         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37036         
37037         this.wrapEl = this.el; //this.el.wrap();
37038         var ti = [];
37039         if (config.toolbar.items) {
37040             ti = config.toolbar.items ;
37041             delete config.toolbar.items ;
37042         }
37043         
37044         var nitems = [];
37045         this.toolbar.render(this.wrapEl, 'before');
37046         for(var i =0;i < ti.length;i++) {
37047           //  Roo.log(['add child', items[i]]);
37048             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37049         }
37050         this.toolbar.items = nitems;
37051         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37052         delete config.toolbar;
37053         
37054     }
37055     /*
37056     // xtype created footer. - not sure if will work as we normally have to render first..
37057     if (this.footer && !this.footer.el && this.footer.xtype) {
37058         if (!this.wrapEl) {
37059             this.wrapEl = this.el.wrap();
37060         }
37061     
37062         this.footer.container = this.wrapEl.createChild();
37063          
37064         this.footer = Roo.factory(this.footer, Roo);
37065         
37066     }
37067     */
37068     
37069      if(typeof config == "string"){
37070         this.title = config;
37071     }else{
37072         Roo.apply(this, config);
37073     }
37074     
37075     if(this.resizeEl){
37076         this.resizeEl = Roo.get(this.resizeEl, true);
37077     }else{
37078         this.resizeEl = this.el;
37079     }
37080     // handle view.xtype
37081     
37082  
37083     
37084     
37085     this.addEvents({
37086         /**
37087          * @event activate
37088          * Fires when this panel is activated. 
37089          * @param {Roo.ContentPanel} this
37090          */
37091         "activate" : true,
37092         /**
37093          * @event deactivate
37094          * Fires when this panel is activated. 
37095          * @param {Roo.ContentPanel} this
37096          */
37097         "deactivate" : true,
37098
37099         /**
37100          * @event resize
37101          * Fires when this panel is resized if fitToFrame is true.
37102          * @param {Roo.ContentPanel} this
37103          * @param {Number} width The width after any component adjustments
37104          * @param {Number} height The height after any component adjustments
37105          */
37106         "resize" : true,
37107         
37108          /**
37109          * @event render
37110          * Fires when this tab is created
37111          * @param {Roo.ContentPanel} this
37112          */
37113         "render" : true
37114         
37115         
37116         
37117     });
37118     
37119
37120     
37121     
37122     if(this.autoScroll){
37123         this.resizeEl.setStyle("overflow", "auto");
37124     } else {
37125         // fix randome scrolling
37126         //this.el.on('scroll', function() {
37127         //    Roo.log('fix random scolling');
37128         //    this.scrollTo('top',0); 
37129         //});
37130     }
37131     content = content || this.content;
37132     if(content){
37133         this.setContent(content);
37134     }
37135     if(config && config.url){
37136         this.setUrl(this.url, this.params, this.loadOnce);
37137     }
37138     
37139     
37140     
37141     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37142     
37143     if (this.view && typeof(this.view.xtype) != 'undefined') {
37144         this.view.el = this.el.appendChild(document.createElement("div"));
37145         this.view = Roo.factory(this.view); 
37146         this.view.render  &&  this.view.render(false, '');  
37147     }
37148     
37149     
37150     this.fireEvent('render', this);
37151 };
37152
37153 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37154     
37155     tabTip : '',
37156     
37157     setRegion : function(region){
37158         this.region = region;
37159         this.setActiveClass(region && !this.background);
37160     },
37161     
37162     
37163     setActiveClass: function(state)
37164     {
37165         if(state){
37166            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37167            this.el.setStyle('position','relative');
37168         }else{
37169            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37170            this.el.setStyle('position', 'absolute');
37171         } 
37172     },
37173     
37174     /**
37175      * Returns the toolbar for this Panel if one was configured. 
37176      * @return {Roo.Toolbar} 
37177      */
37178     getToolbar : function(){
37179         return this.toolbar;
37180     },
37181     
37182     setActiveState : function(active)
37183     {
37184         this.active = active;
37185         this.setActiveClass(active);
37186         if(!active){
37187             if(this.fireEvent("deactivate", this) === false){
37188                 return false;
37189             }
37190             return true;
37191         }
37192         this.fireEvent("activate", this);
37193         return true;
37194     },
37195     /**
37196      * Updates this panel's element
37197      * @param {String} content The new content
37198      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37199     */
37200     setContent : function(content, loadScripts){
37201         this.el.update(content, loadScripts);
37202     },
37203
37204     ignoreResize : function(w, h){
37205         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37206             return true;
37207         }else{
37208             this.lastSize = {width: w, height: h};
37209             return false;
37210         }
37211     },
37212     /**
37213      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37214      * @return {Roo.UpdateManager} The UpdateManager
37215      */
37216     getUpdateManager : function(){
37217         return this.el.getUpdateManager();
37218     },
37219      /**
37220      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37221      * @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:
37222 <pre><code>
37223 panel.load({
37224     url: "your-url.php",
37225     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37226     callback: yourFunction,
37227     scope: yourObject, //(optional scope)
37228     discardUrl: false,
37229     nocache: false,
37230     text: "Loading...",
37231     timeout: 30,
37232     scripts: false
37233 });
37234 </code></pre>
37235      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37236      * 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.
37237      * @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}
37238      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37239      * @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.
37240      * @return {Roo.ContentPanel} this
37241      */
37242     load : function(){
37243         var um = this.el.getUpdateManager();
37244         um.update.apply(um, arguments);
37245         return this;
37246     },
37247
37248
37249     /**
37250      * 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.
37251      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37252      * @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)
37253      * @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)
37254      * @return {Roo.UpdateManager} The UpdateManager
37255      */
37256     setUrl : function(url, params, loadOnce){
37257         if(this.refreshDelegate){
37258             this.removeListener("activate", this.refreshDelegate);
37259         }
37260         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37261         this.on("activate", this.refreshDelegate);
37262         return this.el.getUpdateManager();
37263     },
37264     
37265     _handleRefresh : function(url, params, loadOnce){
37266         if(!loadOnce || !this.loaded){
37267             var updater = this.el.getUpdateManager();
37268             updater.update(url, params, this._setLoaded.createDelegate(this));
37269         }
37270     },
37271     
37272     _setLoaded : function(){
37273         this.loaded = true;
37274     }, 
37275     
37276     /**
37277      * Returns this panel's id
37278      * @return {String} 
37279      */
37280     getId : function(){
37281         return this.el.id;
37282     },
37283     
37284     /** 
37285      * Returns this panel's element - used by regiosn to add.
37286      * @return {Roo.Element} 
37287      */
37288     getEl : function(){
37289         return this.wrapEl || this.el;
37290     },
37291     
37292    
37293     
37294     adjustForComponents : function(width, height)
37295     {
37296         //Roo.log('adjustForComponents ');
37297         if(this.resizeEl != this.el){
37298             width -= this.el.getFrameWidth('lr');
37299             height -= this.el.getFrameWidth('tb');
37300         }
37301         if(this.toolbar){
37302             var te = this.toolbar.getEl();
37303             te.setWidth(width);
37304             height -= te.getHeight();
37305         }
37306         if(this.footer){
37307             var te = this.footer.getEl();
37308             te.setWidth(width);
37309             height -= te.getHeight();
37310         }
37311         
37312         
37313         if(this.adjustments){
37314             width += this.adjustments[0];
37315             height += this.adjustments[1];
37316         }
37317         return {"width": width, "height": height};
37318     },
37319     
37320     setSize : function(width, height){
37321         if(this.fitToFrame && !this.ignoreResize(width, height)){
37322             if(this.fitContainer && this.resizeEl != this.el){
37323                 this.el.setSize(width, height);
37324             }
37325             var size = this.adjustForComponents(width, height);
37326             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37327             this.fireEvent('resize', this, size.width, size.height);
37328         }
37329     },
37330     
37331     /**
37332      * Returns this panel's title
37333      * @return {String} 
37334      */
37335     getTitle : function(){
37336         
37337         if (typeof(this.title) != 'object') {
37338             return this.title;
37339         }
37340         
37341         var t = '';
37342         for (var k in this.title) {
37343             if (!this.title.hasOwnProperty(k)) {
37344                 continue;
37345             }
37346             
37347             if (k.indexOf('-') >= 0) {
37348                 var s = k.split('-');
37349                 for (var i = 0; i<s.length; i++) {
37350                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37351                 }
37352             } else {
37353                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37354             }
37355         }
37356         return t;
37357     },
37358     
37359     /**
37360      * Set this panel's title
37361      * @param {String} title
37362      */
37363     setTitle : function(title){
37364         this.title = title;
37365         if(this.region){
37366             this.region.updatePanelTitle(this, title);
37367         }
37368     },
37369     
37370     /**
37371      * Returns true is this panel was configured to be closable
37372      * @return {Boolean} 
37373      */
37374     isClosable : function(){
37375         return this.closable;
37376     },
37377     
37378     beforeSlide : function(){
37379         this.el.clip();
37380         this.resizeEl.clip();
37381     },
37382     
37383     afterSlide : function(){
37384         this.el.unclip();
37385         this.resizeEl.unclip();
37386     },
37387     
37388     /**
37389      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37390      *   Will fail silently if the {@link #setUrl} method has not been called.
37391      *   This does not activate the panel, just updates its content.
37392      */
37393     refresh : function(){
37394         if(this.refreshDelegate){
37395            this.loaded = false;
37396            this.refreshDelegate();
37397         }
37398     },
37399     
37400     /**
37401      * Destroys this panel
37402      */
37403     destroy : function(){
37404         this.el.removeAllListeners();
37405         var tempEl = document.createElement("span");
37406         tempEl.appendChild(this.el.dom);
37407         tempEl.innerHTML = "";
37408         this.el.remove();
37409         this.el = null;
37410     },
37411     
37412     /**
37413      * form - if the content panel contains a form - this is a reference to it.
37414      * @type {Roo.form.Form}
37415      */
37416     form : false,
37417     /**
37418      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37419      *    This contains a reference to it.
37420      * @type {Roo.View}
37421      */
37422     view : false,
37423     
37424       /**
37425      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37426      * <pre><code>
37427
37428 layout.addxtype({
37429        xtype : 'Form',
37430        items: [ .... ]
37431    }
37432 );
37433
37434 </code></pre>
37435      * @param {Object} cfg Xtype definition of item to add.
37436      */
37437     
37438     
37439     getChildContainer: function () {
37440         return this.getEl();
37441     }
37442     
37443     
37444     /*
37445         var  ret = new Roo.factory(cfg);
37446         return ret;
37447         
37448         
37449         // add form..
37450         if (cfg.xtype.match(/^Form$/)) {
37451             
37452             var el;
37453             //if (this.footer) {
37454             //    el = this.footer.container.insertSibling(false, 'before');
37455             //} else {
37456                 el = this.el.createChild();
37457             //}
37458
37459             this.form = new  Roo.form.Form(cfg);
37460             
37461             
37462             if ( this.form.allItems.length) {
37463                 this.form.render(el.dom);
37464             }
37465             return this.form;
37466         }
37467         // should only have one of theses..
37468         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37469             // views.. should not be just added - used named prop 'view''
37470             
37471             cfg.el = this.el.appendChild(document.createElement("div"));
37472             // factory?
37473             
37474             var ret = new Roo.factory(cfg);
37475              
37476              ret.render && ret.render(false, ''); // render blank..
37477             this.view = ret;
37478             return ret;
37479         }
37480         return false;
37481     }
37482     \*/
37483 });
37484  
37485 /**
37486  * @class Roo.bootstrap.panel.Grid
37487  * @extends Roo.bootstrap.panel.Content
37488  * @constructor
37489  * Create a new GridPanel.
37490  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37491  * @param {Object} config A the config object
37492   
37493  */
37494
37495
37496
37497 Roo.bootstrap.panel.Grid = function(config)
37498 {
37499     
37500       
37501     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37502         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37503
37504     config.el = this.wrapper;
37505     //this.el = this.wrapper;
37506     
37507       if (config.container) {
37508         // ctor'ed from a Border/panel.grid
37509         
37510         
37511         this.wrapper.setStyle("overflow", "hidden");
37512         this.wrapper.addClass('roo-grid-container');
37513
37514     }
37515     
37516     
37517     if(config.toolbar){
37518         var tool_el = this.wrapper.createChild();    
37519         this.toolbar = Roo.factory(config.toolbar);
37520         var ti = [];
37521         if (config.toolbar.items) {
37522             ti = config.toolbar.items ;
37523             delete config.toolbar.items ;
37524         }
37525         
37526         var nitems = [];
37527         this.toolbar.render(tool_el);
37528         for(var i =0;i < ti.length;i++) {
37529           //  Roo.log(['add child', items[i]]);
37530             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37531         }
37532         this.toolbar.items = nitems;
37533         
37534         delete config.toolbar;
37535     }
37536     
37537     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37538     config.grid.scrollBody = true;;
37539     config.grid.monitorWindowResize = false; // turn off autosizing
37540     config.grid.autoHeight = false;
37541     config.grid.autoWidth = false;
37542     
37543     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37544     
37545     if (config.background) {
37546         // render grid on panel activation (if panel background)
37547         this.on('activate', function(gp) {
37548             if (!gp.grid.rendered) {
37549                 gp.grid.render(this.wrapper);
37550                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37551             }
37552         });
37553             
37554     } else {
37555         this.grid.render(this.wrapper);
37556         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37557
37558     }
37559     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37560     // ??? needed ??? config.el = this.wrapper;
37561     
37562     
37563     
37564   
37565     // xtype created footer. - not sure if will work as we normally have to render first..
37566     if (this.footer && !this.footer.el && this.footer.xtype) {
37567         
37568         var ctr = this.grid.getView().getFooterPanel(true);
37569         this.footer.dataSource = this.grid.dataSource;
37570         this.footer = Roo.factory(this.footer, Roo);
37571         this.footer.render(ctr);
37572         
37573     }
37574     
37575     
37576     
37577     
37578      
37579 };
37580
37581 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37582     getId : function(){
37583         return this.grid.id;
37584     },
37585     
37586     /**
37587      * Returns the grid for this panel
37588      * @return {Roo.bootstrap.Table} 
37589      */
37590     getGrid : function(){
37591         return this.grid;    
37592     },
37593     
37594     setSize : function(width, height){
37595         if(!this.ignoreResize(width, height)){
37596             var grid = this.grid;
37597             var size = this.adjustForComponents(width, height);
37598             var gridel = grid.getGridEl();
37599             gridel.setSize(size.width, size.height);
37600             /*
37601             var thd = grid.getGridEl().select('thead',true).first();
37602             var tbd = grid.getGridEl().select('tbody', true).first();
37603             if (tbd) {
37604                 tbd.setSize(width, height - thd.getHeight());
37605             }
37606             */
37607             grid.autoSize();
37608         }
37609     },
37610      
37611     
37612     
37613     beforeSlide : function(){
37614         this.grid.getView().scroller.clip();
37615     },
37616     
37617     afterSlide : function(){
37618         this.grid.getView().scroller.unclip();
37619     },
37620     
37621     destroy : function(){
37622         this.grid.destroy();
37623         delete this.grid;
37624         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37625     }
37626 });
37627
37628 /**
37629  * @class Roo.bootstrap.panel.Nest
37630  * @extends Roo.bootstrap.panel.Content
37631  * @constructor
37632  * Create a new Panel, that can contain a layout.Border.
37633  * 
37634  * 
37635  * @param {Roo.BorderLayout} layout The layout for this panel
37636  * @param {String/Object} config A string to set only the title or a config object
37637  */
37638 Roo.bootstrap.panel.Nest = function(config)
37639 {
37640     // construct with only one argument..
37641     /* FIXME - implement nicer consturctors
37642     if (layout.layout) {
37643         config = layout;
37644         layout = config.layout;
37645         delete config.layout;
37646     }
37647     if (layout.xtype && !layout.getEl) {
37648         // then layout needs constructing..
37649         layout = Roo.factory(layout, Roo);
37650     }
37651     */
37652     
37653     config.el =  config.layout.getEl();
37654     
37655     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37656     
37657     config.layout.monitorWindowResize = false; // turn off autosizing
37658     this.layout = config.layout;
37659     this.layout.getEl().addClass("roo-layout-nested-layout");
37660     
37661     
37662     
37663     
37664 };
37665
37666 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37667
37668     setSize : function(width, height){
37669         if(!this.ignoreResize(width, height)){
37670             var size = this.adjustForComponents(width, height);
37671             var el = this.layout.getEl();
37672             if (size.height < 1) {
37673                 el.setWidth(size.width);   
37674             } else {
37675                 el.setSize(size.width, size.height);
37676             }
37677             var touch = el.dom.offsetWidth;
37678             this.layout.layout();
37679             // ie requires a double layout on the first pass
37680             if(Roo.isIE && !this.initialized){
37681                 this.initialized = true;
37682                 this.layout.layout();
37683             }
37684         }
37685     },
37686     
37687     // activate all subpanels if not currently active..
37688     
37689     setActiveState : function(active){
37690         this.active = active;
37691         this.setActiveClass(active);
37692         
37693         if(!active){
37694             this.fireEvent("deactivate", this);
37695             return;
37696         }
37697         
37698         this.fireEvent("activate", this);
37699         // not sure if this should happen before or after..
37700         if (!this.layout) {
37701             return; // should not happen..
37702         }
37703         var reg = false;
37704         for (var r in this.layout.regions) {
37705             reg = this.layout.getRegion(r);
37706             if (reg.getActivePanel()) {
37707                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37708                 reg.setActivePanel(reg.getActivePanel());
37709                 continue;
37710             }
37711             if (!reg.panels.length) {
37712                 continue;
37713             }
37714             reg.showPanel(reg.getPanel(0));
37715         }
37716         
37717         
37718         
37719         
37720     },
37721     
37722     /**
37723      * Returns the nested BorderLayout for this panel
37724      * @return {Roo.BorderLayout} 
37725      */
37726     getLayout : function(){
37727         return this.layout;
37728     },
37729     
37730      /**
37731      * Adds a xtype elements to the layout of the nested panel
37732      * <pre><code>
37733
37734 panel.addxtype({
37735        xtype : 'ContentPanel',
37736        region: 'west',
37737        items: [ .... ]
37738    }
37739 );
37740
37741 panel.addxtype({
37742         xtype : 'NestedLayoutPanel',
37743         region: 'west',
37744         layout: {
37745            center: { },
37746            west: { }   
37747         },
37748         items : [ ... list of content panels or nested layout panels.. ]
37749    }
37750 );
37751 </code></pre>
37752      * @param {Object} cfg Xtype definition of item to add.
37753      */
37754     addxtype : function(cfg) {
37755         return this.layout.addxtype(cfg);
37756     
37757     }
37758 });        /*
37759  * Based on:
37760  * Ext JS Library 1.1.1
37761  * Copyright(c) 2006-2007, Ext JS, LLC.
37762  *
37763  * Originally Released Under LGPL - original licence link has changed is not relivant.
37764  *
37765  * Fork - LGPL
37766  * <script type="text/javascript">
37767  */
37768 /**
37769  * @class Roo.TabPanel
37770  * @extends Roo.util.Observable
37771  * A lightweight tab container.
37772  * <br><br>
37773  * Usage:
37774  * <pre><code>
37775 // basic tabs 1, built from existing content
37776 var tabs = new Roo.TabPanel("tabs1");
37777 tabs.addTab("script", "View Script");
37778 tabs.addTab("markup", "View Markup");
37779 tabs.activate("script");
37780
37781 // more advanced tabs, built from javascript
37782 var jtabs = new Roo.TabPanel("jtabs");
37783 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37784
37785 // set up the UpdateManager
37786 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37787 var updater = tab2.getUpdateManager();
37788 updater.setDefaultUrl("ajax1.htm");
37789 tab2.on('activate', updater.refresh, updater, true);
37790
37791 // Use setUrl for Ajax loading
37792 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37793 tab3.setUrl("ajax2.htm", null, true);
37794
37795 // Disabled tab
37796 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37797 tab4.disable();
37798
37799 jtabs.activate("jtabs-1");
37800  * </code></pre>
37801  * @constructor
37802  * Create a new TabPanel.
37803  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37804  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37805  */
37806 Roo.bootstrap.panel.Tabs = function(config){
37807     /**
37808     * The container element for this TabPanel.
37809     * @type Roo.Element
37810     */
37811     this.el = Roo.get(config.el);
37812     delete config.el;
37813     if(config){
37814         if(typeof config == "boolean"){
37815             this.tabPosition = config ? "bottom" : "top";
37816         }else{
37817             Roo.apply(this, config);
37818         }
37819     }
37820     
37821     if(this.tabPosition == "bottom"){
37822         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37823         this.el.addClass("roo-tabs-bottom");
37824     }
37825     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37826     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37827     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37828     if(Roo.isIE){
37829         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37830     }
37831     if(this.tabPosition != "bottom"){
37832         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37833          * @type Roo.Element
37834          */
37835         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37836         this.el.addClass("roo-tabs-top");
37837     }
37838     this.items = [];
37839
37840     this.bodyEl.setStyle("position", "relative");
37841
37842     this.active = null;
37843     this.activateDelegate = this.activate.createDelegate(this);
37844
37845     this.addEvents({
37846         /**
37847          * @event tabchange
37848          * Fires when the active tab changes
37849          * @param {Roo.TabPanel} this
37850          * @param {Roo.TabPanelItem} activePanel The new active tab
37851          */
37852         "tabchange": true,
37853         /**
37854          * @event beforetabchange
37855          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37856          * @param {Roo.TabPanel} this
37857          * @param {Object} e Set cancel to true on this object to cancel the tab change
37858          * @param {Roo.TabPanelItem} tab The tab being changed to
37859          */
37860         "beforetabchange" : true
37861     });
37862
37863     Roo.EventManager.onWindowResize(this.onResize, this);
37864     this.cpad = this.el.getPadding("lr");
37865     this.hiddenCount = 0;
37866
37867
37868     // toolbar on the tabbar support...
37869     if (this.toolbar) {
37870         alert("no toolbar support yet");
37871         this.toolbar  = false;
37872         /*
37873         var tcfg = this.toolbar;
37874         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37875         this.toolbar = new Roo.Toolbar(tcfg);
37876         if (Roo.isSafari) {
37877             var tbl = tcfg.container.child('table', true);
37878             tbl.setAttribute('width', '100%');
37879         }
37880         */
37881         
37882     }
37883    
37884
37885
37886     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37887 };
37888
37889 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37890     /*
37891      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37892      */
37893     tabPosition : "top",
37894     /*
37895      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37896      */
37897     currentTabWidth : 0,
37898     /*
37899      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37900      */
37901     minTabWidth : 40,
37902     /*
37903      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37904      */
37905     maxTabWidth : 250,
37906     /*
37907      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37908      */
37909     preferredTabWidth : 175,
37910     /*
37911      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37912      */
37913     resizeTabs : false,
37914     /*
37915      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37916      */
37917     monitorResize : true,
37918     /*
37919      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37920      */
37921     toolbar : false,
37922
37923     /**
37924      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37925      * @param {String} id The id of the div to use <b>or create</b>
37926      * @param {String} text The text for the tab
37927      * @param {String} content (optional) Content to put in the TabPanelItem body
37928      * @param {Boolean} closable (optional) True to create a close icon on the tab
37929      * @return {Roo.TabPanelItem} The created TabPanelItem
37930      */
37931     addTab : function(id, text, content, closable, tpl)
37932     {
37933         var item = new Roo.bootstrap.panel.TabItem({
37934             panel: this,
37935             id : id,
37936             text : text,
37937             closable : closable,
37938             tpl : tpl
37939         });
37940         this.addTabItem(item);
37941         if(content){
37942             item.setContent(content);
37943         }
37944         return item;
37945     },
37946
37947     /**
37948      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37949      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37950      * @return {Roo.TabPanelItem}
37951      */
37952     getTab : function(id){
37953         return this.items[id];
37954     },
37955
37956     /**
37957      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37958      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37959      */
37960     hideTab : function(id){
37961         var t = this.items[id];
37962         if(!t.isHidden()){
37963            t.setHidden(true);
37964            this.hiddenCount++;
37965            this.autoSizeTabs();
37966         }
37967     },
37968
37969     /**
37970      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37971      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37972      */
37973     unhideTab : function(id){
37974         var t = this.items[id];
37975         if(t.isHidden()){
37976            t.setHidden(false);
37977            this.hiddenCount--;
37978            this.autoSizeTabs();
37979         }
37980     },
37981
37982     /**
37983      * Adds an existing {@link Roo.TabPanelItem}.
37984      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37985      */
37986     addTabItem : function(item){
37987         this.items[item.id] = item;
37988         this.items.push(item);
37989       //  if(this.resizeTabs){
37990     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37991   //         this.autoSizeTabs();
37992 //        }else{
37993 //            item.autoSize();
37994        // }
37995     },
37996
37997     /**
37998      * Removes a {@link Roo.TabPanelItem}.
37999      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38000      */
38001     removeTab : function(id){
38002         var items = this.items;
38003         var tab = items[id];
38004         if(!tab) { return; }
38005         var index = items.indexOf(tab);
38006         if(this.active == tab && items.length > 1){
38007             var newTab = this.getNextAvailable(index);
38008             if(newTab) {
38009                 newTab.activate();
38010             }
38011         }
38012         this.stripEl.dom.removeChild(tab.pnode.dom);
38013         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38014             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38015         }
38016         items.splice(index, 1);
38017         delete this.items[tab.id];
38018         tab.fireEvent("close", tab);
38019         tab.purgeListeners();
38020         this.autoSizeTabs();
38021     },
38022
38023     getNextAvailable : function(start){
38024         var items = this.items;
38025         var index = start;
38026         // look for a next tab that will slide over to
38027         // replace the one being removed
38028         while(index < items.length){
38029             var item = items[++index];
38030             if(item && !item.isHidden()){
38031                 return item;
38032             }
38033         }
38034         // if one isn't found select the previous tab (on the left)
38035         index = start;
38036         while(index >= 0){
38037             var item = items[--index];
38038             if(item && !item.isHidden()){
38039                 return item;
38040             }
38041         }
38042         return null;
38043     },
38044
38045     /**
38046      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38047      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38048      */
38049     disableTab : function(id){
38050         var tab = this.items[id];
38051         if(tab && this.active != tab){
38052             tab.disable();
38053         }
38054     },
38055
38056     /**
38057      * Enables a {@link Roo.TabPanelItem} that is disabled.
38058      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38059      */
38060     enableTab : function(id){
38061         var tab = this.items[id];
38062         tab.enable();
38063     },
38064
38065     /**
38066      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38067      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38068      * @return {Roo.TabPanelItem} The TabPanelItem.
38069      */
38070     activate : function(id){
38071         var tab = this.items[id];
38072         if(!tab){
38073             return null;
38074         }
38075         if(tab == this.active || tab.disabled){
38076             return tab;
38077         }
38078         var e = {};
38079         this.fireEvent("beforetabchange", this, e, tab);
38080         if(e.cancel !== true && !tab.disabled){
38081             if(this.active){
38082                 this.active.hide();
38083             }
38084             this.active = this.items[id];
38085             this.active.show();
38086             this.fireEvent("tabchange", this, this.active);
38087         }
38088         return tab;
38089     },
38090
38091     /**
38092      * Gets the active {@link Roo.TabPanelItem}.
38093      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38094      */
38095     getActiveTab : function(){
38096         return this.active;
38097     },
38098
38099     /**
38100      * Updates the tab body element to fit the height of the container element
38101      * for overflow scrolling
38102      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38103      */
38104     syncHeight : function(targetHeight){
38105         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38106         var bm = this.bodyEl.getMargins();
38107         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38108         this.bodyEl.setHeight(newHeight);
38109         return newHeight;
38110     },
38111
38112     onResize : function(){
38113         if(this.monitorResize){
38114             this.autoSizeTabs();
38115         }
38116     },
38117
38118     /**
38119      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38120      */
38121     beginUpdate : function(){
38122         this.updating = true;
38123     },
38124
38125     /**
38126      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38127      */
38128     endUpdate : function(){
38129         this.updating = false;
38130         this.autoSizeTabs();
38131     },
38132
38133     /**
38134      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38135      */
38136     autoSizeTabs : function(){
38137         var count = this.items.length;
38138         var vcount = count - this.hiddenCount;
38139         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38140             return;
38141         }
38142         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38143         var availWidth = Math.floor(w / vcount);
38144         var b = this.stripBody;
38145         if(b.getWidth() > w){
38146             var tabs = this.items;
38147             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38148             if(availWidth < this.minTabWidth){
38149                 /*if(!this.sleft){    // incomplete scrolling code
38150                     this.createScrollButtons();
38151                 }
38152                 this.showScroll();
38153                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38154             }
38155         }else{
38156             if(this.currentTabWidth < this.preferredTabWidth){
38157                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38158             }
38159         }
38160     },
38161
38162     /**
38163      * Returns the number of tabs in this TabPanel.
38164      * @return {Number}
38165      */
38166      getCount : function(){
38167          return this.items.length;
38168      },
38169
38170     /**
38171      * Resizes all the tabs to the passed width
38172      * @param {Number} The new width
38173      */
38174     setTabWidth : function(width){
38175         this.currentTabWidth = width;
38176         for(var i = 0, len = this.items.length; i < len; i++) {
38177                 if(!this.items[i].isHidden()) {
38178                 this.items[i].setWidth(width);
38179             }
38180         }
38181     },
38182
38183     /**
38184      * Destroys this TabPanel
38185      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38186      */
38187     destroy : function(removeEl){
38188         Roo.EventManager.removeResizeListener(this.onResize, this);
38189         for(var i = 0, len = this.items.length; i < len; i++){
38190             this.items[i].purgeListeners();
38191         }
38192         if(removeEl === true){
38193             this.el.update("");
38194             this.el.remove();
38195         }
38196     },
38197     
38198     createStrip : function(container)
38199     {
38200         var strip = document.createElement("nav");
38201         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38202         container.appendChild(strip);
38203         return strip;
38204     },
38205     
38206     createStripList : function(strip)
38207     {
38208         // div wrapper for retard IE
38209         // returns the "tr" element.
38210         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38211         //'<div class="x-tabs-strip-wrap">'+
38212           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38213           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38214         return strip.firstChild; //.firstChild.firstChild.firstChild;
38215     },
38216     createBody : function(container)
38217     {
38218         var body = document.createElement("div");
38219         Roo.id(body, "tab-body");
38220         //Roo.fly(body).addClass("x-tabs-body");
38221         Roo.fly(body).addClass("tab-content");
38222         container.appendChild(body);
38223         return body;
38224     },
38225     createItemBody :function(bodyEl, id){
38226         var body = Roo.getDom(id);
38227         if(!body){
38228             body = document.createElement("div");
38229             body.id = id;
38230         }
38231         //Roo.fly(body).addClass("x-tabs-item-body");
38232         Roo.fly(body).addClass("tab-pane");
38233          bodyEl.insertBefore(body, bodyEl.firstChild);
38234         return body;
38235     },
38236     /** @private */
38237     createStripElements :  function(stripEl, text, closable, tpl)
38238     {
38239         var td = document.createElement("li"); // was td..
38240         
38241         
38242         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38243         
38244         
38245         stripEl.appendChild(td);
38246         /*if(closable){
38247             td.className = "x-tabs-closable";
38248             if(!this.closeTpl){
38249                 this.closeTpl = new Roo.Template(
38250                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38251                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38252                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38253                 );
38254             }
38255             var el = this.closeTpl.overwrite(td, {"text": text});
38256             var close = el.getElementsByTagName("div")[0];
38257             var inner = el.getElementsByTagName("em")[0];
38258             return {"el": el, "close": close, "inner": inner};
38259         } else {
38260         */
38261         // not sure what this is..
38262 //            if(!this.tabTpl){
38263                 //this.tabTpl = new Roo.Template(
38264                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38265                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38266                 //);
38267 //                this.tabTpl = new Roo.Template(
38268 //                   '<a href="#">' +
38269 //                   '<span unselectable="on"' +
38270 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38271 //                            ' >{text}</span></a>'
38272 //                );
38273 //                
38274 //            }
38275
38276
38277             var template = tpl || this.tabTpl || false;
38278             
38279             if(!template){
38280                 
38281                 template = new Roo.Template(
38282                    '<a href="#">' +
38283                    '<span unselectable="on"' +
38284                             (this.disableTooltips ? '' : ' title="{text}"') +
38285                             ' >{text}</span></a>'
38286                 );
38287             }
38288             
38289             switch (typeof(template)) {
38290                 case 'object' :
38291                     break;
38292                 case 'string' :
38293                     template = new Roo.Template(template);
38294                     break;
38295                 default :
38296                     break;
38297             }
38298             
38299             var el = template.overwrite(td, {"text": text});
38300             
38301             var inner = el.getElementsByTagName("span")[0];
38302             
38303             return {"el": el, "inner": inner};
38304             
38305     }
38306         
38307     
38308 });
38309
38310 /**
38311  * @class Roo.TabPanelItem
38312  * @extends Roo.util.Observable
38313  * Represents an individual item (tab plus body) in a TabPanel.
38314  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38315  * @param {String} id The id of this TabPanelItem
38316  * @param {String} text The text for the tab of this TabPanelItem
38317  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38318  */
38319 Roo.bootstrap.panel.TabItem = function(config){
38320     /**
38321      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38322      * @type Roo.TabPanel
38323      */
38324     this.tabPanel = config.panel;
38325     /**
38326      * The id for this TabPanelItem
38327      * @type String
38328      */
38329     this.id = config.id;
38330     /** @private */
38331     this.disabled = false;
38332     /** @private */
38333     this.text = config.text;
38334     /** @private */
38335     this.loaded = false;
38336     this.closable = config.closable;
38337
38338     /**
38339      * The body element for this TabPanelItem.
38340      * @type Roo.Element
38341      */
38342     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38343     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38344     this.bodyEl.setStyle("display", "block");
38345     this.bodyEl.setStyle("zoom", "1");
38346     //this.hideAction();
38347
38348     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38349     /** @private */
38350     this.el = Roo.get(els.el);
38351     this.inner = Roo.get(els.inner, true);
38352     this.textEl = Roo.get(this.el.dom.firstChild, true);
38353     this.pnode = Roo.get(els.el.parentNode, true);
38354 //    this.el.on("mousedown", this.onTabMouseDown, this);
38355     this.el.on("click", this.onTabClick, this);
38356     /** @private */
38357     if(config.closable){
38358         var c = Roo.get(els.close, true);
38359         c.dom.title = this.closeText;
38360         c.addClassOnOver("close-over");
38361         c.on("click", this.closeClick, this);
38362      }
38363
38364     this.addEvents({
38365          /**
38366          * @event activate
38367          * Fires when this tab becomes the active tab.
38368          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38369          * @param {Roo.TabPanelItem} this
38370          */
38371         "activate": true,
38372         /**
38373          * @event beforeclose
38374          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38375          * @param {Roo.TabPanelItem} this
38376          * @param {Object} e Set cancel to true on this object to cancel the close.
38377          */
38378         "beforeclose": true,
38379         /**
38380          * @event close
38381          * Fires when this tab is closed.
38382          * @param {Roo.TabPanelItem} this
38383          */
38384          "close": true,
38385         /**
38386          * @event deactivate
38387          * Fires when this tab is no longer the active tab.
38388          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38389          * @param {Roo.TabPanelItem} this
38390          */
38391          "deactivate" : true
38392     });
38393     this.hidden = false;
38394
38395     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38396 };
38397
38398 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38399            {
38400     purgeListeners : function(){
38401        Roo.util.Observable.prototype.purgeListeners.call(this);
38402        this.el.removeAllListeners();
38403     },
38404     /**
38405      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38406      */
38407     show : function(){
38408         this.pnode.addClass("active");
38409         this.showAction();
38410         if(Roo.isOpera){
38411             this.tabPanel.stripWrap.repaint();
38412         }
38413         this.fireEvent("activate", this.tabPanel, this);
38414     },
38415
38416     /**
38417      * Returns true if this tab is the active tab.
38418      * @return {Boolean}
38419      */
38420     isActive : function(){
38421         return this.tabPanel.getActiveTab() == this;
38422     },
38423
38424     /**
38425      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38426      */
38427     hide : function(){
38428         this.pnode.removeClass("active");
38429         this.hideAction();
38430         this.fireEvent("deactivate", this.tabPanel, this);
38431     },
38432
38433     hideAction : function(){
38434         this.bodyEl.hide();
38435         this.bodyEl.setStyle("position", "absolute");
38436         this.bodyEl.setLeft("-20000px");
38437         this.bodyEl.setTop("-20000px");
38438     },
38439
38440     showAction : function(){
38441         this.bodyEl.setStyle("position", "relative");
38442         this.bodyEl.setTop("");
38443         this.bodyEl.setLeft("");
38444         this.bodyEl.show();
38445     },
38446
38447     /**
38448      * Set the tooltip for the tab.
38449      * @param {String} tooltip The tab's tooltip
38450      */
38451     setTooltip : function(text){
38452         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38453             this.textEl.dom.qtip = text;
38454             this.textEl.dom.removeAttribute('title');
38455         }else{
38456             this.textEl.dom.title = text;
38457         }
38458     },
38459
38460     onTabClick : function(e){
38461         e.preventDefault();
38462         this.tabPanel.activate(this.id);
38463     },
38464
38465     onTabMouseDown : function(e){
38466         e.preventDefault();
38467         this.tabPanel.activate(this.id);
38468     },
38469 /*
38470     getWidth : function(){
38471         return this.inner.getWidth();
38472     },
38473
38474     setWidth : function(width){
38475         var iwidth = width - this.pnode.getPadding("lr");
38476         this.inner.setWidth(iwidth);
38477         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38478         this.pnode.setWidth(width);
38479     },
38480 */
38481     /**
38482      * Show or hide the tab
38483      * @param {Boolean} hidden True to hide or false to show.
38484      */
38485     setHidden : function(hidden){
38486         this.hidden = hidden;
38487         this.pnode.setStyle("display", hidden ? "none" : "");
38488     },
38489
38490     /**
38491      * Returns true if this tab is "hidden"
38492      * @return {Boolean}
38493      */
38494     isHidden : function(){
38495         return this.hidden;
38496     },
38497
38498     /**
38499      * Returns the text for this tab
38500      * @return {String}
38501      */
38502     getText : function(){
38503         return this.text;
38504     },
38505     /*
38506     autoSize : function(){
38507         //this.el.beginMeasure();
38508         this.textEl.setWidth(1);
38509         /*
38510          *  #2804 [new] Tabs in Roojs
38511          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38512          */
38513         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38514         //this.el.endMeasure();
38515     //},
38516
38517     /**
38518      * Sets the text for the tab (Note: this also sets the tooltip text)
38519      * @param {String} text The tab's text and tooltip
38520      */
38521     setText : function(text){
38522         this.text = text;
38523         this.textEl.update(text);
38524         this.setTooltip(text);
38525         //if(!this.tabPanel.resizeTabs){
38526         //    this.autoSize();
38527         //}
38528     },
38529     /**
38530      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38531      */
38532     activate : function(){
38533         this.tabPanel.activate(this.id);
38534     },
38535
38536     /**
38537      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38538      */
38539     disable : function(){
38540         if(this.tabPanel.active != this){
38541             this.disabled = true;
38542             this.pnode.addClass("disabled");
38543         }
38544     },
38545
38546     /**
38547      * Enables this TabPanelItem if it was previously disabled.
38548      */
38549     enable : function(){
38550         this.disabled = false;
38551         this.pnode.removeClass("disabled");
38552     },
38553
38554     /**
38555      * Sets the content for this TabPanelItem.
38556      * @param {String} content The content
38557      * @param {Boolean} loadScripts true to look for and load scripts
38558      */
38559     setContent : function(content, loadScripts){
38560         this.bodyEl.update(content, loadScripts);
38561     },
38562
38563     /**
38564      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38565      * @return {Roo.UpdateManager} The UpdateManager
38566      */
38567     getUpdateManager : function(){
38568         return this.bodyEl.getUpdateManager();
38569     },
38570
38571     /**
38572      * Set a URL to be used to load the content for this TabPanelItem.
38573      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38574      * @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)
38575      * @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)
38576      * @return {Roo.UpdateManager} The UpdateManager
38577      */
38578     setUrl : function(url, params, loadOnce){
38579         if(this.refreshDelegate){
38580             this.un('activate', this.refreshDelegate);
38581         }
38582         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38583         this.on("activate", this.refreshDelegate);
38584         return this.bodyEl.getUpdateManager();
38585     },
38586
38587     /** @private */
38588     _handleRefresh : function(url, params, loadOnce){
38589         if(!loadOnce || !this.loaded){
38590             var updater = this.bodyEl.getUpdateManager();
38591             updater.update(url, params, this._setLoaded.createDelegate(this));
38592         }
38593     },
38594
38595     /**
38596      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38597      *   Will fail silently if the setUrl method has not been called.
38598      *   This does not activate the panel, just updates its content.
38599      */
38600     refresh : function(){
38601         if(this.refreshDelegate){
38602            this.loaded = false;
38603            this.refreshDelegate();
38604         }
38605     },
38606
38607     /** @private */
38608     _setLoaded : function(){
38609         this.loaded = true;
38610     },
38611
38612     /** @private */
38613     closeClick : function(e){
38614         var o = {};
38615         e.stopEvent();
38616         this.fireEvent("beforeclose", this, o);
38617         if(o.cancel !== true){
38618             this.tabPanel.removeTab(this.id);
38619         }
38620     },
38621     /**
38622      * The text displayed in the tooltip for the close icon.
38623      * @type String
38624      */
38625     closeText : "Close this tab"
38626 });
38627 /**
38628 *    This script refer to:
38629 *    Title: International Telephone Input
38630 *    Author: Jack O'Connor
38631 *    Code version:  v12.1.12
38632 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38633 **/
38634
38635 Roo.bootstrap.PhoneInputData = function() {
38636     var d = [
38637       [
38638         "Afghanistan (‫افغانستان‬‎)",
38639         "af",
38640         "93"
38641       ],
38642       [
38643         "Albania (Shqipëri)",
38644         "al",
38645         "355"
38646       ],
38647       [
38648         "Algeria (‫الجزائر‬‎)",
38649         "dz",
38650         "213"
38651       ],
38652       [
38653         "American Samoa",
38654         "as",
38655         "1684"
38656       ],
38657       [
38658         "Andorra",
38659         "ad",
38660         "376"
38661       ],
38662       [
38663         "Angola",
38664         "ao",
38665         "244"
38666       ],
38667       [
38668         "Anguilla",
38669         "ai",
38670         "1264"
38671       ],
38672       [
38673         "Antigua and Barbuda",
38674         "ag",
38675         "1268"
38676       ],
38677       [
38678         "Argentina",
38679         "ar",
38680         "54"
38681       ],
38682       [
38683         "Armenia (Հայաստան)",
38684         "am",
38685         "374"
38686       ],
38687       [
38688         "Aruba",
38689         "aw",
38690         "297"
38691       ],
38692       [
38693         "Australia",
38694         "au",
38695         "61",
38696         0
38697       ],
38698       [
38699         "Austria (Österreich)",
38700         "at",
38701         "43"
38702       ],
38703       [
38704         "Azerbaijan (Azərbaycan)",
38705         "az",
38706         "994"
38707       ],
38708       [
38709         "Bahamas",
38710         "bs",
38711         "1242"
38712       ],
38713       [
38714         "Bahrain (‫البحرين‬‎)",
38715         "bh",
38716         "973"
38717       ],
38718       [
38719         "Bangladesh (বাংলাদেশ)",
38720         "bd",
38721         "880"
38722       ],
38723       [
38724         "Barbados",
38725         "bb",
38726         "1246"
38727       ],
38728       [
38729         "Belarus (Беларусь)",
38730         "by",
38731         "375"
38732       ],
38733       [
38734         "Belgium (België)",
38735         "be",
38736         "32"
38737       ],
38738       [
38739         "Belize",
38740         "bz",
38741         "501"
38742       ],
38743       [
38744         "Benin (Bénin)",
38745         "bj",
38746         "229"
38747       ],
38748       [
38749         "Bermuda",
38750         "bm",
38751         "1441"
38752       ],
38753       [
38754         "Bhutan (འབྲུག)",
38755         "bt",
38756         "975"
38757       ],
38758       [
38759         "Bolivia",
38760         "bo",
38761         "591"
38762       ],
38763       [
38764         "Bosnia and Herzegovina (Босна и Херцеговина)",
38765         "ba",
38766         "387"
38767       ],
38768       [
38769         "Botswana",
38770         "bw",
38771         "267"
38772       ],
38773       [
38774         "Brazil (Brasil)",
38775         "br",
38776         "55"
38777       ],
38778       [
38779         "British Indian Ocean Territory",
38780         "io",
38781         "246"
38782       ],
38783       [
38784         "British Virgin Islands",
38785         "vg",
38786         "1284"
38787       ],
38788       [
38789         "Brunei",
38790         "bn",
38791         "673"
38792       ],
38793       [
38794         "Bulgaria (България)",
38795         "bg",
38796         "359"
38797       ],
38798       [
38799         "Burkina Faso",
38800         "bf",
38801         "226"
38802       ],
38803       [
38804         "Burundi (Uburundi)",
38805         "bi",
38806         "257"
38807       ],
38808       [
38809         "Cambodia (កម្ពុជា)",
38810         "kh",
38811         "855"
38812       ],
38813       [
38814         "Cameroon (Cameroun)",
38815         "cm",
38816         "237"
38817       ],
38818       [
38819         "Canada",
38820         "ca",
38821         "1",
38822         1,
38823         ["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"]
38824       ],
38825       [
38826         "Cape Verde (Kabu Verdi)",
38827         "cv",
38828         "238"
38829       ],
38830       [
38831         "Caribbean Netherlands",
38832         "bq",
38833         "599",
38834         1
38835       ],
38836       [
38837         "Cayman Islands",
38838         "ky",
38839         "1345"
38840       ],
38841       [
38842         "Central African Republic (République centrafricaine)",
38843         "cf",
38844         "236"
38845       ],
38846       [
38847         "Chad (Tchad)",
38848         "td",
38849         "235"
38850       ],
38851       [
38852         "Chile",
38853         "cl",
38854         "56"
38855       ],
38856       [
38857         "China (中国)",
38858         "cn",
38859         "86"
38860       ],
38861       [
38862         "Christmas Island",
38863         "cx",
38864         "61",
38865         2
38866       ],
38867       [
38868         "Cocos (Keeling) Islands",
38869         "cc",
38870         "61",
38871         1
38872       ],
38873       [
38874         "Colombia",
38875         "co",
38876         "57"
38877       ],
38878       [
38879         "Comoros (‫جزر القمر‬‎)",
38880         "km",
38881         "269"
38882       ],
38883       [
38884         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38885         "cd",
38886         "243"
38887       ],
38888       [
38889         "Congo (Republic) (Congo-Brazzaville)",
38890         "cg",
38891         "242"
38892       ],
38893       [
38894         "Cook Islands",
38895         "ck",
38896         "682"
38897       ],
38898       [
38899         "Costa Rica",
38900         "cr",
38901         "506"
38902       ],
38903       [
38904         "Côte d’Ivoire",
38905         "ci",
38906         "225"
38907       ],
38908       [
38909         "Croatia (Hrvatska)",
38910         "hr",
38911         "385"
38912       ],
38913       [
38914         "Cuba",
38915         "cu",
38916         "53"
38917       ],
38918       [
38919         "Curaçao",
38920         "cw",
38921         "599",
38922         0
38923       ],
38924       [
38925         "Cyprus (Κύπρος)",
38926         "cy",
38927         "357"
38928       ],
38929       [
38930         "Czech Republic (Česká republika)",
38931         "cz",
38932         "420"
38933       ],
38934       [
38935         "Denmark (Danmark)",
38936         "dk",
38937         "45"
38938       ],
38939       [
38940         "Djibouti",
38941         "dj",
38942         "253"
38943       ],
38944       [
38945         "Dominica",
38946         "dm",
38947         "1767"
38948       ],
38949       [
38950         "Dominican Republic (República Dominicana)",
38951         "do",
38952         "1",
38953         2,
38954         ["809", "829", "849"]
38955       ],
38956       [
38957         "Ecuador",
38958         "ec",
38959         "593"
38960       ],
38961       [
38962         "Egypt (‫مصر‬‎)",
38963         "eg",
38964         "20"
38965       ],
38966       [
38967         "El Salvador",
38968         "sv",
38969         "503"
38970       ],
38971       [
38972         "Equatorial Guinea (Guinea Ecuatorial)",
38973         "gq",
38974         "240"
38975       ],
38976       [
38977         "Eritrea",
38978         "er",
38979         "291"
38980       ],
38981       [
38982         "Estonia (Eesti)",
38983         "ee",
38984         "372"
38985       ],
38986       [
38987         "Ethiopia",
38988         "et",
38989         "251"
38990       ],
38991       [
38992         "Falkland Islands (Islas Malvinas)",
38993         "fk",
38994         "500"
38995       ],
38996       [
38997         "Faroe Islands (Føroyar)",
38998         "fo",
38999         "298"
39000       ],
39001       [
39002         "Fiji",
39003         "fj",
39004         "679"
39005       ],
39006       [
39007         "Finland (Suomi)",
39008         "fi",
39009         "358",
39010         0
39011       ],
39012       [
39013         "France",
39014         "fr",
39015         "33"
39016       ],
39017       [
39018         "French Guiana (Guyane française)",
39019         "gf",
39020         "594"
39021       ],
39022       [
39023         "French Polynesia (Polynésie française)",
39024         "pf",
39025         "689"
39026       ],
39027       [
39028         "Gabon",
39029         "ga",
39030         "241"
39031       ],
39032       [
39033         "Gambia",
39034         "gm",
39035         "220"
39036       ],
39037       [
39038         "Georgia (საქართველო)",
39039         "ge",
39040         "995"
39041       ],
39042       [
39043         "Germany (Deutschland)",
39044         "de",
39045         "49"
39046       ],
39047       [
39048         "Ghana (Gaana)",
39049         "gh",
39050         "233"
39051       ],
39052       [
39053         "Gibraltar",
39054         "gi",
39055         "350"
39056       ],
39057       [
39058         "Greece (Ελλάδα)",
39059         "gr",
39060         "30"
39061       ],
39062       [
39063         "Greenland (Kalaallit Nunaat)",
39064         "gl",
39065         "299"
39066       ],
39067       [
39068         "Grenada",
39069         "gd",
39070         "1473"
39071       ],
39072       [
39073         "Guadeloupe",
39074         "gp",
39075         "590",
39076         0
39077       ],
39078       [
39079         "Guam",
39080         "gu",
39081         "1671"
39082       ],
39083       [
39084         "Guatemala",
39085         "gt",
39086         "502"
39087       ],
39088       [
39089         "Guernsey",
39090         "gg",
39091         "44",
39092         1
39093       ],
39094       [
39095         "Guinea (Guinée)",
39096         "gn",
39097         "224"
39098       ],
39099       [
39100         "Guinea-Bissau (Guiné Bissau)",
39101         "gw",
39102         "245"
39103       ],
39104       [
39105         "Guyana",
39106         "gy",
39107         "592"
39108       ],
39109       [
39110         "Haiti",
39111         "ht",
39112         "509"
39113       ],
39114       [
39115         "Honduras",
39116         "hn",
39117         "504"
39118       ],
39119       [
39120         "Hong Kong (香港)",
39121         "hk",
39122         "852"
39123       ],
39124       [
39125         "Hungary (Magyarország)",
39126         "hu",
39127         "36"
39128       ],
39129       [
39130         "Iceland (Ísland)",
39131         "is",
39132         "354"
39133       ],
39134       [
39135         "India (भारत)",
39136         "in",
39137         "91"
39138       ],
39139       [
39140         "Indonesia",
39141         "id",
39142         "62"
39143       ],
39144       [
39145         "Iran (‫ایران‬‎)",
39146         "ir",
39147         "98"
39148       ],
39149       [
39150         "Iraq (‫العراق‬‎)",
39151         "iq",
39152         "964"
39153       ],
39154       [
39155         "Ireland",
39156         "ie",
39157         "353"
39158       ],
39159       [
39160         "Isle of Man",
39161         "im",
39162         "44",
39163         2
39164       ],
39165       [
39166         "Israel (‫ישראל‬‎)",
39167         "il",
39168         "972"
39169       ],
39170       [
39171         "Italy (Italia)",
39172         "it",
39173         "39",
39174         0
39175       ],
39176       [
39177         "Jamaica",
39178         "jm",
39179         "1876"
39180       ],
39181       [
39182         "Japan (日本)",
39183         "jp",
39184         "81"
39185       ],
39186       [
39187         "Jersey",
39188         "je",
39189         "44",
39190         3
39191       ],
39192       [
39193         "Jordan (‫الأردن‬‎)",
39194         "jo",
39195         "962"
39196       ],
39197       [
39198         "Kazakhstan (Казахстан)",
39199         "kz",
39200         "7",
39201         1
39202       ],
39203       [
39204         "Kenya",
39205         "ke",
39206         "254"
39207       ],
39208       [
39209         "Kiribati",
39210         "ki",
39211         "686"
39212       ],
39213       [
39214         "Kosovo",
39215         "xk",
39216         "383"
39217       ],
39218       [
39219         "Kuwait (‫الكويت‬‎)",
39220         "kw",
39221         "965"
39222       ],
39223       [
39224         "Kyrgyzstan (Кыргызстан)",
39225         "kg",
39226         "996"
39227       ],
39228       [
39229         "Laos (ລາວ)",
39230         "la",
39231         "856"
39232       ],
39233       [
39234         "Latvia (Latvija)",
39235         "lv",
39236         "371"
39237       ],
39238       [
39239         "Lebanon (‫لبنان‬‎)",
39240         "lb",
39241         "961"
39242       ],
39243       [
39244         "Lesotho",
39245         "ls",
39246         "266"
39247       ],
39248       [
39249         "Liberia",
39250         "lr",
39251         "231"
39252       ],
39253       [
39254         "Libya (‫ليبيا‬‎)",
39255         "ly",
39256         "218"
39257       ],
39258       [
39259         "Liechtenstein",
39260         "li",
39261         "423"
39262       ],
39263       [
39264         "Lithuania (Lietuva)",
39265         "lt",
39266         "370"
39267       ],
39268       [
39269         "Luxembourg",
39270         "lu",
39271         "352"
39272       ],
39273       [
39274         "Macau (澳門)",
39275         "mo",
39276         "853"
39277       ],
39278       [
39279         "Macedonia (FYROM) (Македонија)",
39280         "mk",
39281         "389"
39282       ],
39283       [
39284         "Madagascar (Madagasikara)",
39285         "mg",
39286         "261"
39287       ],
39288       [
39289         "Malawi",
39290         "mw",
39291         "265"
39292       ],
39293       [
39294         "Malaysia",
39295         "my",
39296         "60"
39297       ],
39298       [
39299         "Maldives",
39300         "mv",
39301         "960"
39302       ],
39303       [
39304         "Mali",
39305         "ml",
39306         "223"
39307       ],
39308       [
39309         "Malta",
39310         "mt",
39311         "356"
39312       ],
39313       [
39314         "Marshall Islands",
39315         "mh",
39316         "692"
39317       ],
39318       [
39319         "Martinique",
39320         "mq",
39321         "596"
39322       ],
39323       [
39324         "Mauritania (‫موريتانيا‬‎)",
39325         "mr",
39326         "222"
39327       ],
39328       [
39329         "Mauritius (Moris)",
39330         "mu",
39331         "230"
39332       ],
39333       [
39334         "Mayotte",
39335         "yt",
39336         "262",
39337         1
39338       ],
39339       [
39340         "Mexico (México)",
39341         "mx",
39342         "52"
39343       ],
39344       [
39345         "Micronesia",
39346         "fm",
39347         "691"
39348       ],
39349       [
39350         "Moldova (Republica Moldova)",
39351         "md",
39352         "373"
39353       ],
39354       [
39355         "Monaco",
39356         "mc",
39357         "377"
39358       ],
39359       [
39360         "Mongolia (Монгол)",
39361         "mn",
39362         "976"
39363       ],
39364       [
39365         "Montenegro (Crna Gora)",
39366         "me",
39367         "382"
39368       ],
39369       [
39370         "Montserrat",
39371         "ms",
39372         "1664"
39373       ],
39374       [
39375         "Morocco (‫المغرب‬‎)",
39376         "ma",
39377         "212",
39378         0
39379       ],
39380       [
39381         "Mozambique (Moçambique)",
39382         "mz",
39383         "258"
39384       ],
39385       [
39386         "Myanmar (Burma) (မြန်မာ)",
39387         "mm",
39388         "95"
39389       ],
39390       [
39391         "Namibia (Namibië)",
39392         "na",
39393         "264"
39394       ],
39395       [
39396         "Nauru",
39397         "nr",
39398         "674"
39399       ],
39400       [
39401         "Nepal (नेपाल)",
39402         "np",
39403         "977"
39404       ],
39405       [
39406         "Netherlands (Nederland)",
39407         "nl",
39408         "31"
39409       ],
39410       [
39411         "New Caledonia (Nouvelle-Calédonie)",
39412         "nc",
39413         "687"
39414       ],
39415       [
39416         "New Zealand",
39417         "nz",
39418         "64"
39419       ],
39420       [
39421         "Nicaragua",
39422         "ni",
39423         "505"
39424       ],
39425       [
39426         "Niger (Nijar)",
39427         "ne",
39428         "227"
39429       ],
39430       [
39431         "Nigeria",
39432         "ng",
39433         "234"
39434       ],
39435       [
39436         "Niue",
39437         "nu",
39438         "683"
39439       ],
39440       [
39441         "Norfolk Island",
39442         "nf",
39443         "672"
39444       ],
39445       [
39446         "North Korea (조선 민주주의 인민 공화국)",
39447         "kp",
39448         "850"
39449       ],
39450       [
39451         "Northern Mariana Islands",
39452         "mp",
39453         "1670"
39454       ],
39455       [
39456         "Norway (Norge)",
39457         "no",
39458         "47",
39459         0
39460       ],
39461       [
39462         "Oman (‫عُمان‬‎)",
39463         "om",
39464         "968"
39465       ],
39466       [
39467         "Pakistan (‫پاکستان‬‎)",
39468         "pk",
39469         "92"
39470       ],
39471       [
39472         "Palau",
39473         "pw",
39474         "680"
39475       ],
39476       [
39477         "Palestine (‫فلسطين‬‎)",
39478         "ps",
39479         "970"
39480       ],
39481       [
39482         "Panama (Panamá)",
39483         "pa",
39484         "507"
39485       ],
39486       [
39487         "Papua New Guinea",
39488         "pg",
39489         "675"
39490       ],
39491       [
39492         "Paraguay",
39493         "py",
39494         "595"
39495       ],
39496       [
39497         "Peru (Perú)",
39498         "pe",
39499         "51"
39500       ],
39501       [
39502         "Philippines",
39503         "ph",
39504         "63"
39505       ],
39506       [
39507         "Poland (Polska)",
39508         "pl",
39509         "48"
39510       ],
39511       [
39512         "Portugal",
39513         "pt",
39514         "351"
39515       ],
39516       [
39517         "Puerto Rico",
39518         "pr",
39519         "1",
39520         3,
39521         ["787", "939"]
39522       ],
39523       [
39524         "Qatar (‫قطر‬‎)",
39525         "qa",
39526         "974"
39527       ],
39528       [
39529         "Réunion (La Réunion)",
39530         "re",
39531         "262",
39532         0
39533       ],
39534       [
39535         "Romania (România)",
39536         "ro",
39537         "40"
39538       ],
39539       [
39540         "Russia (Россия)",
39541         "ru",
39542         "7",
39543         0
39544       ],
39545       [
39546         "Rwanda",
39547         "rw",
39548         "250"
39549       ],
39550       [
39551         "Saint Barthélemy",
39552         "bl",
39553         "590",
39554         1
39555       ],
39556       [
39557         "Saint Helena",
39558         "sh",
39559         "290"
39560       ],
39561       [
39562         "Saint Kitts and Nevis",
39563         "kn",
39564         "1869"
39565       ],
39566       [
39567         "Saint Lucia",
39568         "lc",
39569         "1758"
39570       ],
39571       [
39572         "Saint Martin (Saint-Martin (partie française))",
39573         "mf",
39574         "590",
39575         2
39576       ],
39577       [
39578         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39579         "pm",
39580         "508"
39581       ],
39582       [
39583         "Saint Vincent and the Grenadines",
39584         "vc",
39585         "1784"
39586       ],
39587       [
39588         "Samoa",
39589         "ws",
39590         "685"
39591       ],
39592       [
39593         "San Marino",
39594         "sm",
39595         "378"
39596       ],
39597       [
39598         "São Tomé and Príncipe (São Tomé e Príncipe)",
39599         "st",
39600         "239"
39601       ],
39602       [
39603         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39604         "sa",
39605         "966"
39606       ],
39607       [
39608         "Senegal (Sénégal)",
39609         "sn",
39610         "221"
39611       ],
39612       [
39613         "Serbia (Србија)",
39614         "rs",
39615         "381"
39616       ],
39617       [
39618         "Seychelles",
39619         "sc",
39620         "248"
39621       ],
39622       [
39623         "Sierra Leone",
39624         "sl",
39625         "232"
39626       ],
39627       [
39628         "Singapore",
39629         "sg",
39630         "65"
39631       ],
39632       [
39633         "Sint Maarten",
39634         "sx",
39635         "1721"
39636       ],
39637       [
39638         "Slovakia (Slovensko)",
39639         "sk",
39640         "421"
39641       ],
39642       [
39643         "Slovenia (Slovenija)",
39644         "si",
39645         "386"
39646       ],
39647       [
39648         "Solomon Islands",
39649         "sb",
39650         "677"
39651       ],
39652       [
39653         "Somalia (Soomaaliya)",
39654         "so",
39655         "252"
39656       ],
39657       [
39658         "South Africa",
39659         "za",
39660         "27"
39661       ],
39662       [
39663         "South Korea (대한민국)",
39664         "kr",
39665         "82"
39666       ],
39667       [
39668         "South Sudan (‫جنوب السودان‬‎)",
39669         "ss",
39670         "211"
39671       ],
39672       [
39673         "Spain (España)",
39674         "es",
39675         "34"
39676       ],
39677       [
39678         "Sri Lanka (ශ්‍රී ලංකාව)",
39679         "lk",
39680         "94"
39681       ],
39682       [
39683         "Sudan (‫السودان‬‎)",
39684         "sd",
39685         "249"
39686       ],
39687       [
39688         "Suriname",
39689         "sr",
39690         "597"
39691       ],
39692       [
39693         "Svalbard and Jan Mayen",
39694         "sj",
39695         "47",
39696         1
39697       ],
39698       [
39699         "Swaziland",
39700         "sz",
39701         "268"
39702       ],
39703       [
39704         "Sweden (Sverige)",
39705         "se",
39706         "46"
39707       ],
39708       [
39709         "Switzerland (Schweiz)",
39710         "ch",
39711         "41"
39712       ],
39713       [
39714         "Syria (‫سوريا‬‎)",
39715         "sy",
39716         "963"
39717       ],
39718       [
39719         "Taiwan (台灣)",
39720         "tw",
39721         "886"
39722       ],
39723       [
39724         "Tajikistan",
39725         "tj",
39726         "992"
39727       ],
39728       [
39729         "Tanzania",
39730         "tz",
39731         "255"
39732       ],
39733       [
39734         "Thailand (ไทย)",
39735         "th",
39736         "66"
39737       ],
39738       [
39739         "Timor-Leste",
39740         "tl",
39741         "670"
39742       ],
39743       [
39744         "Togo",
39745         "tg",
39746         "228"
39747       ],
39748       [
39749         "Tokelau",
39750         "tk",
39751         "690"
39752       ],
39753       [
39754         "Tonga",
39755         "to",
39756         "676"
39757       ],
39758       [
39759         "Trinidad and Tobago",
39760         "tt",
39761         "1868"
39762       ],
39763       [
39764         "Tunisia (‫تونس‬‎)",
39765         "tn",
39766         "216"
39767       ],
39768       [
39769         "Turkey (Türkiye)",
39770         "tr",
39771         "90"
39772       ],
39773       [
39774         "Turkmenistan",
39775         "tm",
39776         "993"
39777       ],
39778       [
39779         "Turks and Caicos Islands",
39780         "tc",
39781         "1649"
39782       ],
39783       [
39784         "Tuvalu",
39785         "tv",
39786         "688"
39787       ],
39788       [
39789         "U.S. Virgin Islands",
39790         "vi",
39791         "1340"
39792       ],
39793       [
39794         "Uganda",
39795         "ug",
39796         "256"
39797       ],
39798       [
39799         "Ukraine (Україна)",
39800         "ua",
39801         "380"
39802       ],
39803       [
39804         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39805         "ae",
39806         "971"
39807       ],
39808       [
39809         "United Kingdom",
39810         "gb",
39811         "44",
39812         0
39813       ],
39814       [
39815         "United States",
39816         "us",
39817         "1",
39818         0
39819       ],
39820       [
39821         "Uruguay",
39822         "uy",
39823         "598"
39824       ],
39825       [
39826         "Uzbekistan (Oʻzbekiston)",
39827         "uz",
39828         "998"
39829       ],
39830       [
39831         "Vanuatu",
39832         "vu",
39833         "678"
39834       ],
39835       [
39836         "Vatican City (Città del Vaticano)",
39837         "va",
39838         "39",
39839         1
39840       ],
39841       [
39842         "Venezuela",
39843         "ve",
39844         "58"
39845       ],
39846       [
39847         "Vietnam (Việt Nam)",
39848         "vn",
39849         "84"
39850       ],
39851       [
39852         "Wallis and Futuna (Wallis-et-Futuna)",
39853         "wf",
39854         "681"
39855       ],
39856       [
39857         "Western Sahara (‫الصحراء الغربية‬‎)",
39858         "eh",
39859         "212",
39860         1
39861       ],
39862       [
39863         "Yemen (‫اليمن‬‎)",
39864         "ye",
39865         "967"
39866       ],
39867       [
39868         "Zambia",
39869         "zm",
39870         "260"
39871       ],
39872       [
39873         "Zimbabwe",
39874         "zw",
39875         "263"
39876       ],
39877       [
39878         "Åland Islands",
39879         "ax",
39880         "358",
39881         1
39882       ]
39883   ];
39884   
39885   return d;
39886 }/**
39887 *    This script refer to:
39888 *    Title: International Telephone Input
39889 *    Author: Jack O'Connor
39890 *    Code version:  v12.1.12
39891 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39892 **/
39893
39894 /**
39895  * @class Roo.bootstrap.PhoneInput
39896  * @extends Roo.bootstrap.TriggerField
39897  * An input with International dial-code selection
39898  
39899  * @cfg {String} defaultDialCode default '+852'
39900  * @cfg {Array} preferedCountries default []
39901   
39902  * @constructor
39903  * Create a new PhoneInput.
39904  * @param {Object} config Configuration options
39905  */
39906
39907 Roo.bootstrap.PhoneInput = function(config) {
39908     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39909 };
39910
39911 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39912         
39913         listWidth: undefined,
39914         
39915         selectedClass: 'active',
39916         
39917         invalidClass : "has-warning",
39918         
39919         validClass: 'has-success',
39920         
39921         allowed: '0123456789',
39922         
39923         max_length: 15,
39924         
39925         /**
39926          * @cfg {String} defaultDialCode The default dial code when initializing the input
39927          */
39928         defaultDialCode: '+852',
39929         
39930         /**
39931          * @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
39932          */
39933         preferedCountries: false,
39934         
39935         getAutoCreate : function()
39936         {
39937             var data = Roo.bootstrap.PhoneInputData();
39938             var align = this.labelAlign || this.parentLabelAlign();
39939             var id = Roo.id();
39940             
39941             this.allCountries = [];
39942             this.dialCodeMapping = [];
39943             
39944             for (var i = 0; i < data.length; i++) {
39945               var c = data[i];
39946               this.allCountries[i] = {
39947                 name: c[0],
39948                 iso2: c[1],
39949                 dialCode: c[2],
39950                 priority: c[3] || 0,
39951                 areaCodes: c[4] || null
39952               };
39953               this.dialCodeMapping[c[2]] = {
39954                   name: c[0],
39955                   iso2: c[1],
39956                   priority: c[3] || 0,
39957                   areaCodes: c[4] || null
39958               };
39959             }
39960             
39961             var cfg = {
39962                 cls: 'form-group',
39963                 cn: []
39964             };
39965             
39966             var input =  {
39967                 tag: 'input',
39968                 id : id,
39969                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39970                 maxlength: this.max_length,
39971                 cls : 'form-control tel-input',
39972                 autocomplete: 'new-password'
39973             };
39974             
39975             var hiddenInput = {
39976                 tag: 'input',
39977                 type: 'hidden',
39978                 cls: 'hidden-tel-input'
39979             };
39980             
39981             if (this.name) {
39982                 hiddenInput.name = this.name;
39983             }
39984             
39985             if (this.disabled) {
39986                 input.disabled = true;
39987             }
39988             
39989             var flag_container = {
39990                 tag: 'div',
39991                 cls: 'flag-box',
39992                 cn: [
39993                     {
39994                         tag: 'div',
39995                         cls: 'flag'
39996                     },
39997                     {
39998                         tag: 'div',
39999                         cls: 'caret'
40000                     }
40001                 ]
40002             };
40003             
40004             var box = {
40005                 tag: 'div',
40006                 cls: this.hasFeedback ? 'has-feedback' : '',
40007                 cn: [
40008                     hiddenInput,
40009                     input,
40010                     {
40011                         tag: 'input',
40012                         cls: 'dial-code-holder',
40013                         disabled: true
40014                     }
40015                 ]
40016             };
40017             
40018             var container = {
40019                 cls: 'roo-select2-container input-group',
40020                 cn: [
40021                     flag_container,
40022                     box
40023                 ]
40024             };
40025             
40026             if (this.fieldLabel.length) {
40027                 var indicator = {
40028                     tag: 'i',
40029                     tooltip: 'This field is required'
40030                 };
40031                 
40032                 var label = {
40033                     tag: 'label',
40034                     'for':  id,
40035                     cls: 'control-label',
40036                     cn: []
40037                 };
40038                 
40039                 var label_text = {
40040                     tag: 'span',
40041                     html: this.fieldLabel
40042                 };
40043                 
40044                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40045                 label.cn = [
40046                     indicator,
40047                     label_text
40048                 ];
40049                 
40050                 if(this.indicatorpos == 'right') {
40051                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40052                     label.cn = [
40053                         label_text,
40054                         indicator
40055                     ];
40056                 }
40057                 
40058                 if(align == 'left') {
40059                     container = {
40060                         tag: 'div',
40061                         cn: [
40062                             container
40063                         ]
40064                     };
40065                     
40066                     if(this.labelWidth > 12){
40067                         label.style = "width: " + this.labelWidth + 'px';
40068                     }
40069                     if(this.labelWidth < 13 && this.labelmd == 0){
40070                         this.labelmd = this.labelWidth;
40071                     }
40072                     if(this.labellg > 0){
40073                         label.cls += ' col-lg-' + this.labellg;
40074                         input.cls += ' col-lg-' + (12 - this.labellg);
40075                     }
40076                     if(this.labelmd > 0){
40077                         label.cls += ' col-md-' + this.labelmd;
40078                         container.cls += ' col-md-' + (12 - this.labelmd);
40079                     }
40080                     if(this.labelsm > 0){
40081                         label.cls += ' col-sm-' + this.labelsm;
40082                         container.cls += ' col-sm-' + (12 - this.labelsm);
40083                     }
40084                     if(this.labelxs > 0){
40085                         label.cls += ' col-xs-' + this.labelxs;
40086                         container.cls += ' col-xs-' + (12 - this.labelxs);
40087                     }
40088                 }
40089             }
40090             
40091             cfg.cn = [
40092                 label,
40093                 container
40094             ];
40095             
40096             var settings = this;
40097             
40098             ['xs','sm','md','lg'].map(function(size){
40099                 if (settings[size]) {
40100                     cfg.cls += ' col-' + size + '-' + settings[size];
40101                 }
40102             });
40103             
40104             this.store = new Roo.data.Store({
40105                 proxy : new Roo.data.MemoryProxy({}),
40106                 reader : new Roo.data.JsonReader({
40107                     fields : [
40108                         {
40109                             'name' : 'name',
40110                             'type' : 'string'
40111                         },
40112                         {
40113                             'name' : 'iso2',
40114                             'type' : 'string'
40115                         },
40116                         {
40117                             'name' : 'dialCode',
40118                             'type' : 'string'
40119                         },
40120                         {
40121                             'name' : 'priority',
40122                             'type' : 'string'
40123                         },
40124                         {
40125                             'name' : 'areaCodes',
40126                             'type' : 'string'
40127                         }
40128                     ]
40129                 })
40130             });
40131             
40132             if(!this.preferedCountries) {
40133                 this.preferedCountries = [
40134                     'hk',
40135                     'gb',
40136                     'us'
40137                 ];
40138             }
40139             
40140             var p = this.preferedCountries.reverse();
40141             
40142             if(p) {
40143                 for (var i = 0; i < p.length; i++) {
40144                     for (var j = 0; j < this.allCountries.length; j++) {
40145                         if(this.allCountries[j].iso2 == p[i]) {
40146                             var t = this.allCountries[j];
40147                             this.allCountries.splice(j,1);
40148                             this.allCountries.unshift(t);
40149                         }
40150                     } 
40151                 }
40152             }
40153             
40154             this.store.proxy.data = {
40155                 success: true,
40156                 data: this.allCountries
40157             };
40158             
40159             return cfg;
40160         },
40161         
40162         initEvents : function()
40163         {
40164             this.createList();
40165             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40166             
40167             this.indicator = this.indicatorEl();
40168             this.flag = this.flagEl();
40169             this.dialCodeHolder = this.dialCodeHolderEl();
40170             
40171             this.trigger = this.el.select('div.flag-box',true).first();
40172             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40173             
40174             var _this = this;
40175             
40176             (function(){
40177                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40178                 _this.list.setWidth(lw);
40179             }).defer(100);
40180             
40181             this.list.on('mouseover', this.onViewOver, this);
40182             this.list.on('mousemove', this.onViewMove, this);
40183             this.inputEl().on("keyup", this.onKeyUp, this);
40184             this.inputEl().on("keypress", this.onKeyPress, this);
40185             
40186             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40187
40188             this.view = new Roo.View(this.list, this.tpl, {
40189                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40190             });
40191             
40192             this.view.on('click', this.onViewClick, this);
40193             this.setValue(this.defaultDialCode);
40194         },
40195         
40196         onTriggerClick : function(e)
40197         {
40198             Roo.log('trigger click');
40199             if(this.disabled){
40200                 return;
40201             }
40202             
40203             if(this.isExpanded()){
40204                 this.collapse();
40205                 this.hasFocus = false;
40206             }else {
40207                 this.store.load({});
40208                 this.hasFocus = true;
40209                 this.expand();
40210             }
40211         },
40212         
40213         isExpanded : function()
40214         {
40215             return this.list.isVisible();
40216         },
40217         
40218         collapse : function()
40219         {
40220             if(!this.isExpanded()){
40221                 return;
40222             }
40223             this.list.hide();
40224             Roo.get(document).un('mousedown', this.collapseIf, this);
40225             Roo.get(document).un('mousewheel', this.collapseIf, this);
40226             this.fireEvent('collapse', this);
40227             this.validate();
40228         },
40229         
40230         expand : function()
40231         {
40232             Roo.log('expand');
40233
40234             if(this.isExpanded() || !this.hasFocus){
40235                 return;
40236             }
40237             
40238             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40239             this.list.setWidth(lw);
40240             
40241             this.list.show();
40242             this.restrictHeight();
40243             
40244             Roo.get(document).on('mousedown', this.collapseIf, this);
40245             Roo.get(document).on('mousewheel', this.collapseIf, this);
40246             
40247             this.fireEvent('expand', this);
40248         },
40249         
40250         restrictHeight : function()
40251         {
40252             this.list.alignTo(this.inputEl(), this.listAlign);
40253             this.list.alignTo(this.inputEl(), this.listAlign);
40254         },
40255         
40256         onViewOver : function(e, t)
40257         {
40258             if(this.inKeyMode){
40259                 return;
40260             }
40261             var item = this.view.findItemFromChild(t);
40262             
40263             if(item){
40264                 var index = this.view.indexOf(item);
40265                 this.select(index, false);
40266             }
40267         },
40268
40269         // private
40270         onViewClick : function(view, doFocus, el, e)
40271         {
40272             var index = this.view.getSelectedIndexes()[0];
40273             
40274             var r = this.store.getAt(index);
40275             
40276             if(r){
40277                 this.onSelect(r, index);
40278             }
40279             if(doFocus !== false && !this.blockFocus){
40280                 this.inputEl().focus();
40281             }
40282         },
40283         
40284         onViewMove : function(e, t)
40285         {
40286             this.inKeyMode = false;
40287         },
40288         
40289         select : function(index, scrollIntoView)
40290         {
40291             this.selectedIndex = index;
40292             this.view.select(index);
40293             if(scrollIntoView !== false){
40294                 var el = this.view.getNode(index);
40295                 if(el){
40296                     this.list.scrollChildIntoView(el, false);
40297                 }
40298             }
40299         },
40300         
40301         createList : function()
40302         {
40303             this.list = Roo.get(document.body).createChild({
40304                 tag: 'ul',
40305                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40306                 style: 'display:none'
40307             });
40308             
40309             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40310         },
40311         
40312         collapseIf : function(e)
40313         {
40314             var in_combo  = e.within(this.el);
40315             var in_list =  e.within(this.list);
40316             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40317             
40318             if (in_combo || in_list || is_list) {
40319                 return;
40320             }
40321             this.collapse();
40322         },
40323         
40324         onSelect : function(record, index)
40325         {
40326             if(this.fireEvent('beforeselect', this, record, index) !== false){
40327                 
40328                 this.setFlagClass(record.data.iso2);
40329                 this.setDialCode(record.data.dialCode);
40330                 this.hasFocus = false;
40331                 this.collapse();
40332                 this.fireEvent('select', this, record, index);
40333             }
40334         },
40335         
40336         flagEl : function()
40337         {
40338             var flag = this.el.select('div.flag',true).first();
40339             if(!flag){
40340                 return false;
40341             }
40342             return flag;
40343         },
40344         
40345         dialCodeHolderEl : function()
40346         {
40347             var d = this.el.select('input.dial-code-holder',true).first();
40348             if(!d){
40349                 return false;
40350             }
40351             return d;
40352         },
40353         
40354         setDialCode : function(v)
40355         {
40356             this.dialCodeHolder.dom.value = '+'+v;
40357         },
40358         
40359         setFlagClass : function(n)
40360         {
40361             this.flag.dom.className = 'flag '+n;
40362         },
40363         
40364         getValue : function()
40365         {
40366             var v = this.inputEl().getValue();
40367             if(this.dialCodeHolder) {
40368                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40369             }
40370             return v;
40371         },
40372         
40373         setValue : function(v)
40374         {
40375             var d = this.getDialCode(v);
40376             
40377             //invalid dial code
40378             if(v.length == 0 || !d || d.length == 0) {
40379                 if(this.rendered){
40380                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40381                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40382                 }
40383                 return;
40384             }
40385             
40386             //valid dial code
40387             this.setFlagClass(this.dialCodeMapping[d].iso2);
40388             this.setDialCode(d);
40389             this.inputEl().dom.value = v.replace('+'+d,'');
40390             this.hiddenEl().dom.value = this.getValue();
40391             
40392             this.validate();
40393         },
40394         
40395         getDialCode : function(v)
40396         {
40397             v = v ||  '';
40398             
40399             if (v.length == 0) {
40400                 return this.dialCodeHolder.dom.value;
40401             }
40402             
40403             var dialCode = "";
40404             if (v.charAt(0) != "+") {
40405                 return false;
40406             }
40407             var numericChars = "";
40408             for (var i = 1; i < v.length; i++) {
40409               var c = v.charAt(i);
40410               if (!isNaN(c)) {
40411                 numericChars += c;
40412                 if (this.dialCodeMapping[numericChars]) {
40413                   dialCode = v.substr(1, i);
40414                 }
40415                 if (numericChars.length == 4) {
40416                   break;
40417                 }
40418               }
40419             }
40420             return dialCode;
40421         },
40422         
40423         reset : function()
40424         {
40425             this.setValue(this.defaultDialCode);
40426             this.validate();
40427         },
40428         
40429         hiddenEl : function()
40430         {
40431             return this.el.select('input.hidden-tel-input',true).first();
40432         },
40433         
40434         // after setting val
40435         onKeyUp : function(e){
40436             this.setValue(this.getValue());
40437         },
40438         
40439         onKeyPress : function(e){
40440             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40441                 e.stopEvent();
40442             }
40443         }
40444         
40445 });
40446 /**
40447  * @class Roo.bootstrap.MoneyField
40448  * @extends Roo.bootstrap.ComboBox
40449  * Bootstrap MoneyField class
40450  * 
40451  * @constructor
40452  * Create a new MoneyField.
40453  * @param {Object} config Configuration options
40454  */
40455
40456 Roo.bootstrap.MoneyField = function(config) {
40457     
40458     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40459     
40460 };
40461
40462 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40463     
40464     /**
40465      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40466      */
40467     allowDecimals : true,
40468     /**
40469      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40470      */
40471     decimalSeparator : ".",
40472     /**
40473      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40474      */
40475     decimalPrecision : 0,
40476     /**
40477      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40478      */
40479     allowNegative : true,
40480     /**
40481      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40482      */
40483     allowZero: true,
40484     /**
40485      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40486      */
40487     minValue : Number.NEGATIVE_INFINITY,
40488     /**
40489      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40490      */
40491     maxValue : Number.MAX_VALUE,
40492     /**
40493      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40494      */
40495     minText : "The minimum value for this field is {0}",
40496     /**
40497      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40498      */
40499     maxText : "The maximum value for this field is {0}",
40500     /**
40501      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40502      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40503      */
40504     nanText : "{0} is not a valid number",
40505     /**
40506      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40507      */
40508     castInt : true,
40509     /**
40510      * @cfg {String} defaults currency of the MoneyField
40511      * value should be in lkey
40512      */
40513     defaultCurrency : false,
40514     /**
40515      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40516      */
40517     thousandsDelimiter : false,
40518     /**
40519      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40520      */
40521     max_length: false,
40522     
40523     inputlg : 9,
40524     inputmd : 9,
40525     inputsm : 9,
40526     inputxs : 6,
40527     
40528     store : false,
40529     
40530     getAutoCreate : function()
40531     {
40532         var align = this.labelAlign || this.parentLabelAlign();
40533         
40534         var id = Roo.id();
40535
40536         var cfg = {
40537             cls: 'form-group',
40538             cn: []
40539         };
40540
40541         var input =  {
40542             tag: 'input',
40543             id : id,
40544             cls : 'form-control roo-money-amount-input',
40545             autocomplete: 'new-password'
40546         };
40547         
40548         var hiddenInput = {
40549             tag: 'input',
40550             type: 'hidden',
40551             id: Roo.id(),
40552             cls: 'hidden-number-input'
40553         };
40554         
40555         if(this.max_length) {
40556             input.maxlength = this.max_length; 
40557         }
40558         
40559         if (this.name) {
40560             hiddenInput.name = this.name;
40561         }
40562
40563         if (this.disabled) {
40564             input.disabled = true;
40565         }
40566
40567         var clg = 12 - this.inputlg;
40568         var cmd = 12 - this.inputmd;
40569         var csm = 12 - this.inputsm;
40570         var cxs = 12 - this.inputxs;
40571         
40572         var container = {
40573             tag : 'div',
40574             cls : 'row roo-money-field',
40575             cn : [
40576                 {
40577                     tag : 'div',
40578                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40579                     cn : [
40580                         {
40581                             tag : 'div',
40582                             cls: 'roo-select2-container input-group',
40583                             cn: [
40584                                 {
40585                                     tag : 'input',
40586                                     cls : 'form-control roo-money-currency-input',
40587                                     autocomplete: 'new-password',
40588                                     readOnly : 1,
40589                                     name : this.currencyName
40590                                 },
40591                                 {
40592                                     tag :'span',
40593                                     cls : 'input-group-addon',
40594                                     cn : [
40595                                         {
40596                                             tag: 'span',
40597                                             cls: 'caret'
40598                                         }
40599                                     ]
40600                                 }
40601                             ]
40602                         }
40603                     ]
40604                 },
40605                 {
40606                     tag : 'div',
40607                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40608                     cn : [
40609                         {
40610                             tag: 'div',
40611                             cls: this.hasFeedback ? 'has-feedback' : '',
40612                             cn: [
40613                                 input
40614                             ]
40615                         }
40616                     ]
40617                 }
40618             ]
40619             
40620         };
40621         
40622         if (this.fieldLabel.length) {
40623             var indicator = {
40624                 tag: 'i',
40625                 tooltip: 'This field is required'
40626             };
40627
40628             var label = {
40629                 tag: 'label',
40630                 'for':  id,
40631                 cls: 'control-label',
40632                 cn: []
40633             };
40634
40635             var label_text = {
40636                 tag: 'span',
40637                 html: this.fieldLabel
40638             };
40639
40640             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40641             label.cn = [
40642                 indicator,
40643                 label_text
40644             ];
40645
40646             if(this.indicatorpos == 'right') {
40647                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40648                 label.cn = [
40649                     label_text,
40650                     indicator
40651                 ];
40652             }
40653
40654             if(align == 'left') {
40655                 container = {
40656                     tag: 'div',
40657                     cn: [
40658                         container
40659                     ]
40660                 };
40661
40662                 if(this.labelWidth > 12){
40663                     label.style = "width: " + this.labelWidth + 'px';
40664                 }
40665                 if(this.labelWidth < 13 && this.labelmd == 0){
40666                     this.labelmd = this.labelWidth;
40667                 }
40668                 if(this.labellg > 0){
40669                     label.cls += ' col-lg-' + this.labellg;
40670                     input.cls += ' col-lg-' + (12 - this.labellg);
40671                 }
40672                 if(this.labelmd > 0){
40673                     label.cls += ' col-md-' + this.labelmd;
40674                     container.cls += ' col-md-' + (12 - this.labelmd);
40675                 }
40676                 if(this.labelsm > 0){
40677                     label.cls += ' col-sm-' + this.labelsm;
40678                     container.cls += ' col-sm-' + (12 - this.labelsm);
40679                 }
40680                 if(this.labelxs > 0){
40681                     label.cls += ' col-xs-' + this.labelxs;
40682                     container.cls += ' col-xs-' + (12 - this.labelxs);
40683                 }
40684             }
40685         }
40686
40687         cfg.cn = [
40688             label,
40689             container,
40690             hiddenInput
40691         ];
40692         
40693         var settings = this;
40694
40695         ['xs','sm','md','lg'].map(function(size){
40696             if (settings[size]) {
40697                 cfg.cls += ' col-' + size + '-' + settings[size];
40698             }
40699         });
40700         
40701         return cfg;
40702     },
40703     
40704     initEvents : function()
40705     {
40706         this.indicator = this.indicatorEl();
40707         
40708         this.initCurrencyEvent();
40709         
40710         this.initNumberEvent();
40711     },
40712     
40713     initCurrencyEvent : function()
40714     {
40715         if (!this.store) {
40716             throw "can not find store for combo";
40717         }
40718         
40719         this.store = Roo.factory(this.store, Roo.data);
40720         this.store.parent = this;
40721         
40722         this.createList();
40723         
40724         this.triggerEl = this.el.select('.input-group-addon', true).first();
40725         
40726         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40727         
40728         var _this = this;
40729         
40730         (function(){
40731             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40732             _this.list.setWidth(lw);
40733         }).defer(100);
40734         
40735         this.list.on('mouseover', this.onViewOver, this);
40736         this.list.on('mousemove', this.onViewMove, this);
40737         this.list.on('scroll', this.onViewScroll, this);
40738         
40739         if(!this.tpl){
40740             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40741         }
40742         
40743         this.view = new Roo.View(this.list, this.tpl, {
40744             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40745         });
40746         
40747         this.view.on('click', this.onViewClick, this);
40748         
40749         this.store.on('beforeload', this.onBeforeLoad, this);
40750         this.store.on('load', this.onLoad, this);
40751         this.store.on('loadexception', this.onLoadException, this);
40752         
40753         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40754             "up" : function(e){
40755                 this.inKeyMode = true;
40756                 this.selectPrev();
40757             },
40758
40759             "down" : function(e){
40760                 if(!this.isExpanded()){
40761                     this.onTriggerClick();
40762                 }else{
40763                     this.inKeyMode = true;
40764                     this.selectNext();
40765                 }
40766             },
40767
40768             "enter" : function(e){
40769                 this.collapse();
40770                 
40771                 if(this.fireEvent("specialkey", this, e)){
40772                     this.onViewClick(false);
40773                 }
40774                 
40775                 return true;
40776             },
40777
40778             "esc" : function(e){
40779                 this.collapse();
40780             },
40781
40782             "tab" : function(e){
40783                 this.collapse();
40784                 
40785                 if(this.fireEvent("specialkey", this, e)){
40786                     this.onViewClick(false);
40787                 }
40788                 
40789                 return true;
40790             },
40791
40792             scope : this,
40793
40794             doRelay : function(foo, bar, hname){
40795                 if(hname == 'down' || this.scope.isExpanded()){
40796                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40797                 }
40798                 return true;
40799             },
40800
40801             forceKeyDown: true
40802         });
40803         
40804         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40805         
40806     },
40807     
40808     initNumberEvent : function(e)
40809     {
40810         this.inputEl().on("keydown" , this.fireKey,  this);
40811         this.inputEl().on("focus", this.onFocus,  this);
40812         this.inputEl().on("blur", this.onBlur,  this);
40813         
40814         this.inputEl().relayEvent('keyup', this);
40815         
40816         if(this.indicator){
40817             this.indicator.addClass('invisible');
40818         }
40819  
40820         this.originalValue = this.getValue();
40821         
40822         if(this.validationEvent == 'keyup'){
40823             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40824             this.inputEl().on('keyup', this.filterValidation, this);
40825         }
40826         else if(this.validationEvent !== false){
40827             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40828         }
40829         
40830         if(this.selectOnFocus){
40831             this.on("focus", this.preFocus, this);
40832             
40833         }
40834         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40835             this.inputEl().on("keypress", this.filterKeys, this);
40836         } else {
40837             this.inputEl().relayEvent('keypress', this);
40838         }
40839         
40840         var allowed = "0123456789";
40841         
40842         if(this.allowDecimals){
40843             allowed += this.decimalSeparator;
40844         }
40845         
40846         if(this.allowNegative){
40847             allowed += "-";
40848         }
40849         
40850         if(this.thousandsDelimiter) {
40851             allowed += ",";
40852         }
40853         
40854         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40855         
40856         var keyPress = function(e){
40857             
40858             var k = e.getKey();
40859             
40860             var c = e.getCharCode();
40861             
40862             if(
40863                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40864                     allowed.indexOf(String.fromCharCode(c)) === -1
40865             ){
40866                 e.stopEvent();
40867                 return;
40868             }
40869             
40870             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40871                 return;
40872             }
40873             
40874             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40875                 e.stopEvent();
40876             }
40877         };
40878         
40879         this.inputEl().on("keypress", keyPress, this);
40880         
40881     },
40882     
40883     onTriggerClick : function(e)
40884     {   
40885         if(this.disabled){
40886             return;
40887         }
40888         
40889         this.page = 0;
40890         this.loadNext = false;
40891         
40892         if(this.isExpanded()){
40893             this.collapse();
40894             return;
40895         }
40896         
40897         this.hasFocus = true;
40898         
40899         if(this.triggerAction == 'all') {
40900             this.doQuery(this.allQuery, true);
40901             return;
40902         }
40903         
40904         this.doQuery(this.getRawValue());
40905     },
40906     
40907     getCurrency : function()
40908     {   
40909         var v = this.currencyEl().getValue();
40910         
40911         return v;
40912     },
40913     
40914     restrictHeight : function()
40915     {
40916         this.list.alignTo(this.currencyEl(), this.listAlign);
40917         this.list.alignTo(this.currencyEl(), this.listAlign);
40918     },
40919     
40920     onViewClick : function(view, doFocus, el, e)
40921     {
40922         var index = this.view.getSelectedIndexes()[0];
40923         
40924         var r = this.store.getAt(index);
40925         
40926         if(r){
40927             this.onSelect(r, index);
40928         }
40929     },
40930     
40931     onSelect : function(record, index){
40932         
40933         if(this.fireEvent('beforeselect', this, record, index) !== false){
40934         
40935             this.setFromCurrencyData(index > -1 ? record.data : false);
40936             
40937             this.collapse();
40938             
40939             this.fireEvent('select', this, record, index);
40940         }
40941     },
40942     
40943     setFromCurrencyData : function(o)
40944     {
40945         var currency = '';
40946         
40947         this.lastCurrency = o;
40948         
40949         if (this.currencyField) {
40950             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40951         } else {
40952             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40953         }
40954         
40955         this.lastSelectionText = currency;
40956         
40957         //setting default currency
40958         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40959             this.setCurrency(this.defaultCurrency);
40960             return;
40961         }
40962         
40963         this.setCurrency(currency);
40964     },
40965     
40966     setFromData : function(o)
40967     {
40968         var c = {};
40969         
40970         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40971         
40972         this.setFromCurrencyData(c);
40973         
40974         var value = '';
40975         
40976         if (this.name) {
40977             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40978         } else {
40979             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40980         }
40981         
40982         this.setValue(value);
40983         
40984     },
40985     
40986     setCurrency : function(v)
40987     {   
40988         this.currencyValue = v;
40989         
40990         if(this.rendered){
40991             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40992             this.validate();
40993         }
40994     },
40995     
40996     setValue : function(v)
40997     {
40998         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40999         
41000         this.value = v;
41001         
41002         if(this.rendered){
41003             
41004             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41005             
41006             this.inputEl().dom.value = (v == '') ? '' :
41007                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41008             
41009             if(!this.allowZero && v === '0') {
41010                 this.hiddenEl().dom.value = '';
41011                 this.inputEl().dom.value = '';
41012             }
41013             
41014             this.validate();
41015         }
41016     },
41017     
41018     getRawValue : function()
41019     {
41020         var v = this.inputEl().getValue();
41021         
41022         return v;
41023     },
41024     
41025     getValue : function()
41026     {
41027         return this.fixPrecision(this.parseValue(this.getRawValue()));
41028     },
41029     
41030     parseValue : function(value)
41031     {
41032         if(this.thousandsDelimiter) {
41033             value += "";
41034             r = new RegExp(",", "g");
41035             value = value.replace(r, "");
41036         }
41037         
41038         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41039         return isNaN(value) ? '' : value;
41040         
41041     },
41042     
41043     fixPrecision : function(value)
41044     {
41045         if(this.thousandsDelimiter) {
41046             value += "";
41047             r = new RegExp(",", "g");
41048             value = value.replace(r, "");
41049         }
41050         
41051         var nan = isNaN(value);
41052         
41053         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41054             return nan ? '' : value;
41055         }
41056         return parseFloat(value).toFixed(this.decimalPrecision);
41057     },
41058     
41059     decimalPrecisionFcn : function(v)
41060     {
41061         return Math.floor(v);
41062     },
41063     
41064     validateValue : function(value)
41065     {
41066         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41067             return false;
41068         }
41069         
41070         var num = this.parseValue(value);
41071         
41072         if(isNaN(num)){
41073             this.markInvalid(String.format(this.nanText, value));
41074             return false;
41075         }
41076         
41077         if(num < this.minValue){
41078             this.markInvalid(String.format(this.minText, this.minValue));
41079             return false;
41080         }
41081         
41082         if(num > this.maxValue){
41083             this.markInvalid(String.format(this.maxText, this.maxValue));
41084             return false;
41085         }
41086         
41087         return true;
41088     },
41089     
41090     validate : function()
41091     {
41092         if(this.disabled || this.allowBlank){
41093             this.markValid();
41094             return true;
41095         }
41096         
41097         var currency = this.getCurrency();
41098         
41099         if(this.validateValue(this.getRawValue()) && currency.length){
41100             this.markValid();
41101             return true;
41102         }
41103         
41104         this.markInvalid();
41105         return false;
41106     },
41107     
41108     getName: function()
41109     {
41110         return this.name;
41111     },
41112     
41113     beforeBlur : function()
41114     {
41115         if(!this.castInt){
41116             return;
41117         }
41118         
41119         var v = this.parseValue(this.getRawValue());
41120         
41121         if(v || v == 0){
41122             this.setValue(v);
41123         }
41124     },
41125     
41126     onBlur : function()
41127     {
41128         this.beforeBlur();
41129         
41130         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41131             //this.el.removeClass(this.focusClass);
41132         }
41133         
41134         this.hasFocus = false;
41135         
41136         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41137             this.validate();
41138         }
41139         
41140         var v = this.getValue();
41141         
41142         if(String(v) !== String(this.startValue)){
41143             this.fireEvent('change', this, v, this.startValue);
41144         }
41145         
41146         this.fireEvent("blur", this);
41147     },
41148     
41149     inputEl : function()
41150     {
41151         return this.el.select('.roo-money-amount-input', true).first();
41152     },
41153     
41154     currencyEl : function()
41155     {
41156         return this.el.select('.roo-money-currency-input', true).first();
41157     },
41158     
41159     hiddenEl : function()
41160     {
41161         return this.el.select('input.hidden-number-input',true).first();
41162     }
41163     
41164 });