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';
4264             } else {
4265                 cfg.cls += ' navbar-left';
4266             }
4267         }
4268         
4269         if (this.align === 'right') {
4270             cfg.cls += ' navbar-right';
4271         }
4272         
4273         if (this.inverse) {
4274             cfg.cls += ' navbar-inverse';
4275             
4276         }
4277         
4278         
4279         return cfg;
4280     },
4281     /**
4282     * sets the active Navigation item
4283     * @param {Roo.bootstrap.NavItem} the new current navitem
4284     */
4285     setActiveItem : function(item)
4286     {
4287         var prev = false;
4288         Roo.each(this.navItems, function(v){
4289             if (v == item) {
4290                 return ;
4291             }
4292             if (v.isActive()) {
4293                 v.setActive(false, true);
4294                 prev = v;
4295                 
4296             }
4297             
4298         });
4299
4300         item.setActive(true, true);
4301         this.fireEvent('changed', this, item, prev);
4302         
4303         
4304     },
4305     /**
4306     * gets the active Navigation item
4307     * @return {Roo.bootstrap.NavItem} the current navitem
4308     */
4309     getActive : function()
4310     {
4311         
4312         var prev = false;
4313         Roo.each(this.navItems, function(v){
4314             
4315             if (v.isActive()) {
4316                 prev = v;
4317                 
4318             }
4319             
4320         });
4321         return prev;
4322     },
4323     
4324     indexOfNav : function()
4325     {
4326         
4327         var prev = false;
4328         Roo.each(this.navItems, function(v,i){
4329             
4330             if (v.isActive()) {
4331                 prev = i;
4332                 
4333             }
4334             
4335         });
4336         return prev;
4337     },
4338     /**
4339     * adds a Navigation item
4340     * @param {Roo.bootstrap.NavItem} the navitem to add
4341     */
4342     addItem : function(cfg)
4343     {
4344         var cn = new Roo.bootstrap.NavItem(cfg);
4345         this.register(cn);
4346         cn.parentId = this.id;
4347         cn.onRender(this.el, null);
4348         return cn;
4349     },
4350     /**
4351     * register a Navigation item
4352     * @param {Roo.bootstrap.NavItem} the navitem to add
4353     */
4354     register : function(item)
4355     {
4356         this.navItems.push( item);
4357         item.navId = this.navId;
4358     
4359     },
4360     
4361     /**
4362     * clear all the Navigation item
4363     */
4364    
4365     clearAll : function()
4366     {
4367         this.navItems = [];
4368         this.el.dom.innerHTML = '';
4369     },
4370     
4371     getNavItem: function(tabId)
4372     {
4373         var ret = false;
4374         Roo.each(this.navItems, function(e) {
4375             if (e.tabId == tabId) {
4376                ret =  e;
4377                return false;
4378             }
4379             return true;
4380             
4381         });
4382         return ret;
4383     },
4384     
4385     setActiveNext : function()
4386     {
4387         var i = this.indexOfNav(this.getActive());
4388         if (i > this.navItems.length) {
4389             return;
4390         }
4391         this.setActiveItem(this.navItems[i+1]);
4392     },
4393     setActivePrev : function()
4394     {
4395         var i = this.indexOfNav(this.getActive());
4396         if (i  < 1) {
4397             return;
4398         }
4399         this.setActiveItem(this.navItems[i-1]);
4400     },
4401     clearWasActive : function(except) {
4402         Roo.each(this.navItems, function(e) {
4403             if (e.tabId != except.tabId && e.was_active) {
4404                e.was_active = false;
4405                return false;
4406             }
4407             return true;
4408             
4409         });
4410     },
4411     getWasActive : function ()
4412     {
4413         var r = false;
4414         Roo.each(this.navItems, function(e) {
4415             if (e.was_active) {
4416                r = e;
4417                return false;
4418             }
4419             return true;
4420             
4421         });
4422         return r;
4423     }
4424     
4425     
4426 });
4427
4428  
4429 Roo.apply(Roo.bootstrap.NavGroup, {
4430     
4431     groups: {},
4432      /**
4433     * register a Navigation Group
4434     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4435     */
4436     register : function(navgrp)
4437     {
4438         this.groups[navgrp.navId] = navgrp;
4439         
4440     },
4441     /**
4442     * fetch a Navigation Group based on the navigation ID
4443     * @param {string} the navgroup to add
4444     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4445     */
4446     get: function(navId) {
4447         if (typeof(this.groups[navId]) == 'undefined') {
4448             return false;
4449             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4450         }
4451         return this.groups[navId] ;
4452     }
4453     
4454     
4455     
4456 });
4457
4458  /*
4459  * - LGPL
4460  *
4461  * row
4462  * 
4463  */
4464
4465 /**
4466  * @class Roo.bootstrap.NavItem
4467  * @extends Roo.bootstrap.Component
4468  * Bootstrap Navbar.NavItem class
4469  * @cfg {String} href  link to
4470  * @cfg {String} html content of button
4471  * @cfg {String} badge text inside badge
4472  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4473  * @cfg {String} glyphicon name of glyphicon
4474  * @cfg {String} icon name of font awesome icon
4475  * @cfg {Boolean} active Is item active
4476  * @cfg {Boolean} disabled Is item disabled
4477  
4478  * @cfg {Boolean} preventDefault (true | false) default false
4479  * @cfg {String} tabId the tab that this item activates.
4480  * @cfg {String} tagtype (a|span) render as a href or span?
4481  * @cfg {Boolean} animateRef (true|false) link to element default false  
4482   
4483  * @constructor
4484  * Create a new Navbar Item
4485  * @param {Object} config The config object
4486  */
4487 Roo.bootstrap.NavItem = function(config){
4488     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4489     this.addEvents({
4490         // raw events
4491         /**
4492          * @event click
4493          * The raw click event for the entire grid.
4494          * @param {Roo.EventObject} e
4495          */
4496         "click" : true,
4497          /**
4498             * @event changed
4499             * Fires when the active item active state changes
4500             * @param {Roo.bootstrap.NavItem} this
4501             * @param {boolean} state the new state
4502              
4503          */
4504         'changed': true,
4505         /**
4506             * @event scrollto
4507             * Fires when scroll to element
4508             * @param {Roo.bootstrap.NavItem} this
4509             * @param {Object} options
4510             * @param {Roo.EventObject} e
4511              
4512          */
4513         'scrollto': true
4514     });
4515    
4516 };
4517
4518 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4519     
4520     href: false,
4521     html: '',
4522     badge: '',
4523     icon: false,
4524     glyphicon: false,
4525     active: false,
4526     preventDefault : false,
4527     tabId : false,
4528     tagtype : 'a',
4529     disabled : false,
4530     animateRef : false,
4531     was_active : false,
4532     
4533     getAutoCreate : function(){
4534          
4535         var cfg = {
4536             tag: 'li',
4537             cls: 'nav-item'
4538             
4539         };
4540         
4541         if (this.active) {
4542             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4543         }
4544         if (this.disabled) {
4545             cfg.cls += ' disabled';
4546         }
4547         
4548         if (this.href || this.html || this.glyphicon || this.icon) {
4549             cfg.cn = [
4550                 {
4551                     tag: this.tagtype,
4552                     href : this.href || "#",
4553                     html: this.html || ''
4554                 }
4555             ];
4556             if (this.tagtype == 'a') {
4557                 cfg.cn[0].cls = 'nav-link';
4558             }
4559             if (this.icon) {
4560                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4561             }
4562
4563             if(this.glyphicon) {
4564                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4565             }
4566             
4567             if (this.menu) {
4568                 
4569                 cfg.cn[0].html += " <span class='caret'></span>";
4570              
4571             }
4572             
4573             if (this.badge !== '') {
4574                  
4575                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4576             }
4577         }
4578         
4579         
4580         
4581         return cfg;
4582     },
4583     initEvents: function() 
4584     {
4585         if (typeof (this.menu) != 'undefined') {
4586             this.menu.parentType = this.xtype;
4587             this.menu.triggerEl = this.el;
4588             this.menu = this.addxtype(Roo.apply({}, this.menu));
4589         }
4590         
4591         this.el.select('a',true).on('click', this.onClick, this);
4592         
4593         if(this.tagtype == 'span'){
4594             this.el.select('span',true).on('click', this.onClick, this);
4595         }
4596        
4597         // at this point parent should be available..
4598         this.parent().register(this);
4599     },
4600     
4601     onClick : function(e)
4602     {
4603         if (e.getTarget('.dropdown-menu-item')) {
4604             // did you click on a menu itemm.... - then don't trigger onclick..
4605             return;
4606         }
4607         
4608         if(
4609                 this.preventDefault || 
4610                 this.href == '#' 
4611         ){
4612             Roo.log("NavItem - prevent Default?");
4613             e.preventDefault();
4614         }
4615         
4616         if (this.disabled) {
4617             return;
4618         }
4619         
4620         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4621         if (tg && tg.transition) {
4622             Roo.log("waiting for the transitionend");
4623             return;
4624         }
4625         
4626         
4627         
4628         //Roo.log("fire event clicked");
4629         if(this.fireEvent('click', this, e) === false){
4630             return;
4631         };
4632         
4633         if(this.tagtype == 'span'){
4634             return;
4635         }
4636         
4637         //Roo.log(this.href);
4638         var ael = this.el.select('a',true).first();
4639         //Roo.log(ael);
4640         
4641         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4642             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4643             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4644                 return; // ignore... - it's a 'hash' to another page.
4645             }
4646             Roo.log("NavItem - prevent Default?");
4647             e.preventDefault();
4648             this.scrollToElement(e);
4649         }
4650         
4651         
4652         var p =  this.parent();
4653    
4654         if (['tabs','pills'].indexOf(p.type)!==-1) {
4655             if (typeof(p.setActiveItem) !== 'undefined') {
4656                 p.setActiveItem(this);
4657             }
4658         }
4659         
4660         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4661         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4662             // remove the collapsed menu expand...
4663             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4664         }
4665     },
4666     
4667     isActive: function () {
4668         return this.active
4669     },
4670     setActive : function(state, fire, is_was_active)
4671     {
4672         if (this.active && !state && this.navId) {
4673             this.was_active = true;
4674             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4675             if (nv) {
4676                 nv.clearWasActive(this);
4677             }
4678             
4679         }
4680         this.active = state;
4681         
4682         if (!state ) {
4683             this.el.removeClass('active');
4684         } else if (!this.el.hasClass('active')) {
4685             this.el.addClass('active');
4686         }
4687         if (fire) {
4688             this.fireEvent('changed', this, state);
4689         }
4690         
4691         // show a panel if it's registered and related..
4692         
4693         if (!this.navId || !this.tabId || !state || is_was_active) {
4694             return;
4695         }
4696         
4697         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4698         if (!tg) {
4699             return;
4700         }
4701         var pan = tg.getPanelByName(this.tabId);
4702         if (!pan) {
4703             return;
4704         }
4705         // if we can not flip to new panel - go back to old nav highlight..
4706         if (false == tg.showPanel(pan)) {
4707             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4708             if (nv) {
4709                 var onav = nv.getWasActive();
4710                 if (onav) {
4711                     onav.setActive(true, false, true);
4712                 }
4713             }
4714             
4715         }
4716         
4717         
4718         
4719     },
4720      // this should not be here...
4721     setDisabled : function(state)
4722     {
4723         this.disabled = state;
4724         if (!state ) {
4725             this.el.removeClass('disabled');
4726         } else if (!this.el.hasClass('disabled')) {
4727             this.el.addClass('disabled');
4728         }
4729         
4730     },
4731     
4732     /**
4733      * Fetch the element to display the tooltip on.
4734      * @return {Roo.Element} defaults to this.el
4735      */
4736     tooltipEl : function()
4737     {
4738         return this.el.select('' + this.tagtype + '', true).first();
4739     },
4740     
4741     scrollToElement : function(e)
4742     {
4743         var c = document.body;
4744         
4745         /*
4746          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4747          */
4748         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4749             c = document.documentElement;
4750         }
4751         
4752         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4753         
4754         if(!target){
4755             return;
4756         }
4757
4758         var o = target.calcOffsetsTo(c);
4759         
4760         var options = {
4761             target : target,
4762             value : o[1]
4763         };
4764         
4765         this.fireEvent('scrollto', this, options, e);
4766         
4767         Roo.get(c).scrollTo('top', options.value, true);
4768         
4769         return;
4770     }
4771 });
4772  
4773
4774  /*
4775  * - LGPL
4776  *
4777  * sidebar item
4778  *
4779  *  li
4780  *    <span> icon </span>
4781  *    <span> text </span>
4782  *    <span>badge </span>
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.NavSidebarItem
4787  * @extends Roo.bootstrap.NavItem
4788  * Bootstrap Navbar.NavSidebarItem class
4789  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4790  * {Boolean} open is the menu open
4791  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4792  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4793  * {String} buttonSize (sm|md|lg)the extra classes for the button
4794  * {Boolean} showArrow show arrow next to the text (default true)
4795  * @constructor
4796  * Create a new Navbar Button
4797  * @param {Object} config The config object
4798  */
4799 Roo.bootstrap.NavSidebarItem = function(config){
4800     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4801     this.addEvents({
4802         // raw events
4803         /**
4804          * @event click
4805          * The raw click event for the entire grid.
4806          * @param {Roo.EventObject} e
4807          */
4808         "click" : true,
4809          /**
4810             * @event changed
4811             * Fires when the active item active state changes
4812             * @param {Roo.bootstrap.NavSidebarItem} this
4813             * @param {boolean} state the new state
4814              
4815          */
4816         'changed': true
4817     });
4818    
4819 };
4820
4821 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4822     
4823     badgeWeight : 'default',
4824     
4825     open: false,
4826     
4827     buttonView : false,
4828     
4829     buttonWeight : 'default',
4830     
4831     buttonSize : 'md',
4832     
4833     showArrow : true,
4834     
4835     getAutoCreate : function(){
4836         
4837         
4838         var a = {
4839                 tag: 'a',
4840                 href : this.href || '#',
4841                 cls: '',
4842                 html : '',
4843                 cn : []
4844         };
4845         
4846         if(this.buttonView){
4847             a = {
4848                 tag: 'button',
4849                 href : this.href || '#',
4850                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4851                 html : this.html,
4852                 cn : []
4853             };
4854         }
4855         
4856         var cfg = {
4857             tag: 'li',
4858             cls: '',
4859             cn: [ a ]
4860         };
4861         
4862         if (this.active) {
4863             cfg.cls += ' active';
4864         }
4865         
4866         if (this.disabled) {
4867             cfg.cls += ' disabled';
4868         }
4869         if (this.open) {
4870             cfg.cls += ' open x-open';
4871         }
4872         // left icon..
4873         if (this.glyphicon || this.icon) {
4874             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4875             a.cn.push({ tag : 'i', cls : c }) ;
4876         }
4877         
4878         if(!this.buttonView){
4879             var span = {
4880                 tag: 'span',
4881                 html : this.html || ''
4882             };
4883
4884             a.cn.push(span);
4885             
4886         }
4887         
4888         if (this.badge !== '') {
4889             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4890         }
4891         
4892         if (this.menu) {
4893             
4894             if(this.showArrow){
4895                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4896             }
4897             
4898             a.cls += ' dropdown-toggle treeview' ;
4899         }
4900         
4901         return cfg;
4902     },
4903     
4904     initEvents : function()
4905     { 
4906         if (typeof (this.menu) != 'undefined') {
4907             this.menu.parentType = this.xtype;
4908             this.menu.triggerEl = this.el;
4909             this.menu = this.addxtype(Roo.apply({}, this.menu));
4910         }
4911         
4912         this.el.on('click', this.onClick, this);
4913         
4914         if(this.badge !== ''){
4915             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4916         }
4917         
4918     },
4919     
4920     onClick : function(e)
4921     {
4922         if(this.disabled){
4923             e.preventDefault();
4924             return;
4925         }
4926         
4927         if(this.preventDefault){
4928             e.preventDefault();
4929         }
4930         
4931         this.fireEvent('click', this);
4932     },
4933     
4934     disable : function()
4935     {
4936         this.setDisabled(true);
4937     },
4938     
4939     enable : function()
4940     {
4941         this.setDisabled(false);
4942     },
4943     
4944     setDisabled : function(state)
4945     {
4946         if(this.disabled == state){
4947             return;
4948         }
4949         
4950         this.disabled = state;
4951         
4952         if (state) {
4953             this.el.addClass('disabled');
4954             return;
4955         }
4956         
4957         this.el.removeClass('disabled');
4958         
4959         return;
4960     },
4961     
4962     setActive : function(state)
4963     {
4964         if(this.active == state){
4965             return;
4966         }
4967         
4968         this.active = state;
4969         
4970         if (state) {
4971             this.el.addClass('active');
4972             return;
4973         }
4974         
4975         this.el.removeClass('active');
4976         
4977         return;
4978     },
4979     
4980     isActive: function () 
4981     {
4982         return this.active;
4983     },
4984     
4985     setBadge : function(str)
4986     {
4987         if(!this.badgeEl){
4988             return;
4989         }
4990         
4991         this.badgeEl.dom.innerHTML = str;
4992     }
4993     
4994    
4995      
4996  
4997 });
4998  
4999
5000  /*
5001  * - LGPL
5002  *
5003  * row
5004  * 
5005  */
5006
5007 /**
5008  * @class Roo.bootstrap.Row
5009  * @extends Roo.bootstrap.Component
5010  * Bootstrap Row class (contains columns...)
5011  * 
5012  * @constructor
5013  * Create a new Row
5014  * @param {Object} config The config object
5015  */
5016
5017 Roo.bootstrap.Row = function(config){
5018     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5019 };
5020
5021 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5022     
5023     getAutoCreate : function(){
5024        return {
5025             cls: 'row clearfix'
5026        };
5027     }
5028     
5029     
5030 });
5031
5032  
5033
5034  /*
5035  * - LGPL
5036  *
5037  * element
5038  * 
5039  */
5040
5041 /**
5042  * @class Roo.bootstrap.Element
5043  * @extends Roo.bootstrap.Component
5044  * Bootstrap Element class
5045  * @cfg {String} html contents of the element
5046  * @cfg {String} tag tag of the element
5047  * @cfg {String} cls class of the element
5048  * @cfg {Boolean} preventDefault (true|false) default false
5049  * @cfg {Boolean} clickable (true|false) default false
5050  * 
5051  * @constructor
5052  * Create a new Element
5053  * @param {Object} config The config object
5054  */
5055
5056 Roo.bootstrap.Element = function(config){
5057     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5058     
5059     this.addEvents({
5060         // raw events
5061         /**
5062          * @event click
5063          * When a element is chick
5064          * @param {Roo.bootstrap.Element} this
5065          * @param {Roo.EventObject} e
5066          */
5067         "click" : true
5068     });
5069 };
5070
5071 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5072     
5073     tag: 'div',
5074     cls: '',
5075     html: '',
5076     preventDefault: false, 
5077     clickable: false,
5078     
5079     getAutoCreate : function(){
5080         
5081         var cfg = {
5082             tag: this.tag,
5083             // cls: this.cls, double assign in parent class Component.js :: onRender
5084             html: this.html
5085         };
5086         
5087         return cfg;
5088     },
5089     
5090     initEvents: function() 
5091     {
5092         Roo.bootstrap.Element.superclass.initEvents.call(this);
5093         
5094         if(this.clickable){
5095             this.el.on('click', this.onClick, this);
5096         }
5097         
5098     },
5099     
5100     onClick : function(e)
5101     {
5102         if(this.preventDefault){
5103             e.preventDefault();
5104         }
5105         
5106         this.fireEvent('click', this, e);
5107     },
5108     
5109     getValue : function()
5110     {
5111         return this.el.dom.innerHTML;
5112     },
5113     
5114     setValue : function(value)
5115     {
5116         this.el.dom.innerHTML = value;
5117     }
5118    
5119 });
5120
5121  
5122
5123  /*
5124  * - LGPL
5125  *
5126  * pagination
5127  * 
5128  */
5129
5130 /**
5131  * @class Roo.bootstrap.Pagination
5132  * @extends Roo.bootstrap.Component
5133  * Bootstrap Pagination class
5134  * @cfg {String} size xs | sm | md | lg
5135  * @cfg {Boolean} inverse false | true
5136  * 
5137  * @constructor
5138  * Create a new Pagination
5139  * @param {Object} config The config object
5140  */
5141
5142 Roo.bootstrap.Pagination = function(config){
5143     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5144 };
5145
5146 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5147     
5148     cls: false,
5149     size: false,
5150     inverse: false,
5151     
5152     getAutoCreate : function(){
5153         var cfg = {
5154             tag: 'ul',
5155                 cls: 'pagination'
5156         };
5157         if (this.inverse) {
5158             cfg.cls += ' inverse';
5159         }
5160         if (this.html) {
5161             cfg.html=this.html;
5162         }
5163         if (this.cls) {
5164             cfg.cls += " " + this.cls;
5165         }
5166         return cfg;
5167     }
5168    
5169 });
5170
5171  
5172
5173  /*
5174  * - LGPL
5175  *
5176  * Pagination item
5177  * 
5178  */
5179
5180
5181 /**
5182  * @class Roo.bootstrap.PaginationItem
5183  * @extends Roo.bootstrap.Component
5184  * Bootstrap PaginationItem class
5185  * @cfg {String} html text
5186  * @cfg {String} href the link
5187  * @cfg {Boolean} preventDefault (true | false) default true
5188  * @cfg {Boolean} active (true | false) default false
5189  * @cfg {Boolean} disabled default false
5190  * 
5191  * 
5192  * @constructor
5193  * Create a new PaginationItem
5194  * @param {Object} config The config object
5195  */
5196
5197
5198 Roo.bootstrap.PaginationItem = function(config){
5199     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5200     this.addEvents({
5201         // raw events
5202         /**
5203          * @event click
5204          * The raw click event for the entire grid.
5205          * @param {Roo.EventObject} e
5206          */
5207         "click" : true
5208     });
5209 };
5210
5211 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5212     
5213     href : false,
5214     html : false,
5215     preventDefault: true,
5216     active : false,
5217     cls : false,
5218     disabled: false,
5219     
5220     getAutoCreate : function(){
5221         var cfg= {
5222             tag: 'li',
5223             cn: [
5224                 {
5225                     tag : 'a',
5226                     href : this.href ? this.href : '#',
5227                     html : this.html ? this.html : ''
5228                 }
5229             ]
5230         };
5231         
5232         if(this.cls){
5233             cfg.cls = this.cls;
5234         }
5235         
5236         if(this.disabled){
5237             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5238         }
5239         
5240         if(this.active){
5241             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5242         }
5243         
5244         return cfg;
5245     },
5246     
5247     initEvents: function() {
5248         
5249         this.el.on('click', this.onClick, this);
5250         
5251     },
5252     onClick : function(e)
5253     {
5254         Roo.log('PaginationItem on click ');
5255         if(this.preventDefault){
5256             e.preventDefault();
5257         }
5258         
5259         if(this.disabled){
5260             return;
5261         }
5262         
5263         this.fireEvent('click', this, e);
5264     }
5265    
5266 });
5267
5268  
5269
5270  /*
5271  * - LGPL
5272  *
5273  * slider
5274  * 
5275  */
5276
5277
5278 /**
5279  * @class Roo.bootstrap.Slider
5280  * @extends Roo.bootstrap.Component
5281  * Bootstrap Slider class
5282  *    
5283  * @constructor
5284  * Create a new Slider
5285  * @param {Object} config The config object
5286  */
5287
5288 Roo.bootstrap.Slider = function(config){
5289     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5290 };
5291
5292 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5293     
5294     getAutoCreate : function(){
5295         
5296         var cfg = {
5297             tag: 'div',
5298             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5299             cn: [
5300                 {
5301                     tag: 'a',
5302                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5303                 }
5304             ]
5305         };
5306         
5307         return cfg;
5308     }
5309    
5310 });
5311
5312  /*
5313  * Based on:
5314  * Ext JS Library 1.1.1
5315  * Copyright(c) 2006-2007, Ext JS, LLC.
5316  *
5317  * Originally Released Under LGPL - original licence link has changed is not relivant.
5318  *
5319  * Fork - LGPL
5320  * <script type="text/javascript">
5321  */
5322  
5323
5324 /**
5325  * @class Roo.grid.ColumnModel
5326  * @extends Roo.util.Observable
5327  * This is the default implementation of a ColumnModel used by the Grid. It defines
5328  * the columns in the grid.
5329  * <br>Usage:<br>
5330  <pre><code>
5331  var colModel = new Roo.grid.ColumnModel([
5332         {header: "Ticker", width: 60, sortable: true, locked: true},
5333         {header: "Company Name", width: 150, sortable: true},
5334         {header: "Market Cap.", width: 100, sortable: true},
5335         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5336         {header: "Employees", width: 100, sortable: true, resizable: false}
5337  ]);
5338  </code></pre>
5339  * <p>
5340  
5341  * The config options listed for this class are options which may appear in each
5342  * individual column definition.
5343  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5344  * @constructor
5345  * @param {Object} config An Array of column config objects. See this class's
5346  * config objects for details.
5347 */
5348 Roo.grid.ColumnModel = function(config){
5349         /**
5350      * The config passed into the constructor
5351      */
5352     this.config = config;
5353     this.lookup = {};
5354
5355     // if no id, create one
5356     // if the column does not have a dataIndex mapping,
5357     // map it to the order it is in the config
5358     for(var i = 0, len = config.length; i < len; i++){
5359         var c = config[i];
5360         if(typeof c.dataIndex == "undefined"){
5361             c.dataIndex = i;
5362         }
5363         if(typeof c.renderer == "string"){
5364             c.renderer = Roo.util.Format[c.renderer];
5365         }
5366         if(typeof c.id == "undefined"){
5367             c.id = Roo.id();
5368         }
5369         if(c.editor && c.editor.xtype){
5370             c.editor  = Roo.factory(c.editor, Roo.grid);
5371         }
5372         if(c.editor && c.editor.isFormField){
5373             c.editor = new Roo.grid.GridEditor(c.editor);
5374         }
5375         this.lookup[c.id] = c;
5376     }
5377
5378     /**
5379      * The width of columns which have no width specified (defaults to 100)
5380      * @type Number
5381      */
5382     this.defaultWidth = 100;
5383
5384     /**
5385      * Default sortable of columns which have no sortable specified (defaults to false)
5386      * @type Boolean
5387      */
5388     this.defaultSortable = false;
5389
5390     this.addEvents({
5391         /**
5392              * @event widthchange
5393              * Fires when the width of a column changes.
5394              * @param {ColumnModel} this
5395              * @param {Number} columnIndex The column index
5396              * @param {Number} newWidth The new width
5397              */
5398             "widthchange": true,
5399         /**
5400              * @event headerchange
5401              * Fires when the text of a header changes.
5402              * @param {ColumnModel} this
5403              * @param {Number} columnIndex The column index
5404              * @param {Number} newText The new header text
5405              */
5406             "headerchange": true,
5407         /**
5408              * @event hiddenchange
5409              * Fires when a column is hidden or "unhidden".
5410              * @param {ColumnModel} this
5411              * @param {Number} columnIndex The column index
5412              * @param {Boolean} hidden true if hidden, false otherwise
5413              */
5414             "hiddenchange": true,
5415             /**
5416          * @event columnmoved
5417          * Fires when a column is moved.
5418          * @param {ColumnModel} this
5419          * @param {Number} oldIndex
5420          * @param {Number} newIndex
5421          */
5422         "columnmoved" : true,
5423         /**
5424          * @event columlockchange
5425          * Fires when a column's locked state is changed
5426          * @param {ColumnModel} this
5427          * @param {Number} colIndex
5428          * @param {Boolean} locked true if locked
5429          */
5430         "columnlockchange" : true
5431     });
5432     Roo.grid.ColumnModel.superclass.constructor.call(this);
5433 };
5434 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5435     /**
5436      * @cfg {String} header The header text to display in the Grid view.
5437      */
5438     /**
5439      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5440      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5441      * specified, the column's index is used as an index into the Record's data Array.
5442      */
5443     /**
5444      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5445      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5446      */
5447     /**
5448      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5449      * Defaults to the value of the {@link #defaultSortable} property.
5450      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5451      */
5452     /**
5453      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5454      */
5455     /**
5456      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5457      */
5458     /**
5459      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5460      */
5461     /**
5462      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5463      */
5464     /**
5465      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5466      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5467      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5468      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5469      */
5470        /**
5471      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5472      */
5473     /**
5474      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5475      */
5476     /**
5477      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5478      */
5479     /**
5480      * @cfg {String} cursor (Optional)
5481      */
5482     /**
5483      * @cfg {String} tooltip (Optional)
5484      */
5485     /**
5486      * @cfg {Number} xs (Optional)
5487      */
5488     /**
5489      * @cfg {Number} sm (Optional)
5490      */
5491     /**
5492      * @cfg {Number} md (Optional)
5493      */
5494     /**
5495      * @cfg {Number} lg (Optional)
5496      */
5497     /**
5498      * Returns the id of the column at the specified index.
5499      * @param {Number} index The column index
5500      * @return {String} the id
5501      */
5502     getColumnId : function(index){
5503         return this.config[index].id;
5504     },
5505
5506     /**
5507      * Returns the column for a specified id.
5508      * @param {String} id The column id
5509      * @return {Object} the column
5510      */
5511     getColumnById : function(id){
5512         return this.lookup[id];
5513     },
5514
5515     
5516     /**
5517      * Returns the column for a specified dataIndex.
5518      * @param {String} dataIndex The column dataIndex
5519      * @return {Object|Boolean} the column or false if not found
5520      */
5521     getColumnByDataIndex: function(dataIndex){
5522         var index = this.findColumnIndex(dataIndex);
5523         return index > -1 ? this.config[index] : false;
5524     },
5525     
5526     /**
5527      * Returns the index for a specified column id.
5528      * @param {String} id The column id
5529      * @return {Number} the index, or -1 if not found
5530      */
5531     getIndexById : function(id){
5532         for(var i = 0, len = this.config.length; i < len; i++){
5533             if(this.config[i].id == id){
5534                 return i;
5535             }
5536         }
5537         return -1;
5538     },
5539     
5540     /**
5541      * Returns the index for a specified column dataIndex.
5542      * @param {String} dataIndex The column dataIndex
5543      * @return {Number} the index, or -1 if not found
5544      */
5545     
5546     findColumnIndex : function(dataIndex){
5547         for(var i = 0, len = this.config.length; i < len; i++){
5548             if(this.config[i].dataIndex == dataIndex){
5549                 return i;
5550             }
5551         }
5552         return -1;
5553     },
5554     
5555     
5556     moveColumn : function(oldIndex, newIndex){
5557         var c = this.config[oldIndex];
5558         this.config.splice(oldIndex, 1);
5559         this.config.splice(newIndex, 0, c);
5560         this.dataMap = null;
5561         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5562     },
5563
5564     isLocked : function(colIndex){
5565         return this.config[colIndex].locked === true;
5566     },
5567
5568     setLocked : function(colIndex, value, suppressEvent){
5569         if(this.isLocked(colIndex) == value){
5570             return;
5571         }
5572         this.config[colIndex].locked = value;
5573         if(!suppressEvent){
5574             this.fireEvent("columnlockchange", this, colIndex, value);
5575         }
5576     },
5577
5578     getTotalLockedWidth : function(){
5579         var totalWidth = 0;
5580         for(var i = 0; i < this.config.length; i++){
5581             if(this.isLocked(i) && !this.isHidden(i)){
5582                 this.totalWidth += this.getColumnWidth(i);
5583             }
5584         }
5585         return totalWidth;
5586     },
5587
5588     getLockedCount : function(){
5589         for(var i = 0, len = this.config.length; i < len; i++){
5590             if(!this.isLocked(i)){
5591                 return i;
5592             }
5593         }
5594         
5595         return this.config.length;
5596     },
5597
5598     /**
5599      * Returns the number of columns.
5600      * @return {Number}
5601      */
5602     getColumnCount : function(visibleOnly){
5603         if(visibleOnly === true){
5604             var c = 0;
5605             for(var i = 0, len = this.config.length; i < len; i++){
5606                 if(!this.isHidden(i)){
5607                     c++;
5608                 }
5609             }
5610             return c;
5611         }
5612         return this.config.length;
5613     },
5614
5615     /**
5616      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5617      * @param {Function} fn
5618      * @param {Object} scope (optional)
5619      * @return {Array} result
5620      */
5621     getColumnsBy : function(fn, scope){
5622         var r = [];
5623         for(var i = 0, len = this.config.length; i < len; i++){
5624             var c = this.config[i];
5625             if(fn.call(scope||this, c, i) === true){
5626                 r[r.length] = c;
5627             }
5628         }
5629         return r;
5630     },
5631
5632     /**
5633      * Returns true if the specified column is sortable.
5634      * @param {Number} col The column index
5635      * @return {Boolean}
5636      */
5637     isSortable : function(col){
5638         if(typeof this.config[col].sortable == "undefined"){
5639             return this.defaultSortable;
5640         }
5641         return this.config[col].sortable;
5642     },
5643
5644     /**
5645      * Returns the rendering (formatting) function defined for the column.
5646      * @param {Number} col The column index.
5647      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5648      */
5649     getRenderer : function(col){
5650         if(!this.config[col].renderer){
5651             return Roo.grid.ColumnModel.defaultRenderer;
5652         }
5653         return this.config[col].renderer;
5654     },
5655
5656     /**
5657      * Sets the rendering (formatting) function for a column.
5658      * @param {Number} col The column index
5659      * @param {Function} fn The function to use to process the cell's raw data
5660      * to return HTML markup for the grid view. The render function is called with
5661      * the following parameters:<ul>
5662      * <li>Data value.</li>
5663      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5664      * <li>css A CSS style string to apply to the table cell.</li>
5665      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5666      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5667      * <li>Row index</li>
5668      * <li>Column index</li>
5669      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5670      */
5671     setRenderer : function(col, fn){
5672         this.config[col].renderer = fn;
5673     },
5674
5675     /**
5676      * Returns the width for the specified column.
5677      * @param {Number} col The column index
5678      * @return {Number}
5679      */
5680     getColumnWidth : function(col){
5681         return this.config[col].width * 1 || this.defaultWidth;
5682     },
5683
5684     /**
5685      * Sets the width for a column.
5686      * @param {Number} col The column index
5687      * @param {Number} width The new width
5688      */
5689     setColumnWidth : function(col, width, suppressEvent){
5690         this.config[col].width = width;
5691         this.totalWidth = null;
5692         if(!suppressEvent){
5693              this.fireEvent("widthchange", this, col, width);
5694         }
5695     },
5696
5697     /**
5698      * Returns the total width of all columns.
5699      * @param {Boolean} includeHidden True to include hidden column widths
5700      * @return {Number}
5701      */
5702     getTotalWidth : function(includeHidden){
5703         if(!this.totalWidth){
5704             this.totalWidth = 0;
5705             for(var i = 0, len = this.config.length; i < len; i++){
5706                 if(includeHidden || !this.isHidden(i)){
5707                     this.totalWidth += this.getColumnWidth(i);
5708                 }
5709             }
5710         }
5711         return this.totalWidth;
5712     },
5713
5714     /**
5715      * Returns the header for the specified column.
5716      * @param {Number} col The column index
5717      * @return {String}
5718      */
5719     getColumnHeader : function(col){
5720         return this.config[col].header;
5721     },
5722
5723     /**
5724      * Sets the header for a column.
5725      * @param {Number} col The column index
5726      * @param {String} header The new header
5727      */
5728     setColumnHeader : function(col, header){
5729         this.config[col].header = header;
5730         this.fireEvent("headerchange", this, col, header);
5731     },
5732
5733     /**
5734      * Returns the tooltip for the specified column.
5735      * @param {Number} col The column index
5736      * @return {String}
5737      */
5738     getColumnTooltip : function(col){
5739             return this.config[col].tooltip;
5740     },
5741     /**
5742      * Sets the tooltip for a column.
5743      * @param {Number} col The column index
5744      * @param {String} tooltip The new tooltip
5745      */
5746     setColumnTooltip : function(col, tooltip){
5747             this.config[col].tooltip = tooltip;
5748     },
5749
5750     /**
5751      * Returns the dataIndex for the specified column.
5752      * @param {Number} col The column index
5753      * @return {Number}
5754      */
5755     getDataIndex : function(col){
5756         return this.config[col].dataIndex;
5757     },
5758
5759     /**
5760      * Sets the dataIndex for a column.
5761      * @param {Number} col The column index
5762      * @param {Number} dataIndex The new dataIndex
5763      */
5764     setDataIndex : function(col, dataIndex){
5765         this.config[col].dataIndex = dataIndex;
5766     },
5767
5768     
5769     
5770     /**
5771      * Returns true if the cell is editable.
5772      * @param {Number} colIndex The column index
5773      * @param {Number} rowIndex The row index - this is nto actually used..?
5774      * @return {Boolean}
5775      */
5776     isCellEditable : function(colIndex, rowIndex){
5777         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5778     },
5779
5780     /**
5781      * Returns the editor defined for the cell/column.
5782      * return false or null to disable editing.
5783      * @param {Number} colIndex The column index
5784      * @param {Number} rowIndex The row index
5785      * @return {Object}
5786      */
5787     getCellEditor : function(colIndex, rowIndex){
5788         return this.config[colIndex].editor;
5789     },
5790
5791     /**
5792      * Sets if a column is editable.
5793      * @param {Number} col The column index
5794      * @param {Boolean} editable True if the column is editable
5795      */
5796     setEditable : function(col, editable){
5797         this.config[col].editable = editable;
5798     },
5799
5800
5801     /**
5802      * Returns true if the column is hidden.
5803      * @param {Number} colIndex The column index
5804      * @return {Boolean}
5805      */
5806     isHidden : function(colIndex){
5807         return this.config[colIndex].hidden;
5808     },
5809
5810
5811     /**
5812      * Returns true if the column width cannot be changed
5813      */
5814     isFixed : function(colIndex){
5815         return this.config[colIndex].fixed;
5816     },
5817
5818     /**
5819      * Returns true if the column can be resized
5820      * @return {Boolean}
5821      */
5822     isResizable : function(colIndex){
5823         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5824     },
5825     /**
5826      * Sets if a column is hidden.
5827      * @param {Number} colIndex The column index
5828      * @param {Boolean} hidden True if the column is hidden
5829      */
5830     setHidden : function(colIndex, hidden){
5831         this.config[colIndex].hidden = hidden;
5832         this.totalWidth = null;
5833         this.fireEvent("hiddenchange", this, colIndex, hidden);
5834     },
5835
5836     /**
5837      * Sets the editor for a column.
5838      * @param {Number} col The column index
5839      * @param {Object} editor The editor object
5840      */
5841     setEditor : function(col, editor){
5842         this.config[col].editor = editor;
5843     }
5844 });
5845
5846 Roo.grid.ColumnModel.defaultRenderer = function(value)
5847 {
5848     if(typeof value == "object") {
5849         return value;
5850     }
5851         if(typeof value == "string" && value.length < 1){
5852             return "&#160;";
5853         }
5854     
5855         return String.format("{0}", value);
5856 };
5857
5858 // Alias for backwards compatibility
5859 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5860 /*
5861  * Based on:
5862  * Ext JS Library 1.1.1
5863  * Copyright(c) 2006-2007, Ext JS, LLC.
5864  *
5865  * Originally Released Under LGPL - original licence link has changed is not relivant.
5866  *
5867  * Fork - LGPL
5868  * <script type="text/javascript">
5869  */
5870  
5871 /**
5872  * @class Roo.LoadMask
5873  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5874  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5875  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5876  * element's UpdateManager load indicator and will be destroyed after the initial load.
5877  * @constructor
5878  * Create a new LoadMask
5879  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5880  * @param {Object} config The config object
5881  */
5882 Roo.LoadMask = function(el, config){
5883     this.el = Roo.get(el);
5884     Roo.apply(this, config);
5885     if(this.store){
5886         this.store.on('beforeload', this.onBeforeLoad, this);
5887         this.store.on('load', this.onLoad, this);
5888         this.store.on('loadexception', this.onLoadException, this);
5889         this.removeMask = false;
5890     }else{
5891         var um = this.el.getUpdateManager();
5892         um.showLoadIndicator = false; // disable the default indicator
5893         um.on('beforeupdate', this.onBeforeLoad, this);
5894         um.on('update', this.onLoad, this);
5895         um.on('failure', this.onLoad, this);
5896         this.removeMask = true;
5897     }
5898 };
5899
5900 Roo.LoadMask.prototype = {
5901     /**
5902      * @cfg {Boolean} removeMask
5903      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5904      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5905      */
5906     /**
5907      * @cfg {String} msg
5908      * The text to display in a centered loading message box (defaults to 'Loading...')
5909      */
5910     msg : 'Loading...',
5911     /**
5912      * @cfg {String} msgCls
5913      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5914      */
5915     msgCls : 'x-mask-loading',
5916
5917     /**
5918      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5919      * @type Boolean
5920      */
5921     disabled: false,
5922
5923     /**
5924      * Disables the mask to prevent it from being displayed
5925      */
5926     disable : function(){
5927        this.disabled = true;
5928     },
5929
5930     /**
5931      * Enables the mask so that it can be displayed
5932      */
5933     enable : function(){
5934         this.disabled = false;
5935     },
5936     
5937     onLoadException : function()
5938     {
5939         Roo.log(arguments);
5940         
5941         if (typeof(arguments[3]) != 'undefined') {
5942             Roo.MessageBox.alert("Error loading",arguments[3]);
5943         } 
5944         /*
5945         try {
5946             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5947                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5948             }   
5949         } catch(e) {
5950             
5951         }
5952         */
5953     
5954         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5955     },
5956     // private
5957     onLoad : function()
5958     {
5959         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5960     },
5961
5962     // private
5963     onBeforeLoad : function(){
5964         if(!this.disabled){
5965             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5966         }
5967     },
5968
5969     // private
5970     destroy : function(){
5971         if(this.store){
5972             this.store.un('beforeload', this.onBeforeLoad, this);
5973             this.store.un('load', this.onLoad, this);
5974             this.store.un('loadexception', this.onLoadException, this);
5975         }else{
5976             var um = this.el.getUpdateManager();
5977             um.un('beforeupdate', this.onBeforeLoad, this);
5978             um.un('update', this.onLoad, this);
5979             um.un('failure', this.onLoad, this);
5980         }
5981     }
5982 };/*
5983  * - LGPL
5984  *
5985  * table
5986  * 
5987  */
5988
5989 /**
5990  * @class Roo.bootstrap.Table
5991  * @extends Roo.bootstrap.Component
5992  * Bootstrap Table class
5993  * @cfg {String} cls table class
5994  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5995  * @cfg {String} bgcolor Specifies the background color for a table
5996  * @cfg {Number} border Specifies whether the table cells should have borders or not
5997  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5998  * @cfg {Number} cellspacing Specifies the space between cells
5999  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6000  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6001  * @cfg {String} sortable Specifies that the table should be sortable
6002  * @cfg {String} summary Specifies a summary of the content of a table
6003  * @cfg {Number} width Specifies the width of a table
6004  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6005  * 
6006  * @cfg {boolean} striped Should the rows be alternative striped
6007  * @cfg {boolean} bordered Add borders to the table
6008  * @cfg {boolean} hover Add hover highlighting
6009  * @cfg {boolean} condensed Format condensed
6010  * @cfg {boolean} responsive Format condensed
6011  * @cfg {Boolean} loadMask (true|false) default false
6012  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6013  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6014  * @cfg {Boolean} rowSelection (true|false) default false
6015  * @cfg {Boolean} cellSelection (true|false) default false
6016  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6017  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6018  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6019  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6020  
6021  * 
6022  * @constructor
6023  * Create a new Table
6024  * @param {Object} config The config object
6025  */
6026
6027 Roo.bootstrap.Table = function(config){
6028     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6029     
6030   
6031     
6032     // BC...
6033     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6034     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6035     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6036     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6037     
6038     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6039     if (this.sm) {
6040         this.sm.grid = this;
6041         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6042         this.sm = this.selModel;
6043         this.sm.xmodule = this.xmodule || false;
6044     }
6045     
6046     if (this.cm && typeof(this.cm.config) == 'undefined') {
6047         this.colModel = new Roo.grid.ColumnModel(this.cm);
6048         this.cm = this.colModel;
6049         this.cm.xmodule = this.xmodule || false;
6050     }
6051     if (this.store) {
6052         this.store= Roo.factory(this.store, Roo.data);
6053         this.ds = this.store;
6054         this.ds.xmodule = this.xmodule || false;
6055          
6056     }
6057     if (this.footer && this.store) {
6058         this.footer.dataSource = this.ds;
6059         this.footer = Roo.factory(this.footer);
6060     }
6061     
6062     /** @private */
6063     this.addEvents({
6064         /**
6065          * @event cellclick
6066          * Fires when a cell is clicked
6067          * @param {Roo.bootstrap.Table} this
6068          * @param {Roo.Element} el
6069          * @param {Number} rowIndex
6070          * @param {Number} columnIndex
6071          * @param {Roo.EventObject} e
6072          */
6073         "cellclick" : true,
6074         /**
6075          * @event celldblclick
6076          * Fires when a cell is double clicked
6077          * @param {Roo.bootstrap.Table} this
6078          * @param {Roo.Element} el
6079          * @param {Number} rowIndex
6080          * @param {Number} columnIndex
6081          * @param {Roo.EventObject} e
6082          */
6083         "celldblclick" : true,
6084         /**
6085          * @event rowclick
6086          * Fires when a row is clicked
6087          * @param {Roo.bootstrap.Table} this
6088          * @param {Roo.Element} el
6089          * @param {Number} rowIndex
6090          * @param {Roo.EventObject} e
6091          */
6092         "rowclick" : true,
6093         /**
6094          * @event rowdblclick
6095          * Fires when a row is double clicked
6096          * @param {Roo.bootstrap.Table} this
6097          * @param {Roo.Element} el
6098          * @param {Number} rowIndex
6099          * @param {Roo.EventObject} e
6100          */
6101         "rowdblclick" : true,
6102         /**
6103          * @event mouseover
6104          * Fires when a mouseover occur
6105          * @param {Roo.bootstrap.Table} this
6106          * @param {Roo.Element} el
6107          * @param {Number} rowIndex
6108          * @param {Number} columnIndex
6109          * @param {Roo.EventObject} e
6110          */
6111         "mouseover" : true,
6112         /**
6113          * @event mouseout
6114          * Fires when a mouseout occur
6115          * @param {Roo.bootstrap.Table} this
6116          * @param {Roo.Element} el
6117          * @param {Number} rowIndex
6118          * @param {Number} columnIndex
6119          * @param {Roo.EventObject} e
6120          */
6121         "mouseout" : true,
6122         /**
6123          * @event rowclass
6124          * Fires when a row is rendered, so you can change add a style to it.
6125          * @param {Roo.bootstrap.Table} this
6126          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6127          */
6128         'rowclass' : true,
6129           /**
6130          * @event rowsrendered
6131          * Fires when all the  rows have been rendered
6132          * @param {Roo.bootstrap.Table} this
6133          */
6134         'rowsrendered' : true,
6135         /**
6136          * @event contextmenu
6137          * The raw contextmenu event for the entire grid.
6138          * @param {Roo.EventObject} e
6139          */
6140         "contextmenu" : true,
6141         /**
6142          * @event rowcontextmenu
6143          * Fires when a row is right clicked
6144          * @param {Roo.bootstrap.Table} this
6145          * @param {Number} rowIndex
6146          * @param {Roo.EventObject} e
6147          */
6148         "rowcontextmenu" : true,
6149         /**
6150          * @event cellcontextmenu
6151          * Fires when a cell is right clicked
6152          * @param {Roo.bootstrap.Table} this
6153          * @param {Number} rowIndex
6154          * @param {Number} cellIndex
6155          * @param {Roo.EventObject} e
6156          */
6157          "cellcontextmenu" : true,
6158          /**
6159          * @event headercontextmenu
6160          * Fires when a header is right clicked
6161          * @param {Roo.bootstrap.Table} this
6162          * @param {Number} columnIndex
6163          * @param {Roo.EventObject} e
6164          */
6165         "headercontextmenu" : true
6166     });
6167 };
6168
6169 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6170     
6171     cls: false,
6172     align: false,
6173     bgcolor: false,
6174     border: false,
6175     cellpadding: false,
6176     cellspacing: false,
6177     frame: false,
6178     rules: false,
6179     sortable: false,
6180     summary: false,
6181     width: false,
6182     striped : false,
6183     scrollBody : false,
6184     bordered: false,
6185     hover:  false,
6186     condensed : false,
6187     responsive : false,
6188     sm : false,
6189     cm : false,
6190     store : false,
6191     loadMask : false,
6192     footerShow : true,
6193     headerShow : true,
6194   
6195     rowSelection : false,
6196     cellSelection : false,
6197     layout : false,
6198     
6199     // Roo.Element - the tbody
6200     mainBody: false,
6201     // Roo.Element - thead element
6202     mainHead: false,
6203     
6204     container: false, // used by gridpanel...
6205     
6206     lazyLoad : false,
6207     
6208     CSS : Roo.util.CSS,
6209     
6210     auto_hide_footer : false,
6211     
6212     getAutoCreate : function()
6213     {
6214         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6215         
6216         cfg = {
6217             tag: 'table',
6218             cls : 'table',
6219             cn : []
6220         };
6221         if (this.scrollBody) {
6222             cfg.cls += ' table-body-fixed';
6223         }    
6224         if (this.striped) {
6225             cfg.cls += ' table-striped';
6226         }
6227         
6228         if (this.hover) {
6229             cfg.cls += ' table-hover';
6230         }
6231         if (this.bordered) {
6232             cfg.cls += ' table-bordered';
6233         }
6234         if (this.condensed) {
6235             cfg.cls += ' table-condensed';
6236         }
6237         if (this.responsive) {
6238             cfg.cls += ' table-responsive';
6239         }
6240         
6241         if (this.cls) {
6242             cfg.cls+=  ' ' +this.cls;
6243         }
6244         
6245         // this lot should be simplifed...
6246         var _t = this;
6247         var cp = [
6248             'align',
6249             'bgcolor',
6250             'border',
6251             'cellpadding',
6252             'cellspacing',
6253             'frame',
6254             'rules',
6255             'sortable',
6256             'summary',
6257             'width'
6258         ].forEach(function(k) {
6259             if (_t[k]) {
6260                 cfg[k] = _t[k];
6261             }
6262         });
6263         
6264         
6265         if (this.layout) {
6266             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6267         }
6268         
6269         if(this.store || this.cm){
6270             if(this.headerShow){
6271                 cfg.cn.push(this.renderHeader());
6272             }
6273             
6274             cfg.cn.push(this.renderBody());
6275             
6276             if(this.footerShow){
6277                 cfg.cn.push(this.renderFooter());
6278             }
6279             // where does this come from?
6280             //cfg.cls+=  ' TableGrid';
6281         }
6282         
6283         return { cn : [ cfg ] };
6284     },
6285     
6286     initEvents : function()
6287     {   
6288         if(!this.store || !this.cm){
6289             return;
6290         }
6291         if (this.selModel) {
6292             this.selModel.initEvents();
6293         }
6294         
6295         
6296         //Roo.log('initEvents with ds!!!!');
6297         
6298         this.mainBody = this.el.select('tbody', true).first();
6299         this.mainHead = this.el.select('thead', true).first();
6300         this.mainFoot = this.el.select('tfoot', true).first();
6301         
6302         
6303         
6304         var _this = this;
6305         
6306         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6307             e.on('click', _this.sort, _this);
6308         });
6309         
6310         this.mainBody.on("click", this.onClick, this);
6311         this.mainBody.on("dblclick", this.onDblClick, this);
6312         
6313         // why is this done????? = it breaks dialogs??
6314         //this.parent().el.setStyle('position', 'relative');
6315         
6316         
6317         if (this.footer) {
6318             this.footer.parentId = this.id;
6319             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6320             
6321             if(this.lazyLoad){
6322                 this.el.select('tfoot tr td').first().addClass('hide');
6323             }
6324         } 
6325         
6326         if(this.loadMask) {
6327             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6328         }
6329         
6330         this.store.on('load', this.onLoad, this);
6331         this.store.on('beforeload', this.onBeforeLoad, this);
6332         this.store.on('update', this.onUpdate, this);
6333         this.store.on('add', this.onAdd, this);
6334         this.store.on("clear", this.clear, this);
6335         
6336         this.el.on("contextmenu", this.onContextMenu, this);
6337         
6338         this.mainBody.on('scroll', this.onBodyScroll, this);
6339         
6340         this.cm.on("headerchange", this.onHeaderChange, this);
6341         
6342         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6343         
6344     },
6345     
6346     onContextMenu : function(e, t)
6347     {
6348         this.processEvent("contextmenu", e);
6349     },
6350     
6351     processEvent : function(name, e)
6352     {
6353         if (name != 'touchstart' ) {
6354             this.fireEvent(name, e);    
6355         }
6356         
6357         var t = e.getTarget();
6358         
6359         var cell = Roo.get(t);
6360         
6361         if(!cell){
6362             return;
6363         }
6364         
6365         if(cell.findParent('tfoot', false, true)){
6366             return;
6367         }
6368         
6369         if(cell.findParent('thead', false, true)){
6370             
6371             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6372                 cell = Roo.get(t).findParent('th', false, true);
6373                 if (!cell) {
6374                     Roo.log("failed to find th in thead?");
6375                     Roo.log(e.getTarget());
6376                     return;
6377                 }
6378             }
6379             
6380             var cellIndex = cell.dom.cellIndex;
6381             
6382             var ename = name == 'touchstart' ? 'click' : name;
6383             this.fireEvent("header" + ename, this, cellIndex, e);
6384             
6385             return;
6386         }
6387         
6388         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6389             cell = Roo.get(t).findParent('td', false, true);
6390             if (!cell) {
6391                 Roo.log("failed to find th in tbody?");
6392                 Roo.log(e.getTarget());
6393                 return;
6394             }
6395         }
6396         
6397         var row = cell.findParent('tr', false, true);
6398         var cellIndex = cell.dom.cellIndex;
6399         var rowIndex = row.dom.rowIndex - 1;
6400         
6401         if(row !== false){
6402             
6403             this.fireEvent("row" + name, this, rowIndex, e);
6404             
6405             if(cell !== false){
6406             
6407                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6408             }
6409         }
6410         
6411     },
6412     
6413     onMouseover : function(e, el)
6414     {
6415         var cell = Roo.get(el);
6416         
6417         if(!cell){
6418             return;
6419         }
6420         
6421         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6422             cell = cell.findParent('td', false, true);
6423         }
6424         
6425         var row = cell.findParent('tr', false, true);
6426         var cellIndex = cell.dom.cellIndex;
6427         var rowIndex = row.dom.rowIndex - 1; // start from 0
6428         
6429         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6430         
6431     },
6432     
6433     onMouseout : function(e, el)
6434     {
6435         var cell = Roo.get(el);
6436         
6437         if(!cell){
6438             return;
6439         }
6440         
6441         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6442             cell = cell.findParent('td', false, true);
6443         }
6444         
6445         var row = cell.findParent('tr', false, true);
6446         var cellIndex = cell.dom.cellIndex;
6447         var rowIndex = row.dom.rowIndex - 1; // start from 0
6448         
6449         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6450         
6451     },
6452     
6453     onClick : function(e, el)
6454     {
6455         var cell = Roo.get(el);
6456         
6457         if(!cell || (!this.cellSelection && !this.rowSelection)){
6458             return;
6459         }
6460         
6461         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462             cell = cell.findParent('td', false, true);
6463         }
6464         
6465         if(!cell || typeof(cell) == 'undefined'){
6466             return;
6467         }
6468         
6469         var row = cell.findParent('tr', false, true);
6470         
6471         if(!row || typeof(row) == 'undefined'){
6472             return;
6473         }
6474         
6475         var cellIndex = cell.dom.cellIndex;
6476         var rowIndex = this.getRowIndex(row);
6477         
6478         // why??? - should these not be based on SelectionModel?
6479         if(this.cellSelection){
6480             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6481         }
6482         
6483         if(this.rowSelection){
6484             this.fireEvent('rowclick', this, row, rowIndex, e);
6485         }
6486         
6487         
6488     },
6489         
6490     onDblClick : function(e,el)
6491     {
6492         var cell = Roo.get(el);
6493         
6494         if(!cell || (!this.cellSelection && !this.rowSelection)){
6495             return;
6496         }
6497         
6498         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6499             cell = cell.findParent('td', false, true);
6500         }
6501         
6502         if(!cell || typeof(cell) == 'undefined'){
6503             return;
6504         }
6505         
6506         var row = cell.findParent('tr', false, true);
6507         
6508         if(!row || typeof(row) == 'undefined'){
6509             return;
6510         }
6511         
6512         var cellIndex = cell.dom.cellIndex;
6513         var rowIndex = this.getRowIndex(row);
6514         
6515         if(this.cellSelection){
6516             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6517         }
6518         
6519         if(this.rowSelection){
6520             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6521         }
6522     },
6523     
6524     sort : function(e,el)
6525     {
6526         var col = Roo.get(el);
6527         
6528         if(!col.hasClass('sortable')){
6529             return;
6530         }
6531         
6532         var sort = col.attr('sort');
6533         var dir = 'ASC';
6534         
6535         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6536             dir = 'DESC';
6537         }
6538         
6539         this.store.sortInfo = {field : sort, direction : dir};
6540         
6541         if (this.footer) {
6542             Roo.log("calling footer first");
6543             this.footer.onClick('first');
6544         } else {
6545         
6546             this.store.load({ params : { start : 0 } });
6547         }
6548     },
6549     
6550     renderHeader : function()
6551     {
6552         var header = {
6553             tag: 'thead',
6554             cn : []
6555         };
6556         
6557         var cm = this.cm;
6558         this.totalWidth = 0;
6559         
6560         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6561             
6562             var config = cm.config[i];
6563             
6564             var c = {
6565                 tag: 'th',
6566                 cls : 'x-hcol-' + i,
6567                 style : '',
6568                 html: cm.getColumnHeader(i)
6569             };
6570             
6571             var hh = '';
6572             
6573             if(typeof(config.sortable) != 'undefined' && config.sortable){
6574                 c.cls = 'sortable';
6575                 c.html = '<i class="glyphicon"></i>' + c.html;
6576             }
6577             
6578             if(typeof(config.lgHeader) != 'undefined'){
6579                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6580             }
6581             
6582             if(typeof(config.mdHeader) != 'undefined'){
6583                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6584             }
6585             
6586             if(typeof(config.smHeader) != 'undefined'){
6587                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6588             }
6589             
6590             if(typeof(config.xsHeader) != 'undefined'){
6591                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6592             }
6593             
6594             if(hh.length){
6595                 c.html = hh;
6596             }
6597             
6598             if(typeof(config.tooltip) != 'undefined'){
6599                 c.tooltip = config.tooltip;
6600             }
6601             
6602             if(typeof(config.colspan) != 'undefined'){
6603                 c.colspan = config.colspan;
6604             }
6605             
6606             if(typeof(config.hidden) != 'undefined' && config.hidden){
6607                 c.style += ' display:none;';
6608             }
6609             
6610             if(typeof(config.dataIndex) != 'undefined'){
6611                 c.sort = config.dataIndex;
6612             }
6613             
6614            
6615             
6616             if(typeof(config.align) != 'undefined' && config.align.length){
6617                 c.style += ' text-align:' + config.align + ';';
6618             }
6619             
6620             if(typeof(config.width) != 'undefined'){
6621                 c.style += ' width:' + config.width + 'px;';
6622                 this.totalWidth += config.width;
6623             } else {
6624                 this.totalWidth += 100; // assume minimum of 100 per column?
6625             }
6626             
6627             if(typeof(config.cls) != 'undefined'){
6628                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6629             }
6630             
6631             ['xs','sm','md','lg'].map(function(size){
6632                 
6633                 if(typeof(config[size]) == 'undefined'){
6634                     return;
6635                 }
6636                 
6637                 if (!config[size]) { // 0 = hidden
6638                     c.cls += ' hidden-' + size;
6639                     return;
6640                 }
6641                 
6642                 c.cls += ' col-' + size + '-' + config[size];
6643
6644             });
6645             
6646             header.cn.push(c)
6647         }
6648         
6649         return header;
6650     },
6651     
6652     renderBody : function()
6653     {
6654         var body = {
6655             tag: 'tbody',
6656             cn : [
6657                 {
6658                     tag: 'tr',
6659                     cn : [
6660                         {
6661                             tag : 'td',
6662                             colspan :  this.cm.getColumnCount()
6663                         }
6664                     ]
6665                 }
6666             ]
6667         };
6668         
6669         return body;
6670     },
6671     
6672     renderFooter : function()
6673     {
6674         var footer = {
6675             tag: 'tfoot',
6676             cn : [
6677                 {
6678                     tag: 'tr',
6679                     cn : [
6680                         {
6681                             tag : 'td',
6682                             colspan :  this.cm.getColumnCount()
6683                         }
6684                     ]
6685                 }
6686             ]
6687         };
6688         
6689         return footer;
6690     },
6691     
6692     
6693     
6694     onLoad : function()
6695     {
6696 //        Roo.log('ds onload');
6697         this.clear();
6698         
6699         var _this = this;
6700         var cm = this.cm;
6701         var ds = this.store;
6702         
6703         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6704             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6705             if (_this.store.sortInfo) {
6706                     
6707                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6708                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6709                 }
6710                 
6711                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6712                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6713                 }
6714             }
6715         });
6716         
6717         var tbody =  this.mainBody;
6718               
6719         if(ds.getCount() > 0){
6720             ds.data.each(function(d,rowIndex){
6721                 var row =  this.renderRow(cm, ds, rowIndex);
6722                 
6723                 tbody.createChild(row);
6724                 
6725                 var _this = this;
6726                 
6727                 if(row.cellObjects.length){
6728                     Roo.each(row.cellObjects, function(r){
6729                         _this.renderCellObject(r);
6730                     })
6731                 }
6732                 
6733             }, this);
6734         }
6735         
6736         var tfoot = this.el.select('tfoot', true).first();
6737         
6738         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6739             
6740             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6741             
6742             var total = this.ds.getTotalCount();
6743             
6744             if(this.footer.pageSize < total){
6745                 this.mainFoot.show();
6746             }
6747         }
6748         
6749         Roo.each(this.el.select('tbody td', true).elements, function(e){
6750             e.on('mouseover', _this.onMouseover, _this);
6751         });
6752         
6753         Roo.each(this.el.select('tbody td', true).elements, function(e){
6754             e.on('mouseout', _this.onMouseout, _this);
6755         });
6756         this.fireEvent('rowsrendered', this);
6757         
6758         this.autoSize();
6759     },
6760     
6761     
6762     onUpdate : function(ds,record)
6763     {
6764         this.refreshRow(record);
6765         this.autoSize();
6766     },
6767     
6768     onRemove : function(ds, record, index, isUpdate){
6769         if(isUpdate !== true){
6770             this.fireEvent("beforerowremoved", this, index, record);
6771         }
6772         var bt = this.mainBody.dom;
6773         
6774         var rows = this.el.select('tbody > tr', true).elements;
6775         
6776         if(typeof(rows[index]) != 'undefined'){
6777             bt.removeChild(rows[index].dom);
6778         }
6779         
6780 //        if(bt.rows[index]){
6781 //            bt.removeChild(bt.rows[index]);
6782 //        }
6783         
6784         if(isUpdate !== true){
6785             //this.stripeRows(index);
6786             //this.syncRowHeights(index, index);
6787             //this.layout();
6788             this.fireEvent("rowremoved", this, index, record);
6789         }
6790     },
6791     
6792     onAdd : function(ds, records, rowIndex)
6793     {
6794         //Roo.log('on Add called');
6795         // - note this does not handle multiple adding very well..
6796         var bt = this.mainBody.dom;
6797         for (var i =0 ; i < records.length;i++) {
6798             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6799             //Roo.log(records[i]);
6800             //Roo.log(this.store.getAt(rowIndex+i));
6801             this.insertRow(this.store, rowIndex + i, false);
6802             return;
6803         }
6804         
6805     },
6806     
6807     
6808     refreshRow : function(record){
6809         var ds = this.store, index;
6810         if(typeof record == 'number'){
6811             index = record;
6812             record = ds.getAt(index);
6813         }else{
6814             index = ds.indexOf(record);
6815         }
6816         this.insertRow(ds, index, true);
6817         this.autoSize();
6818         this.onRemove(ds, record, index+1, true);
6819         this.autoSize();
6820         //this.syncRowHeights(index, index);
6821         //this.layout();
6822         this.fireEvent("rowupdated", this, index, record);
6823     },
6824     
6825     insertRow : function(dm, rowIndex, isUpdate){
6826         
6827         if(!isUpdate){
6828             this.fireEvent("beforerowsinserted", this, rowIndex);
6829         }
6830             //var s = this.getScrollState();
6831         var row = this.renderRow(this.cm, this.store, rowIndex);
6832         // insert before rowIndex..
6833         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6834         
6835         var _this = this;
6836                 
6837         if(row.cellObjects.length){
6838             Roo.each(row.cellObjects, function(r){
6839                 _this.renderCellObject(r);
6840             })
6841         }
6842             
6843         if(!isUpdate){
6844             this.fireEvent("rowsinserted", this, rowIndex);
6845             //this.syncRowHeights(firstRow, lastRow);
6846             //this.stripeRows(firstRow);
6847             //this.layout();
6848         }
6849         
6850     },
6851     
6852     
6853     getRowDom : function(rowIndex)
6854     {
6855         var rows = this.el.select('tbody > tr', true).elements;
6856         
6857         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6858         
6859     },
6860     // returns the object tree for a tr..
6861   
6862     
6863     renderRow : function(cm, ds, rowIndex) 
6864     {
6865         var d = ds.getAt(rowIndex);
6866         
6867         var row = {
6868             tag : 'tr',
6869             cls : 'x-row-' + rowIndex,
6870             cn : []
6871         };
6872             
6873         var cellObjects = [];
6874         
6875         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6876             var config = cm.config[i];
6877             
6878             var renderer = cm.getRenderer(i);
6879             var value = '';
6880             var id = false;
6881             
6882             if(typeof(renderer) !== 'undefined'){
6883                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6884             }
6885             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6886             // and are rendered into the cells after the row is rendered - using the id for the element.
6887             
6888             if(typeof(value) === 'object'){
6889                 id = Roo.id();
6890                 cellObjects.push({
6891                     container : id,
6892                     cfg : value 
6893                 })
6894             }
6895             
6896             var rowcfg = {
6897                 record: d,
6898                 rowIndex : rowIndex,
6899                 colIndex : i,
6900                 rowClass : ''
6901             };
6902
6903             this.fireEvent('rowclass', this, rowcfg);
6904             
6905             var td = {
6906                 tag: 'td',
6907                 cls : rowcfg.rowClass + ' x-col-' + i,
6908                 style: '',
6909                 html: (typeof(value) === 'object') ? '' : value
6910             };
6911             
6912             if (id) {
6913                 td.id = id;
6914             }
6915             
6916             if(typeof(config.colspan) != 'undefined'){
6917                 td.colspan = config.colspan;
6918             }
6919             
6920             if(typeof(config.hidden) != 'undefined' && config.hidden){
6921                 td.style += ' display:none;';
6922             }
6923             
6924             if(typeof(config.align) != 'undefined' && config.align.length){
6925                 td.style += ' text-align:' + config.align + ';';
6926             }
6927             if(typeof(config.valign) != 'undefined' && config.valign.length){
6928                 td.style += ' vertical-align:' + config.valign + ';';
6929             }
6930             
6931             if(typeof(config.width) != 'undefined'){
6932                 td.style += ' width:' +  config.width + 'px;';
6933             }
6934             
6935             if(typeof(config.cursor) != 'undefined'){
6936                 td.style += ' cursor:' +  config.cursor + ';';
6937             }
6938             
6939             if(typeof(config.cls) != 'undefined'){
6940                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6941             }
6942             
6943             ['xs','sm','md','lg'].map(function(size){
6944                 
6945                 if(typeof(config[size]) == 'undefined'){
6946                     return;
6947                 }
6948                 
6949                 if (!config[size]) { // 0 = hidden
6950                     td.cls += ' hidden-' + size;
6951                     return;
6952                 }
6953                 
6954                 td.cls += ' col-' + size + '-' + config[size];
6955
6956             });
6957             
6958             row.cn.push(td);
6959            
6960         }
6961         
6962         row.cellObjects = cellObjects;
6963         
6964         return row;
6965           
6966     },
6967     
6968     
6969     
6970     onBeforeLoad : function()
6971     {
6972         
6973     },
6974      /**
6975      * Remove all rows
6976      */
6977     clear : function()
6978     {
6979         this.el.select('tbody', true).first().dom.innerHTML = '';
6980     },
6981     /**
6982      * Show or hide a row.
6983      * @param {Number} rowIndex to show or hide
6984      * @param {Boolean} state hide
6985      */
6986     setRowVisibility : function(rowIndex, state)
6987     {
6988         var bt = this.mainBody.dom;
6989         
6990         var rows = this.el.select('tbody > tr', true).elements;
6991         
6992         if(typeof(rows[rowIndex]) == 'undefined'){
6993             return;
6994         }
6995         rows[rowIndex].dom.style.display = state ? '' : 'none';
6996     },
6997     
6998     
6999     getSelectionModel : function(){
7000         if(!this.selModel){
7001             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7002         }
7003         return this.selModel;
7004     },
7005     /*
7006      * Render the Roo.bootstrap object from renderder
7007      */
7008     renderCellObject : function(r)
7009     {
7010         var _this = this;
7011         
7012         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7013         
7014         var t = r.cfg.render(r.container);
7015         
7016         if(r.cfg.cn){
7017             Roo.each(r.cfg.cn, function(c){
7018                 var child = {
7019                     container: t.getChildContainer(),
7020                     cfg: c
7021                 };
7022                 _this.renderCellObject(child);
7023             })
7024         }
7025     },
7026     
7027     getRowIndex : function(row)
7028     {
7029         var rowIndex = -1;
7030         
7031         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7032             if(el != row){
7033                 return;
7034             }
7035             
7036             rowIndex = index;
7037         });
7038         
7039         return rowIndex;
7040     },
7041      /**
7042      * Returns the grid's underlying element = used by panel.Grid
7043      * @return {Element} The element
7044      */
7045     getGridEl : function(){
7046         return this.el;
7047     },
7048      /**
7049      * Forces a resize - used by panel.Grid
7050      * @return {Element} The element
7051      */
7052     autoSize : function()
7053     {
7054         //var ctr = Roo.get(this.container.dom.parentElement);
7055         var ctr = Roo.get(this.el.dom);
7056         
7057         var thd = this.getGridEl().select('thead',true).first();
7058         var tbd = this.getGridEl().select('tbody', true).first();
7059         var tfd = this.getGridEl().select('tfoot', true).first();
7060         
7061         var cw = ctr.getWidth();
7062         
7063         if (tbd) {
7064             
7065             tbd.setSize(ctr.getWidth(),
7066                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7067             );
7068             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7069             cw -= barsize;
7070         }
7071         cw = Math.max(cw, this.totalWidth);
7072         this.getGridEl().select('tr',true).setWidth(cw);
7073         // resize 'expandable coloumn?
7074         
7075         return; // we doe not have a view in this design..
7076         
7077     },
7078     onBodyScroll: function()
7079     {
7080         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7081         if(this.mainHead){
7082             this.mainHead.setStyle({
7083                 'position' : 'relative',
7084                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7085             });
7086         }
7087         
7088         if(this.lazyLoad){
7089             
7090             var scrollHeight = this.mainBody.dom.scrollHeight;
7091             
7092             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7093             
7094             var height = this.mainBody.getHeight();
7095             
7096             if(scrollHeight - height == scrollTop) {
7097                 
7098                 var total = this.ds.getTotalCount();
7099                 
7100                 if(this.footer.cursor + this.footer.pageSize < total){
7101                     
7102                     this.footer.ds.load({
7103                         params : {
7104                             start : this.footer.cursor + this.footer.pageSize,
7105                             limit : this.footer.pageSize
7106                         },
7107                         add : true
7108                     });
7109                 }
7110             }
7111             
7112         }
7113     },
7114     
7115     onHeaderChange : function()
7116     {
7117         var header = this.renderHeader();
7118         var table = this.el.select('table', true).first();
7119         
7120         this.mainHead.remove();
7121         this.mainHead = table.createChild(header, this.mainBody, false);
7122     },
7123     
7124     onHiddenChange : function(colModel, colIndex, hidden)
7125     {
7126         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7127         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7128         
7129         this.CSS.updateRule(thSelector, "display", "");
7130         this.CSS.updateRule(tdSelector, "display", "");
7131         
7132         if(hidden){
7133             this.CSS.updateRule(thSelector, "display", "none");
7134             this.CSS.updateRule(tdSelector, "display", "none");
7135         }
7136         
7137         this.onHeaderChange();
7138         this.onLoad();
7139     },
7140     
7141     setColumnWidth: function(col_index, width)
7142     {
7143         // width = "md-2 xs-2..."
7144         if(!this.colModel.config[col_index]) {
7145             return;
7146         }
7147         
7148         var w = width.split(" ");
7149         
7150         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7151         
7152         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7153         
7154         
7155         for(var j = 0; j < w.length; j++) {
7156             
7157             if(!w[j]) {
7158                 continue;
7159             }
7160             
7161             var size_cls = w[j].split("-");
7162             
7163             if(!Number.isInteger(size_cls[1] * 1)) {
7164                 continue;
7165             }
7166             
7167             if(!this.colModel.config[col_index][size_cls[0]]) {
7168                 continue;
7169             }
7170             
7171             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7172                 continue;
7173             }
7174             
7175             h_row[0].classList.replace(
7176                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7177                 "col-"+size_cls[0]+"-"+size_cls[1]
7178             );
7179             
7180             for(var i = 0; i < rows.length; i++) {
7181                 
7182                 var size_cls = w[j].split("-");
7183                 
7184                 if(!Number.isInteger(size_cls[1] * 1)) {
7185                     continue;
7186                 }
7187                 
7188                 if(!this.colModel.config[col_index][size_cls[0]]) {
7189                     continue;
7190                 }
7191                 
7192                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7193                     continue;
7194                 }
7195                 
7196                 rows[i].classList.replace(
7197                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7198                     "col-"+size_cls[0]+"-"+size_cls[1]
7199                 );
7200             }
7201             
7202             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7203         }
7204     }
7205 });
7206
7207  
7208
7209  /*
7210  * - LGPL
7211  *
7212  * table cell
7213  * 
7214  */
7215
7216 /**
7217  * @class Roo.bootstrap.TableCell
7218  * @extends Roo.bootstrap.Component
7219  * Bootstrap TableCell class
7220  * @cfg {String} html cell contain text
7221  * @cfg {String} cls cell class
7222  * @cfg {String} tag cell tag (td|th) default td
7223  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7224  * @cfg {String} align Aligns the content in a cell
7225  * @cfg {String} axis Categorizes cells
7226  * @cfg {String} bgcolor Specifies the background color of a cell
7227  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7228  * @cfg {Number} colspan Specifies the number of columns a cell should span
7229  * @cfg {String} headers Specifies one or more header cells a cell is related to
7230  * @cfg {Number} height Sets the height of a cell
7231  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7232  * @cfg {Number} rowspan Sets the number of rows a cell should span
7233  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7234  * @cfg {String} valign Vertical aligns the content in a cell
7235  * @cfg {Number} width Specifies the width of a cell
7236  * 
7237  * @constructor
7238  * Create a new TableCell
7239  * @param {Object} config The config object
7240  */
7241
7242 Roo.bootstrap.TableCell = function(config){
7243     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7244 };
7245
7246 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7247     
7248     html: false,
7249     cls: false,
7250     tag: false,
7251     abbr: false,
7252     align: false,
7253     axis: false,
7254     bgcolor: false,
7255     charoff: false,
7256     colspan: false,
7257     headers: false,
7258     height: false,
7259     nowrap: false,
7260     rowspan: false,
7261     scope: false,
7262     valign: false,
7263     width: false,
7264     
7265     
7266     getAutoCreate : function(){
7267         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7268         
7269         cfg = {
7270             tag: 'td'
7271         };
7272         
7273         if(this.tag){
7274             cfg.tag = this.tag;
7275         }
7276         
7277         if (this.html) {
7278             cfg.html=this.html
7279         }
7280         if (this.cls) {
7281             cfg.cls=this.cls
7282         }
7283         if (this.abbr) {
7284             cfg.abbr=this.abbr
7285         }
7286         if (this.align) {
7287             cfg.align=this.align
7288         }
7289         if (this.axis) {
7290             cfg.axis=this.axis
7291         }
7292         if (this.bgcolor) {
7293             cfg.bgcolor=this.bgcolor
7294         }
7295         if (this.charoff) {
7296             cfg.charoff=this.charoff
7297         }
7298         if (this.colspan) {
7299             cfg.colspan=this.colspan
7300         }
7301         if (this.headers) {
7302             cfg.headers=this.headers
7303         }
7304         if (this.height) {
7305             cfg.height=this.height
7306         }
7307         if (this.nowrap) {
7308             cfg.nowrap=this.nowrap
7309         }
7310         if (this.rowspan) {
7311             cfg.rowspan=this.rowspan
7312         }
7313         if (this.scope) {
7314             cfg.scope=this.scope
7315         }
7316         if (this.valign) {
7317             cfg.valign=this.valign
7318         }
7319         if (this.width) {
7320             cfg.width=this.width
7321         }
7322         
7323         
7324         return cfg;
7325     }
7326    
7327 });
7328
7329  
7330
7331  /*
7332  * - LGPL
7333  *
7334  * table row
7335  * 
7336  */
7337
7338 /**
7339  * @class Roo.bootstrap.TableRow
7340  * @extends Roo.bootstrap.Component
7341  * Bootstrap TableRow class
7342  * @cfg {String} cls row class
7343  * @cfg {String} align Aligns the content in a table row
7344  * @cfg {String} bgcolor Specifies a background color for a table row
7345  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7346  * @cfg {String} valign Vertical aligns the content in a table row
7347  * 
7348  * @constructor
7349  * Create a new TableRow
7350  * @param {Object} config The config object
7351  */
7352
7353 Roo.bootstrap.TableRow = function(config){
7354     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7355 };
7356
7357 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7358     
7359     cls: false,
7360     align: false,
7361     bgcolor: false,
7362     charoff: false,
7363     valign: false,
7364     
7365     getAutoCreate : function(){
7366         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7367         
7368         cfg = {
7369             tag: 'tr'
7370         };
7371             
7372         if(this.cls){
7373             cfg.cls = this.cls;
7374         }
7375         if(this.align){
7376             cfg.align = this.align;
7377         }
7378         if(this.bgcolor){
7379             cfg.bgcolor = this.bgcolor;
7380         }
7381         if(this.charoff){
7382             cfg.charoff = this.charoff;
7383         }
7384         if(this.valign){
7385             cfg.valign = this.valign;
7386         }
7387         
7388         return cfg;
7389     }
7390    
7391 });
7392
7393  
7394
7395  /*
7396  * - LGPL
7397  *
7398  * table body
7399  * 
7400  */
7401
7402 /**
7403  * @class Roo.bootstrap.TableBody
7404  * @extends Roo.bootstrap.Component
7405  * Bootstrap TableBody class
7406  * @cfg {String} cls element class
7407  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7408  * @cfg {String} align Aligns the content inside the element
7409  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7410  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7411  * 
7412  * @constructor
7413  * Create a new TableBody
7414  * @param {Object} config The config object
7415  */
7416
7417 Roo.bootstrap.TableBody = function(config){
7418     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7419 };
7420
7421 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7422     
7423     cls: false,
7424     tag: false,
7425     align: false,
7426     charoff: false,
7427     valign: false,
7428     
7429     getAutoCreate : function(){
7430         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7431         
7432         cfg = {
7433             tag: 'tbody'
7434         };
7435             
7436         if (this.cls) {
7437             cfg.cls=this.cls
7438         }
7439         if(this.tag){
7440             cfg.tag = this.tag;
7441         }
7442         
7443         if(this.align){
7444             cfg.align = this.align;
7445         }
7446         if(this.charoff){
7447             cfg.charoff = this.charoff;
7448         }
7449         if(this.valign){
7450             cfg.valign = this.valign;
7451         }
7452         
7453         return cfg;
7454     }
7455     
7456     
7457 //    initEvents : function()
7458 //    {
7459 //        
7460 //        if(!this.store){
7461 //            return;
7462 //        }
7463 //        
7464 //        this.store = Roo.factory(this.store, Roo.data);
7465 //        this.store.on('load', this.onLoad, this);
7466 //        
7467 //        this.store.load();
7468 //        
7469 //    },
7470 //    
7471 //    onLoad: function () 
7472 //    {   
7473 //        this.fireEvent('load', this);
7474 //    }
7475 //    
7476 //   
7477 });
7478
7479  
7480
7481  /*
7482  * Based on:
7483  * Ext JS Library 1.1.1
7484  * Copyright(c) 2006-2007, Ext JS, LLC.
7485  *
7486  * Originally Released Under LGPL - original licence link has changed is not relivant.
7487  *
7488  * Fork - LGPL
7489  * <script type="text/javascript">
7490  */
7491
7492 // as we use this in bootstrap.
7493 Roo.namespace('Roo.form');
7494  /**
7495  * @class Roo.form.Action
7496  * Internal Class used to handle form actions
7497  * @constructor
7498  * @param {Roo.form.BasicForm} el The form element or its id
7499  * @param {Object} config Configuration options
7500  */
7501
7502  
7503  
7504 // define the action interface
7505 Roo.form.Action = function(form, options){
7506     this.form = form;
7507     this.options = options || {};
7508 };
7509 /**
7510  * Client Validation Failed
7511  * @const 
7512  */
7513 Roo.form.Action.CLIENT_INVALID = 'client';
7514 /**
7515  * Server Validation Failed
7516  * @const 
7517  */
7518 Roo.form.Action.SERVER_INVALID = 'server';
7519  /**
7520  * Connect to Server Failed
7521  * @const 
7522  */
7523 Roo.form.Action.CONNECT_FAILURE = 'connect';
7524 /**
7525  * Reading Data from Server Failed
7526  * @const 
7527  */
7528 Roo.form.Action.LOAD_FAILURE = 'load';
7529
7530 Roo.form.Action.prototype = {
7531     type : 'default',
7532     failureType : undefined,
7533     response : undefined,
7534     result : undefined,
7535
7536     // interface method
7537     run : function(options){
7538
7539     },
7540
7541     // interface method
7542     success : function(response){
7543
7544     },
7545
7546     // interface method
7547     handleResponse : function(response){
7548
7549     },
7550
7551     // default connection failure
7552     failure : function(response){
7553         
7554         this.response = response;
7555         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7556         this.form.afterAction(this, false);
7557     },
7558
7559     processResponse : function(response){
7560         this.response = response;
7561         if(!response.responseText){
7562             return true;
7563         }
7564         this.result = this.handleResponse(response);
7565         return this.result;
7566     },
7567
7568     // utility functions used internally
7569     getUrl : function(appendParams){
7570         var url = this.options.url || this.form.url || this.form.el.dom.action;
7571         if(appendParams){
7572             var p = this.getParams();
7573             if(p){
7574                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7575             }
7576         }
7577         return url;
7578     },
7579
7580     getMethod : function(){
7581         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7582     },
7583
7584     getParams : function(){
7585         var bp = this.form.baseParams;
7586         var p = this.options.params;
7587         if(p){
7588             if(typeof p == "object"){
7589                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7590             }else if(typeof p == 'string' && bp){
7591                 p += '&' + Roo.urlEncode(bp);
7592             }
7593         }else if(bp){
7594             p = Roo.urlEncode(bp);
7595         }
7596         return p;
7597     },
7598
7599     createCallback : function(){
7600         return {
7601             success: this.success,
7602             failure: this.failure,
7603             scope: this,
7604             timeout: (this.form.timeout*1000),
7605             upload: this.form.fileUpload ? this.success : undefined
7606         };
7607     }
7608 };
7609
7610 Roo.form.Action.Submit = function(form, options){
7611     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7612 };
7613
7614 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7615     type : 'submit',
7616
7617     haveProgress : false,
7618     uploadComplete : false,
7619     
7620     // uploadProgress indicator.
7621     uploadProgress : function()
7622     {
7623         if (!this.form.progressUrl) {
7624             return;
7625         }
7626         
7627         if (!this.haveProgress) {
7628             Roo.MessageBox.progress("Uploading", "Uploading");
7629         }
7630         if (this.uploadComplete) {
7631            Roo.MessageBox.hide();
7632            return;
7633         }
7634         
7635         this.haveProgress = true;
7636    
7637         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7638         
7639         var c = new Roo.data.Connection();
7640         c.request({
7641             url : this.form.progressUrl,
7642             params: {
7643                 id : uid
7644             },
7645             method: 'GET',
7646             success : function(req){
7647                //console.log(data);
7648                 var rdata = false;
7649                 var edata;
7650                 try  {
7651                    rdata = Roo.decode(req.responseText)
7652                 } catch (e) {
7653                     Roo.log("Invalid data from server..");
7654                     Roo.log(edata);
7655                     return;
7656                 }
7657                 if (!rdata || !rdata.success) {
7658                     Roo.log(rdata);
7659                     Roo.MessageBox.alert(Roo.encode(rdata));
7660                     return;
7661                 }
7662                 var data = rdata.data;
7663                 
7664                 if (this.uploadComplete) {
7665                    Roo.MessageBox.hide();
7666                    return;
7667                 }
7668                    
7669                 if (data){
7670                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7671                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7672                     );
7673                 }
7674                 this.uploadProgress.defer(2000,this);
7675             },
7676        
7677             failure: function(data) {
7678                 Roo.log('progress url failed ');
7679                 Roo.log(data);
7680             },
7681             scope : this
7682         });
7683            
7684     },
7685     
7686     
7687     run : function()
7688     {
7689         // run get Values on the form, so it syncs any secondary forms.
7690         this.form.getValues();
7691         
7692         var o = this.options;
7693         var method = this.getMethod();
7694         var isPost = method == 'POST';
7695         if(o.clientValidation === false || this.form.isValid()){
7696             
7697             if (this.form.progressUrl) {
7698                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7699                     (new Date() * 1) + '' + Math.random());
7700                     
7701             } 
7702             
7703             
7704             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7705                 form:this.form.el.dom,
7706                 url:this.getUrl(!isPost),
7707                 method: method,
7708                 params:isPost ? this.getParams() : null,
7709                 isUpload: this.form.fileUpload
7710             }));
7711             
7712             this.uploadProgress();
7713
7714         }else if (o.clientValidation !== false){ // client validation failed
7715             this.failureType = Roo.form.Action.CLIENT_INVALID;
7716             this.form.afterAction(this, false);
7717         }
7718     },
7719
7720     success : function(response)
7721     {
7722         this.uploadComplete= true;
7723         if (this.haveProgress) {
7724             Roo.MessageBox.hide();
7725         }
7726         
7727         
7728         var result = this.processResponse(response);
7729         if(result === true || result.success){
7730             this.form.afterAction(this, true);
7731             return;
7732         }
7733         if(result.errors){
7734             this.form.markInvalid(result.errors);
7735             this.failureType = Roo.form.Action.SERVER_INVALID;
7736         }
7737         this.form.afterAction(this, false);
7738     },
7739     failure : function(response)
7740     {
7741         this.uploadComplete= true;
7742         if (this.haveProgress) {
7743             Roo.MessageBox.hide();
7744         }
7745         
7746         this.response = response;
7747         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7748         this.form.afterAction(this, false);
7749     },
7750     
7751     handleResponse : function(response){
7752         if(this.form.errorReader){
7753             var rs = this.form.errorReader.read(response);
7754             var errors = [];
7755             if(rs.records){
7756                 for(var i = 0, len = rs.records.length; i < len; i++) {
7757                     var r = rs.records[i];
7758                     errors[i] = r.data;
7759                 }
7760             }
7761             if(errors.length < 1){
7762                 errors = null;
7763             }
7764             return {
7765                 success : rs.success,
7766                 errors : errors
7767             };
7768         }
7769         var ret = false;
7770         try {
7771             ret = Roo.decode(response.responseText);
7772         } catch (e) {
7773             ret = {
7774                 success: false,
7775                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7776                 errors : []
7777             };
7778         }
7779         return ret;
7780         
7781     }
7782 });
7783
7784
7785 Roo.form.Action.Load = function(form, options){
7786     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7787     this.reader = this.form.reader;
7788 };
7789
7790 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7791     type : 'load',
7792
7793     run : function(){
7794         
7795         Roo.Ajax.request(Roo.apply(
7796                 this.createCallback(), {
7797                     method:this.getMethod(),
7798                     url:this.getUrl(false),
7799                     params:this.getParams()
7800         }));
7801     },
7802
7803     success : function(response){
7804         
7805         var result = this.processResponse(response);
7806         if(result === true || !result.success || !result.data){
7807             this.failureType = Roo.form.Action.LOAD_FAILURE;
7808             this.form.afterAction(this, false);
7809             return;
7810         }
7811         this.form.clearInvalid();
7812         this.form.setValues(result.data);
7813         this.form.afterAction(this, true);
7814     },
7815
7816     handleResponse : function(response){
7817         if(this.form.reader){
7818             var rs = this.form.reader.read(response);
7819             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7820             return {
7821                 success : rs.success,
7822                 data : data
7823             };
7824         }
7825         return Roo.decode(response.responseText);
7826     }
7827 });
7828
7829 Roo.form.Action.ACTION_TYPES = {
7830     'load' : Roo.form.Action.Load,
7831     'submit' : Roo.form.Action.Submit
7832 };/*
7833  * - LGPL
7834  *
7835  * form
7836  *
7837  */
7838
7839 /**
7840  * @class Roo.bootstrap.Form
7841  * @extends Roo.bootstrap.Component
7842  * Bootstrap Form class
7843  * @cfg {String} method  GET | POST (default POST)
7844  * @cfg {String} labelAlign top | left (default top)
7845  * @cfg {String} align left  | right - for navbars
7846  * @cfg {Boolean} loadMask load mask when submit (default true)
7847
7848  *
7849  * @constructor
7850  * Create a new Form
7851  * @param {Object} config The config object
7852  */
7853
7854
7855 Roo.bootstrap.Form = function(config){
7856     
7857     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7858     
7859     Roo.bootstrap.Form.popover.apply();
7860     
7861     this.addEvents({
7862         /**
7863          * @event clientvalidation
7864          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7865          * @param {Form} this
7866          * @param {Boolean} valid true if the form has passed client-side validation
7867          */
7868         clientvalidation: true,
7869         /**
7870          * @event beforeaction
7871          * Fires before any action is performed. Return false to cancel the action.
7872          * @param {Form} this
7873          * @param {Action} action The action to be performed
7874          */
7875         beforeaction: true,
7876         /**
7877          * @event actionfailed
7878          * Fires when an action fails.
7879          * @param {Form} this
7880          * @param {Action} action The action that failed
7881          */
7882         actionfailed : true,
7883         /**
7884          * @event actioncomplete
7885          * Fires when an action is completed.
7886          * @param {Form} this
7887          * @param {Action} action The action that completed
7888          */
7889         actioncomplete : true
7890     });
7891 };
7892
7893 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7894
7895      /**
7896      * @cfg {String} method
7897      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7898      */
7899     method : 'POST',
7900     /**
7901      * @cfg {String} url
7902      * The URL to use for form actions if one isn't supplied in the action options.
7903      */
7904     /**
7905      * @cfg {Boolean} fileUpload
7906      * Set to true if this form is a file upload.
7907      */
7908
7909     /**
7910      * @cfg {Object} baseParams
7911      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7912      */
7913
7914     /**
7915      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7916      */
7917     timeout: 30,
7918     /**
7919      * @cfg {Sting} align (left|right) for navbar forms
7920      */
7921     align : 'left',
7922
7923     // private
7924     activeAction : null,
7925
7926     /**
7927      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7928      * element by passing it or its id or mask the form itself by passing in true.
7929      * @type Mixed
7930      */
7931     waitMsgTarget : false,
7932
7933     loadMask : true,
7934     
7935     /**
7936      * @cfg {Boolean} errorMask (true|false) default false
7937      */
7938     errorMask : false,
7939     
7940     /**
7941      * @cfg {Number} maskOffset Default 100
7942      */
7943     maskOffset : 100,
7944     
7945     /**
7946      * @cfg {Boolean} maskBody
7947      */
7948     maskBody : false,
7949
7950     getAutoCreate : function(){
7951
7952         var cfg = {
7953             tag: 'form',
7954             method : this.method || 'POST',
7955             id : this.id || Roo.id(),
7956             cls : ''
7957         };
7958         if (this.parent().xtype.match(/^Nav/)) {
7959             cfg.cls = 'navbar-form navbar-' + this.align;
7960
7961         }
7962
7963         if (this.labelAlign == 'left' ) {
7964             cfg.cls += ' form-horizontal';
7965         }
7966
7967
7968         return cfg;
7969     },
7970     initEvents : function()
7971     {
7972         this.el.on('submit', this.onSubmit, this);
7973         // this was added as random key presses on the form where triggering form submit.
7974         this.el.on('keypress', function(e) {
7975             if (e.getCharCode() != 13) {
7976                 return true;
7977             }
7978             // we might need to allow it for textareas.. and some other items.
7979             // check e.getTarget().
7980
7981             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7982                 return true;
7983             }
7984
7985             Roo.log("keypress blocked");
7986
7987             e.preventDefault();
7988             return false;
7989         });
7990         
7991     },
7992     // private
7993     onSubmit : function(e){
7994         e.stopEvent();
7995     },
7996
7997      /**
7998      * Returns true if client-side validation on the form is successful.
7999      * @return Boolean
8000      */
8001     isValid : function(){
8002         var items = this.getItems();
8003         var valid = true;
8004         var target = false;
8005         
8006         items.each(function(f){
8007             
8008             if(f.validate()){
8009                 return;
8010             }
8011             
8012             Roo.log('invalid field: ' + f.name);
8013             
8014             valid = false;
8015
8016             if(!target && f.el.isVisible(true)){
8017                 target = f;
8018             }
8019            
8020         });
8021         
8022         if(this.errorMask && !valid){
8023             Roo.bootstrap.Form.popover.mask(this, target);
8024         }
8025         
8026         return valid;
8027     },
8028     
8029     /**
8030      * Returns true if any fields in this form have changed since their original load.
8031      * @return Boolean
8032      */
8033     isDirty : function(){
8034         var dirty = false;
8035         var items = this.getItems();
8036         items.each(function(f){
8037            if(f.isDirty()){
8038                dirty = true;
8039                return false;
8040            }
8041            return true;
8042         });
8043         return dirty;
8044     },
8045      /**
8046      * Performs a predefined action (submit or load) or custom actions you define on this form.
8047      * @param {String} actionName The name of the action type
8048      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8049      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8050      * accept other config options):
8051      * <pre>
8052 Property          Type             Description
8053 ----------------  ---------------  ----------------------------------------------------------------------------------
8054 url               String           The url for the action (defaults to the form's url)
8055 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8056 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8057 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8058                                    validate the form on the client (defaults to false)
8059      * </pre>
8060      * @return {BasicForm} this
8061      */
8062     doAction : function(action, options){
8063         if(typeof action == 'string'){
8064             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8065         }
8066         if(this.fireEvent('beforeaction', this, action) !== false){
8067             this.beforeAction(action);
8068             action.run.defer(100, action);
8069         }
8070         return this;
8071     },
8072
8073     // private
8074     beforeAction : function(action){
8075         var o = action.options;
8076         
8077         if(this.loadMask){
8078             
8079             if(this.maskBody){
8080                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8081             } else {
8082                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8083             }
8084         }
8085         // not really supported yet.. ??
8086
8087         //if(this.waitMsgTarget === true){
8088         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8089         //}else if(this.waitMsgTarget){
8090         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8091         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8092         //}else {
8093         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8094        // }
8095
8096     },
8097
8098     // private
8099     afterAction : function(action, success){
8100         this.activeAction = null;
8101         var o = action.options;
8102
8103         if(this.loadMask){
8104             
8105             if(this.maskBody){
8106                 Roo.get(document.body).unmask();
8107             } else {
8108                 this.el.unmask();
8109             }
8110         }
8111         
8112         //if(this.waitMsgTarget === true){
8113 //            this.el.unmask();
8114         //}else if(this.waitMsgTarget){
8115         //    this.waitMsgTarget.unmask();
8116         //}else{
8117         //    Roo.MessageBox.updateProgress(1);
8118         //    Roo.MessageBox.hide();
8119        // }
8120         //
8121         if(success){
8122             if(o.reset){
8123                 this.reset();
8124             }
8125             Roo.callback(o.success, o.scope, [this, action]);
8126             this.fireEvent('actioncomplete', this, action);
8127
8128         }else{
8129
8130             // failure condition..
8131             // we have a scenario where updates need confirming.
8132             // eg. if a locking scenario exists..
8133             // we look for { errors : { needs_confirm : true }} in the response.
8134             if (
8135                 (typeof(action.result) != 'undefined')  &&
8136                 (typeof(action.result.errors) != 'undefined')  &&
8137                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8138            ){
8139                 var _t = this;
8140                 Roo.log("not supported yet");
8141                  /*
8142
8143                 Roo.MessageBox.confirm(
8144                     "Change requires confirmation",
8145                     action.result.errorMsg,
8146                     function(r) {
8147                         if (r != 'yes') {
8148                             return;
8149                         }
8150                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8151                     }
8152
8153                 );
8154                 */
8155
8156
8157                 return;
8158             }
8159
8160             Roo.callback(o.failure, o.scope, [this, action]);
8161             // show an error message if no failed handler is set..
8162             if (!this.hasListener('actionfailed')) {
8163                 Roo.log("need to add dialog support");
8164                 /*
8165                 Roo.MessageBox.alert("Error",
8166                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8167                         action.result.errorMsg :
8168                         "Saving Failed, please check your entries or try again"
8169                 );
8170                 */
8171             }
8172
8173             this.fireEvent('actionfailed', this, action);
8174         }
8175
8176     },
8177     /**
8178      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8179      * @param {String} id The value to search for
8180      * @return Field
8181      */
8182     findField : function(id){
8183         var items = this.getItems();
8184         var field = items.get(id);
8185         if(!field){
8186              items.each(function(f){
8187                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8188                     field = f;
8189                     return false;
8190                 }
8191                 return true;
8192             });
8193         }
8194         return field || null;
8195     },
8196      /**
8197      * Mark fields in this form invalid in bulk.
8198      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8199      * @return {BasicForm} this
8200      */
8201     markInvalid : function(errors){
8202         if(errors instanceof Array){
8203             for(var i = 0, len = errors.length; i < len; i++){
8204                 var fieldError = errors[i];
8205                 var f = this.findField(fieldError.id);
8206                 if(f){
8207                     f.markInvalid(fieldError.msg);
8208                 }
8209             }
8210         }else{
8211             var field, id;
8212             for(id in errors){
8213                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8214                     field.markInvalid(errors[id]);
8215                 }
8216             }
8217         }
8218         //Roo.each(this.childForms || [], function (f) {
8219         //    f.markInvalid(errors);
8220         //});
8221
8222         return this;
8223     },
8224
8225     /**
8226      * Set values for fields in this form in bulk.
8227      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8228      * @return {BasicForm} this
8229      */
8230     setValues : function(values){
8231         if(values instanceof Array){ // array of objects
8232             for(var i = 0, len = values.length; i < len; i++){
8233                 var v = values[i];
8234                 var f = this.findField(v.id);
8235                 if(f){
8236                     f.setValue(v.value);
8237                     if(this.trackResetOnLoad){
8238                         f.originalValue = f.getValue();
8239                     }
8240                 }
8241             }
8242         }else{ // object hash
8243             var field, id;
8244             for(id in values){
8245                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8246
8247                     if (field.setFromData &&
8248                         field.valueField &&
8249                         field.displayField &&
8250                         // combos' with local stores can
8251                         // be queried via setValue()
8252                         // to set their value..
8253                         (field.store && !field.store.isLocal)
8254                         ) {
8255                         // it's a combo
8256                         var sd = { };
8257                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8258                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8259                         field.setFromData(sd);
8260
8261                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8262                         
8263                         field.setFromData(values);
8264                         
8265                     } else {
8266                         field.setValue(values[id]);
8267                     }
8268
8269
8270                     if(this.trackResetOnLoad){
8271                         field.originalValue = field.getValue();
8272                     }
8273                 }
8274             }
8275         }
8276
8277         //Roo.each(this.childForms || [], function (f) {
8278         //    f.setValues(values);
8279         //});
8280
8281         return this;
8282     },
8283
8284     /**
8285      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8286      * they are returned as an array.
8287      * @param {Boolean} asString
8288      * @return {Object}
8289      */
8290     getValues : function(asString){
8291         //if (this.childForms) {
8292             // copy values from the child forms
8293         //    Roo.each(this.childForms, function (f) {
8294         //        this.setValues(f.getValues());
8295         //    }, this);
8296         //}
8297
8298
8299
8300         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8301         if(asString === true){
8302             return fs;
8303         }
8304         return Roo.urlDecode(fs);
8305     },
8306
8307     /**
8308      * Returns the fields in this form as an object with key/value pairs.
8309      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8310      * @return {Object}
8311      */
8312     getFieldValues : function(with_hidden)
8313     {
8314         var items = this.getItems();
8315         var ret = {};
8316         items.each(function(f){
8317             
8318             if (!f.getName()) {
8319                 return;
8320             }
8321             
8322             var v = f.getValue();
8323             
8324             if (f.inputType =='radio') {
8325                 if (typeof(ret[f.getName()]) == 'undefined') {
8326                     ret[f.getName()] = ''; // empty..
8327                 }
8328
8329                 if (!f.el.dom.checked) {
8330                     return;
8331
8332                 }
8333                 v = f.el.dom.value;
8334
8335             }
8336             
8337             if(f.xtype == 'MoneyField'){
8338                 ret[f.currencyName] = f.getCurrency();
8339             }
8340
8341             // not sure if this supported any more..
8342             if ((typeof(v) == 'object') && f.getRawValue) {
8343                 v = f.getRawValue() ; // dates..
8344             }
8345             // combo boxes where name != hiddenName...
8346             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8347                 ret[f.name] = f.getRawValue();
8348             }
8349             ret[f.getName()] = v;
8350         });
8351
8352         return ret;
8353     },
8354
8355     /**
8356      * Clears all invalid messages in this form.
8357      * @return {BasicForm} this
8358      */
8359     clearInvalid : function(){
8360         var items = this.getItems();
8361
8362         items.each(function(f){
8363            f.clearInvalid();
8364         });
8365
8366         return this;
8367     },
8368
8369     /**
8370      * Resets this form.
8371      * @return {BasicForm} this
8372      */
8373     reset : function(){
8374         var items = this.getItems();
8375         items.each(function(f){
8376             f.reset();
8377         });
8378
8379         Roo.each(this.childForms || [], function (f) {
8380             f.reset();
8381         });
8382
8383
8384         return this;
8385     },
8386     
8387     getItems : function()
8388     {
8389         var r=new Roo.util.MixedCollection(false, function(o){
8390             return o.id || (o.id = Roo.id());
8391         });
8392         var iter = function(el) {
8393             if (el.inputEl) {
8394                 r.add(el);
8395             }
8396             if (!el.items) {
8397                 return;
8398             }
8399             Roo.each(el.items,function(e) {
8400                 iter(e);
8401             });
8402         };
8403
8404         iter(this);
8405         return r;
8406     },
8407     
8408     hideFields : function(items)
8409     {
8410         Roo.each(items, function(i){
8411             
8412             var f = this.findField(i);
8413             
8414             if(!f){
8415                 return;
8416             }
8417             
8418             f.hide();
8419             
8420         }, this);
8421     },
8422     
8423     showFields : function(items)
8424     {
8425         Roo.each(items, function(i){
8426             
8427             var f = this.findField(i);
8428             
8429             if(!f){
8430                 return;
8431             }
8432             
8433             f.show();
8434             
8435         }, this);
8436     }
8437
8438 });
8439
8440 Roo.apply(Roo.bootstrap.Form, {
8441     
8442     popover : {
8443         
8444         padding : 5,
8445         
8446         isApplied : false,
8447         
8448         isMasked : false,
8449         
8450         form : false,
8451         
8452         target : false,
8453         
8454         toolTip : false,
8455         
8456         intervalID : false,
8457         
8458         maskEl : false,
8459         
8460         apply : function()
8461         {
8462             if(this.isApplied){
8463                 return;
8464             }
8465             
8466             this.maskEl = {
8467                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8468                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8469                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8470                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8471             };
8472             
8473             this.maskEl.top.enableDisplayMode("block");
8474             this.maskEl.left.enableDisplayMode("block");
8475             this.maskEl.bottom.enableDisplayMode("block");
8476             this.maskEl.right.enableDisplayMode("block");
8477             
8478             this.toolTip = new Roo.bootstrap.Tooltip({
8479                 cls : 'roo-form-error-popover',
8480                 alignment : {
8481                     'left' : ['r-l', [-2,0], 'right'],
8482                     'right' : ['l-r', [2,0], 'left'],
8483                     'bottom' : ['tl-bl', [0,2], 'top'],
8484                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8485                 }
8486             });
8487             
8488             this.toolTip.render(Roo.get(document.body));
8489
8490             this.toolTip.el.enableDisplayMode("block");
8491             
8492             Roo.get(document.body).on('click', function(){
8493                 this.unmask();
8494             }, this);
8495             
8496             Roo.get(document.body).on('touchstart', function(){
8497                 this.unmask();
8498             }, this);
8499             
8500             this.isApplied = true
8501         },
8502         
8503         mask : function(form, target)
8504         {
8505             this.form = form;
8506             
8507             this.target = target;
8508             
8509             if(!this.form.errorMask || !target.el){
8510                 return;
8511             }
8512             
8513             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8514             
8515             Roo.log(scrollable);
8516             
8517             var ot = this.target.el.calcOffsetsTo(scrollable);
8518             
8519             var scrollTo = ot[1] - this.form.maskOffset;
8520             
8521             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8522             
8523             scrollable.scrollTo('top', scrollTo);
8524             
8525             var box = this.target.el.getBox();
8526             Roo.log(box);
8527             var zIndex = Roo.bootstrap.Modal.zIndex++;
8528
8529             
8530             this.maskEl.top.setStyle('position', 'absolute');
8531             this.maskEl.top.setStyle('z-index', zIndex);
8532             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8533             this.maskEl.top.setLeft(0);
8534             this.maskEl.top.setTop(0);
8535             this.maskEl.top.show();
8536             
8537             this.maskEl.left.setStyle('position', 'absolute');
8538             this.maskEl.left.setStyle('z-index', zIndex);
8539             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8540             this.maskEl.left.setLeft(0);
8541             this.maskEl.left.setTop(box.y - this.padding);
8542             this.maskEl.left.show();
8543
8544             this.maskEl.bottom.setStyle('position', 'absolute');
8545             this.maskEl.bottom.setStyle('z-index', zIndex);
8546             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8547             this.maskEl.bottom.setLeft(0);
8548             this.maskEl.bottom.setTop(box.bottom + this.padding);
8549             this.maskEl.bottom.show();
8550
8551             this.maskEl.right.setStyle('position', 'absolute');
8552             this.maskEl.right.setStyle('z-index', zIndex);
8553             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8554             this.maskEl.right.setLeft(box.right + this.padding);
8555             this.maskEl.right.setTop(box.y - this.padding);
8556             this.maskEl.right.show();
8557
8558             this.toolTip.bindEl = this.target.el;
8559
8560             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8561
8562             var tip = this.target.blankText;
8563
8564             if(this.target.getValue() !== '' ) {
8565                 
8566                 if (this.target.invalidText.length) {
8567                     tip = this.target.invalidText;
8568                 } else if (this.target.regexText.length){
8569                     tip = this.target.regexText;
8570                 }
8571             }
8572
8573             this.toolTip.show(tip);
8574
8575             this.intervalID = window.setInterval(function() {
8576                 Roo.bootstrap.Form.popover.unmask();
8577             }, 10000);
8578
8579             window.onwheel = function(){ return false;};
8580             
8581             (function(){ this.isMasked = true; }).defer(500, this);
8582             
8583         },
8584         
8585         unmask : function()
8586         {
8587             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8588                 return;
8589             }
8590             
8591             this.maskEl.top.setStyle('position', 'absolute');
8592             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8593             this.maskEl.top.hide();
8594
8595             this.maskEl.left.setStyle('position', 'absolute');
8596             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8597             this.maskEl.left.hide();
8598
8599             this.maskEl.bottom.setStyle('position', 'absolute');
8600             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8601             this.maskEl.bottom.hide();
8602
8603             this.maskEl.right.setStyle('position', 'absolute');
8604             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8605             this.maskEl.right.hide();
8606             
8607             this.toolTip.hide();
8608             
8609             this.toolTip.el.hide();
8610             
8611             window.onwheel = function(){ return true;};
8612             
8613             if(this.intervalID){
8614                 window.clearInterval(this.intervalID);
8615                 this.intervalID = false;
8616             }
8617             
8618             this.isMasked = false;
8619             
8620         }
8621         
8622     }
8623     
8624 });
8625
8626 /*
8627  * Based on:
8628  * Ext JS Library 1.1.1
8629  * Copyright(c) 2006-2007, Ext JS, LLC.
8630  *
8631  * Originally Released Under LGPL - original licence link has changed is not relivant.
8632  *
8633  * Fork - LGPL
8634  * <script type="text/javascript">
8635  */
8636 /**
8637  * @class Roo.form.VTypes
8638  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8639  * @singleton
8640  */
8641 Roo.form.VTypes = function(){
8642     // closure these in so they are only created once.
8643     var alpha = /^[a-zA-Z_]+$/;
8644     var alphanum = /^[a-zA-Z0-9_]+$/;
8645     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8646     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8647
8648     // All these messages and functions are configurable
8649     return {
8650         /**
8651          * The function used to validate email addresses
8652          * @param {String} value The email address
8653          */
8654         'email' : function(v){
8655             return email.test(v);
8656         },
8657         /**
8658          * The error text to display when the email validation function returns false
8659          * @type String
8660          */
8661         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8662         /**
8663          * The keystroke filter mask to be applied on email input
8664          * @type RegExp
8665          */
8666         'emailMask' : /[a-z0-9_\.\-@]/i,
8667
8668         /**
8669          * The function used to validate URLs
8670          * @param {String} value The URL
8671          */
8672         'url' : function(v){
8673             return url.test(v);
8674         },
8675         /**
8676          * The error text to display when the url validation function returns false
8677          * @type String
8678          */
8679         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8680         
8681         /**
8682          * The function used to validate alpha values
8683          * @param {String} value The value
8684          */
8685         'alpha' : function(v){
8686             return alpha.test(v);
8687         },
8688         /**
8689          * The error text to display when the alpha validation function returns false
8690          * @type String
8691          */
8692         'alphaText' : 'This field should only contain letters and _',
8693         /**
8694          * The keystroke filter mask to be applied on alpha input
8695          * @type RegExp
8696          */
8697         'alphaMask' : /[a-z_]/i,
8698
8699         /**
8700          * The function used to validate alphanumeric values
8701          * @param {String} value The value
8702          */
8703         'alphanum' : function(v){
8704             return alphanum.test(v);
8705         },
8706         /**
8707          * The error text to display when the alphanumeric validation function returns false
8708          * @type String
8709          */
8710         'alphanumText' : 'This field should only contain letters, numbers and _',
8711         /**
8712          * The keystroke filter mask to be applied on alphanumeric input
8713          * @type RegExp
8714          */
8715         'alphanumMask' : /[a-z0-9_]/i
8716     };
8717 }();/*
8718  * - LGPL
8719  *
8720  * Input
8721  * 
8722  */
8723
8724 /**
8725  * @class Roo.bootstrap.Input
8726  * @extends Roo.bootstrap.Component
8727  * Bootstrap Input class
8728  * @cfg {Boolean} disabled is it disabled
8729  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8730  * @cfg {String} name name of the input
8731  * @cfg {string} fieldLabel - the label associated
8732  * @cfg {string} placeholder - placeholder to put in text.
8733  * @cfg {string}  before - input group add on before
8734  * @cfg {string} after - input group add on after
8735  * @cfg {string} size - (lg|sm) or leave empty..
8736  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8737  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8738  * @cfg {Number} md colspan out of 12 for computer-sized screens
8739  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8740  * @cfg {string} value default value of the input
8741  * @cfg {Number} labelWidth set the width of label 
8742  * @cfg {Number} labellg set the width of label (1-12)
8743  * @cfg {Number} labelmd set the width of label (1-12)
8744  * @cfg {Number} labelsm set the width of label (1-12)
8745  * @cfg {Number} labelxs set the width of label (1-12)
8746  * @cfg {String} labelAlign (top|left)
8747  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8748  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8749  * @cfg {String} indicatorpos (left|right) default left
8750  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8751  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8752
8753  * @cfg {String} align (left|center|right) Default left
8754  * @cfg {Boolean} forceFeedback (true|false) Default false
8755  * 
8756  * @constructor
8757  * Create a new Input
8758  * @param {Object} config The config object
8759  */
8760
8761 Roo.bootstrap.Input = function(config){
8762     
8763     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8764     
8765     this.addEvents({
8766         /**
8767          * @event focus
8768          * Fires when this field receives input focus.
8769          * @param {Roo.form.Field} this
8770          */
8771         focus : true,
8772         /**
8773          * @event blur
8774          * Fires when this field loses input focus.
8775          * @param {Roo.form.Field} this
8776          */
8777         blur : true,
8778         /**
8779          * @event specialkey
8780          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8781          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8782          * @param {Roo.form.Field} this
8783          * @param {Roo.EventObject} e The event object
8784          */
8785         specialkey : true,
8786         /**
8787          * @event change
8788          * Fires just before the field blurs if the field value has changed.
8789          * @param {Roo.form.Field} this
8790          * @param {Mixed} newValue The new value
8791          * @param {Mixed} oldValue The original value
8792          */
8793         change : true,
8794         /**
8795          * @event invalid
8796          * Fires after the field has been marked as invalid.
8797          * @param {Roo.form.Field} this
8798          * @param {String} msg The validation message
8799          */
8800         invalid : true,
8801         /**
8802          * @event valid
8803          * Fires after the field has been validated with no errors.
8804          * @param {Roo.form.Field} this
8805          */
8806         valid : true,
8807          /**
8808          * @event keyup
8809          * Fires after the key up
8810          * @param {Roo.form.Field} this
8811          * @param {Roo.EventObject}  e The event Object
8812          */
8813         keyup : true
8814     });
8815 };
8816
8817 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8818      /**
8819      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8820       automatic validation (defaults to "keyup").
8821      */
8822     validationEvent : "keyup",
8823      /**
8824      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8825      */
8826     validateOnBlur : true,
8827     /**
8828      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8829      */
8830     validationDelay : 250,
8831      /**
8832      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8833      */
8834     focusClass : "x-form-focus",  // not needed???
8835     
8836        
8837     /**
8838      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8839      */
8840     invalidClass : "has-warning",
8841     
8842     /**
8843      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8844      */
8845     validClass : "has-success",
8846     
8847     /**
8848      * @cfg {Boolean} hasFeedback (true|false) default true
8849      */
8850     hasFeedback : true,
8851     
8852     /**
8853      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8854      */
8855     invalidFeedbackClass : "glyphicon-warning-sign",
8856     
8857     /**
8858      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8859      */
8860     validFeedbackClass : "glyphicon-ok",
8861     
8862     /**
8863      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8864      */
8865     selectOnFocus : false,
8866     
8867      /**
8868      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8869      */
8870     maskRe : null,
8871        /**
8872      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8873      */
8874     vtype : null,
8875     
8876       /**
8877      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8878      */
8879     disableKeyFilter : false,
8880     
8881        /**
8882      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8883      */
8884     disabled : false,
8885      /**
8886      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8887      */
8888     allowBlank : true,
8889     /**
8890      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8891      */
8892     blankText : "Please complete this mandatory field",
8893     
8894      /**
8895      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8896      */
8897     minLength : 0,
8898     /**
8899      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8900      */
8901     maxLength : Number.MAX_VALUE,
8902     /**
8903      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8904      */
8905     minLengthText : "The minimum length for this field is {0}",
8906     /**
8907      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8908      */
8909     maxLengthText : "The maximum length for this field is {0}",
8910   
8911     
8912     /**
8913      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8914      * If available, this function will be called only after the basic validators all return true, and will be passed the
8915      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8916      */
8917     validator : null,
8918     /**
8919      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8920      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8921      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8922      */
8923     regex : null,
8924     /**
8925      * @cfg {String} regexText -- Depricated - use Invalid Text
8926      */
8927     regexText : "",
8928     
8929     /**
8930      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8931      */
8932     invalidText : "",
8933     
8934     
8935     
8936     autocomplete: false,
8937     
8938     
8939     fieldLabel : '',
8940     inputType : 'text',
8941     
8942     name : false,
8943     placeholder: false,
8944     before : false,
8945     after : false,
8946     size : false,
8947     hasFocus : false,
8948     preventMark: false,
8949     isFormField : true,
8950     value : '',
8951     labelWidth : 2,
8952     labelAlign : false,
8953     readOnly : false,
8954     align : false,
8955     formatedValue : false,
8956     forceFeedback : false,
8957     
8958     indicatorpos : 'left',
8959     
8960     labellg : 0,
8961     labelmd : 0,
8962     labelsm : 0,
8963     labelxs : 0,
8964     
8965     capture : '',
8966     accept : '',
8967     
8968     parentLabelAlign : function()
8969     {
8970         var parent = this;
8971         while (parent.parent()) {
8972             parent = parent.parent();
8973             if (typeof(parent.labelAlign) !='undefined') {
8974                 return parent.labelAlign;
8975             }
8976         }
8977         return 'left';
8978         
8979     },
8980     
8981     getAutoCreate : function()
8982     {
8983         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8984         
8985         var id = Roo.id();
8986         
8987         var cfg = {};
8988         
8989         if(this.inputType != 'hidden'){
8990             cfg.cls = 'form-group' //input-group
8991         }
8992         
8993         var input =  {
8994             tag: 'input',
8995             id : id,
8996             type : this.inputType,
8997             value : this.value,
8998             cls : 'form-control',
8999             placeholder : this.placeholder || '',
9000             autocomplete : this.autocomplete || 'new-password'
9001         };
9002         
9003         if(this.capture.length){
9004             input.capture = this.capture;
9005         }
9006         
9007         if(this.accept.length){
9008             input.accept = this.accept + "/*";
9009         }
9010         
9011         if(this.align){
9012             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9013         }
9014         
9015         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9016             input.maxLength = this.maxLength;
9017         }
9018         
9019         if (this.disabled) {
9020             input.disabled=true;
9021         }
9022         
9023         if (this.readOnly) {
9024             input.readonly=true;
9025         }
9026         
9027         if (this.name) {
9028             input.name = this.name;
9029         }
9030         
9031         if (this.size) {
9032             input.cls += ' input-' + this.size;
9033         }
9034         
9035         var settings=this;
9036         ['xs','sm','md','lg'].map(function(size){
9037             if (settings[size]) {
9038                 cfg.cls += ' col-' + size + '-' + settings[size];
9039             }
9040         });
9041         
9042         var inputblock = input;
9043         
9044         var feedback = {
9045             tag: 'span',
9046             cls: 'glyphicon form-control-feedback'
9047         };
9048             
9049         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9050             
9051             inputblock = {
9052                 cls : 'has-feedback',
9053                 cn :  [
9054                     input,
9055                     feedback
9056                 ] 
9057             };  
9058         }
9059         
9060         if (this.before || this.after) {
9061             
9062             inputblock = {
9063                 cls : 'input-group',
9064                 cn :  [] 
9065             };
9066             
9067             if (this.before && typeof(this.before) == 'string') {
9068                 
9069                 inputblock.cn.push({
9070                     tag :'span',
9071                     cls : 'roo-input-before input-group-addon',
9072                     html : this.before
9073                 });
9074             }
9075             if (this.before && typeof(this.before) == 'object') {
9076                 this.before = Roo.factory(this.before);
9077                 
9078                 inputblock.cn.push({
9079                     tag :'span',
9080                     cls : 'roo-input-before input-group-' +
9081                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9082                 });
9083             }
9084             
9085             inputblock.cn.push(input);
9086             
9087             if (this.after && typeof(this.after) == 'string') {
9088                 inputblock.cn.push({
9089                     tag :'span',
9090                     cls : 'roo-input-after input-group-addon',
9091                     html : this.after
9092                 });
9093             }
9094             if (this.after && typeof(this.after) == 'object') {
9095                 this.after = Roo.factory(this.after);
9096                 
9097                 inputblock.cn.push({
9098                     tag :'span',
9099                     cls : 'roo-input-after input-group-' +
9100                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9101                 });
9102             }
9103             
9104             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9105                 inputblock.cls += ' has-feedback';
9106                 inputblock.cn.push(feedback);
9107             }
9108         };
9109         
9110         if (align ==='left' && this.fieldLabel.length) {
9111             
9112             cfg.cls += ' roo-form-group-label-left';
9113             
9114             cfg.cn = [
9115                 {
9116                     tag : 'i',
9117                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9118                     tooltip : 'This field is required'
9119                 },
9120                 {
9121                     tag: 'label',
9122                     'for' :  id,
9123                     cls : 'control-label',
9124                     html : this.fieldLabel
9125
9126                 },
9127                 {
9128                     cls : "", 
9129                     cn: [
9130                         inputblock
9131                     ]
9132                 }
9133             ];
9134             
9135             var labelCfg = cfg.cn[1];
9136             var contentCfg = cfg.cn[2];
9137             
9138             if(this.indicatorpos == 'right'){
9139                 cfg.cn = [
9140                     {
9141                         tag: 'label',
9142                         'for' :  id,
9143                         cls : 'control-label',
9144                         cn : [
9145                             {
9146                                 tag : 'span',
9147                                 html : this.fieldLabel
9148                             },
9149                             {
9150                                 tag : 'i',
9151                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9152                                 tooltip : 'This field is required'
9153                             }
9154                         ]
9155                     },
9156                     {
9157                         cls : "",
9158                         cn: [
9159                             inputblock
9160                         ]
9161                     }
9162
9163                 ];
9164                 
9165                 labelCfg = cfg.cn[0];
9166                 contentCfg = cfg.cn[1];
9167             
9168             }
9169             
9170             if(this.labelWidth > 12){
9171                 labelCfg.style = "width: " + this.labelWidth + 'px';
9172             }
9173             
9174             if(this.labelWidth < 13 && this.labelmd == 0){
9175                 this.labelmd = this.labelWidth;
9176             }
9177             
9178             if(this.labellg > 0){
9179                 labelCfg.cls += ' col-lg-' + this.labellg;
9180                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9181             }
9182             
9183             if(this.labelmd > 0){
9184                 labelCfg.cls += ' col-md-' + this.labelmd;
9185                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9186             }
9187             
9188             if(this.labelsm > 0){
9189                 labelCfg.cls += ' col-sm-' + this.labelsm;
9190                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9191             }
9192             
9193             if(this.labelxs > 0){
9194                 labelCfg.cls += ' col-xs-' + this.labelxs;
9195                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9196             }
9197             
9198             
9199         } else if ( this.fieldLabel.length) {
9200                 
9201             cfg.cn = [
9202                 {
9203                     tag : 'i',
9204                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9205                     tooltip : 'This field is required'
9206                 },
9207                 {
9208                     tag: 'label',
9209                    //cls : 'input-group-addon',
9210                     html : this.fieldLabel
9211
9212                 },
9213
9214                inputblock
9215
9216            ];
9217            
9218            if(this.indicatorpos == 'right'){
9219                 
9220                 cfg.cn = [
9221                     {
9222                         tag: 'label',
9223                        //cls : 'input-group-addon',
9224                         html : this.fieldLabel
9225
9226                     },
9227                     {
9228                         tag : 'i',
9229                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9230                         tooltip : 'This field is required'
9231                     },
9232
9233                    inputblock
9234
9235                ];
9236
9237             }
9238
9239         } else {
9240             
9241             cfg.cn = [
9242
9243                     inputblock
9244
9245             ];
9246                 
9247                 
9248         };
9249         
9250         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9251            cfg.cls += ' navbar-form';
9252         }
9253         
9254         if (this.parentType === 'NavGroup') {
9255            cfg.cls += ' navbar-form';
9256            cfg.tag = 'li';
9257         }
9258         
9259         return cfg;
9260         
9261     },
9262     /**
9263      * return the real input element.
9264      */
9265     inputEl: function ()
9266     {
9267         return this.el.select('input.form-control',true).first();
9268     },
9269     
9270     tooltipEl : function()
9271     {
9272         return this.inputEl();
9273     },
9274     
9275     indicatorEl : function()
9276     {
9277         var indicator = this.el.select('i.roo-required-indicator',true).first();
9278         
9279         if(!indicator){
9280             return false;
9281         }
9282         
9283         return indicator;
9284         
9285     },
9286     
9287     setDisabled : function(v)
9288     {
9289         var i  = this.inputEl().dom;
9290         if (!v) {
9291             i.removeAttribute('disabled');
9292             return;
9293             
9294         }
9295         i.setAttribute('disabled','true');
9296     },
9297     initEvents : function()
9298     {
9299           
9300         this.inputEl().on("keydown" , this.fireKey,  this);
9301         this.inputEl().on("focus", this.onFocus,  this);
9302         this.inputEl().on("blur", this.onBlur,  this);
9303         
9304         this.inputEl().relayEvent('keyup', this);
9305         
9306         this.indicator = this.indicatorEl();
9307         
9308         if(this.indicator){
9309             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9310         }
9311  
9312         // reference to original value for reset
9313         this.originalValue = this.getValue();
9314         //Roo.form.TextField.superclass.initEvents.call(this);
9315         if(this.validationEvent == 'keyup'){
9316             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9317             this.inputEl().on('keyup', this.filterValidation, this);
9318         }
9319         else if(this.validationEvent !== false){
9320             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9321         }
9322         
9323         if(this.selectOnFocus){
9324             this.on("focus", this.preFocus, this);
9325             
9326         }
9327         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9328             this.inputEl().on("keypress", this.filterKeys, this);
9329         } else {
9330             this.inputEl().relayEvent('keypress', this);
9331         }
9332        /* if(this.grow){
9333             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9334             this.el.on("click", this.autoSize,  this);
9335         }
9336         */
9337         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9338             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9339         }
9340         
9341         if (typeof(this.before) == 'object') {
9342             this.before.render(this.el.select('.roo-input-before',true).first());
9343         }
9344         if (typeof(this.after) == 'object') {
9345             this.after.render(this.el.select('.roo-input-after',true).first());
9346         }
9347         
9348         this.inputEl().on('change', this.onChange, this);
9349         
9350     },
9351     filterValidation : function(e){
9352         if(!e.isNavKeyPress()){
9353             this.validationTask.delay(this.validationDelay);
9354         }
9355     },
9356      /**
9357      * Validates the field value
9358      * @return {Boolean} True if the value is valid, else false
9359      */
9360     validate : function(){
9361         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9362         if(this.disabled || this.validateValue(this.getRawValue())){
9363             this.markValid();
9364             return true;
9365         }
9366         
9367         this.markInvalid();
9368         return false;
9369     },
9370     
9371     
9372     /**
9373      * Validates a value according to the field's validation rules and marks the field as invalid
9374      * if the validation fails
9375      * @param {Mixed} value The value to validate
9376      * @return {Boolean} True if the value is valid, else false
9377      */
9378     validateValue : function(value)
9379     {
9380         if(this.getVisibilityEl().hasClass('hidden')){
9381             return true;
9382         }
9383         
9384         if(value.length < 1)  { // if it's blank
9385             if(this.allowBlank){
9386                 return true;
9387             }
9388             return false;
9389         }
9390         
9391         if(value.length < this.minLength){
9392             return false;
9393         }
9394         if(value.length > this.maxLength){
9395             return false;
9396         }
9397         if(this.vtype){
9398             var vt = Roo.form.VTypes;
9399             if(!vt[this.vtype](value, this)){
9400                 return false;
9401             }
9402         }
9403         if(typeof this.validator == "function"){
9404             var msg = this.validator(value);
9405             if(msg !== true){
9406                 return false;
9407             }
9408             if (typeof(msg) == 'string') {
9409                 this.invalidText = msg;
9410             }
9411         }
9412         
9413         if(this.regex && !this.regex.test(value)){
9414             return false;
9415         }
9416         
9417         return true;
9418     },
9419     
9420      // private
9421     fireKey : function(e){
9422         //Roo.log('field ' + e.getKey());
9423         if(e.isNavKeyPress()){
9424             this.fireEvent("specialkey", this, e);
9425         }
9426     },
9427     focus : function (selectText){
9428         if(this.rendered){
9429             this.inputEl().focus();
9430             if(selectText === true){
9431                 this.inputEl().dom.select();
9432             }
9433         }
9434         return this;
9435     } ,
9436     
9437     onFocus : function(){
9438         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9439            // this.el.addClass(this.focusClass);
9440         }
9441         if(!this.hasFocus){
9442             this.hasFocus = true;
9443             this.startValue = this.getValue();
9444             this.fireEvent("focus", this);
9445         }
9446     },
9447     
9448     beforeBlur : Roo.emptyFn,
9449
9450     
9451     // private
9452     onBlur : function(){
9453         this.beforeBlur();
9454         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9455             //this.el.removeClass(this.focusClass);
9456         }
9457         this.hasFocus = false;
9458         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9459             this.validate();
9460         }
9461         var v = this.getValue();
9462         if(String(v) !== String(this.startValue)){
9463             this.fireEvent('change', this, v, this.startValue);
9464         }
9465         this.fireEvent("blur", this);
9466     },
9467     
9468     onChange : function(e)
9469     {
9470         var v = this.getValue();
9471         if(String(v) !== String(this.startValue)){
9472             this.fireEvent('change', this, v, this.startValue);
9473         }
9474         
9475     },
9476     
9477     /**
9478      * Resets the current field value to the originally loaded value and clears any validation messages
9479      */
9480     reset : function(){
9481         this.setValue(this.originalValue);
9482         this.validate();
9483     },
9484      /**
9485      * Returns the name of the field
9486      * @return {Mixed} name The name field
9487      */
9488     getName: function(){
9489         return this.name;
9490     },
9491      /**
9492      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9493      * @return {Mixed} value The field value
9494      */
9495     getValue : function(){
9496         
9497         var v = this.inputEl().getValue();
9498         
9499         return v;
9500     },
9501     /**
9502      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9503      * @return {Mixed} value The field value
9504      */
9505     getRawValue : function(){
9506         var v = this.inputEl().getValue();
9507         
9508         return v;
9509     },
9510     
9511     /**
9512      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9513      * @param {Mixed} value The value to set
9514      */
9515     setRawValue : function(v){
9516         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9517     },
9518     
9519     selectText : function(start, end){
9520         var v = this.getRawValue();
9521         if(v.length > 0){
9522             start = start === undefined ? 0 : start;
9523             end = end === undefined ? v.length : end;
9524             var d = this.inputEl().dom;
9525             if(d.setSelectionRange){
9526                 d.setSelectionRange(start, end);
9527             }else if(d.createTextRange){
9528                 var range = d.createTextRange();
9529                 range.moveStart("character", start);
9530                 range.moveEnd("character", v.length-end);
9531                 range.select();
9532             }
9533         }
9534     },
9535     
9536     /**
9537      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9538      * @param {Mixed} value The value to set
9539      */
9540     setValue : function(v){
9541         this.value = v;
9542         if(this.rendered){
9543             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9544             this.validate();
9545         }
9546     },
9547     
9548     /*
9549     processValue : function(value){
9550         if(this.stripCharsRe){
9551             var newValue = value.replace(this.stripCharsRe, '');
9552             if(newValue !== value){
9553                 this.setRawValue(newValue);
9554                 return newValue;
9555             }
9556         }
9557         return value;
9558     },
9559   */
9560     preFocus : function(){
9561         
9562         if(this.selectOnFocus){
9563             this.inputEl().dom.select();
9564         }
9565     },
9566     filterKeys : function(e){
9567         var k = e.getKey();
9568         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9569             return;
9570         }
9571         var c = e.getCharCode(), cc = String.fromCharCode(c);
9572         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9573             return;
9574         }
9575         if(!this.maskRe.test(cc)){
9576             e.stopEvent();
9577         }
9578     },
9579      /**
9580      * Clear any invalid styles/messages for this field
9581      */
9582     clearInvalid : function(){
9583         
9584         if(!this.el || this.preventMark){ // not rendered
9585             return;
9586         }
9587         
9588      
9589         this.el.removeClass(this.invalidClass);
9590         
9591         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9592             
9593             var feedback = this.el.select('.form-control-feedback', true).first();
9594             
9595             if(feedback){
9596                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9597             }
9598             
9599         }
9600         
9601         if(this.indicator){
9602             this.indicator.removeClass('visible');
9603             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9604         }
9605         
9606         this.fireEvent('valid', this);
9607     },
9608     
9609      /**
9610      * Mark this field as valid
9611      */
9612     markValid : function()
9613     {
9614         if(!this.el  || this.preventMark){ // not rendered...
9615             return;
9616         }
9617         
9618         this.el.removeClass([this.invalidClass, this.validClass]);
9619         
9620         var feedback = this.el.select('.form-control-feedback', true).first();
9621             
9622         if(feedback){
9623             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9624         }
9625         
9626         if(this.indicator){
9627             this.indicator.removeClass('visible');
9628             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9629         }
9630         
9631         if(this.disabled){
9632             return;
9633         }
9634         
9635         if(this.allowBlank && !this.getRawValue().length){
9636             return;
9637         }
9638         
9639         this.el.addClass(this.validClass);
9640         
9641         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9642             
9643             var feedback = this.el.select('.form-control-feedback', true).first();
9644             
9645             if(feedback){
9646                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9647                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9648             }
9649             
9650         }
9651         
9652         this.fireEvent('valid', this);
9653     },
9654     
9655      /**
9656      * Mark this field as invalid
9657      * @param {String} msg The validation message
9658      */
9659     markInvalid : function(msg)
9660     {
9661         if(!this.el  || this.preventMark){ // not rendered
9662             return;
9663         }
9664         
9665         this.el.removeClass([this.invalidClass, this.validClass]);
9666         
9667         var feedback = this.el.select('.form-control-feedback', true).first();
9668             
9669         if(feedback){
9670             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9671         }
9672
9673         if(this.disabled){
9674             return;
9675         }
9676         
9677         if(this.allowBlank && !this.getRawValue().length){
9678             return;
9679         }
9680         
9681         if(this.indicator){
9682             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9683             this.indicator.addClass('visible');
9684         }
9685         
9686         this.el.addClass(this.invalidClass);
9687         
9688         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9689             
9690             var feedback = this.el.select('.form-control-feedback', true).first();
9691             
9692             if(feedback){
9693                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9694                 
9695                 if(this.getValue().length || this.forceFeedback){
9696                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9697                 }
9698                 
9699             }
9700             
9701         }
9702         
9703         this.fireEvent('invalid', this, msg);
9704     },
9705     // private
9706     SafariOnKeyDown : function(event)
9707     {
9708         // this is a workaround for a password hang bug on chrome/ webkit.
9709         if (this.inputEl().dom.type != 'password') {
9710             return;
9711         }
9712         
9713         var isSelectAll = false;
9714         
9715         if(this.inputEl().dom.selectionEnd > 0){
9716             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9717         }
9718         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9719             event.preventDefault();
9720             this.setValue('');
9721             return;
9722         }
9723         
9724         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9725             
9726             event.preventDefault();
9727             // this is very hacky as keydown always get's upper case.
9728             //
9729             var cc = String.fromCharCode(event.getCharCode());
9730             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9731             
9732         }
9733     },
9734     adjustWidth : function(tag, w){
9735         tag = tag.toLowerCase();
9736         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9737             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9738                 if(tag == 'input'){
9739                     return w + 2;
9740                 }
9741                 if(tag == 'textarea'){
9742                     return w-2;
9743                 }
9744             }else if(Roo.isOpera){
9745                 if(tag == 'input'){
9746                     return w + 2;
9747                 }
9748                 if(tag == 'textarea'){
9749                     return w-2;
9750                 }
9751             }
9752         }
9753         return w;
9754     },
9755     
9756     setFieldLabel : function(v)
9757     {
9758         if(!this.rendered){
9759             return;
9760         }
9761         
9762         if(this.indicator){
9763             var ar = this.el.select('label > span',true);
9764             
9765             if (ar.elements.length) {
9766                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9767                 this.fieldLabel = v;
9768                 return;
9769             }
9770             
9771             var br = this.el.select('label',true);
9772             
9773             if(br.elements.length) {
9774                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9775                 this.fieldLabel = v;
9776                 return;
9777             }
9778             
9779             Roo.log('Cannot Found any of label > span || label in input');
9780             return;
9781         }
9782         
9783         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9784         this.fieldLabel = v;
9785         
9786         
9787     }
9788 });
9789
9790  
9791 /*
9792  * - LGPL
9793  *
9794  * Input
9795  * 
9796  */
9797
9798 /**
9799  * @class Roo.bootstrap.TextArea
9800  * @extends Roo.bootstrap.Input
9801  * Bootstrap TextArea class
9802  * @cfg {Number} cols Specifies the visible width of a text area
9803  * @cfg {Number} rows Specifies the visible number of lines in a text area
9804  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9805  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9806  * @cfg {string} html text
9807  * 
9808  * @constructor
9809  * Create a new TextArea
9810  * @param {Object} config The config object
9811  */
9812
9813 Roo.bootstrap.TextArea = function(config){
9814     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9815    
9816 };
9817
9818 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9819      
9820     cols : false,
9821     rows : 5,
9822     readOnly : false,
9823     warp : 'soft',
9824     resize : false,
9825     value: false,
9826     html: false,
9827     
9828     getAutoCreate : function(){
9829         
9830         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9831         
9832         var id = Roo.id();
9833         
9834         var cfg = {};
9835         
9836         if(this.inputType != 'hidden'){
9837             cfg.cls = 'form-group' //input-group
9838         }
9839         
9840         var input =  {
9841             tag: 'textarea',
9842             id : id,
9843             warp : this.warp,
9844             rows : this.rows,
9845             value : this.value || '',
9846             html: this.html || '',
9847             cls : 'form-control',
9848             placeholder : this.placeholder || '' 
9849             
9850         };
9851         
9852         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9853             input.maxLength = this.maxLength;
9854         }
9855         
9856         if(this.resize){
9857             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9858         }
9859         
9860         if(this.cols){
9861             input.cols = this.cols;
9862         }
9863         
9864         if (this.readOnly) {
9865             input.readonly = true;
9866         }
9867         
9868         if (this.name) {
9869             input.name = this.name;
9870         }
9871         
9872         if (this.size) {
9873             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9874         }
9875         
9876         var settings=this;
9877         ['xs','sm','md','lg'].map(function(size){
9878             if (settings[size]) {
9879                 cfg.cls += ' col-' + size + '-' + settings[size];
9880             }
9881         });
9882         
9883         var inputblock = input;
9884         
9885         if(this.hasFeedback && !this.allowBlank){
9886             
9887             var feedback = {
9888                 tag: 'span',
9889                 cls: 'glyphicon form-control-feedback'
9890             };
9891
9892             inputblock = {
9893                 cls : 'has-feedback',
9894                 cn :  [
9895                     input,
9896                     feedback
9897                 ] 
9898             };  
9899         }
9900         
9901         
9902         if (this.before || this.after) {
9903             
9904             inputblock = {
9905                 cls : 'input-group',
9906                 cn :  [] 
9907             };
9908             if (this.before) {
9909                 inputblock.cn.push({
9910                     tag :'span',
9911                     cls : 'input-group-addon',
9912                     html : this.before
9913                 });
9914             }
9915             
9916             inputblock.cn.push(input);
9917             
9918             if(this.hasFeedback && !this.allowBlank){
9919                 inputblock.cls += ' has-feedback';
9920                 inputblock.cn.push(feedback);
9921             }
9922             
9923             if (this.after) {
9924                 inputblock.cn.push({
9925                     tag :'span',
9926                     cls : 'input-group-addon',
9927                     html : this.after
9928                 });
9929             }
9930             
9931         }
9932         
9933         if (align ==='left' && this.fieldLabel.length) {
9934             cfg.cn = [
9935                 {
9936                     tag: 'label',
9937                     'for' :  id,
9938                     cls : 'control-label',
9939                     html : this.fieldLabel
9940                 },
9941                 {
9942                     cls : "",
9943                     cn: [
9944                         inputblock
9945                     ]
9946                 }
9947
9948             ];
9949             
9950             if(this.labelWidth > 12){
9951                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9952             }
9953
9954             if(this.labelWidth < 13 && this.labelmd == 0){
9955                 this.labelmd = this.labelWidth;
9956             }
9957
9958             if(this.labellg > 0){
9959                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9960                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9961             }
9962
9963             if(this.labelmd > 0){
9964                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9965                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9966             }
9967
9968             if(this.labelsm > 0){
9969                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9970                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9971             }
9972
9973             if(this.labelxs > 0){
9974                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9975                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9976             }
9977             
9978         } else if ( this.fieldLabel.length) {
9979             cfg.cn = [
9980
9981                {
9982                    tag: 'label',
9983                    //cls : 'input-group-addon',
9984                    html : this.fieldLabel
9985
9986                },
9987
9988                inputblock
9989
9990            ];
9991
9992         } else {
9993
9994             cfg.cn = [
9995
9996                 inputblock
9997
9998             ];
9999                 
10000         }
10001         
10002         if (this.disabled) {
10003             input.disabled=true;
10004         }
10005         
10006         return cfg;
10007         
10008     },
10009     /**
10010      * return the real textarea element.
10011      */
10012     inputEl: function ()
10013     {
10014         return this.el.select('textarea.form-control',true).first();
10015     },
10016     
10017     /**
10018      * Clear any invalid styles/messages for this field
10019      */
10020     clearInvalid : function()
10021     {
10022         
10023         if(!this.el || this.preventMark){ // not rendered
10024             return;
10025         }
10026         
10027         var label = this.el.select('label', true).first();
10028         var icon = this.el.select('i.fa-star', true).first();
10029         
10030         if(label && icon){
10031             icon.remove();
10032         }
10033         
10034         this.el.removeClass(this.invalidClass);
10035         
10036         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10037             
10038             var feedback = this.el.select('.form-control-feedback', true).first();
10039             
10040             if(feedback){
10041                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10042             }
10043             
10044         }
10045         
10046         this.fireEvent('valid', this);
10047     },
10048     
10049      /**
10050      * Mark this field as valid
10051      */
10052     markValid : function()
10053     {
10054         if(!this.el  || this.preventMark){ // not rendered
10055             return;
10056         }
10057         
10058         this.el.removeClass([this.invalidClass, this.validClass]);
10059         
10060         var feedback = this.el.select('.form-control-feedback', true).first();
10061             
10062         if(feedback){
10063             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10064         }
10065
10066         if(this.disabled || this.allowBlank){
10067             return;
10068         }
10069         
10070         var label = this.el.select('label', true).first();
10071         var icon = this.el.select('i.fa-star', true).first();
10072         
10073         if(label && icon){
10074             icon.remove();
10075         }
10076         
10077         this.el.addClass(this.validClass);
10078         
10079         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10080             
10081             var feedback = this.el.select('.form-control-feedback', true).first();
10082             
10083             if(feedback){
10084                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10085                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10086             }
10087             
10088         }
10089         
10090         this.fireEvent('valid', this);
10091     },
10092     
10093      /**
10094      * Mark this field as invalid
10095      * @param {String} msg The validation message
10096      */
10097     markInvalid : function(msg)
10098     {
10099         if(!this.el  || this.preventMark){ // not rendered
10100             return;
10101         }
10102         
10103         this.el.removeClass([this.invalidClass, this.validClass]);
10104         
10105         var feedback = this.el.select('.form-control-feedback', true).first();
10106             
10107         if(feedback){
10108             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10109         }
10110
10111         if(this.disabled || this.allowBlank){
10112             return;
10113         }
10114         
10115         var label = this.el.select('label', true).first();
10116         var icon = this.el.select('i.fa-star', true).first();
10117         
10118         if(!this.getValue().length && label && !icon){
10119             this.el.createChild({
10120                 tag : 'i',
10121                 cls : 'text-danger fa fa-lg fa-star',
10122                 tooltip : 'This field is required',
10123                 style : 'margin-right:5px;'
10124             }, label, true);
10125         }
10126
10127         this.el.addClass(this.invalidClass);
10128         
10129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10130             
10131             var feedback = this.el.select('.form-control-feedback', true).first();
10132             
10133             if(feedback){
10134                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10135                 
10136                 if(this.getValue().length || this.forceFeedback){
10137                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10138                 }
10139                 
10140             }
10141             
10142         }
10143         
10144         this.fireEvent('invalid', this, msg);
10145     }
10146 });
10147
10148  
10149 /*
10150  * - LGPL
10151  *
10152  * trigger field - base class for combo..
10153  * 
10154  */
10155  
10156 /**
10157  * @class Roo.bootstrap.TriggerField
10158  * @extends Roo.bootstrap.Input
10159  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10160  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10161  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10162  * for which you can provide a custom implementation.  For example:
10163  * <pre><code>
10164 var trigger = new Roo.bootstrap.TriggerField();
10165 trigger.onTriggerClick = myTriggerFn;
10166 trigger.applyTo('my-field');
10167 </code></pre>
10168  *
10169  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10170  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10171  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10172  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10173  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10174
10175  * @constructor
10176  * Create a new TriggerField.
10177  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10178  * to the base TextField)
10179  */
10180 Roo.bootstrap.TriggerField = function(config){
10181     this.mimicing = false;
10182     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10183 };
10184
10185 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10186     /**
10187      * @cfg {String} triggerClass A CSS class to apply to the trigger
10188      */
10189      /**
10190      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10191      */
10192     hideTrigger:false,
10193
10194     /**
10195      * @cfg {Boolean} removable (true|false) special filter default false
10196      */
10197     removable : false,
10198     
10199     /** @cfg {Boolean} grow @hide */
10200     /** @cfg {Number} growMin @hide */
10201     /** @cfg {Number} growMax @hide */
10202
10203     /**
10204      * @hide 
10205      * @method
10206      */
10207     autoSize: Roo.emptyFn,
10208     // private
10209     monitorTab : true,
10210     // private
10211     deferHeight : true,
10212
10213     
10214     actionMode : 'wrap',
10215     
10216     caret : false,
10217     
10218     
10219     getAutoCreate : function(){
10220        
10221         var align = this.labelAlign || this.parentLabelAlign();
10222         
10223         var id = Roo.id();
10224         
10225         var cfg = {
10226             cls: 'form-group' //input-group
10227         };
10228         
10229         
10230         var input =  {
10231             tag: 'input',
10232             id : id,
10233             type : this.inputType,
10234             cls : 'form-control',
10235             autocomplete: 'new-password',
10236             placeholder : this.placeholder || '' 
10237             
10238         };
10239         if (this.name) {
10240             input.name = this.name;
10241         }
10242         if (this.size) {
10243             input.cls += ' input-' + this.size;
10244         }
10245         
10246         if (this.disabled) {
10247             input.disabled=true;
10248         }
10249         
10250         var inputblock = input;
10251         
10252         if(this.hasFeedback && !this.allowBlank){
10253             
10254             var feedback = {
10255                 tag: 'span',
10256                 cls: 'glyphicon form-control-feedback'
10257             };
10258             
10259             if(this.removable && !this.editable && !this.tickable){
10260                 inputblock = {
10261                     cls : 'has-feedback',
10262                     cn :  [
10263                         inputblock,
10264                         {
10265                             tag: 'button',
10266                             html : 'x',
10267                             cls : 'roo-combo-removable-btn close'
10268                         },
10269                         feedback
10270                     ] 
10271                 };
10272             } else {
10273                 inputblock = {
10274                     cls : 'has-feedback',
10275                     cn :  [
10276                         inputblock,
10277                         feedback
10278                     ] 
10279                 };
10280             }
10281
10282         } else {
10283             if(this.removable && !this.editable && !this.tickable){
10284                 inputblock = {
10285                     cls : 'roo-removable',
10286                     cn :  [
10287                         inputblock,
10288                         {
10289                             tag: 'button',
10290                             html : 'x',
10291                             cls : 'roo-combo-removable-btn close'
10292                         }
10293                     ] 
10294                 };
10295             }
10296         }
10297         
10298         if (this.before || this.after) {
10299             
10300             inputblock = {
10301                 cls : 'input-group',
10302                 cn :  [] 
10303             };
10304             if (this.before) {
10305                 inputblock.cn.push({
10306                     tag :'span',
10307                     cls : 'input-group-addon',
10308                     html : this.before
10309                 });
10310             }
10311             
10312             inputblock.cn.push(input);
10313             
10314             if(this.hasFeedback && !this.allowBlank){
10315                 inputblock.cls += ' has-feedback';
10316                 inputblock.cn.push(feedback);
10317             }
10318             
10319             if (this.after) {
10320                 inputblock.cn.push({
10321                     tag :'span',
10322                     cls : 'input-group-addon',
10323                     html : this.after
10324                 });
10325             }
10326             
10327         };
10328         
10329         var box = {
10330             tag: 'div',
10331             cn: [
10332                 {
10333                     tag: 'input',
10334                     type : 'hidden',
10335                     cls: 'form-hidden-field'
10336                 },
10337                 inputblock
10338             ]
10339             
10340         };
10341         
10342         if(this.multiple){
10343             box = {
10344                 tag: 'div',
10345                 cn: [
10346                     {
10347                         tag: 'input',
10348                         type : 'hidden',
10349                         cls: 'form-hidden-field'
10350                     },
10351                     {
10352                         tag: 'ul',
10353                         cls: 'roo-select2-choices',
10354                         cn:[
10355                             {
10356                                 tag: 'li',
10357                                 cls: 'roo-select2-search-field',
10358                                 cn: [
10359
10360                                     inputblock
10361                                 ]
10362                             }
10363                         ]
10364                     }
10365                 ]
10366             }
10367         };
10368         
10369         var combobox = {
10370             cls: 'roo-select2-container input-group',
10371             cn: [
10372                 box
10373 //                {
10374 //                    tag: 'ul',
10375 //                    cls: 'typeahead typeahead-long dropdown-menu',
10376 //                    style: 'display:none'
10377 //                }
10378             ]
10379         };
10380         
10381         if(!this.multiple && this.showToggleBtn){
10382             
10383             var caret = {
10384                         tag: 'span',
10385                         cls: 'caret'
10386              };
10387             if (this.caret != false) {
10388                 caret = {
10389                      tag: 'i',
10390                      cls: 'fa fa-' + this.caret
10391                 };
10392                 
10393             }
10394             
10395             combobox.cn.push({
10396                 tag :'span',
10397                 cls : 'input-group-addon btn dropdown-toggle',
10398                 cn : [
10399                     caret,
10400                     {
10401                         tag: 'span',
10402                         cls: 'combobox-clear',
10403                         cn  : [
10404                             {
10405                                 tag : 'i',
10406                                 cls: 'icon-remove'
10407                             }
10408                         ]
10409                     }
10410                 ]
10411
10412             })
10413         }
10414         
10415         if(this.multiple){
10416             combobox.cls += ' roo-select2-container-multi';
10417         }
10418         
10419         if (align ==='left' && this.fieldLabel.length) {
10420             
10421             cfg.cls += ' roo-form-group-label-left';
10422
10423             cfg.cn = [
10424                 {
10425                     tag : 'i',
10426                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10427                     tooltip : 'This field is required'
10428                 },
10429                 {
10430                     tag: 'label',
10431                     'for' :  id,
10432                     cls : 'control-label',
10433                     html : this.fieldLabel
10434
10435                 },
10436                 {
10437                     cls : "", 
10438                     cn: [
10439                         combobox
10440                     ]
10441                 }
10442
10443             ];
10444             
10445             var labelCfg = cfg.cn[1];
10446             var contentCfg = cfg.cn[2];
10447             
10448             if(this.indicatorpos == 'right'){
10449                 cfg.cn = [
10450                     {
10451                         tag: 'label',
10452                         'for' :  id,
10453                         cls : 'control-label',
10454                         cn : [
10455                             {
10456                                 tag : 'span',
10457                                 html : this.fieldLabel
10458                             },
10459                             {
10460                                 tag : 'i',
10461                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10462                                 tooltip : 'This field is required'
10463                             }
10464                         ]
10465                     },
10466                     {
10467                         cls : "", 
10468                         cn: [
10469                             combobox
10470                         ]
10471                     }
10472
10473                 ];
10474                 
10475                 labelCfg = cfg.cn[0];
10476                 contentCfg = cfg.cn[1];
10477             }
10478             
10479             if(this.labelWidth > 12){
10480                 labelCfg.style = "width: " + this.labelWidth + 'px';
10481             }
10482             
10483             if(this.labelWidth < 13 && this.labelmd == 0){
10484                 this.labelmd = this.labelWidth;
10485             }
10486             
10487             if(this.labellg > 0){
10488                 labelCfg.cls += ' col-lg-' + this.labellg;
10489                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10490             }
10491             
10492             if(this.labelmd > 0){
10493                 labelCfg.cls += ' col-md-' + this.labelmd;
10494                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10495             }
10496             
10497             if(this.labelsm > 0){
10498                 labelCfg.cls += ' col-sm-' + this.labelsm;
10499                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10500             }
10501             
10502             if(this.labelxs > 0){
10503                 labelCfg.cls += ' col-xs-' + this.labelxs;
10504                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10505             }
10506             
10507         } else if ( this.fieldLabel.length) {
10508 //                Roo.log(" label");
10509             cfg.cn = [
10510                 {
10511                    tag : 'i',
10512                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10513                    tooltip : 'This field is required'
10514                },
10515                {
10516                    tag: 'label',
10517                    //cls : 'input-group-addon',
10518                    html : this.fieldLabel
10519
10520                },
10521
10522                combobox
10523
10524             ];
10525             
10526             if(this.indicatorpos == 'right'){
10527                 
10528                 cfg.cn = [
10529                     {
10530                        tag: 'label',
10531                        cn : [
10532                            {
10533                                tag : 'span',
10534                                html : this.fieldLabel
10535                            },
10536                            {
10537                               tag : 'i',
10538                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10539                               tooltip : 'This field is required'
10540                            }
10541                        ]
10542
10543                     },
10544                     combobox
10545
10546                 ];
10547
10548             }
10549
10550         } else {
10551             
10552 //                Roo.log(" no label && no align");
10553                 cfg = combobox
10554                      
10555                 
10556         }
10557         
10558         var settings=this;
10559         ['xs','sm','md','lg'].map(function(size){
10560             if (settings[size]) {
10561                 cfg.cls += ' col-' + size + '-' + settings[size];
10562             }
10563         });
10564         
10565         return cfg;
10566         
10567     },
10568     
10569     
10570     
10571     // private
10572     onResize : function(w, h){
10573 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10574 //        if(typeof w == 'number'){
10575 //            var x = w - this.trigger.getWidth();
10576 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10577 //            this.trigger.setStyle('left', x+'px');
10578 //        }
10579     },
10580
10581     // private
10582     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10583
10584     // private
10585     getResizeEl : function(){
10586         return this.inputEl();
10587     },
10588
10589     // private
10590     getPositionEl : function(){
10591         return this.inputEl();
10592     },
10593
10594     // private
10595     alignErrorIcon : function(){
10596         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10597     },
10598
10599     // private
10600     initEvents : function(){
10601         
10602         this.createList();
10603         
10604         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10605         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10606         if(!this.multiple && this.showToggleBtn){
10607             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10608             if(this.hideTrigger){
10609                 this.trigger.setDisplayed(false);
10610             }
10611             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10612         }
10613         
10614         if(this.multiple){
10615             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10616         }
10617         
10618         if(this.removable && !this.editable && !this.tickable){
10619             var close = this.closeTriggerEl();
10620             
10621             if(close){
10622                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10623                 close.on('click', this.removeBtnClick, this, close);
10624             }
10625         }
10626         
10627         //this.trigger.addClassOnOver('x-form-trigger-over');
10628         //this.trigger.addClassOnClick('x-form-trigger-click');
10629         
10630         //if(!this.width){
10631         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10632         //}
10633     },
10634     
10635     closeTriggerEl : function()
10636     {
10637         var close = this.el.select('.roo-combo-removable-btn', true).first();
10638         return close ? close : false;
10639     },
10640     
10641     removeBtnClick : function(e, h, el)
10642     {
10643         e.preventDefault();
10644         
10645         if(this.fireEvent("remove", this) !== false){
10646             this.reset();
10647             this.fireEvent("afterremove", this)
10648         }
10649     },
10650     
10651     createList : function()
10652     {
10653         this.list = Roo.get(document.body).createChild({
10654             tag: 'ul',
10655             cls: 'typeahead typeahead-long dropdown-menu',
10656             style: 'display:none'
10657         });
10658         
10659         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10660         
10661     },
10662
10663     // private
10664     initTrigger : function(){
10665        
10666     },
10667
10668     // private
10669     onDestroy : function(){
10670         if(this.trigger){
10671             this.trigger.removeAllListeners();
10672           //  this.trigger.remove();
10673         }
10674         //if(this.wrap){
10675         //    this.wrap.remove();
10676         //}
10677         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10678     },
10679
10680     // private
10681     onFocus : function(){
10682         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10683         /*
10684         if(!this.mimicing){
10685             this.wrap.addClass('x-trigger-wrap-focus');
10686             this.mimicing = true;
10687             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10688             if(this.monitorTab){
10689                 this.el.on("keydown", this.checkTab, this);
10690             }
10691         }
10692         */
10693     },
10694
10695     // private
10696     checkTab : function(e){
10697         if(e.getKey() == e.TAB){
10698             this.triggerBlur();
10699         }
10700     },
10701
10702     // private
10703     onBlur : function(){
10704         // do nothing
10705     },
10706
10707     // private
10708     mimicBlur : function(e, t){
10709         /*
10710         if(!this.wrap.contains(t) && this.validateBlur()){
10711             this.triggerBlur();
10712         }
10713         */
10714     },
10715
10716     // private
10717     triggerBlur : function(){
10718         this.mimicing = false;
10719         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10720         if(this.monitorTab){
10721             this.el.un("keydown", this.checkTab, this);
10722         }
10723         //this.wrap.removeClass('x-trigger-wrap-focus');
10724         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10725     },
10726
10727     // private
10728     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10729     validateBlur : function(e, t){
10730         return true;
10731     },
10732
10733     // private
10734     onDisable : function(){
10735         this.inputEl().dom.disabled = true;
10736         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10737         //if(this.wrap){
10738         //    this.wrap.addClass('x-item-disabled');
10739         //}
10740     },
10741
10742     // private
10743     onEnable : function(){
10744         this.inputEl().dom.disabled = false;
10745         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10746         //if(this.wrap){
10747         //    this.el.removeClass('x-item-disabled');
10748         //}
10749     },
10750
10751     // private
10752     onShow : function(){
10753         var ae = this.getActionEl();
10754         
10755         if(ae){
10756             ae.dom.style.display = '';
10757             ae.dom.style.visibility = 'visible';
10758         }
10759     },
10760
10761     // private
10762     
10763     onHide : function(){
10764         var ae = this.getActionEl();
10765         ae.dom.style.display = 'none';
10766     },
10767
10768     /**
10769      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10770      * by an implementing function.
10771      * @method
10772      * @param {EventObject} e
10773      */
10774     onTriggerClick : Roo.emptyFn
10775 });
10776  /*
10777  * Based on:
10778  * Ext JS Library 1.1.1
10779  * Copyright(c) 2006-2007, Ext JS, LLC.
10780  *
10781  * Originally Released Under LGPL - original licence link has changed is not relivant.
10782  *
10783  * Fork - LGPL
10784  * <script type="text/javascript">
10785  */
10786
10787
10788 /**
10789  * @class Roo.data.SortTypes
10790  * @singleton
10791  * Defines the default sorting (casting?) comparison functions used when sorting data.
10792  */
10793 Roo.data.SortTypes = {
10794     /**
10795      * Default sort that does nothing
10796      * @param {Mixed} s The value being converted
10797      * @return {Mixed} The comparison value
10798      */
10799     none : function(s){
10800         return s;
10801     },
10802     
10803     /**
10804      * The regular expression used to strip tags
10805      * @type {RegExp}
10806      * @property
10807      */
10808     stripTagsRE : /<\/?[^>]+>/gi,
10809     
10810     /**
10811      * Strips all HTML tags to sort on text only
10812      * @param {Mixed} s The value being converted
10813      * @return {String} The comparison value
10814      */
10815     asText : function(s){
10816         return String(s).replace(this.stripTagsRE, "");
10817     },
10818     
10819     /**
10820      * Strips all HTML tags to sort on text only - Case insensitive
10821      * @param {Mixed} s The value being converted
10822      * @return {String} The comparison value
10823      */
10824     asUCText : function(s){
10825         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10826     },
10827     
10828     /**
10829      * Case insensitive string
10830      * @param {Mixed} s The value being converted
10831      * @return {String} The comparison value
10832      */
10833     asUCString : function(s) {
10834         return String(s).toUpperCase();
10835     },
10836     
10837     /**
10838      * Date sorting
10839      * @param {Mixed} s The value being converted
10840      * @return {Number} The comparison value
10841      */
10842     asDate : function(s) {
10843         if(!s){
10844             return 0;
10845         }
10846         if(s instanceof Date){
10847             return s.getTime();
10848         }
10849         return Date.parse(String(s));
10850     },
10851     
10852     /**
10853      * Float sorting
10854      * @param {Mixed} s The value being converted
10855      * @return {Float} The comparison value
10856      */
10857     asFloat : function(s) {
10858         var val = parseFloat(String(s).replace(/,/g, ""));
10859         if(isNaN(val)) {
10860             val = 0;
10861         }
10862         return val;
10863     },
10864     
10865     /**
10866      * Integer sorting
10867      * @param {Mixed} s The value being converted
10868      * @return {Number} The comparison value
10869      */
10870     asInt : function(s) {
10871         var val = parseInt(String(s).replace(/,/g, ""));
10872         if(isNaN(val)) {
10873             val = 0;
10874         }
10875         return val;
10876     }
10877 };/*
10878  * Based on:
10879  * Ext JS Library 1.1.1
10880  * Copyright(c) 2006-2007, Ext JS, LLC.
10881  *
10882  * Originally Released Under LGPL - original licence link has changed is not relivant.
10883  *
10884  * Fork - LGPL
10885  * <script type="text/javascript">
10886  */
10887
10888 /**
10889 * @class Roo.data.Record
10890  * Instances of this class encapsulate both record <em>definition</em> information, and record
10891  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10892  * to access Records cached in an {@link Roo.data.Store} object.<br>
10893  * <p>
10894  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10895  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10896  * objects.<br>
10897  * <p>
10898  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10899  * @constructor
10900  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10901  * {@link #create}. The parameters are the same.
10902  * @param {Array} data An associative Array of data values keyed by the field name.
10903  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10904  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10905  * not specified an integer id is generated.
10906  */
10907 Roo.data.Record = function(data, id){
10908     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10909     this.data = data;
10910 };
10911
10912 /**
10913  * Generate a constructor for a specific record layout.
10914  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10915  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10916  * Each field definition object may contain the following properties: <ul>
10917  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10918  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10919  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10920  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10921  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10922  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10923  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10924  * this may be omitted.</p></li>
10925  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10926  * <ul><li>auto (Default, implies no conversion)</li>
10927  * <li>string</li>
10928  * <li>int</li>
10929  * <li>float</li>
10930  * <li>boolean</li>
10931  * <li>date</li></ul></p></li>
10932  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10933  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10934  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10935  * by the Reader into an object that will be stored in the Record. It is passed the
10936  * following parameters:<ul>
10937  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10938  * </ul></p></li>
10939  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10940  * </ul>
10941  * <br>usage:<br><pre><code>
10942 var TopicRecord = Roo.data.Record.create(
10943     {name: 'title', mapping: 'topic_title'},
10944     {name: 'author', mapping: 'username'},
10945     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10946     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10947     {name: 'lastPoster', mapping: 'user2'},
10948     {name: 'excerpt', mapping: 'post_text'}
10949 );
10950
10951 var myNewRecord = new TopicRecord({
10952     title: 'Do my job please',
10953     author: 'noobie',
10954     totalPosts: 1,
10955     lastPost: new Date(),
10956     lastPoster: 'Animal',
10957     excerpt: 'No way dude!'
10958 });
10959 myStore.add(myNewRecord);
10960 </code></pre>
10961  * @method create
10962  * @static
10963  */
10964 Roo.data.Record.create = function(o){
10965     var f = function(){
10966         f.superclass.constructor.apply(this, arguments);
10967     };
10968     Roo.extend(f, Roo.data.Record);
10969     var p = f.prototype;
10970     p.fields = new Roo.util.MixedCollection(false, function(field){
10971         return field.name;
10972     });
10973     for(var i = 0, len = o.length; i < len; i++){
10974         p.fields.add(new Roo.data.Field(o[i]));
10975     }
10976     f.getField = function(name){
10977         return p.fields.get(name);  
10978     };
10979     return f;
10980 };
10981
10982 Roo.data.Record.AUTO_ID = 1000;
10983 Roo.data.Record.EDIT = 'edit';
10984 Roo.data.Record.REJECT = 'reject';
10985 Roo.data.Record.COMMIT = 'commit';
10986
10987 Roo.data.Record.prototype = {
10988     /**
10989      * Readonly flag - true if this record has been modified.
10990      * @type Boolean
10991      */
10992     dirty : false,
10993     editing : false,
10994     error: null,
10995     modified: null,
10996
10997     // private
10998     join : function(store){
10999         this.store = store;
11000     },
11001
11002     /**
11003      * Set the named field to the specified value.
11004      * @param {String} name The name of the field to set.
11005      * @param {Object} value The value to set the field to.
11006      */
11007     set : function(name, value){
11008         if(this.data[name] == value){
11009             return;
11010         }
11011         this.dirty = true;
11012         if(!this.modified){
11013             this.modified = {};
11014         }
11015         if(typeof this.modified[name] == 'undefined'){
11016             this.modified[name] = this.data[name];
11017         }
11018         this.data[name] = value;
11019         if(!this.editing && this.store){
11020             this.store.afterEdit(this);
11021         }       
11022     },
11023
11024     /**
11025      * Get the value of the named field.
11026      * @param {String} name The name of the field to get the value of.
11027      * @return {Object} The value of the field.
11028      */
11029     get : function(name){
11030         return this.data[name]; 
11031     },
11032
11033     // private
11034     beginEdit : function(){
11035         this.editing = true;
11036         this.modified = {}; 
11037     },
11038
11039     // private
11040     cancelEdit : function(){
11041         this.editing = false;
11042         delete this.modified;
11043     },
11044
11045     // private
11046     endEdit : function(){
11047         this.editing = false;
11048         if(this.dirty && this.store){
11049             this.store.afterEdit(this);
11050         }
11051     },
11052
11053     /**
11054      * Usually called by the {@link Roo.data.Store} which owns the Record.
11055      * Rejects all changes made to the Record since either creation, or the last commit operation.
11056      * Modified fields are reverted to their original values.
11057      * <p>
11058      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11059      * of reject operations.
11060      */
11061     reject : function(){
11062         var m = this.modified;
11063         for(var n in m){
11064             if(typeof m[n] != "function"){
11065                 this.data[n] = m[n];
11066             }
11067         }
11068         this.dirty = false;
11069         delete this.modified;
11070         this.editing = false;
11071         if(this.store){
11072             this.store.afterReject(this);
11073         }
11074     },
11075
11076     /**
11077      * Usually called by the {@link Roo.data.Store} which owns the Record.
11078      * Commits all changes made to the Record since either creation, or the last commit operation.
11079      * <p>
11080      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11081      * of commit operations.
11082      */
11083     commit : function(){
11084         this.dirty = false;
11085         delete this.modified;
11086         this.editing = false;
11087         if(this.store){
11088             this.store.afterCommit(this);
11089         }
11090     },
11091
11092     // private
11093     hasError : function(){
11094         return this.error != null;
11095     },
11096
11097     // private
11098     clearError : function(){
11099         this.error = null;
11100     },
11101
11102     /**
11103      * Creates a copy of this record.
11104      * @param {String} id (optional) A new record id if you don't want to use this record's id
11105      * @return {Record}
11106      */
11107     copy : function(newId) {
11108         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11109     }
11110 };/*
11111  * Based on:
11112  * Ext JS Library 1.1.1
11113  * Copyright(c) 2006-2007, Ext JS, LLC.
11114  *
11115  * Originally Released Under LGPL - original licence link has changed is not relivant.
11116  *
11117  * Fork - LGPL
11118  * <script type="text/javascript">
11119  */
11120
11121
11122
11123 /**
11124  * @class Roo.data.Store
11125  * @extends Roo.util.Observable
11126  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11127  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11128  * <p>
11129  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
11130  * has no knowledge of the format of the data returned by the Proxy.<br>
11131  * <p>
11132  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11133  * instances from the data object. These records are cached and made available through accessor functions.
11134  * @constructor
11135  * Creates a new Store.
11136  * @param {Object} config A config object containing the objects needed for the Store to access data,
11137  * and read the data into Records.
11138  */
11139 Roo.data.Store = function(config){
11140     this.data = new Roo.util.MixedCollection(false);
11141     this.data.getKey = function(o){
11142         return o.id;
11143     };
11144     this.baseParams = {};
11145     // private
11146     this.paramNames = {
11147         "start" : "start",
11148         "limit" : "limit",
11149         "sort" : "sort",
11150         "dir" : "dir",
11151         "multisort" : "_multisort"
11152     };
11153
11154     if(config && config.data){
11155         this.inlineData = config.data;
11156         delete config.data;
11157     }
11158
11159     Roo.apply(this, config);
11160     
11161     if(this.reader){ // reader passed
11162         this.reader = Roo.factory(this.reader, Roo.data);
11163         this.reader.xmodule = this.xmodule || false;
11164         if(!this.recordType){
11165             this.recordType = this.reader.recordType;
11166         }
11167         if(this.reader.onMetaChange){
11168             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11169         }
11170     }
11171
11172     if(this.recordType){
11173         this.fields = this.recordType.prototype.fields;
11174     }
11175     this.modified = [];
11176
11177     this.addEvents({
11178         /**
11179          * @event datachanged
11180          * Fires when the data cache has changed, and a widget which is using this Store
11181          * as a Record cache should refresh its view.
11182          * @param {Store} this
11183          */
11184         datachanged : true,
11185         /**
11186          * @event metachange
11187          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11188          * @param {Store} this
11189          * @param {Object} meta The JSON metadata
11190          */
11191         metachange : true,
11192         /**
11193          * @event add
11194          * Fires when Records have been added to the Store
11195          * @param {Store} this
11196          * @param {Roo.data.Record[]} records The array of Records added
11197          * @param {Number} index The index at which the record(s) were added
11198          */
11199         add : true,
11200         /**
11201          * @event remove
11202          * Fires when a Record has been removed from the Store
11203          * @param {Store} this
11204          * @param {Roo.data.Record} record The Record that was removed
11205          * @param {Number} index The index at which the record was removed
11206          */
11207         remove : true,
11208         /**
11209          * @event update
11210          * Fires when a Record has been updated
11211          * @param {Store} this
11212          * @param {Roo.data.Record} record The Record that was updated
11213          * @param {String} operation The update operation being performed.  Value may be one of:
11214          * <pre><code>
11215  Roo.data.Record.EDIT
11216  Roo.data.Record.REJECT
11217  Roo.data.Record.COMMIT
11218          * </code></pre>
11219          */
11220         update : true,
11221         /**
11222          * @event clear
11223          * Fires when the data cache has been cleared.
11224          * @param {Store} this
11225          */
11226         clear : true,
11227         /**
11228          * @event beforeload
11229          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11230          * the load action will be canceled.
11231          * @param {Store} this
11232          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11233          */
11234         beforeload : true,
11235         /**
11236          * @event beforeloadadd
11237          * Fires after a new set of Records has been loaded.
11238          * @param {Store} this
11239          * @param {Roo.data.Record[]} records The Records that were loaded
11240          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11241          */
11242         beforeloadadd : true,
11243         /**
11244          * @event load
11245          * Fires after a new set of Records has been loaded, before they are added to the store.
11246          * @param {Store} this
11247          * @param {Roo.data.Record[]} records The Records that were loaded
11248          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11249          * @params {Object} return from reader
11250          */
11251         load : true,
11252         /**
11253          * @event loadexception
11254          * Fires if an exception occurs in the Proxy during loading.
11255          * Called with the signature of the Proxy's "loadexception" event.
11256          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11257          * 
11258          * @param {Proxy} 
11259          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11260          * @param {Object} load options 
11261          * @param {Object} jsonData from your request (normally this contains the Exception)
11262          */
11263         loadexception : true
11264     });
11265     
11266     if(this.proxy){
11267         this.proxy = Roo.factory(this.proxy, Roo.data);
11268         this.proxy.xmodule = this.xmodule || false;
11269         this.relayEvents(this.proxy,  ["loadexception"]);
11270     }
11271     this.sortToggle = {};
11272     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11273
11274     Roo.data.Store.superclass.constructor.call(this);
11275
11276     if(this.inlineData){
11277         this.loadData(this.inlineData);
11278         delete this.inlineData;
11279     }
11280 };
11281
11282 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11283      /**
11284     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11285     * without a remote query - used by combo/forms at present.
11286     */
11287     
11288     /**
11289     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11290     */
11291     /**
11292     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11293     */
11294     /**
11295     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11296     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11297     */
11298     /**
11299     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11300     * on any HTTP request
11301     */
11302     /**
11303     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11304     */
11305     /**
11306     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11307     */
11308     multiSort: false,
11309     /**
11310     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11311     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11312     */
11313     remoteSort : false,
11314
11315     /**
11316     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11317      * loaded or when a record is removed. (defaults to false).
11318     */
11319     pruneModifiedRecords : false,
11320
11321     // private
11322     lastOptions : null,
11323
11324     /**
11325      * Add Records to the Store and fires the add event.
11326      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11327      */
11328     add : function(records){
11329         records = [].concat(records);
11330         for(var i = 0, len = records.length; i < len; i++){
11331             records[i].join(this);
11332         }
11333         var index = this.data.length;
11334         this.data.addAll(records);
11335         this.fireEvent("add", this, records, index);
11336     },
11337
11338     /**
11339      * Remove a Record from the Store and fires the remove event.
11340      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11341      */
11342     remove : function(record){
11343         var index = this.data.indexOf(record);
11344         this.data.removeAt(index);
11345  
11346         if(this.pruneModifiedRecords){
11347             this.modified.remove(record);
11348         }
11349         this.fireEvent("remove", this, record, index);
11350     },
11351
11352     /**
11353      * Remove all Records from the Store and fires the clear event.
11354      */
11355     removeAll : function(){
11356         this.data.clear();
11357         if(this.pruneModifiedRecords){
11358             this.modified = [];
11359         }
11360         this.fireEvent("clear", this);
11361     },
11362
11363     /**
11364      * Inserts Records to the Store at the given index and fires the add event.
11365      * @param {Number} index The start index at which to insert the passed Records.
11366      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11367      */
11368     insert : function(index, records){
11369         records = [].concat(records);
11370         for(var i = 0, len = records.length; i < len; i++){
11371             this.data.insert(index, records[i]);
11372             records[i].join(this);
11373         }
11374         this.fireEvent("add", this, records, index);
11375     },
11376
11377     /**
11378      * Get the index within the cache of the passed Record.
11379      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11380      * @return {Number} The index of the passed Record. Returns -1 if not found.
11381      */
11382     indexOf : function(record){
11383         return this.data.indexOf(record);
11384     },
11385
11386     /**
11387      * Get the index within the cache of the Record with the passed id.
11388      * @param {String} id The id of the Record to find.
11389      * @return {Number} The index of the Record. Returns -1 if not found.
11390      */
11391     indexOfId : function(id){
11392         return this.data.indexOfKey(id);
11393     },
11394
11395     /**
11396      * Get the Record with the specified id.
11397      * @param {String} id The id of the Record to find.
11398      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11399      */
11400     getById : function(id){
11401         return this.data.key(id);
11402     },
11403
11404     /**
11405      * Get the Record at the specified index.
11406      * @param {Number} index The index of the Record to find.
11407      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11408      */
11409     getAt : function(index){
11410         return this.data.itemAt(index);
11411     },
11412
11413     /**
11414      * Returns a range of Records between specified indices.
11415      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11416      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11417      * @return {Roo.data.Record[]} An array of Records
11418      */
11419     getRange : function(start, end){
11420         return this.data.getRange(start, end);
11421     },
11422
11423     // private
11424     storeOptions : function(o){
11425         o = Roo.apply({}, o);
11426         delete o.callback;
11427         delete o.scope;
11428         this.lastOptions = o;
11429     },
11430
11431     /**
11432      * Loads the Record cache from the configured Proxy using the configured Reader.
11433      * <p>
11434      * If using remote paging, then the first load call must specify the <em>start</em>
11435      * and <em>limit</em> properties in the options.params property to establish the initial
11436      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11437      * <p>
11438      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11439      * and this call will return before the new data has been loaded. Perform any post-processing
11440      * in a callback function, or in a "load" event handler.</strong>
11441      * <p>
11442      * @param {Object} options An object containing properties which control loading options:<ul>
11443      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11444      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11445      * passed the following arguments:<ul>
11446      * <li>r : Roo.data.Record[]</li>
11447      * <li>options: Options object from the load call</li>
11448      * <li>success: Boolean success indicator</li></ul></li>
11449      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11450      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11451      * </ul>
11452      */
11453     load : function(options){
11454         options = options || {};
11455         if(this.fireEvent("beforeload", this, options) !== false){
11456             this.storeOptions(options);
11457             var p = Roo.apply(options.params || {}, this.baseParams);
11458             // if meta was not loaded from remote source.. try requesting it.
11459             if (!this.reader.metaFromRemote) {
11460                 p._requestMeta = 1;
11461             }
11462             if(this.sortInfo && this.remoteSort){
11463                 var pn = this.paramNames;
11464                 p[pn["sort"]] = this.sortInfo.field;
11465                 p[pn["dir"]] = this.sortInfo.direction;
11466             }
11467             if (this.multiSort) {
11468                 var pn = this.paramNames;
11469                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11470             }
11471             
11472             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11473         }
11474     },
11475
11476     /**
11477      * Reloads the Record cache from the configured Proxy using the configured Reader and
11478      * the options from the last load operation performed.
11479      * @param {Object} options (optional) An object containing properties which may override the options
11480      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11481      * the most recently used options are reused).
11482      */
11483     reload : function(options){
11484         this.load(Roo.applyIf(options||{}, this.lastOptions));
11485     },
11486
11487     // private
11488     // Called as a callback by the Reader during a load operation.
11489     loadRecords : function(o, options, success){
11490         if(!o || success === false){
11491             if(success !== false){
11492                 this.fireEvent("load", this, [], options, o);
11493             }
11494             if(options.callback){
11495                 options.callback.call(options.scope || this, [], options, false);
11496             }
11497             return;
11498         }
11499         // if data returned failure - throw an exception.
11500         if (o.success === false) {
11501             // show a message if no listener is registered.
11502             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11503                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11504             }
11505             // loadmask wil be hooked into this..
11506             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11507             return;
11508         }
11509         var r = o.records, t = o.totalRecords || r.length;
11510         
11511         this.fireEvent("beforeloadadd", this, r, options, o);
11512         
11513         if(!options || options.add !== true){
11514             if(this.pruneModifiedRecords){
11515                 this.modified = [];
11516             }
11517             for(var i = 0, len = r.length; i < len; i++){
11518                 r[i].join(this);
11519             }
11520             if(this.snapshot){
11521                 this.data = this.snapshot;
11522                 delete this.snapshot;
11523             }
11524             this.data.clear();
11525             this.data.addAll(r);
11526             this.totalLength = t;
11527             this.applySort();
11528             this.fireEvent("datachanged", this);
11529         }else{
11530             this.totalLength = Math.max(t, this.data.length+r.length);
11531             this.add(r);
11532         }
11533         
11534         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11535                 
11536             var e = new Roo.data.Record({});
11537
11538             e.set(this.parent.displayField, this.parent.emptyTitle);
11539             e.set(this.parent.valueField, '');
11540
11541             this.insert(0, e);
11542         }
11543             
11544         this.fireEvent("load", this, r, options, o);
11545         if(options.callback){
11546             options.callback.call(options.scope || this, r, options, true);
11547         }
11548     },
11549
11550
11551     /**
11552      * Loads data from a passed data block. A Reader which understands the format of the data
11553      * must have been configured in the constructor.
11554      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11555      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11556      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11557      */
11558     loadData : function(o, append){
11559         var r = this.reader.readRecords(o);
11560         this.loadRecords(r, {add: append}, true);
11561     },
11562
11563     /**
11564      * Gets the number of cached records.
11565      * <p>
11566      * <em>If using paging, this may not be the total size of the dataset. If the data object
11567      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11568      * the data set size</em>
11569      */
11570     getCount : function(){
11571         return this.data.length || 0;
11572     },
11573
11574     /**
11575      * Gets the total number of records in the dataset as returned by the server.
11576      * <p>
11577      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11578      * the dataset size</em>
11579      */
11580     getTotalCount : function(){
11581         return this.totalLength || 0;
11582     },
11583
11584     /**
11585      * Returns the sort state of the Store as an object with two properties:
11586      * <pre><code>
11587  field {String} The name of the field by which the Records are sorted
11588  direction {String} The sort order, "ASC" or "DESC"
11589      * </code></pre>
11590      */
11591     getSortState : function(){
11592         return this.sortInfo;
11593     },
11594
11595     // private
11596     applySort : function(){
11597         if(this.sortInfo && !this.remoteSort){
11598             var s = this.sortInfo, f = s.field;
11599             var st = this.fields.get(f).sortType;
11600             var fn = function(r1, r2){
11601                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11602                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11603             };
11604             this.data.sort(s.direction, fn);
11605             if(this.snapshot && this.snapshot != this.data){
11606                 this.snapshot.sort(s.direction, fn);
11607             }
11608         }
11609     },
11610
11611     /**
11612      * Sets the default sort column and order to be used by the next load operation.
11613      * @param {String} fieldName The name of the field to sort by.
11614      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11615      */
11616     setDefaultSort : function(field, dir){
11617         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11618     },
11619
11620     /**
11621      * Sort the Records.
11622      * If remote sorting is used, the sort is performed on the server, and the cache is
11623      * reloaded. If local sorting is used, the cache is sorted internally.
11624      * @param {String} fieldName The name of the field to sort by.
11625      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11626      */
11627     sort : function(fieldName, dir){
11628         var f = this.fields.get(fieldName);
11629         if(!dir){
11630             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11631             
11632             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11633                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11634             }else{
11635                 dir = f.sortDir;
11636             }
11637         }
11638         this.sortToggle[f.name] = dir;
11639         this.sortInfo = {field: f.name, direction: dir};
11640         if(!this.remoteSort){
11641             this.applySort();
11642             this.fireEvent("datachanged", this);
11643         }else{
11644             this.load(this.lastOptions);
11645         }
11646     },
11647
11648     /**
11649      * Calls the specified function for each of the Records in the cache.
11650      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11651      * Returning <em>false</em> aborts and exits the iteration.
11652      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11653      */
11654     each : function(fn, scope){
11655         this.data.each(fn, scope);
11656     },
11657
11658     /**
11659      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11660      * (e.g., during paging).
11661      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11662      */
11663     getModifiedRecords : function(){
11664         return this.modified;
11665     },
11666
11667     // private
11668     createFilterFn : function(property, value, anyMatch){
11669         if(!value.exec){ // not a regex
11670             value = String(value);
11671             if(value.length == 0){
11672                 return false;
11673             }
11674             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11675         }
11676         return function(r){
11677             return value.test(r.data[property]);
11678         };
11679     },
11680
11681     /**
11682      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11683      * @param {String} property A field on your records
11684      * @param {Number} start The record index to start at (defaults to 0)
11685      * @param {Number} end The last record index to include (defaults to length - 1)
11686      * @return {Number} The sum
11687      */
11688     sum : function(property, start, end){
11689         var rs = this.data.items, v = 0;
11690         start = start || 0;
11691         end = (end || end === 0) ? end : rs.length-1;
11692
11693         for(var i = start; i <= end; i++){
11694             v += (rs[i].data[property] || 0);
11695         }
11696         return v;
11697     },
11698
11699     /**
11700      * Filter the records by a specified property.
11701      * @param {String} field A field on your records
11702      * @param {String/RegExp} value Either a string that the field
11703      * should start with or a RegExp to test against the field
11704      * @param {Boolean} anyMatch True to match any part not just the beginning
11705      */
11706     filter : function(property, value, anyMatch){
11707         var fn = this.createFilterFn(property, value, anyMatch);
11708         return fn ? this.filterBy(fn) : this.clearFilter();
11709     },
11710
11711     /**
11712      * Filter by a function. The specified function will be called with each
11713      * record in this data source. If the function returns true the record is included,
11714      * otherwise it is filtered.
11715      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11716      * @param {Object} scope (optional) The scope of the function (defaults to this)
11717      */
11718     filterBy : function(fn, scope){
11719         this.snapshot = this.snapshot || this.data;
11720         this.data = this.queryBy(fn, scope||this);
11721         this.fireEvent("datachanged", this);
11722     },
11723
11724     /**
11725      * Query the records by a specified property.
11726      * @param {String} field A field on your records
11727      * @param {String/RegExp} value Either a string that the field
11728      * should start with or a RegExp to test against the field
11729      * @param {Boolean} anyMatch True to match any part not just the beginning
11730      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11731      */
11732     query : function(property, value, anyMatch){
11733         var fn = this.createFilterFn(property, value, anyMatch);
11734         return fn ? this.queryBy(fn) : this.data.clone();
11735     },
11736
11737     /**
11738      * Query by a function. The specified function will be called with each
11739      * record in this data source. If the function returns true the record is included
11740      * in the results.
11741      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11742      * @param {Object} scope (optional) The scope of the function (defaults to this)
11743       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11744      **/
11745     queryBy : function(fn, scope){
11746         var data = this.snapshot || this.data;
11747         return data.filterBy(fn, scope||this);
11748     },
11749
11750     /**
11751      * Collects unique values for a particular dataIndex from this store.
11752      * @param {String} dataIndex The property to collect
11753      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11754      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11755      * @return {Array} An array of the unique values
11756      **/
11757     collect : function(dataIndex, allowNull, bypassFilter){
11758         var d = (bypassFilter === true && this.snapshot) ?
11759                 this.snapshot.items : this.data.items;
11760         var v, sv, r = [], l = {};
11761         for(var i = 0, len = d.length; i < len; i++){
11762             v = d[i].data[dataIndex];
11763             sv = String(v);
11764             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11765                 l[sv] = true;
11766                 r[r.length] = v;
11767             }
11768         }
11769         return r;
11770     },
11771
11772     /**
11773      * Revert to a view of the Record cache with no filtering applied.
11774      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11775      */
11776     clearFilter : function(suppressEvent){
11777         if(this.snapshot && this.snapshot != this.data){
11778             this.data = this.snapshot;
11779             delete this.snapshot;
11780             if(suppressEvent !== true){
11781                 this.fireEvent("datachanged", this);
11782             }
11783         }
11784     },
11785
11786     // private
11787     afterEdit : function(record){
11788         if(this.modified.indexOf(record) == -1){
11789             this.modified.push(record);
11790         }
11791         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11792     },
11793     
11794     // private
11795     afterReject : function(record){
11796         this.modified.remove(record);
11797         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11798     },
11799
11800     // private
11801     afterCommit : function(record){
11802         this.modified.remove(record);
11803         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11804     },
11805
11806     /**
11807      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11808      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11809      */
11810     commitChanges : function(){
11811         var m = this.modified.slice(0);
11812         this.modified = [];
11813         for(var i = 0, len = m.length; i < len; i++){
11814             m[i].commit();
11815         }
11816     },
11817
11818     /**
11819      * Cancel outstanding changes on all changed records.
11820      */
11821     rejectChanges : function(){
11822         var m = this.modified.slice(0);
11823         this.modified = [];
11824         for(var i = 0, len = m.length; i < len; i++){
11825             m[i].reject();
11826         }
11827     },
11828
11829     onMetaChange : function(meta, rtype, o){
11830         this.recordType = rtype;
11831         this.fields = rtype.prototype.fields;
11832         delete this.snapshot;
11833         this.sortInfo = meta.sortInfo || this.sortInfo;
11834         this.modified = [];
11835         this.fireEvent('metachange', this, this.reader.meta);
11836     },
11837     
11838     moveIndex : function(data, type)
11839     {
11840         var index = this.indexOf(data);
11841         
11842         var newIndex = index + type;
11843         
11844         this.remove(data);
11845         
11846         this.insert(newIndex, data);
11847         
11848     }
11849 });/*
11850  * Based on:
11851  * Ext JS Library 1.1.1
11852  * Copyright(c) 2006-2007, Ext JS, LLC.
11853  *
11854  * Originally Released Under LGPL - original licence link has changed is not relivant.
11855  *
11856  * Fork - LGPL
11857  * <script type="text/javascript">
11858  */
11859
11860 /**
11861  * @class Roo.data.SimpleStore
11862  * @extends Roo.data.Store
11863  * Small helper class to make creating Stores from Array data easier.
11864  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11865  * @cfg {Array} fields An array of field definition objects, or field name strings.
11866  * @cfg {Array} data The multi-dimensional array of data
11867  * @constructor
11868  * @param {Object} config
11869  */
11870 Roo.data.SimpleStore = function(config){
11871     Roo.data.SimpleStore.superclass.constructor.call(this, {
11872         isLocal : true,
11873         reader: new Roo.data.ArrayReader({
11874                 id: config.id
11875             },
11876             Roo.data.Record.create(config.fields)
11877         ),
11878         proxy : new Roo.data.MemoryProxy(config.data)
11879     });
11880     this.load();
11881 };
11882 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11883  * Based on:
11884  * Ext JS Library 1.1.1
11885  * Copyright(c) 2006-2007, Ext JS, LLC.
11886  *
11887  * Originally Released Under LGPL - original licence link has changed is not relivant.
11888  *
11889  * Fork - LGPL
11890  * <script type="text/javascript">
11891  */
11892
11893 /**
11894 /**
11895  * @extends Roo.data.Store
11896  * @class Roo.data.JsonStore
11897  * Small helper class to make creating Stores for JSON data easier. <br/>
11898 <pre><code>
11899 var store = new Roo.data.JsonStore({
11900     url: 'get-images.php',
11901     root: 'images',
11902     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11903 });
11904 </code></pre>
11905  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11906  * JsonReader and HttpProxy (unless inline data is provided).</b>
11907  * @cfg {Array} fields An array of field definition objects, or field name strings.
11908  * @constructor
11909  * @param {Object} config
11910  */
11911 Roo.data.JsonStore = function(c){
11912     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11913         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11914         reader: new Roo.data.JsonReader(c, c.fields)
11915     }));
11916 };
11917 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11918  * Based on:
11919  * Ext JS Library 1.1.1
11920  * Copyright(c) 2006-2007, Ext JS, LLC.
11921  *
11922  * Originally Released Under LGPL - original licence link has changed is not relivant.
11923  *
11924  * Fork - LGPL
11925  * <script type="text/javascript">
11926  */
11927
11928  
11929 Roo.data.Field = function(config){
11930     if(typeof config == "string"){
11931         config = {name: config};
11932     }
11933     Roo.apply(this, config);
11934     
11935     if(!this.type){
11936         this.type = "auto";
11937     }
11938     
11939     var st = Roo.data.SortTypes;
11940     // named sortTypes are supported, here we look them up
11941     if(typeof this.sortType == "string"){
11942         this.sortType = st[this.sortType];
11943     }
11944     
11945     // set default sortType for strings and dates
11946     if(!this.sortType){
11947         switch(this.type){
11948             case "string":
11949                 this.sortType = st.asUCString;
11950                 break;
11951             case "date":
11952                 this.sortType = st.asDate;
11953                 break;
11954             default:
11955                 this.sortType = st.none;
11956         }
11957     }
11958
11959     // define once
11960     var stripRe = /[\$,%]/g;
11961
11962     // prebuilt conversion function for this field, instead of
11963     // switching every time we're reading a value
11964     if(!this.convert){
11965         var cv, dateFormat = this.dateFormat;
11966         switch(this.type){
11967             case "":
11968             case "auto":
11969             case undefined:
11970                 cv = function(v){ return v; };
11971                 break;
11972             case "string":
11973                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11974                 break;
11975             case "int":
11976                 cv = function(v){
11977                     return v !== undefined && v !== null && v !== '' ?
11978                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11979                     };
11980                 break;
11981             case "float":
11982                 cv = function(v){
11983                     return v !== undefined && v !== null && v !== '' ?
11984                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11985                     };
11986                 break;
11987             case "bool":
11988             case "boolean":
11989                 cv = function(v){ return v === true || v === "true" || v == 1; };
11990                 break;
11991             case "date":
11992                 cv = function(v){
11993                     if(!v){
11994                         return '';
11995                     }
11996                     if(v instanceof Date){
11997                         return v;
11998                     }
11999                     if(dateFormat){
12000                         if(dateFormat == "timestamp"){
12001                             return new Date(v*1000);
12002                         }
12003                         return Date.parseDate(v, dateFormat);
12004                     }
12005                     var parsed = Date.parse(v);
12006                     return parsed ? new Date(parsed) : null;
12007                 };
12008              break;
12009             
12010         }
12011         this.convert = cv;
12012     }
12013 };
12014
12015 Roo.data.Field.prototype = {
12016     dateFormat: null,
12017     defaultValue: "",
12018     mapping: null,
12019     sortType : null,
12020     sortDir : "ASC"
12021 };/*
12022  * Based on:
12023  * Ext JS Library 1.1.1
12024  * Copyright(c) 2006-2007, Ext JS, LLC.
12025  *
12026  * Originally Released Under LGPL - original licence link has changed is not relivant.
12027  *
12028  * Fork - LGPL
12029  * <script type="text/javascript">
12030  */
12031  
12032 // Base class for reading structured data from a data source.  This class is intended to be
12033 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12034
12035 /**
12036  * @class Roo.data.DataReader
12037  * Base class for reading structured data from a data source.  This class is intended to be
12038  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12039  */
12040
12041 Roo.data.DataReader = function(meta, recordType){
12042     
12043     this.meta = meta;
12044     
12045     this.recordType = recordType instanceof Array ? 
12046         Roo.data.Record.create(recordType) : recordType;
12047 };
12048
12049 Roo.data.DataReader.prototype = {
12050      /**
12051      * Create an empty record
12052      * @param {Object} data (optional) - overlay some values
12053      * @return {Roo.data.Record} record created.
12054      */
12055     newRow :  function(d) {
12056         var da =  {};
12057         this.recordType.prototype.fields.each(function(c) {
12058             switch( c.type) {
12059                 case 'int' : da[c.name] = 0; break;
12060                 case 'date' : da[c.name] = new Date(); break;
12061                 case 'float' : da[c.name] = 0.0; break;
12062                 case 'boolean' : da[c.name] = false; break;
12063                 default : da[c.name] = ""; break;
12064             }
12065             
12066         });
12067         return new this.recordType(Roo.apply(da, d));
12068     }
12069     
12070 };/*
12071  * Based on:
12072  * Ext JS Library 1.1.1
12073  * Copyright(c) 2006-2007, Ext JS, LLC.
12074  *
12075  * Originally Released Under LGPL - original licence link has changed is not relivant.
12076  *
12077  * Fork - LGPL
12078  * <script type="text/javascript">
12079  */
12080
12081 /**
12082  * @class Roo.data.DataProxy
12083  * @extends Roo.data.Observable
12084  * This class is an abstract base class for implementations which provide retrieval of
12085  * unformatted data objects.<br>
12086  * <p>
12087  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12088  * (of the appropriate type which knows how to parse the data object) to provide a block of
12089  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12090  * <p>
12091  * Custom implementations must implement the load method as described in
12092  * {@link Roo.data.HttpProxy#load}.
12093  */
12094 Roo.data.DataProxy = function(){
12095     this.addEvents({
12096         /**
12097          * @event beforeload
12098          * Fires before a network request is made to retrieve a data object.
12099          * @param {Object} This DataProxy object.
12100          * @param {Object} params The params parameter to the load function.
12101          */
12102         beforeload : true,
12103         /**
12104          * @event load
12105          * Fires before the load method's callback is called.
12106          * @param {Object} This DataProxy object.
12107          * @param {Object} o The data object.
12108          * @param {Object} arg The callback argument object passed to the load function.
12109          */
12110         load : true,
12111         /**
12112          * @event loadexception
12113          * Fires if an Exception occurs during data retrieval.
12114          * @param {Object} This DataProxy object.
12115          * @param {Object} o The data object.
12116          * @param {Object} arg The callback argument object passed to the load function.
12117          * @param {Object} e The Exception.
12118          */
12119         loadexception : true
12120     });
12121     Roo.data.DataProxy.superclass.constructor.call(this);
12122 };
12123
12124 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12125
12126     /**
12127      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12128      */
12129 /*
12130  * Based on:
12131  * Ext JS Library 1.1.1
12132  * Copyright(c) 2006-2007, Ext JS, LLC.
12133  *
12134  * Originally Released Under LGPL - original licence link has changed is not relivant.
12135  *
12136  * Fork - LGPL
12137  * <script type="text/javascript">
12138  */
12139 /**
12140  * @class Roo.data.MemoryProxy
12141  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12142  * to the Reader when its load method is called.
12143  * @constructor
12144  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12145  */
12146 Roo.data.MemoryProxy = function(data){
12147     if (data.data) {
12148         data = data.data;
12149     }
12150     Roo.data.MemoryProxy.superclass.constructor.call(this);
12151     this.data = data;
12152 };
12153
12154 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12155     
12156     /**
12157      * Load data from the requested source (in this case an in-memory
12158      * data object passed to the constructor), read the data object into
12159      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12160      * process that block using the passed callback.
12161      * @param {Object} params This parameter is not used by the MemoryProxy class.
12162      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12163      * object into a block of Roo.data.Records.
12164      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12165      * The function must be passed <ul>
12166      * <li>The Record block object</li>
12167      * <li>The "arg" argument from the load function</li>
12168      * <li>A boolean success indicator</li>
12169      * </ul>
12170      * @param {Object} scope The scope in which to call the callback
12171      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12172      */
12173     load : function(params, reader, callback, scope, arg){
12174         params = params || {};
12175         var result;
12176         try {
12177             result = reader.readRecords(this.data);
12178         }catch(e){
12179             this.fireEvent("loadexception", this, arg, null, e);
12180             callback.call(scope, null, arg, false);
12181             return;
12182         }
12183         callback.call(scope, result, arg, true);
12184     },
12185     
12186     // private
12187     update : function(params, records){
12188         
12189     }
12190 });/*
12191  * Based on:
12192  * Ext JS Library 1.1.1
12193  * Copyright(c) 2006-2007, Ext JS, LLC.
12194  *
12195  * Originally Released Under LGPL - original licence link has changed is not relivant.
12196  *
12197  * Fork - LGPL
12198  * <script type="text/javascript">
12199  */
12200 /**
12201  * @class Roo.data.HttpProxy
12202  * @extends Roo.data.DataProxy
12203  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12204  * configured to reference a certain URL.<br><br>
12205  * <p>
12206  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12207  * from which the running page was served.<br><br>
12208  * <p>
12209  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12210  * <p>
12211  * Be aware that to enable the browser to parse an XML document, the server must set
12212  * the Content-Type header in the HTTP response to "text/xml".
12213  * @constructor
12214  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12215  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12216  * will be used to make the request.
12217  */
12218 Roo.data.HttpProxy = function(conn){
12219     Roo.data.HttpProxy.superclass.constructor.call(this);
12220     // is conn a conn config or a real conn?
12221     this.conn = conn;
12222     this.useAjax = !conn || !conn.events;
12223   
12224 };
12225
12226 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12227     // thse are take from connection...
12228     
12229     /**
12230      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12231      */
12232     /**
12233      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12234      * extra parameters to each request made by this object. (defaults to undefined)
12235      */
12236     /**
12237      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12238      *  to each request made by this object. (defaults to undefined)
12239      */
12240     /**
12241      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12242      */
12243     /**
12244      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12245      */
12246      /**
12247      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12248      * @type Boolean
12249      */
12250   
12251
12252     /**
12253      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12254      * @type Boolean
12255      */
12256     /**
12257      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12258      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12259      * a finer-grained basis than the DataProxy events.
12260      */
12261     getConnection : function(){
12262         return this.useAjax ? Roo.Ajax : this.conn;
12263     },
12264
12265     /**
12266      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12267      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12268      * process that block using the passed callback.
12269      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12270      * for the request to the remote server.
12271      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12272      * object into a block of Roo.data.Records.
12273      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12274      * The function must be passed <ul>
12275      * <li>The Record block object</li>
12276      * <li>The "arg" argument from the load function</li>
12277      * <li>A boolean success indicator</li>
12278      * </ul>
12279      * @param {Object} scope The scope in which to call the callback
12280      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12281      */
12282     load : function(params, reader, callback, scope, arg){
12283         if(this.fireEvent("beforeload", this, params) !== false){
12284             var  o = {
12285                 params : params || {},
12286                 request: {
12287                     callback : callback,
12288                     scope : scope,
12289                     arg : arg
12290                 },
12291                 reader: reader,
12292                 callback : this.loadResponse,
12293                 scope: this
12294             };
12295             if(this.useAjax){
12296                 Roo.applyIf(o, this.conn);
12297                 if(this.activeRequest){
12298                     Roo.Ajax.abort(this.activeRequest);
12299                 }
12300                 this.activeRequest = Roo.Ajax.request(o);
12301             }else{
12302                 this.conn.request(o);
12303             }
12304         }else{
12305             callback.call(scope||this, null, arg, false);
12306         }
12307     },
12308
12309     // private
12310     loadResponse : function(o, success, response){
12311         delete this.activeRequest;
12312         if(!success){
12313             this.fireEvent("loadexception", this, o, response);
12314             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12315             return;
12316         }
12317         var result;
12318         try {
12319             result = o.reader.read(response);
12320         }catch(e){
12321             this.fireEvent("loadexception", this, o, response, e);
12322             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12323             return;
12324         }
12325         
12326         this.fireEvent("load", this, o, o.request.arg);
12327         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12328     },
12329
12330     // private
12331     update : function(dataSet){
12332
12333     },
12334
12335     // private
12336     updateResponse : function(dataSet){
12337
12338     }
12339 });/*
12340  * Based on:
12341  * Ext JS Library 1.1.1
12342  * Copyright(c) 2006-2007, Ext JS, LLC.
12343  *
12344  * Originally Released Under LGPL - original licence link has changed is not relivant.
12345  *
12346  * Fork - LGPL
12347  * <script type="text/javascript">
12348  */
12349
12350 /**
12351  * @class Roo.data.ScriptTagProxy
12352  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12353  * other than the originating domain of the running page.<br><br>
12354  * <p>
12355  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12356  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12357  * <p>
12358  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12359  * source code that is used as the source inside a &lt;script> tag.<br><br>
12360  * <p>
12361  * In order for the browser to process the returned data, the server must wrap the data object
12362  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12363  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12364  * depending on whether the callback name was passed:
12365  * <p>
12366  * <pre><code>
12367 boolean scriptTag = false;
12368 String cb = request.getParameter("callback");
12369 if (cb != null) {
12370     scriptTag = true;
12371     response.setContentType("text/javascript");
12372 } else {
12373     response.setContentType("application/x-json");
12374 }
12375 Writer out = response.getWriter();
12376 if (scriptTag) {
12377     out.write(cb + "(");
12378 }
12379 out.print(dataBlock.toJsonString());
12380 if (scriptTag) {
12381     out.write(");");
12382 }
12383 </pre></code>
12384  *
12385  * @constructor
12386  * @param {Object} config A configuration object.
12387  */
12388 Roo.data.ScriptTagProxy = function(config){
12389     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12390     Roo.apply(this, config);
12391     this.head = document.getElementsByTagName("head")[0];
12392 };
12393
12394 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12395
12396 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12397     /**
12398      * @cfg {String} url The URL from which to request the data object.
12399      */
12400     /**
12401      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12402      */
12403     timeout : 30000,
12404     /**
12405      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12406      * the server the name of the callback function set up by the load call to process the returned data object.
12407      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12408      * javascript output which calls this named function passing the data object as its only parameter.
12409      */
12410     callbackParam : "callback",
12411     /**
12412      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12413      * name to the request.
12414      */
12415     nocache : true,
12416
12417     /**
12418      * Load data from the configured URL, read the data object into
12419      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12420      * process that block using the passed callback.
12421      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12422      * for the request to the remote server.
12423      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12424      * object into a block of Roo.data.Records.
12425      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12426      * The function must be passed <ul>
12427      * <li>The Record block object</li>
12428      * <li>The "arg" argument from the load function</li>
12429      * <li>A boolean success indicator</li>
12430      * </ul>
12431      * @param {Object} scope The scope in which to call the callback
12432      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12433      */
12434     load : function(params, reader, callback, scope, arg){
12435         if(this.fireEvent("beforeload", this, params) !== false){
12436
12437             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12438
12439             var url = this.url;
12440             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12441             if(this.nocache){
12442                 url += "&_dc=" + (new Date().getTime());
12443             }
12444             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12445             var trans = {
12446                 id : transId,
12447                 cb : "stcCallback"+transId,
12448                 scriptId : "stcScript"+transId,
12449                 params : params,
12450                 arg : arg,
12451                 url : url,
12452                 callback : callback,
12453                 scope : scope,
12454                 reader : reader
12455             };
12456             var conn = this;
12457
12458             window[trans.cb] = function(o){
12459                 conn.handleResponse(o, trans);
12460             };
12461
12462             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12463
12464             if(this.autoAbort !== false){
12465                 this.abort();
12466             }
12467
12468             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12469
12470             var script = document.createElement("script");
12471             script.setAttribute("src", url);
12472             script.setAttribute("type", "text/javascript");
12473             script.setAttribute("id", trans.scriptId);
12474             this.head.appendChild(script);
12475
12476             this.trans = trans;
12477         }else{
12478             callback.call(scope||this, null, arg, false);
12479         }
12480     },
12481
12482     // private
12483     isLoading : function(){
12484         return this.trans ? true : false;
12485     },
12486
12487     /**
12488      * Abort the current server request.
12489      */
12490     abort : function(){
12491         if(this.isLoading()){
12492             this.destroyTrans(this.trans);
12493         }
12494     },
12495
12496     // private
12497     destroyTrans : function(trans, isLoaded){
12498         this.head.removeChild(document.getElementById(trans.scriptId));
12499         clearTimeout(trans.timeoutId);
12500         if(isLoaded){
12501             window[trans.cb] = undefined;
12502             try{
12503                 delete window[trans.cb];
12504             }catch(e){}
12505         }else{
12506             // if hasn't been loaded, wait for load to remove it to prevent script error
12507             window[trans.cb] = function(){
12508                 window[trans.cb] = undefined;
12509                 try{
12510                     delete window[trans.cb];
12511                 }catch(e){}
12512             };
12513         }
12514     },
12515
12516     // private
12517     handleResponse : function(o, trans){
12518         this.trans = false;
12519         this.destroyTrans(trans, true);
12520         var result;
12521         try {
12522             result = trans.reader.readRecords(o);
12523         }catch(e){
12524             this.fireEvent("loadexception", this, o, trans.arg, e);
12525             trans.callback.call(trans.scope||window, null, trans.arg, false);
12526             return;
12527         }
12528         this.fireEvent("load", this, o, trans.arg);
12529         trans.callback.call(trans.scope||window, result, trans.arg, true);
12530     },
12531
12532     // private
12533     handleFailure : function(trans){
12534         this.trans = false;
12535         this.destroyTrans(trans, false);
12536         this.fireEvent("loadexception", this, null, trans.arg);
12537         trans.callback.call(trans.scope||window, null, trans.arg, false);
12538     }
12539 });/*
12540  * Based on:
12541  * Ext JS Library 1.1.1
12542  * Copyright(c) 2006-2007, Ext JS, LLC.
12543  *
12544  * Originally Released Under LGPL - original licence link has changed is not relivant.
12545  *
12546  * Fork - LGPL
12547  * <script type="text/javascript">
12548  */
12549
12550 /**
12551  * @class Roo.data.JsonReader
12552  * @extends Roo.data.DataReader
12553  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12554  * based on mappings in a provided Roo.data.Record constructor.
12555  * 
12556  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12557  * in the reply previously. 
12558  * 
12559  * <p>
12560  * Example code:
12561  * <pre><code>
12562 var RecordDef = Roo.data.Record.create([
12563     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12564     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12565 ]);
12566 var myReader = new Roo.data.JsonReader({
12567     totalProperty: "results",    // The property which contains the total dataset size (optional)
12568     root: "rows",                // The property which contains an Array of row objects
12569     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12570 }, RecordDef);
12571 </code></pre>
12572  * <p>
12573  * This would consume a JSON file like this:
12574  * <pre><code>
12575 { 'results': 2, 'rows': [
12576     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12577     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12578 }
12579 </code></pre>
12580  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12581  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12582  * paged from the remote server.
12583  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12584  * @cfg {String} root name of the property which contains the Array of row objects.
12585  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12586  * @cfg {Array} fields Array of field definition objects
12587  * @constructor
12588  * Create a new JsonReader
12589  * @param {Object} meta Metadata configuration options
12590  * @param {Object} recordType Either an Array of field definition objects,
12591  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12592  */
12593 Roo.data.JsonReader = function(meta, recordType){
12594     
12595     meta = meta || {};
12596     // set some defaults:
12597     Roo.applyIf(meta, {
12598         totalProperty: 'total',
12599         successProperty : 'success',
12600         root : 'data',
12601         id : 'id'
12602     });
12603     
12604     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12605 };
12606 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12607     
12608     /**
12609      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12610      * Used by Store query builder to append _requestMeta to params.
12611      * 
12612      */
12613     metaFromRemote : false,
12614     /**
12615      * This method is only used by a DataProxy which has retrieved data from a remote server.
12616      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12617      * @return {Object} data A data block which is used by an Roo.data.Store object as
12618      * a cache of Roo.data.Records.
12619      */
12620     read : function(response){
12621         var json = response.responseText;
12622        
12623         var o = /* eval:var:o */ eval("("+json+")");
12624         if(!o) {
12625             throw {message: "JsonReader.read: Json object not found"};
12626         }
12627         
12628         if(o.metaData){
12629             
12630             delete this.ef;
12631             this.metaFromRemote = true;
12632             this.meta = o.metaData;
12633             this.recordType = Roo.data.Record.create(o.metaData.fields);
12634             this.onMetaChange(this.meta, this.recordType, o);
12635         }
12636         return this.readRecords(o);
12637     },
12638
12639     // private function a store will implement
12640     onMetaChange : function(meta, recordType, o){
12641
12642     },
12643
12644     /**
12645          * @ignore
12646          */
12647     simpleAccess: function(obj, subsc) {
12648         return obj[subsc];
12649     },
12650
12651         /**
12652          * @ignore
12653          */
12654     getJsonAccessor: function(){
12655         var re = /[\[\.]/;
12656         return function(expr) {
12657             try {
12658                 return(re.test(expr))
12659                     ? new Function("obj", "return obj." + expr)
12660                     : function(obj){
12661                         return obj[expr];
12662                     };
12663             } catch(e){}
12664             return Roo.emptyFn;
12665         };
12666     }(),
12667
12668     /**
12669      * Create a data block containing Roo.data.Records from an XML document.
12670      * @param {Object} o An object which contains an Array of row objects in the property specified
12671      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12672      * which contains the total size of the dataset.
12673      * @return {Object} data A data block which is used by an Roo.data.Store object as
12674      * a cache of Roo.data.Records.
12675      */
12676     readRecords : function(o){
12677         /**
12678          * After any data loads, the raw JSON data is available for further custom processing.
12679          * @type Object
12680          */
12681         this.o = o;
12682         var s = this.meta, Record = this.recordType,
12683             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12684
12685 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12686         if (!this.ef) {
12687             if(s.totalProperty) {
12688                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12689                 }
12690                 if(s.successProperty) {
12691                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12692                 }
12693                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12694                 if (s.id) {
12695                         var g = this.getJsonAccessor(s.id);
12696                         this.getId = function(rec) {
12697                                 var r = g(rec);  
12698                                 return (r === undefined || r === "") ? null : r;
12699                         };
12700                 } else {
12701                         this.getId = function(){return null;};
12702                 }
12703             this.ef = [];
12704             for(var jj = 0; jj < fl; jj++){
12705                 f = fi[jj];
12706                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12707                 this.ef[jj] = this.getJsonAccessor(map);
12708             }
12709         }
12710
12711         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12712         if(s.totalProperty){
12713             var vt = parseInt(this.getTotal(o), 10);
12714             if(!isNaN(vt)){
12715                 totalRecords = vt;
12716             }
12717         }
12718         if(s.successProperty){
12719             var vs = this.getSuccess(o);
12720             if(vs === false || vs === 'false'){
12721                 success = false;
12722             }
12723         }
12724         var records = [];
12725         for(var i = 0; i < c; i++){
12726                 var n = root[i];
12727             var values = {};
12728             var id = this.getId(n);
12729             for(var j = 0; j < fl; j++){
12730                 f = fi[j];
12731             var v = this.ef[j](n);
12732             if (!f.convert) {
12733                 Roo.log('missing convert for ' + f.name);
12734                 Roo.log(f);
12735                 continue;
12736             }
12737             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12738             }
12739             var record = new Record(values, id);
12740             record.json = n;
12741             records[i] = record;
12742         }
12743         return {
12744             raw : o,
12745             success : success,
12746             records : records,
12747             totalRecords : totalRecords
12748         };
12749     }
12750 });/*
12751  * Based on:
12752  * Ext JS Library 1.1.1
12753  * Copyright(c) 2006-2007, Ext JS, LLC.
12754  *
12755  * Originally Released Under LGPL - original licence link has changed is not relivant.
12756  *
12757  * Fork - LGPL
12758  * <script type="text/javascript">
12759  */
12760
12761 /**
12762  * @class Roo.data.ArrayReader
12763  * @extends Roo.data.DataReader
12764  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12765  * Each element of that Array represents a row of data fields. The
12766  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12767  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12768  * <p>
12769  * Example code:.
12770  * <pre><code>
12771 var RecordDef = Roo.data.Record.create([
12772     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12773     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12774 ]);
12775 var myReader = new Roo.data.ArrayReader({
12776     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12777 }, RecordDef);
12778 </code></pre>
12779  * <p>
12780  * This would consume an Array like this:
12781  * <pre><code>
12782 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12783   </code></pre>
12784  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12785  * @constructor
12786  * Create a new JsonReader
12787  * @param {Object} meta Metadata configuration options.
12788  * @param {Object} recordType Either an Array of field definition objects
12789  * as specified to {@link Roo.data.Record#create},
12790  * or an {@link Roo.data.Record} object
12791  * created using {@link Roo.data.Record#create}.
12792  */
12793 Roo.data.ArrayReader = function(meta, recordType){
12794     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12795 };
12796
12797 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12798     /**
12799      * Create a data block containing Roo.data.Records from an XML document.
12800      * @param {Object} o An Array of row objects which represents the dataset.
12801      * @return {Object} data A data block which is used by an Roo.data.Store object as
12802      * a cache of Roo.data.Records.
12803      */
12804     readRecords : function(o){
12805         var sid = this.meta ? this.meta.id : null;
12806         var recordType = this.recordType, fields = recordType.prototype.fields;
12807         var records = [];
12808         var root = o;
12809             for(var i = 0; i < root.length; i++){
12810                     var n = root[i];
12811                 var values = {};
12812                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12813                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12814                 var f = fields.items[j];
12815                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12816                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12817                 v = f.convert(v);
12818                 values[f.name] = v;
12819             }
12820                 var record = new recordType(values, id);
12821                 record.json = n;
12822                 records[records.length] = record;
12823             }
12824             return {
12825                 records : records,
12826                 totalRecords : records.length
12827             };
12828     }
12829 });/*
12830  * - LGPL
12831  * * 
12832  */
12833
12834 /**
12835  * @class Roo.bootstrap.ComboBox
12836  * @extends Roo.bootstrap.TriggerField
12837  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12838  * @cfg {Boolean} append (true|false) default false
12839  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12840  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12841  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12842  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12843  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12844  * @cfg {Boolean} animate default true
12845  * @cfg {Boolean} emptyResultText only for touch device
12846  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12847  * @cfg {String} emptyTitle default ''
12848  * @constructor
12849  * Create a new ComboBox.
12850  * @param {Object} config Configuration options
12851  */
12852 Roo.bootstrap.ComboBox = function(config){
12853     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12854     this.addEvents({
12855         /**
12856          * @event expand
12857          * Fires when the dropdown list is expanded
12858         * @param {Roo.bootstrap.ComboBox} combo This combo box
12859         */
12860         'expand' : true,
12861         /**
12862          * @event collapse
12863          * Fires when the dropdown list is collapsed
12864         * @param {Roo.bootstrap.ComboBox} combo This combo box
12865         */
12866         'collapse' : true,
12867         /**
12868          * @event beforeselect
12869          * Fires before a list item is selected. Return false to cancel the selection.
12870         * @param {Roo.bootstrap.ComboBox} combo This combo box
12871         * @param {Roo.data.Record} record The data record returned from the underlying store
12872         * @param {Number} index The index of the selected item in the dropdown list
12873         */
12874         'beforeselect' : true,
12875         /**
12876          * @event select
12877          * Fires when a list item is selected
12878         * @param {Roo.bootstrap.ComboBox} combo This combo box
12879         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12880         * @param {Number} index The index of the selected item in the dropdown list
12881         */
12882         'select' : true,
12883         /**
12884          * @event beforequery
12885          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12886          * The event object passed has these properties:
12887         * @param {Roo.bootstrap.ComboBox} combo This combo box
12888         * @param {String} query The query
12889         * @param {Boolean} forceAll true to force "all" query
12890         * @param {Boolean} cancel true to cancel the query
12891         * @param {Object} e The query event object
12892         */
12893         'beforequery': true,
12894          /**
12895          * @event add
12896          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12897         * @param {Roo.bootstrap.ComboBox} combo This combo box
12898         */
12899         'add' : true,
12900         /**
12901          * @event edit
12902          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12903         * @param {Roo.bootstrap.ComboBox} combo This combo box
12904         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12905         */
12906         'edit' : true,
12907         /**
12908          * @event remove
12909          * Fires when the remove value from the combobox array
12910         * @param {Roo.bootstrap.ComboBox} combo This combo box
12911         */
12912         'remove' : true,
12913         /**
12914          * @event afterremove
12915          * Fires when the remove value from the combobox array
12916         * @param {Roo.bootstrap.ComboBox} combo This combo box
12917         */
12918         'afterremove' : true,
12919         /**
12920          * @event specialfilter
12921          * Fires when specialfilter
12922             * @param {Roo.bootstrap.ComboBox} combo This combo box
12923             */
12924         'specialfilter' : true,
12925         /**
12926          * @event tick
12927          * Fires when tick the element
12928             * @param {Roo.bootstrap.ComboBox} combo This combo box
12929             */
12930         'tick' : true,
12931         /**
12932          * @event touchviewdisplay
12933          * Fires when touch view require special display (default is using displayField)
12934             * @param {Roo.bootstrap.ComboBox} combo This combo box
12935             * @param {Object} cfg set html .
12936             */
12937         'touchviewdisplay' : true
12938         
12939     });
12940     
12941     this.item = [];
12942     this.tickItems = [];
12943     
12944     this.selectedIndex = -1;
12945     if(this.mode == 'local'){
12946         if(config.queryDelay === undefined){
12947             this.queryDelay = 10;
12948         }
12949         if(config.minChars === undefined){
12950             this.minChars = 0;
12951         }
12952     }
12953 };
12954
12955 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12956      
12957     /**
12958      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12959      * rendering into an Roo.Editor, defaults to false)
12960      */
12961     /**
12962      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12963      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12964      */
12965     /**
12966      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12967      */
12968     /**
12969      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12970      * the dropdown list (defaults to undefined, with no header element)
12971      */
12972
12973      /**
12974      * @cfg {String/Roo.Template} tpl The template to use to render the output
12975      */
12976      
12977      /**
12978      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12979      */
12980     listWidth: undefined,
12981     /**
12982      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12983      * mode = 'remote' or 'text' if mode = 'local')
12984      */
12985     displayField: undefined,
12986     
12987     /**
12988      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12989      * mode = 'remote' or 'value' if mode = 'local'). 
12990      * Note: use of a valueField requires the user make a selection
12991      * in order for a value to be mapped.
12992      */
12993     valueField: undefined,
12994     /**
12995      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12996      */
12997     modalTitle : '',
12998     
12999     /**
13000      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13001      * field's data value (defaults to the underlying DOM element's name)
13002      */
13003     hiddenName: undefined,
13004     /**
13005      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13006      */
13007     listClass: '',
13008     /**
13009      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13010      */
13011     selectedClass: 'active',
13012     
13013     /**
13014      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13015      */
13016     shadow:'sides',
13017     /**
13018      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13019      * anchor positions (defaults to 'tl-bl')
13020      */
13021     listAlign: 'tl-bl?',
13022     /**
13023      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13024      */
13025     maxHeight: 300,
13026     /**
13027      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13028      * query specified by the allQuery config option (defaults to 'query')
13029      */
13030     triggerAction: 'query',
13031     /**
13032      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13033      * (defaults to 4, does not apply if editable = false)
13034      */
13035     minChars : 4,
13036     /**
13037      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13038      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13039      */
13040     typeAhead: false,
13041     /**
13042      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13043      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13044      */
13045     queryDelay: 500,
13046     /**
13047      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13048      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13049      */
13050     pageSize: 0,
13051     /**
13052      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13053      * when editable = true (defaults to false)
13054      */
13055     selectOnFocus:false,
13056     /**
13057      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13058      */
13059     queryParam: 'query',
13060     /**
13061      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13062      * when mode = 'remote' (defaults to 'Loading...')
13063      */
13064     loadingText: 'Loading...',
13065     /**
13066      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13067      */
13068     resizable: false,
13069     /**
13070      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13071      */
13072     handleHeight : 8,
13073     /**
13074      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13075      * traditional select (defaults to true)
13076      */
13077     editable: true,
13078     /**
13079      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13080      */
13081     allQuery: '',
13082     /**
13083      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13084      */
13085     mode: 'remote',
13086     /**
13087      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13088      * listWidth has a higher value)
13089      */
13090     minListWidth : 70,
13091     /**
13092      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13093      * allow the user to set arbitrary text into the field (defaults to false)
13094      */
13095     forceSelection:false,
13096     /**
13097      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13098      * if typeAhead = true (defaults to 250)
13099      */
13100     typeAheadDelay : 250,
13101     /**
13102      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13103      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13104      */
13105     valueNotFoundText : undefined,
13106     /**
13107      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13108      */
13109     blockFocus : false,
13110     
13111     /**
13112      * @cfg {Boolean} disableClear Disable showing of clear button.
13113      */
13114     disableClear : false,
13115     /**
13116      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13117      */
13118     alwaysQuery : false,
13119     
13120     /**
13121      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13122      */
13123     multiple : false,
13124     
13125     /**
13126      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13127      */
13128     invalidClass : "has-warning",
13129     
13130     /**
13131      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13132      */
13133     validClass : "has-success",
13134     
13135     /**
13136      * @cfg {Boolean} specialFilter (true|false) special filter default false
13137      */
13138     specialFilter : false,
13139     
13140     /**
13141      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13142      */
13143     mobileTouchView : true,
13144     
13145     /**
13146      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13147      */
13148     useNativeIOS : false,
13149     
13150     /**
13151      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13152      */
13153     mobile_restrict_height : false,
13154     
13155     ios_options : false,
13156     
13157     //private
13158     addicon : false,
13159     editicon: false,
13160     
13161     page: 0,
13162     hasQuery: false,
13163     append: false,
13164     loadNext: false,
13165     autoFocus : true,
13166     tickable : false,
13167     btnPosition : 'right',
13168     triggerList : true,
13169     showToggleBtn : true,
13170     animate : true,
13171     emptyResultText: 'Empty',
13172     triggerText : 'Select',
13173     emptyTitle : '',
13174     
13175     // element that contains real text value.. (when hidden is used..)
13176     
13177     getAutoCreate : function()
13178     {   
13179         var cfg = false;
13180         //render
13181         /*
13182          * Render classic select for iso
13183          */
13184         
13185         if(Roo.isIOS && this.useNativeIOS){
13186             cfg = this.getAutoCreateNativeIOS();
13187             return cfg;
13188         }
13189         
13190         /*
13191          * Touch Devices
13192          */
13193         
13194         if(Roo.isTouch && this.mobileTouchView){
13195             cfg = this.getAutoCreateTouchView();
13196             return cfg;;
13197         }
13198         
13199         /*
13200          *  Normal ComboBox
13201          */
13202         if(!this.tickable){
13203             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13204             return cfg;
13205         }
13206         
13207         /*
13208          *  ComboBox with tickable selections
13209          */
13210              
13211         var align = this.labelAlign || this.parentLabelAlign();
13212         
13213         cfg = {
13214             cls : 'form-group roo-combobox-tickable' //input-group
13215         };
13216         
13217         var btn_text_select = '';
13218         var btn_text_done = '';
13219         var btn_text_cancel = '';
13220         
13221         if (this.btn_text_show) {
13222             btn_text_select = 'Select';
13223             btn_text_done = 'Done';
13224             btn_text_cancel = 'Cancel'; 
13225         }
13226         
13227         var buttons = {
13228             tag : 'div',
13229             cls : 'tickable-buttons',
13230             cn : [
13231                 {
13232                     tag : 'button',
13233                     type : 'button',
13234                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13235                     //html : this.triggerText
13236                     html: btn_text_select
13237                 },
13238                 {
13239                     tag : 'button',
13240                     type : 'button',
13241                     name : 'ok',
13242                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13243                     //html : 'Done'
13244                     html: btn_text_done
13245                 },
13246                 {
13247                     tag : 'button',
13248                     type : 'button',
13249                     name : 'cancel',
13250                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13251                     //html : 'Cancel'
13252                     html: btn_text_cancel
13253                 }
13254             ]
13255         };
13256         
13257         if(this.editable){
13258             buttons.cn.unshift({
13259                 tag: 'input',
13260                 cls: 'roo-select2-search-field-input'
13261             });
13262         }
13263         
13264         var _this = this;
13265         
13266         Roo.each(buttons.cn, function(c){
13267             if (_this.size) {
13268                 c.cls += ' btn-' + _this.size;
13269             }
13270
13271             if (_this.disabled) {
13272                 c.disabled = true;
13273             }
13274         });
13275         
13276         var box = {
13277             tag: 'div',
13278             cn: [
13279                 {
13280                     tag: 'input',
13281                     type : 'hidden',
13282                     cls: 'form-hidden-field'
13283                 },
13284                 {
13285                     tag: 'ul',
13286                     cls: 'roo-select2-choices',
13287                     cn:[
13288                         {
13289                             tag: 'li',
13290                             cls: 'roo-select2-search-field',
13291                             cn: [
13292                                 buttons
13293                             ]
13294                         }
13295                     ]
13296                 }
13297             ]
13298         };
13299         
13300         var combobox = {
13301             cls: 'roo-select2-container input-group roo-select2-container-multi',
13302             cn: [
13303                 box
13304 //                {
13305 //                    tag: 'ul',
13306 //                    cls: 'typeahead typeahead-long dropdown-menu',
13307 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13308 //                }
13309             ]
13310         };
13311         
13312         if(this.hasFeedback && !this.allowBlank){
13313             
13314             var feedback = {
13315                 tag: 'span',
13316                 cls: 'glyphicon form-control-feedback'
13317             };
13318
13319             combobox.cn.push(feedback);
13320         }
13321         
13322         
13323         if (align ==='left' && this.fieldLabel.length) {
13324             
13325             cfg.cls += ' roo-form-group-label-left';
13326             
13327             cfg.cn = [
13328                 {
13329                     tag : 'i',
13330                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13331                     tooltip : 'This field is required'
13332                 },
13333                 {
13334                     tag: 'label',
13335                     'for' :  id,
13336                     cls : 'control-label',
13337                     html : this.fieldLabel
13338
13339                 },
13340                 {
13341                     cls : "", 
13342                     cn: [
13343                         combobox
13344                     ]
13345                 }
13346
13347             ];
13348             
13349             var labelCfg = cfg.cn[1];
13350             var contentCfg = cfg.cn[2];
13351             
13352
13353             if(this.indicatorpos == 'right'){
13354                 
13355                 cfg.cn = [
13356                     {
13357                         tag: 'label',
13358                         'for' :  id,
13359                         cls : 'control-label',
13360                         cn : [
13361                             {
13362                                 tag : 'span',
13363                                 html : this.fieldLabel
13364                             },
13365                             {
13366                                 tag : 'i',
13367                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13368                                 tooltip : 'This field is required'
13369                             }
13370                         ]
13371                     },
13372                     {
13373                         cls : "",
13374                         cn: [
13375                             combobox
13376                         ]
13377                     }
13378
13379                 ];
13380                 
13381                 
13382                 
13383                 labelCfg = cfg.cn[0];
13384                 contentCfg = cfg.cn[1];
13385             
13386             }
13387             
13388             if(this.labelWidth > 12){
13389                 labelCfg.style = "width: " + this.labelWidth + 'px';
13390             }
13391             
13392             if(this.labelWidth < 13 && this.labelmd == 0){
13393                 this.labelmd = this.labelWidth;
13394             }
13395             
13396             if(this.labellg > 0){
13397                 labelCfg.cls += ' col-lg-' + this.labellg;
13398                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13399             }
13400             
13401             if(this.labelmd > 0){
13402                 labelCfg.cls += ' col-md-' + this.labelmd;
13403                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13404             }
13405             
13406             if(this.labelsm > 0){
13407                 labelCfg.cls += ' col-sm-' + this.labelsm;
13408                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13409             }
13410             
13411             if(this.labelxs > 0){
13412                 labelCfg.cls += ' col-xs-' + this.labelxs;
13413                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13414             }
13415                 
13416                 
13417         } else if ( this.fieldLabel.length) {
13418 //                Roo.log(" label");
13419                  cfg.cn = [
13420                     {
13421                         tag : 'i',
13422                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13423                         tooltip : 'This field is required'
13424                     },
13425                     {
13426                         tag: 'label',
13427                         //cls : 'input-group-addon',
13428                         html : this.fieldLabel
13429                     },
13430                     combobox
13431                 ];
13432                 
13433                 if(this.indicatorpos == 'right'){
13434                     cfg.cn = [
13435                         {
13436                             tag: 'label',
13437                             //cls : 'input-group-addon',
13438                             html : this.fieldLabel
13439                         },
13440                         {
13441                             tag : 'i',
13442                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13443                             tooltip : 'This field is required'
13444                         },
13445                         combobox
13446                     ];
13447                     
13448                 }
13449
13450         } else {
13451             
13452 //                Roo.log(" no label && no align");
13453                 cfg = combobox
13454                      
13455                 
13456         }
13457          
13458         var settings=this;
13459         ['xs','sm','md','lg'].map(function(size){
13460             if (settings[size]) {
13461                 cfg.cls += ' col-' + size + '-' + settings[size];
13462             }
13463         });
13464         
13465         return cfg;
13466         
13467     },
13468     
13469     _initEventsCalled : false,
13470     
13471     // private
13472     initEvents: function()
13473     {   
13474         if (this._initEventsCalled) { // as we call render... prevent looping...
13475             return;
13476         }
13477         this._initEventsCalled = true;
13478         
13479         if (!this.store) {
13480             throw "can not find store for combo";
13481         }
13482         
13483         this.indicator = this.indicatorEl();
13484         
13485         this.store = Roo.factory(this.store, Roo.data);
13486         this.store.parent = this;
13487         
13488         // if we are building from html. then this element is so complex, that we can not really
13489         // use the rendered HTML.
13490         // so we have to trash and replace the previous code.
13491         if (Roo.XComponent.build_from_html) {
13492             // remove this element....
13493             var e = this.el.dom, k=0;
13494             while (e ) { e = e.previousSibling;  ++k;}
13495
13496             this.el.remove();
13497             
13498             this.el=false;
13499             this.rendered = false;
13500             
13501             this.render(this.parent().getChildContainer(true), k);
13502         }
13503         
13504         if(Roo.isIOS && this.useNativeIOS){
13505             this.initIOSView();
13506             return;
13507         }
13508         
13509         /*
13510          * Touch Devices
13511          */
13512         
13513         if(Roo.isTouch && this.mobileTouchView){
13514             this.initTouchView();
13515             return;
13516         }
13517         
13518         if(this.tickable){
13519             this.initTickableEvents();
13520             return;
13521         }
13522         
13523         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13524         
13525         if(this.hiddenName){
13526             
13527             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13528             
13529             this.hiddenField.dom.value =
13530                 this.hiddenValue !== undefined ? this.hiddenValue :
13531                 this.value !== undefined ? this.value : '';
13532
13533             // prevent input submission
13534             this.el.dom.removeAttribute('name');
13535             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13536              
13537              
13538         }
13539         //if(Roo.isGecko){
13540         //    this.el.dom.setAttribute('autocomplete', 'off');
13541         //}
13542         
13543         var cls = 'x-combo-list';
13544         
13545         //this.list = new Roo.Layer({
13546         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13547         //});
13548         
13549         var _this = this;
13550         
13551         (function(){
13552             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13553             _this.list.setWidth(lw);
13554         }).defer(100);
13555         
13556         this.list.on('mouseover', this.onViewOver, this);
13557         this.list.on('mousemove', this.onViewMove, this);
13558         this.list.on('scroll', this.onViewScroll, this);
13559         
13560         /*
13561         this.list.swallowEvent('mousewheel');
13562         this.assetHeight = 0;
13563
13564         if(this.title){
13565             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13566             this.assetHeight += this.header.getHeight();
13567         }
13568
13569         this.innerList = this.list.createChild({cls:cls+'-inner'});
13570         this.innerList.on('mouseover', this.onViewOver, this);
13571         this.innerList.on('mousemove', this.onViewMove, this);
13572         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13573         
13574         if(this.allowBlank && !this.pageSize && !this.disableClear){
13575             this.footer = this.list.createChild({cls:cls+'-ft'});
13576             this.pageTb = new Roo.Toolbar(this.footer);
13577            
13578         }
13579         if(this.pageSize){
13580             this.footer = this.list.createChild({cls:cls+'-ft'});
13581             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13582                     {pageSize: this.pageSize});
13583             
13584         }
13585         
13586         if (this.pageTb && this.allowBlank && !this.disableClear) {
13587             var _this = this;
13588             this.pageTb.add(new Roo.Toolbar.Fill(), {
13589                 cls: 'x-btn-icon x-btn-clear',
13590                 text: '&#160;',
13591                 handler: function()
13592                 {
13593                     _this.collapse();
13594                     _this.clearValue();
13595                     _this.onSelect(false, -1);
13596                 }
13597             });
13598         }
13599         if (this.footer) {
13600             this.assetHeight += this.footer.getHeight();
13601         }
13602         */
13603             
13604         if(!this.tpl){
13605             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13606         }
13607
13608         this.view = new Roo.View(this.list, this.tpl, {
13609             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13610         });
13611         //this.view.wrapEl.setDisplayed(false);
13612         this.view.on('click', this.onViewClick, this);
13613         
13614         
13615         this.store.on('beforeload', this.onBeforeLoad, this);
13616         this.store.on('load', this.onLoad, this);
13617         this.store.on('loadexception', this.onLoadException, this);
13618         /*
13619         if(this.resizable){
13620             this.resizer = new Roo.Resizable(this.list,  {
13621                pinned:true, handles:'se'
13622             });
13623             this.resizer.on('resize', function(r, w, h){
13624                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13625                 this.listWidth = w;
13626                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13627                 this.restrictHeight();
13628             }, this);
13629             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13630         }
13631         */
13632         if(!this.editable){
13633             this.editable = true;
13634             this.setEditable(false);
13635         }
13636         
13637         /*
13638         
13639         if (typeof(this.events.add.listeners) != 'undefined') {
13640             
13641             this.addicon = this.wrap.createChild(
13642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13643        
13644             this.addicon.on('click', function(e) {
13645                 this.fireEvent('add', this);
13646             }, this);
13647         }
13648         if (typeof(this.events.edit.listeners) != 'undefined') {
13649             
13650             this.editicon = this.wrap.createChild(
13651                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13652             if (this.addicon) {
13653                 this.editicon.setStyle('margin-left', '40px');
13654             }
13655             this.editicon.on('click', function(e) {
13656                 
13657                 // we fire even  if inothing is selected..
13658                 this.fireEvent('edit', this, this.lastData );
13659                 
13660             }, this);
13661         }
13662         */
13663         
13664         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13665             "up" : function(e){
13666                 this.inKeyMode = true;
13667                 this.selectPrev();
13668             },
13669
13670             "down" : function(e){
13671                 if(!this.isExpanded()){
13672                     this.onTriggerClick();
13673                 }else{
13674                     this.inKeyMode = true;
13675                     this.selectNext();
13676                 }
13677             },
13678
13679             "enter" : function(e){
13680 //                this.onViewClick();
13681                 //return true;
13682                 this.collapse();
13683                 
13684                 if(this.fireEvent("specialkey", this, e)){
13685                     this.onViewClick(false);
13686                 }
13687                 
13688                 return true;
13689             },
13690
13691             "esc" : function(e){
13692                 this.collapse();
13693             },
13694
13695             "tab" : function(e){
13696                 this.collapse();
13697                 
13698                 if(this.fireEvent("specialkey", this, e)){
13699                     this.onViewClick(false);
13700                 }
13701                 
13702                 return true;
13703             },
13704
13705             scope : this,
13706
13707             doRelay : function(foo, bar, hname){
13708                 if(hname == 'down' || this.scope.isExpanded()){
13709                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13710                 }
13711                 return true;
13712             },
13713
13714             forceKeyDown: true
13715         });
13716         
13717         
13718         this.queryDelay = Math.max(this.queryDelay || 10,
13719                 this.mode == 'local' ? 10 : 250);
13720         
13721         
13722         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13723         
13724         if(this.typeAhead){
13725             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13726         }
13727         if(this.editable !== false){
13728             this.inputEl().on("keyup", this.onKeyUp, this);
13729         }
13730         if(this.forceSelection){
13731             this.inputEl().on('blur', this.doForce, this);
13732         }
13733         
13734         if(this.multiple){
13735             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13736             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13737         }
13738     },
13739     
13740     initTickableEvents: function()
13741     {   
13742         this.createList();
13743         
13744         if(this.hiddenName){
13745             
13746             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13747             
13748             this.hiddenField.dom.value =
13749                 this.hiddenValue !== undefined ? this.hiddenValue :
13750                 this.value !== undefined ? this.value : '';
13751
13752             // prevent input submission
13753             this.el.dom.removeAttribute('name');
13754             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13755              
13756              
13757         }
13758         
13759 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13760         
13761         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13762         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13763         if(this.triggerList){
13764             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13765         }
13766          
13767         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13768         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13769         
13770         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13771         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13772         
13773         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13774         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13775         
13776         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13777         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13778         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13779         
13780         this.okBtn.hide();
13781         this.cancelBtn.hide();
13782         
13783         var _this = this;
13784         
13785         (function(){
13786             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13787             _this.list.setWidth(lw);
13788         }).defer(100);
13789         
13790         this.list.on('mouseover', this.onViewOver, this);
13791         this.list.on('mousemove', this.onViewMove, this);
13792         
13793         this.list.on('scroll', this.onViewScroll, this);
13794         
13795         if(!this.tpl){
13796             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13797                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13798         }
13799
13800         this.view = new Roo.View(this.list, this.tpl, {
13801             singleSelect:true,
13802             tickable:true,
13803             parent:this,
13804             store: this.store,
13805             selectedClass: this.selectedClass
13806         });
13807         
13808         //this.view.wrapEl.setDisplayed(false);
13809         this.view.on('click', this.onViewClick, this);
13810         
13811         
13812         
13813         this.store.on('beforeload', this.onBeforeLoad, this);
13814         this.store.on('load', this.onLoad, this);
13815         this.store.on('loadexception', this.onLoadException, this);
13816         
13817         if(this.editable){
13818             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13819                 "up" : function(e){
13820                     this.inKeyMode = true;
13821                     this.selectPrev();
13822                 },
13823
13824                 "down" : function(e){
13825                     this.inKeyMode = true;
13826                     this.selectNext();
13827                 },
13828
13829                 "enter" : function(e){
13830                     if(this.fireEvent("specialkey", this, e)){
13831                         this.onViewClick(false);
13832                     }
13833                     
13834                     return true;
13835                 },
13836
13837                 "esc" : function(e){
13838                     this.onTickableFooterButtonClick(e, false, false);
13839                 },
13840
13841                 "tab" : function(e){
13842                     this.fireEvent("specialkey", this, e);
13843                     
13844                     this.onTickableFooterButtonClick(e, false, false);
13845                     
13846                     return true;
13847                 },
13848
13849                 scope : this,
13850
13851                 doRelay : function(e, fn, key){
13852                     if(this.scope.isExpanded()){
13853                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13854                     }
13855                     return true;
13856                 },
13857
13858                 forceKeyDown: true
13859             });
13860         }
13861         
13862         this.queryDelay = Math.max(this.queryDelay || 10,
13863                 this.mode == 'local' ? 10 : 250);
13864         
13865         
13866         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13867         
13868         if(this.typeAhead){
13869             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13870         }
13871         
13872         if(this.editable !== false){
13873             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13874         }
13875         
13876         this.indicator = this.indicatorEl();
13877         
13878         if(this.indicator){
13879             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13880             this.indicator.hide();
13881         }
13882         
13883     },
13884
13885     onDestroy : function(){
13886         if(this.view){
13887             this.view.setStore(null);
13888             this.view.el.removeAllListeners();
13889             this.view.el.remove();
13890             this.view.purgeListeners();
13891         }
13892         if(this.list){
13893             this.list.dom.innerHTML  = '';
13894         }
13895         
13896         if(this.store){
13897             this.store.un('beforeload', this.onBeforeLoad, this);
13898             this.store.un('load', this.onLoad, this);
13899             this.store.un('loadexception', this.onLoadException, this);
13900         }
13901         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13902     },
13903
13904     // private
13905     fireKey : function(e){
13906         if(e.isNavKeyPress() && !this.list.isVisible()){
13907             this.fireEvent("specialkey", this, e);
13908         }
13909     },
13910
13911     // private
13912     onResize: function(w, h){
13913 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13914 //        
13915 //        if(typeof w != 'number'){
13916 //            // we do not handle it!?!?
13917 //            return;
13918 //        }
13919 //        var tw = this.trigger.getWidth();
13920 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13921 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13922 //        var x = w - tw;
13923 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13924 //            
13925 //        //this.trigger.setStyle('left', x+'px');
13926 //        
13927 //        if(this.list && this.listWidth === undefined){
13928 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13929 //            this.list.setWidth(lw);
13930 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13931 //        }
13932         
13933     
13934         
13935     },
13936
13937     /**
13938      * Allow or prevent the user from directly editing the field text.  If false is passed,
13939      * the user will only be able to select from the items defined in the dropdown list.  This method
13940      * is the runtime equivalent of setting the 'editable' config option at config time.
13941      * @param {Boolean} value True to allow the user to directly edit the field text
13942      */
13943     setEditable : function(value){
13944         if(value == this.editable){
13945             return;
13946         }
13947         this.editable = value;
13948         if(!value){
13949             this.inputEl().dom.setAttribute('readOnly', true);
13950             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13951             this.inputEl().addClass('x-combo-noedit');
13952         }else{
13953             this.inputEl().dom.setAttribute('readOnly', false);
13954             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13955             this.inputEl().removeClass('x-combo-noedit');
13956         }
13957     },
13958
13959     // private
13960     
13961     onBeforeLoad : function(combo,opts){
13962         if(!this.hasFocus){
13963             return;
13964         }
13965          if (!opts.add) {
13966             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13967          }
13968         this.restrictHeight();
13969         this.selectedIndex = -1;
13970     },
13971
13972     // private
13973     onLoad : function(){
13974         
13975         this.hasQuery = false;
13976         
13977         if(!this.hasFocus){
13978             return;
13979         }
13980         
13981         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13982             this.loading.hide();
13983         }
13984         
13985         if(this.store.getCount() > 0){
13986             
13987             this.expand();
13988             this.restrictHeight();
13989             if(this.lastQuery == this.allQuery){
13990                 if(this.editable && !this.tickable){
13991                     this.inputEl().dom.select();
13992                 }
13993                 
13994                 if(
13995                     !this.selectByValue(this.value, true) &&
13996                     this.autoFocus && 
13997                     (
13998                         !this.store.lastOptions ||
13999                         typeof(this.store.lastOptions.add) == 'undefined' || 
14000                         this.store.lastOptions.add != true
14001                     )
14002                 ){
14003                     this.select(0, true);
14004                 }
14005             }else{
14006                 if(this.autoFocus){
14007                     this.selectNext();
14008                 }
14009                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14010                     this.taTask.delay(this.typeAheadDelay);
14011                 }
14012             }
14013         }else{
14014             this.onEmptyResults();
14015         }
14016         
14017         //this.el.focus();
14018     },
14019     // private
14020     onLoadException : function()
14021     {
14022         this.hasQuery = false;
14023         
14024         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14025             this.loading.hide();
14026         }
14027         
14028         if(this.tickable && this.editable){
14029             return;
14030         }
14031         
14032         this.collapse();
14033         // only causes errors at present
14034         //Roo.log(this.store.reader.jsonData);
14035         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14036             // fixme
14037             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14038         //}
14039         
14040         
14041     },
14042     // private
14043     onTypeAhead : function(){
14044         if(this.store.getCount() > 0){
14045             var r = this.store.getAt(0);
14046             var newValue = r.data[this.displayField];
14047             var len = newValue.length;
14048             var selStart = this.getRawValue().length;
14049             
14050             if(selStart != len){
14051                 this.setRawValue(newValue);
14052                 this.selectText(selStart, newValue.length);
14053             }
14054         }
14055     },
14056
14057     // private
14058     onSelect : function(record, index){
14059         
14060         if(this.fireEvent('beforeselect', this, record, index) !== false){
14061         
14062             this.setFromData(index > -1 ? record.data : false);
14063             
14064             this.collapse();
14065             this.fireEvent('select', this, record, index);
14066         }
14067     },
14068
14069     /**
14070      * Returns the currently selected field value or empty string if no value is set.
14071      * @return {String} value The selected value
14072      */
14073     getValue : function()
14074     {
14075         if(Roo.isIOS && this.useNativeIOS){
14076             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14077         }
14078         
14079         if(this.multiple){
14080             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14081         }
14082         
14083         if(this.valueField){
14084             return typeof this.value != 'undefined' ? this.value : '';
14085         }else{
14086             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14087         }
14088     },
14089     
14090     getRawValue : function()
14091     {
14092         if(Roo.isIOS && this.useNativeIOS){
14093             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14094         }
14095         
14096         var v = this.inputEl().getValue();
14097         
14098         return v;
14099     },
14100
14101     /**
14102      * Clears any text/value currently set in the field
14103      */
14104     clearValue : function(){
14105         
14106         if(this.hiddenField){
14107             this.hiddenField.dom.value = '';
14108         }
14109         this.value = '';
14110         this.setRawValue('');
14111         this.lastSelectionText = '';
14112         this.lastData = false;
14113         
14114         var close = this.closeTriggerEl();
14115         
14116         if(close){
14117             close.hide();
14118         }
14119         
14120         this.validate();
14121         
14122     },
14123
14124     /**
14125      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14126      * will be displayed in the field.  If the value does not match the data value of an existing item,
14127      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14128      * Otherwise the field will be blank (although the value will still be set).
14129      * @param {String} value The value to match
14130      */
14131     setValue : function(v)
14132     {
14133         if(Roo.isIOS && this.useNativeIOS){
14134             this.setIOSValue(v);
14135             return;
14136         }
14137         
14138         if(this.multiple){
14139             this.syncValue();
14140             return;
14141         }
14142         
14143         var text = v;
14144         if(this.valueField){
14145             var r = this.findRecord(this.valueField, v);
14146             if(r){
14147                 text = r.data[this.displayField];
14148             }else if(this.valueNotFoundText !== undefined){
14149                 text = this.valueNotFoundText;
14150             }
14151         }
14152         this.lastSelectionText = text;
14153         if(this.hiddenField){
14154             this.hiddenField.dom.value = v;
14155         }
14156         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14157         this.value = v;
14158         
14159         var close = this.closeTriggerEl();
14160         
14161         if(close){
14162             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14163         }
14164         
14165         this.validate();
14166     },
14167     /**
14168      * @property {Object} the last set data for the element
14169      */
14170     
14171     lastData : false,
14172     /**
14173      * Sets the value of the field based on a object which is related to the record format for the store.
14174      * @param {Object} value the value to set as. or false on reset?
14175      */
14176     setFromData : function(o){
14177         
14178         if(this.multiple){
14179             this.addItem(o);
14180             return;
14181         }
14182             
14183         var dv = ''; // display value
14184         var vv = ''; // value value..
14185         this.lastData = o;
14186         if (this.displayField) {
14187             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14188         } else {
14189             // this is an error condition!!!
14190             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14191         }
14192         
14193         if(this.valueField){
14194             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14195         }
14196         
14197         var close = this.closeTriggerEl();
14198         
14199         if(close){
14200             if(dv.length || vv * 1 > 0){
14201                 close.show() ;
14202                 this.blockFocus=true;
14203             } else {
14204                 close.hide();
14205             }             
14206         }
14207         
14208         if(this.hiddenField){
14209             this.hiddenField.dom.value = vv;
14210             
14211             this.lastSelectionText = dv;
14212             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14213             this.value = vv;
14214             return;
14215         }
14216         // no hidden field.. - we store the value in 'value', but still display
14217         // display field!!!!
14218         this.lastSelectionText = dv;
14219         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14220         this.value = vv;
14221         
14222         
14223         
14224     },
14225     // private
14226     reset : function(){
14227         // overridden so that last data is reset..
14228         
14229         if(this.multiple){
14230             this.clearItem();
14231             return;
14232         }
14233         
14234         this.setValue(this.originalValue);
14235         //this.clearInvalid();
14236         this.lastData = false;
14237         if (this.view) {
14238             this.view.clearSelections();
14239         }
14240         
14241         this.validate();
14242     },
14243     // private
14244     findRecord : function(prop, value){
14245         var record;
14246         if(this.store.getCount() > 0){
14247             this.store.each(function(r){
14248                 if(r.data[prop] == value){
14249                     record = r;
14250                     return false;
14251                 }
14252                 return true;
14253             });
14254         }
14255         return record;
14256     },
14257     
14258     getName: function()
14259     {
14260         // returns hidden if it's set..
14261         if (!this.rendered) {return ''};
14262         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14263         
14264     },
14265     // private
14266     onViewMove : function(e, t){
14267         this.inKeyMode = false;
14268     },
14269
14270     // private
14271     onViewOver : function(e, t){
14272         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14273             return;
14274         }
14275         var item = this.view.findItemFromChild(t);
14276         
14277         if(item){
14278             var index = this.view.indexOf(item);
14279             this.select(index, false);
14280         }
14281     },
14282
14283     // private
14284     onViewClick : function(view, doFocus, el, e)
14285     {
14286         var index = this.view.getSelectedIndexes()[0];
14287         
14288         var r = this.store.getAt(index);
14289         
14290         if(this.tickable){
14291             
14292             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14293                 return;
14294             }
14295             
14296             var rm = false;
14297             var _this = this;
14298             
14299             Roo.each(this.tickItems, function(v,k){
14300                 
14301                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14302                     Roo.log(v);
14303                     _this.tickItems.splice(k, 1);
14304                     
14305                     if(typeof(e) == 'undefined' && view == false){
14306                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14307                     }
14308                     
14309                     rm = true;
14310                     return;
14311                 }
14312             });
14313             
14314             if(rm){
14315                 return;
14316             }
14317             
14318             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14319                 this.tickItems.push(r.data);
14320             }
14321             
14322             if(typeof(e) == 'undefined' && view == false){
14323                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14324             }
14325                     
14326             return;
14327         }
14328         
14329         if(r){
14330             this.onSelect(r, index);
14331         }
14332         if(doFocus !== false && !this.blockFocus){
14333             this.inputEl().focus();
14334         }
14335     },
14336
14337     // private
14338     restrictHeight : function(){
14339         //this.innerList.dom.style.height = '';
14340         //var inner = this.innerList.dom;
14341         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14342         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14343         //this.list.beginUpdate();
14344         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14345         this.list.alignTo(this.inputEl(), this.listAlign);
14346         this.list.alignTo(this.inputEl(), this.listAlign);
14347         //this.list.endUpdate();
14348     },
14349
14350     // private
14351     onEmptyResults : function(){
14352         
14353         if(this.tickable && this.editable){
14354             this.hasFocus = false;
14355             this.restrictHeight();
14356             return;
14357         }
14358         
14359         this.collapse();
14360     },
14361
14362     /**
14363      * Returns true if the dropdown list is expanded, else false.
14364      */
14365     isExpanded : function(){
14366         return this.list.isVisible();
14367     },
14368
14369     /**
14370      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14371      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14372      * @param {String} value The data value of the item to select
14373      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14374      * selected item if it is not currently in view (defaults to true)
14375      * @return {Boolean} True if the value matched an item in the list, else false
14376      */
14377     selectByValue : function(v, scrollIntoView){
14378         if(v !== undefined && v !== null){
14379             var r = this.findRecord(this.valueField || this.displayField, v);
14380             if(r){
14381                 this.select(this.store.indexOf(r), scrollIntoView);
14382                 return true;
14383             }
14384         }
14385         return false;
14386     },
14387
14388     /**
14389      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14390      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14391      * @param {Number} index The zero-based index of the list item to select
14392      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14393      * selected item if it is not currently in view (defaults to true)
14394      */
14395     select : function(index, scrollIntoView){
14396         this.selectedIndex = index;
14397         this.view.select(index);
14398         if(scrollIntoView !== false){
14399             var el = this.view.getNode(index);
14400             /*
14401              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14402              */
14403             if(el){
14404                 this.list.scrollChildIntoView(el, false);
14405             }
14406         }
14407     },
14408
14409     // private
14410     selectNext : function(){
14411         var ct = this.store.getCount();
14412         if(ct > 0){
14413             if(this.selectedIndex == -1){
14414                 this.select(0);
14415             }else if(this.selectedIndex < ct-1){
14416                 this.select(this.selectedIndex+1);
14417             }
14418         }
14419     },
14420
14421     // private
14422     selectPrev : function(){
14423         var ct = this.store.getCount();
14424         if(ct > 0){
14425             if(this.selectedIndex == -1){
14426                 this.select(0);
14427             }else if(this.selectedIndex != 0){
14428                 this.select(this.selectedIndex-1);
14429             }
14430         }
14431     },
14432
14433     // private
14434     onKeyUp : function(e){
14435         if(this.editable !== false && !e.isSpecialKey()){
14436             this.lastKey = e.getKey();
14437             this.dqTask.delay(this.queryDelay);
14438         }
14439     },
14440
14441     // private
14442     validateBlur : function(){
14443         return !this.list || !this.list.isVisible();   
14444     },
14445
14446     // private
14447     initQuery : function(){
14448         
14449         var v = this.getRawValue();
14450         
14451         if(this.tickable && this.editable){
14452             v = this.tickableInputEl().getValue();
14453         }
14454         
14455         this.doQuery(v);
14456     },
14457
14458     // private
14459     doForce : function(){
14460         if(this.inputEl().dom.value.length > 0){
14461             this.inputEl().dom.value =
14462                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14463              
14464         }
14465     },
14466
14467     /**
14468      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14469      * query allowing the query action to be canceled if needed.
14470      * @param {String} query The SQL query to execute
14471      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14472      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14473      * saved in the current store (defaults to false)
14474      */
14475     doQuery : function(q, forceAll){
14476         
14477         if(q === undefined || q === null){
14478             q = '';
14479         }
14480         var qe = {
14481             query: q,
14482             forceAll: forceAll,
14483             combo: this,
14484             cancel:false
14485         };
14486         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14487             return false;
14488         }
14489         q = qe.query;
14490         
14491         forceAll = qe.forceAll;
14492         if(forceAll === true || (q.length >= this.minChars)){
14493             
14494             this.hasQuery = true;
14495             
14496             if(this.lastQuery != q || this.alwaysQuery){
14497                 this.lastQuery = q;
14498                 if(this.mode == 'local'){
14499                     this.selectedIndex = -1;
14500                     if(forceAll){
14501                         this.store.clearFilter();
14502                     }else{
14503                         
14504                         if(this.specialFilter){
14505                             this.fireEvent('specialfilter', this);
14506                             this.onLoad();
14507                             return;
14508                         }
14509                         
14510                         this.store.filter(this.displayField, q);
14511                     }
14512                     
14513                     this.store.fireEvent("datachanged", this.store);
14514                     
14515                     this.onLoad();
14516                     
14517                     
14518                 }else{
14519                     
14520                     this.store.baseParams[this.queryParam] = q;
14521                     
14522                     var options = {params : this.getParams(q)};
14523                     
14524                     if(this.loadNext){
14525                         options.add = true;
14526                         options.params.start = this.page * this.pageSize;
14527                     }
14528                     
14529                     this.store.load(options);
14530                     
14531                     /*
14532                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14533                      *  we should expand the list on onLoad
14534                      *  so command out it
14535                      */
14536 //                    this.expand();
14537                 }
14538             }else{
14539                 this.selectedIndex = -1;
14540                 this.onLoad();   
14541             }
14542         }
14543         
14544         this.loadNext = false;
14545     },
14546     
14547     // private
14548     getParams : function(q){
14549         var p = {};
14550         //p[this.queryParam] = q;
14551         
14552         if(this.pageSize){
14553             p.start = 0;
14554             p.limit = this.pageSize;
14555         }
14556         return p;
14557     },
14558
14559     /**
14560      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14561      */
14562     collapse : function(){
14563         if(!this.isExpanded()){
14564             return;
14565         }
14566         
14567         this.list.hide();
14568         
14569         this.hasFocus = false;
14570         
14571         if(this.tickable){
14572             this.okBtn.hide();
14573             this.cancelBtn.hide();
14574             this.trigger.show();
14575             
14576             if(this.editable){
14577                 this.tickableInputEl().dom.value = '';
14578                 this.tickableInputEl().blur();
14579             }
14580             
14581         }
14582         
14583         Roo.get(document).un('mousedown', this.collapseIf, this);
14584         Roo.get(document).un('mousewheel', this.collapseIf, this);
14585         if (!this.editable) {
14586             Roo.get(document).un('keydown', this.listKeyPress, this);
14587         }
14588         this.fireEvent('collapse', this);
14589         
14590         this.validate();
14591     },
14592
14593     // private
14594     collapseIf : function(e){
14595         var in_combo  = e.within(this.el);
14596         var in_list =  e.within(this.list);
14597         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14598         
14599         if (in_combo || in_list || is_list) {
14600             //e.stopPropagation();
14601             return;
14602         }
14603         
14604         if(this.tickable){
14605             this.onTickableFooterButtonClick(e, false, false);
14606         }
14607
14608         this.collapse();
14609         
14610     },
14611
14612     /**
14613      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14614      */
14615     expand : function(){
14616        
14617         if(this.isExpanded() || !this.hasFocus){
14618             return;
14619         }
14620         
14621         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14622         this.list.setWidth(lw);
14623         
14624         Roo.log('expand');
14625         
14626         this.list.show();
14627         
14628         this.restrictHeight();
14629         
14630         if(this.tickable){
14631             
14632             this.tickItems = Roo.apply([], this.item);
14633             
14634             this.okBtn.show();
14635             this.cancelBtn.show();
14636             this.trigger.hide();
14637             
14638             if(this.editable){
14639                 this.tickableInputEl().focus();
14640             }
14641             
14642         }
14643         
14644         Roo.get(document).on('mousedown', this.collapseIf, this);
14645         Roo.get(document).on('mousewheel', this.collapseIf, this);
14646         if (!this.editable) {
14647             Roo.get(document).on('keydown', this.listKeyPress, this);
14648         }
14649         
14650         this.fireEvent('expand', this);
14651     },
14652
14653     // private
14654     // Implements the default empty TriggerField.onTriggerClick function
14655     onTriggerClick : function(e)
14656     {
14657         Roo.log('trigger click');
14658         
14659         if(this.disabled || !this.triggerList){
14660             return;
14661         }
14662         
14663         this.page = 0;
14664         this.loadNext = false;
14665         
14666         if(this.isExpanded()){
14667             this.collapse();
14668             if (!this.blockFocus) {
14669                 this.inputEl().focus();
14670             }
14671             
14672         }else {
14673             this.hasFocus = true;
14674             if(this.triggerAction == 'all') {
14675                 this.doQuery(this.allQuery, true);
14676             } else {
14677                 this.doQuery(this.getRawValue());
14678             }
14679             if (!this.blockFocus) {
14680                 this.inputEl().focus();
14681             }
14682         }
14683     },
14684     
14685     onTickableTriggerClick : function(e)
14686     {
14687         if(this.disabled){
14688             return;
14689         }
14690         
14691         this.page = 0;
14692         this.loadNext = false;
14693         this.hasFocus = true;
14694         
14695         if(this.triggerAction == 'all') {
14696             this.doQuery(this.allQuery, true);
14697         } else {
14698             this.doQuery(this.getRawValue());
14699         }
14700     },
14701     
14702     onSearchFieldClick : function(e)
14703     {
14704         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14705             this.onTickableFooterButtonClick(e, false, false);
14706             return;
14707         }
14708         
14709         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14710             return;
14711         }
14712         
14713         this.page = 0;
14714         this.loadNext = false;
14715         this.hasFocus = true;
14716         
14717         if(this.triggerAction == 'all') {
14718             this.doQuery(this.allQuery, true);
14719         } else {
14720             this.doQuery(this.getRawValue());
14721         }
14722     },
14723     
14724     listKeyPress : function(e)
14725     {
14726         //Roo.log('listkeypress');
14727         // scroll to first matching element based on key pres..
14728         if (e.isSpecialKey()) {
14729             return false;
14730         }
14731         var k = String.fromCharCode(e.getKey()).toUpperCase();
14732         //Roo.log(k);
14733         var match  = false;
14734         var csel = this.view.getSelectedNodes();
14735         var cselitem = false;
14736         if (csel.length) {
14737             var ix = this.view.indexOf(csel[0]);
14738             cselitem  = this.store.getAt(ix);
14739             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14740                 cselitem = false;
14741             }
14742             
14743         }
14744         
14745         this.store.each(function(v) { 
14746             if (cselitem) {
14747                 // start at existing selection.
14748                 if (cselitem.id == v.id) {
14749                     cselitem = false;
14750                 }
14751                 return true;
14752             }
14753                 
14754             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14755                 match = this.store.indexOf(v);
14756                 return false;
14757             }
14758             return true;
14759         }, this);
14760         
14761         if (match === false) {
14762             return true; // no more action?
14763         }
14764         // scroll to?
14765         this.view.select(match);
14766         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14767         sn.scrollIntoView(sn.dom.parentNode, false);
14768     },
14769     
14770     onViewScroll : function(e, t){
14771         
14772         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
14773             return;
14774         }
14775         
14776         this.hasQuery = true;
14777         
14778         this.loading = this.list.select('.loading', true).first();
14779         
14780         if(this.loading === null){
14781             this.list.createChild({
14782                 tag: 'div',
14783                 cls: 'loading roo-select2-more-results roo-select2-active',
14784                 html: 'Loading more results...'
14785             });
14786             
14787             this.loading = this.list.select('.loading', true).first();
14788             
14789             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14790             
14791             this.loading.hide();
14792         }
14793         
14794         this.loading.show();
14795         
14796         var _combo = this;
14797         
14798         this.page++;
14799         this.loadNext = true;
14800         
14801         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14802         
14803         return;
14804     },
14805     
14806     addItem : function(o)
14807     {   
14808         var dv = ''; // display value
14809         
14810         if (this.displayField) {
14811             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14812         } else {
14813             // this is an error condition!!!
14814             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14815         }
14816         
14817         if(!dv.length){
14818             return;
14819         }
14820         
14821         var choice = this.choices.createChild({
14822             tag: 'li',
14823             cls: 'roo-select2-search-choice',
14824             cn: [
14825                 {
14826                     tag: 'div',
14827                     html: dv
14828                 },
14829                 {
14830                     tag: 'a',
14831                     href: '#',
14832                     cls: 'roo-select2-search-choice-close fa fa-times',
14833                     tabindex: '-1'
14834                 }
14835             ]
14836             
14837         }, this.searchField);
14838         
14839         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14840         
14841         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14842         
14843         this.item.push(o);
14844         
14845         this.lastData = o;
14846         
14847         this.syncValue();
14848         
14849         this.inputEl().dom.value = '';
14850         
14851         this.validate();
14852     },
14853     
14854     onRemoveItem : function(e, _self, o)
14855     {
14856         e.preventDefault();
14857         
14858         this.lastItem = Roo.apply([], this.item);
14859         
14860         var index = this.item.indexOf(o.data) * 1;
14861         
14862         if( index < 0){
14863             Roo.log('not this item?!');
14864             return;
14865         }
14866         
14867         this.item.splice(index, 1);
14868         o.item.remove();
14869         
14870         this.syncValue();
14871         
14872         this.fireEvent('remove', this, e);
14873         
14874         this.validate();
14875         
14876     },
14877     
14878     syncValue : function()
14879     {
14880         if(!this.item.length){
14881             this.clearValue();
14882             return;
14883         }
14884             
14885         var value = [];
14886         var _this = this;
14887         Roo.each(this.item, function(i){
14888             if(_this.valueField){
14889                 value.push(i[_this.valueField]);
14890                 return;
14891             }
14892
14893             value.push(i);
14894         });
14895
14896         this.value = value.join(',');
14897
14898         if(this.hiddenField){
14899             this.hiddenField.dom.value = this.value;
14900         }
14901         
14902         this.store.fireEvent("datachanged", this.store);
14903         
14904         this.validate();
14905     },
14906     
14907     clearItem : function()
14908     {
14909         if(!this.multiple){
14910             return;
14911         }
14912         
14913         this.item = [];
14914         
14915         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14916            c.remove();
14917         });
14918         
14919         this.syncValue();
14920         
14921         this.validate();
14922         
14923         if(this.tickable && !Roo.isTouch){
14924             this.view.refresh();
14925         }
14926     },
14927     
14928     inputEl: function ()
14929     {
14930         if(Roo.isIOS && this.useNativeIOS){
14931             return this.el.select('select.roo-ios-select', true).first();
14932         }
14933         
14934         if(Roo.isTouch && this.mobileTouchView){
14935             return this.el.select('input.form-control',true).first();
14936         }
14937         
14938         if(this.tickable){
14939             return this.searchField;
14940         }
14941         
14942         return this.el.select('input.form-control',true).first();
14943     },
14944     
14945     onTickableFooterButtonClick : function(e, btn, el)
14946     {
14947         e.preventDefault();
14948         
14949         this.lastItem = Roo.apply([], this.item);
14950         
14951         if(btn && btn.name == 'cancel'){
14952             this.tickItems = Roo.apply([], this.item);
14953             this.collapse();
14954             return;
14955         }
14956         
14957         this.clearItem();
14958         
14959         var _this = this;
14960         
14961         Roo.each(this.tickItems, function(o){
14962             _this.addItem(o);
14963         });
14964         
14965         this.collapse();
14966         
14967     },
14968     
14969     validate : function()
14970     {
14971         if(this.getVisibilityEl().hasClass('hidden')){
14972             return true;
14973         }
14974         
14975         var v = this.getRawValue();
14976         
14977         if(this.multiple){
14978             v = this.getValue();
14979         }
14980         
14981         if(this.disabled || this.allowBlank || v.length){
14982             this.markValid();
14983             return true;
14984         }
14985         
14986         this.markInvalid();
14987         return false;
14988     },
14989     
14990     tickableInputEl : function()
14991     {
14992         if(!this.tickable || !this.editable){
14993             return this.inputEl();
14994         }
14995         
14996         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14997     },
14998     
14999     
15000     getAutoCreateTouchView : function()
15001     {
15002         var id = Roo.id();
15003         
15004         var cfg = {
15005             cls: 'form-group' //input-group
15006         };
15007         
15008         var input =  {
15009             tag: 'input',
15010             id : id,
15011             type : this.inputType,
15012             cls : 'form-control x-combo-noedit',
15013             autocomplete: 'new-password',
15014             placeholder : this.placeholder || '',
15015             readonly : true
15016         };
15017         
15018         if (this.name) {
15019             input.name = this.name;
15020         }
15021         
15022         if (this.size) {
15023             input.cls += ' input-' + this.size;
15024         }
15025         
15026         if (this.disabled) {
15027             input.disabled = true;
15028         }
15029         
15030         var inputblock = {
15031             cls : '',
15032             cn : [
15033                 input
15034             ]
15035         };
15036         
15037         if(this.before){
15038             inputblock.cls += ' input-group';
15039             
15040             inputblock.cn.unshift({
15041                 tag :'span',
15042                 cls : 'input-group-addon',
15043                 html : this.before
15044             });
15045         }
15046         
15047         if(this.removable && !this.multiple){
15048             inputblock.cls += ' roo-removable';
15049             
15050             inputblock.cn.push({
15051                 tag: 'button',
15052                 html : 'x',
15053                 cls : 'roo-combo-removable-btn close'
15054             });
15055         }
15056
15057         if(this.hasFeedback && !this.allowBlank){
15058             
15059             inputblock.cls += ' has-feedback';
15060             
15061             inputblock.cn.push({
15062                 tag: 'span',
15063                 cls: 'glyphicon form-control-feedback'
15064             });
15065             
15066         }
15067         
15068         if (this.after) {
15069             
15070             inputblock.cls += (this.before) ? '' : ' input-group';
15071             
15072             inputblock.cn.push({
15073                 tag :'span',
15074                 cls : 'input-group-addon',
15075                 html : this.after
15076             });
15077         }
15078
15079         var box = {
15080             tag: 'div',
15081             cn: [
15082                 {
15083                     tag: 'input',
15084                     type : 'hidden',
15085                     cls: 'form-hidden-field'
15086                 },
15087                 inputblock
15088             ]
15089             
15090         };
15091         
15092         if(this.multiple){
15093             box = {
15094                 tag: 'div',
15095                 cn: [
15096                     {
15097                         tag: 'input',
15098                         type : 'hidden',
15099                         cls: 'form-hidden-field'
15100                     },
15101                     {
15102                         tag: 'ul',
15103                         cls: 'roo-select2-choices',
15104                         cn:[
15105                             {
15106                                 tag: 'li',
15107                                 cls: 'roo-select2-search-field',
15108                                 cn: [
15109
15110                                     inputblock
15111                                 ]
15112                             }
15113                         ]
15114                     }
15115                 ]
15116             }
15117         };
15118         
15119         var combobox = {
15120             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15121             cn: [
15122                 box
15123             ]
15124         };
15125         
15126         if(!this.multiple && this.showToggleBtn){
15127             
15128             var caret = {
15129                         tag: 'span',
15130                         cls: 'caret'
15131             };
15132             
15133             if (this.caret != false) {
15134                 caret = {
15135                      tag: 'i',
15136                      cls: 'fa fa-' + this.caret
15137                 };
15138                 
15139             }
15140             
15141             combobox.cn.push({
15142                 tag :'span',
15143                 cls : 'input-group-addon btn dropdown-toggle',
15144                 cn : [
15145                     caret,
15146                     {
15147                         tag: 'span',
15148                         cls: 'combobox-clear',
15149                         cn  : [
15150                             {
15151                                 tag : 'i',
15152                                 cls: 'icon-remove'
15153                             }
15154                         ]
15155                     }
15156                 ]
15157
15158             })
15159         }
15160         
15161         if(this.multiple){
15162             combobox.cls += ' roo-select2-container-multi';
15163         }
15164         
15165         var align = this.labelAlign || this.parentLabelAlign();
15166         
15167         if (align ==='left' && this.fieldLabel.length) {
15168
15169             cfg.cn = [
15170                 {
15171                    tag : 'i',
15172                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15173                    tooltip : 'This field is required'
15174                 },
15175                 {
15176                     tag: 'label',
15177                     cls : 'control-label',
15178                     html : this.fieldLabel
15179
15180                 },
15181                 {
15182                     cls : '', 
15183                     cn: [
15184                         combobox
15185                     ]
15186                 }
15187             ];
15188             
15189             var labelCfg = cfg.cn[1];
15190             var contentCfg = cfg.cn[2];
15191             
15192
15193             if(this.indicatorpos == 'right'){
15194                 cfg.cn = [
15195                     {
15196                         tag: 'label',
15197                         'for' :  id,
15198                         cls : 'control-label',
15199                         cn : [
15200                             {
15201                                 tag : 'span',
15202                                 html : this.fieldLabel
15203                             },
15204                             {
15205                                 tag : 'i',
15206                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15207                                 tooltip : 'This field is required'
15208                             }
15209                         ]
15210                     },
15211                     {
15212                         cls : "",
15213                         cn: [
15214                             combobox
15215                         ]
15216                     }
15217
15218                 ];
15219                 
15220                 labelCfg = cfg.cn[0];
15221                 contentCfg = cfg.cn[1];
15222             }
15223             
15224            
15225             
15226             if(this.labelWidth > 12){
15227                 labelCfg.style = "width: " + this.labelWidth + 'px';
15228             }
15229             
15230             if(this.labelWidth < 13 && this.labelmd == 0){
15231                 this.labelmd = this.labelWidth;
15232             }
15233             
15234             if(this.labellg > 0){
15235                 labelCfg.cls += ' col-lg-' + this.labellg;
15236                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15237             }
15238             
15239             if(this.labelmd > 0){
15240                 labelCfg.cls += ' col-md-' + this.labelmd;
15241                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15242             }
15243             
15244             if(this.labelsm > 0){
15245                 labelCfg.cls += ' col-sm-' + this.labelsm;
15246                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15247             }
15248             
15249             if(this.labelxs > 0){
15250                 labelCfg.cls += ' col-xs-' + this.labelxs;
15251                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15252             }
15253                 
15254                 
15255         } else if ( this.fieldLabel.length) {
15256             cfg.cn = [
15257                 {
15258                    tag : 'i',
15259                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15260                    tooltip : 'This field is required'
15261                 },
15262                 {
15263                     tag: 'label',
15264                     cls : 'control-label',
15265                     html : this.fieldLabel
15266
15267                 },
15268                 {
15269                     cls : '', 
15270                     cn: [
15271                         combobox
15272                     ]
15273                 }
15274             ];
15275             
15276             if(this.indicatorpos == 'right'){
15277                 cfg.cn = [
15278                     {
15279                         tag: 'label',
15280                         cls : 'control-label',
15281                         html : this.fieldLabel,
15282                         cn : [
15283                             {
15284                                tag : 'i',
15285                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15286                                tooltip : 'This field is required'
15287                             }
15288                         ]
15289                     },
15290                     {
15291                         cls : '', 
15292                         cn: [
15293                             combobox
15294                         ]
15295                     }
15296                 ];
15297             }
15298         } else {
15299             cfg.cn = combobox;    
15300         }
15301         
15302         
15303         var settings = this;
15304         
15305         ['xs','sm','md','lg'].map(function(size){
15306             if (settings[size]) {
15307                 cfg.cls += ' col-' + size + '-' + settings[size];
15308             }
15309         });
15310         
15311         return cfg;
15312     },
15313     
15314     initTouchView : function()
15315     {
15316         this.renderTouchView();
15317         
15318         this.touchViewEl.on('scroll', function(){
15319             this.el.dom.scrollTop = 0;
15320         }, this);
15321         
15322         this.originalValue = this.getValue();
15323         
15324         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15325         
15326         this.inputEl().on("click", this.showTouchView, this);
15327         if (this.triggerEl) {
15328             this.triggerEl.on("click", this.showTouchView, this);
15329         }
15330         
15331         
15332         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15333         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15334         
15335         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15336         
15337         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15338         this.store.on('load', this.onTouchViewLoad, this);
15339         this.store.on('loadexception', this.onTouchViewLoadException, this);
15340         
15341         if(this.hiddenName){
15342             
15343             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15344             
15345             this.hiddenField.dom.value =
15346                 this.hiddenValue !== undefined ? this.hiddenValue :
15347                 this.value !== undefined ? this.value : '';
15348         
15349             this.el.dom.removeAttribute('name');
15350             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15351         }
15352         
15353         if(this.multiple){
15354             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15355             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15356         }
15357         
15358         if(this.removable && !this.multiple){
15359             var close = this.closeTriggerEl();
15360             if(close){
15361                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15362                 close.on('click', this.removeBtnClick, this, close);
15363             }
15364         }
15365         /*
15366          * fix the bug in Safari iOS8
15367          */
15368         this.inputEl().on("focus", function(e){
15369             document.activeElement.blur();
15370         }, this);
15371         
15372         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15373         
15374         return;
15375         
15376         
15377     },
15378     
15379     renderTouchView : function()
15380     {
15381         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15382         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15383         
15384         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15385         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15386         
15387         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15388         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15389         this.touchViewBodyEl.setStyle('overflow', 'auto');
15390         
15391         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15392         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15393         
15394         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15395         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15396         
15397     },
15398     
15399     showTouchView : function()
15400     {
15401         if(this.disabled){
15402             return;
15403         }
15404         
15405         this.touchViewHeaderEl.hide();
15406
15407         if(this.modalTitle.length){
15408             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15409             this.touchViewHeaderEl.show();
15410         }
15411
15412         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15413         this.touchViewEl.show();
15414
15415         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15416         
15417         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15418         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15419
15420         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15421
15422         if(this.modalTitle.length){
15423             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15424         }
15425         
15426         this.touchViewBodyEl.setHeight(bodyHeight);
15427
15428         if(this.animate){
15429             var _this = this;
15430             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15431         }else{
15432             this.touchViewEl.addClass('in');
15433         }
15434         
15435         if(this._touchViewMask){
15436             Roo.get(document.body).addClass("x-body-masked");
15437             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15438             this._touchViewMask.setStyle('z-index', 10000);
15439             this._touchViewMask.addClass('show');
15440         }
15441         
15442         this.doTouchViewQuery();
15443         
15444     },
15445     
15446     hideTouchView : function()
15447     {
15448         this.touchViewEl.removeClass('in');
15449
15450         if(this.animate){
15451             var _this = this;
15452             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15453         }else{
15454             this.touchViewEl.setStyle('display', 'none');
15455         }
15456         
15457         if(this._touchViewMask){
15458             this._touchViewMask.removeClass('show');
15459             Roo.get(document.body).removeClass("x-body-masked");
15460         }
15461     },
15462     
15463     setTouchViewValue : function()
15464     {
15465         if(this.multiple){
15466             this.clearItem();
15467         
15468             var _this = this;
15469
15470             Roo.each(this.tickItems, function(o){
15471                 this.addItem(o);
15472             }, this);
15473         }
15474         
15475         this.hideTouchView();
15476     },
15477     
15478     doTouchViewQuery : function()
15479     {
15480         var qe = {
15481             query: '',
15482             forceAll: true,
15483             combo: this,
15484             cancel:false
15485         };
15486         
15487         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15488             return false;
15489         }
15490         
15491         if(!this.alwaysQuery || this.mode == 'local'){
15492             this.onTouchViewLoad();
15493             return;
15494         }
15495         
15496         this.store.load();
15497     },
15498     
15499     onTouchViewBeforeLoad : function(combo,opts)
15500     {
15501         return;
15502     },
15503
15504     // private
15505     onTouchViewLoad : function()
15506     {
15507         if(this.store.getCount() < 1){
15508             this.onTouchViewEmptyResults();
15509             return;
15510         }
15511         
15512         this.clearTouchView();
15513         
15514         var rawValue = this.getRawValue();
15515         
15516         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15517         
15518         this.tickItems = [];
15519         
15520         this.store.data.each(function(d, rowIndex){
15521             var row = this.touchViewListGroup.createChild(template);
15522             
15523             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15524                 row.addClass(d.data.cls);
15525             }
15526             
15527             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15528                 var cfg = {
15529                     data : d.data,
15530                     html : d.data[this.displayField]
15531                 };
15532                 
15533                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15534                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15535                 }
15536             }
15537             row.removeClass('selected');
15538             if(!this.multiple && this.valueField &&
15539                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15540             {
15541                 // radio buttons..
15542                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15543                 row.addClass('selected');
15544             }
15545             
15546             if(this.multiple && this.valueField &&
15547                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15548             {
15549                 
15550                 // checkboxes...
15551                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15552                 this.tickItems.push(d.data);
15553             }
15554             
15555             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15556             
15557         }, this);
15558         
15559         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15560         
15561         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15562
15563         if(this.modalTitle.length){
15564             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15565         }
15566
15567         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15568         
15569         if(this.mobile_restrict_height && listHeight < bodyHeight){
15570             this.touchViewBodyEl.setHeight(listHeight);
15571         }
15572         
15573         var _this = this;
15574         
15575         if(firstChecked && listHeight > bodyHeight){
15576             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15577         }
15578         
15579     },
15580     
15581     onTouchViewLoadException : function()
15582     {
15583         this.hideTouchView();
15584     },
15585     
15586     onTouchViewEmptyResults : function()
15587     {
15588         this.clearTouchView();
15589         
15590         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15591         
15592         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15593         
15594     },
15595     
15596     clearTouchView : function()
15597     {
15598         this.touchViewListGroup.dom.innerHTML = '';
15599     },
15600     
15601     onTouchViewClick : function(e, el, o)
15602     {
15603         e.preventDefault();
15604         
15605         var row = o.row;
15606         var rowIndex = o.rowIndex;
15607         
15608         var r = this.store.getAt(rowIndex);
15609         
15610         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15611             
15612             if(!this.multiple){
15613                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15614                     c.dom.removeAttribute('checked');
15615                 }, this);
15616
15617                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15618
15619                 this.setFromData(r.data);
15620
15621                 var close = this.closeTriggerEl();
15622
15623                 if(close){
15624                     close.show();
15625                 }
15626
15627                 this.hideTouchView();
15628
15629                 this.fireEvent('select', this, r, rowIndex);
15630
15631                 return;
15632             }
15633
15634             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15635                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15636                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15637                 return;
15638             }
15639
15640             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15641             this.addItem(r.data);
15642             this.tickItems.push(r.data);
15643         }
15644     },
15645     
15646     getAutoCreateNativeIOS : function()
15647     {
15648         var cfg = {
15649             cls: 'form-group' //input-group,
15650         };
15651         
15652         var combobox =  {
15653             tag: 'select',
15654             cls : 'roo-ios-select'
15655         };
15656         
15657         if (this.name) {
15658             combobox.name = this.name;
15659         }
15660         
15661         if (this.disabled) {
15662             combobox.disabled = true;
15663         }
15664         
15665         var settings = this;
15666         
15667         ['xs','sm','md','lg'].map(function(size){
15668             if (settings[size]) {
15669                 cfg.cls += ' col-' + size + '-' + settings[size];
15670             }
15671         });
15672         
15673         cfg.cn = combobox;
15674         
15675         return cfg;
15676         
15677     },
15678     
15679     initIOSView : function()
15680     {
15681         this.store.on('load', this.onIOSViewLoad, this);
15682         
15683         return;
15684     },
15685     
15686     onIOSViewLoad : function()
15687     {
15688         if(this.store.getCount() < 1){
15689             return;
15690         }
15691         
15692         this.clearIOSView();
15693         
15694         if(this.allowBlank) {
15695             
15696             var default_text = '-- SELECT --';
15697             
15698             if(this.placeholder.length){
15699                 default_text = this.placeholder;
15700             }
15701             
15702             if(this.emptyTitle.length){
15703                 default_text += ' - ' + this.emptyTitle + ' -';
15704             }
15705             
15706             var opt = this.inputEl().createChild({
15707                 tag: 'option',
15708                 value : 0,
15709                 html : default_text
15710             });
15711             
15712             var o = {};
15713             o[this.valueField] = 0;
15714             o[this.displayField] = default_text;
15715             
15716             this.ios_options.push({
15717                 data : o,
15718                 el : opt
15719             });
15720             
15721         }
15722         
15723         this.store.data.each(function(d, rowIndex){
15724             
15725             var html = '';
15726             
15727             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15728                 html = d.data[this.displayField];
15729             }
15730             
15731             var value = '';
15732             
15733             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15734                 value = d.data[this.valueField];
15735             }
15736             
15737             var option = {
15738                 tag: 'option',
15739                 value : value,
15740                 html : html
15741             };
15742             
15743             if(this.value == d.data[this.valueField]){
15744                 option['selected'] = true;
15745             }
15746             
15747             var opt = this.inputEl().createChild(option);
15748             
15749             this.ios_options.push({
15750                 data : d.data,
15751                 el : opt
15752             });
15753             
15754         }, this);
15755         
15756         this.inputEl().on('change', function(){
15757            this.fireEvent('select', this);
15758         }, this);
15759         
15760     },
15761     
15762     clearIOSView: function()
15763     {
15764         this.inputEl().dom.innerHTML = '';
15765         
15766         this.ios_options = [];
15767     },
15768     
15769     setIOSValue: function(v)
15770     {
15771         this.value = v;
15772         
15773         if(!this.ios_options){
15774             return;
15775         }
15776         
15777         Roo.each(this.ios_options, function(opts){
15778            
15779            opts.el.dom.removeAttribute('selected');
15780            
15781            if(opts.data[this.valueField] != v){
15782                return;
15783            }
15784            
15785            opts.el.dom.setAttribute('selected', true);
15786            
15787         }, this);
15788     }
15789
15790     /** 
15791     * @cfg {Boolean} grow 
15792     * @hide 
15793     */
15794     /** 
15795     * @cfg {Number} growMin 
15796     * @hide 
15797     */
15798     /** 
15799     * @cfg {Number} growMax 
15800     * @hide 
15801     */
15802     /**
15803      * @hide
15804      * @method autoSize
15805      */
15806 });
15807
15808 Roo.apply(Roo.bootstrap.ComboBox,  {
15809     
15810     header : {
15811         tag: 'div',
15812         cls: 'modal-header',
15813         cn: [
15814             {
15815                 tag: 'h4',
15816                 cls: 'modal-title'
15817             }
15818         ]
15819     },
15820     
15821     body : {
15822         tag: 'div',
15823         cls: 'modal-body',
15824         cn: [
15825             {
15826                 tag: 'ul',
15827                 cls: 'list-group'
15828             }
15829         ]
15830     },
15831     
15832     listItemRadio : {
15833         tag: 'li',
15834         cls: 'list-group-item',
15835         cn: [
15836             {
15837                 tag: 'span',
15838                 cls: 'roo-combobox-list-group-item-value'
15839             },
15840             {
15841                 tag: 'div',
15842                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15843                 cn: [
15844                     {
15845                         tag: 'input',
15846                         type: 'radio'
15847                     },
15848                     {
15849                         tag: 'label'
15850                     }
15851                 ]
15852             }
15853         ]
15854     },
15855     
15856     listItemCheckbox : {
15857         tag: 'li',
15858         cls: 'list-group-item',
15859         cn: [
15860             {
15861                 tag: 'span',
15862                 cls: 'roo-combobox-list-group-item-value'
15863             },
15864             {
15865                 tag: 'div',
15866                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15867                 cn: [
15868                     {
15869                         tag: 'input',
15870                         type: 'checkbox'
15871                     },
15872                     {
15873                         tag: 'label'
15874                     }
15875                 ]
15876             }
15877         ]
15878     },
15879     
15880     emptyResult : {
15881         tag: 'div',
15882         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15883     },
15884     
15885     footer : {
15886         tag: 'div',
15887         cls: 'modal-footer',
15888         cn: [
15889             {
15890                 tag: 'div',
15891                 cls: 'row',
15892                 cn: [
15893                     {
15894                         tag: 'div',
15895                         cls: 'col-xs-6 text-left',
15896                         cn: {
15897                             tag: 'button',
15898                             cls: 'btn btn-danger roo-touch-view-cancel',
15899                             html: 'Cancel'
15900                         }
15901                     },
15902                     {
15903                         tag: 'div',
15904                         cls: 'col-xs-6 text-right',
15905                         cn: {
15906                             tag: 'button',
15907                             cls: 'btn btn-success roo-touch-view-ok',
15908                             html: 'OK'
15909                         }
15910                     }
15911                 ]
15912             }
15913         ]
15914         
15915     }
15916 });
15917
15918 Roo.apply(Roo.bootstrap.ComboBox,  {
15919     
15920     touchViewTemplate : {
15921         tag: 'div',
15922         cls: 'modal fade roo-combobox-touch-view',
15923         cn: [
15924             {
15925                 tag: 'div',
15926                 cls: 'modal-dialog',
15927                 style : 'position:fixed', // we have to fix position....
15928                 cn: [
15929                     {
15930                         tag: 'div',
15931                         cls: 'modal-content',
15932                         cn: [
15933                             Roo.bootstrap.ComboBox.header,
15934                             Roo.bootstrap.ComboBox.body,
15935                             Roo.bootstrap.ComboBox.footer
15936                         ]
15937                     }
15938                 ]
15939             }
15940         ]
15941     }
15942 });/*
15943  * Based on:
15944  * Ext JS Library 1.1.1
15945  * Copyright(c) 2006-2007, Ext JS, LLC.
15946  *
15947  * Originally Released Under LGPL - original licence link has changed is not relivant.
15948  *
15949  * Fork - LGPL
15950  * <script type="text/javascript">
15951  */
15952
15953 /**
15954  * @class Roo.View
15955  * @extends Roo.util.Observable
15956  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15957  * This class also supports single and multi selection modes. <br>
15958  * Create a data model bound view:
15959  <pre><code>
15960  var store = new Roo.data.Store(...);
15961
15962  var view = new Roo.View({
15963     el : "my-element",
15964     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15965  
15966     singleSelect: true,
15967     selectedClass: "ydataview-selected",
15968     store: store
15969  });
15970
15971  // listen for node click?
15972  view.on("click", function(vw, index, node, e){
15973  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15974  });
15975
15976  // load XML data
15977  dataModel.load("foobar.xml");
15978  </code></pre>
15979  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15980  * <br><br>
15981  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15982  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15983  * 
15984  * Note: old style constructor is still suported (container, template, config)
15985  * 
15986  * @constructor
15987  * Create a new View
15988  * @param {Object} config The config object
15989  * 
15990  */
15991 Roo.View = function(config, depreciated_tpl, depreciated_config){
15992     
15993     this.parent = false;
15994     
15995     if (typeof(depreciated_tpl) == 'undefined') {
15996         // new way.. - universal constructor.
15997         Roo.apply(this, config);
15998         this.el  = Roo.get(this.el);
15999     } else {
16000         // old format..
16001         this.el  = Roo.get(config);
16002         this.tpl = depreciated_tpl;
16003         Roo.apply(this, depreciated_config);
16004     }
16005     this.wrapEl  = this.el.wrap().wrap();
16006     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16007     
16008     
16009     if(typeof(this.tpl) == "string"){
16010         this.tpl = new Roo.Template(this.tpl);
16011     } else {
16012         // support xtype ctors..
16013         this.tpl = new Roo.factory(this.tpl, Roo);
16014     }
16015     
16016     
16017     this.tpl.compile();
16018     
16019     /** @private */
16020     this.addEvents({
16021         /**
16022          * @event beforeclick
16023          * Fires before a click is processed. Returns false to cancel the default action.
16024          * @param {Roo.View} this
16025          * @param {Number} index The index of the target node
16026          * @param {HTMLElement} node The target node
16027          * @param {Roo.EventObject} e The raw event object
16028          */
16029             "beforeclick" : true,
16030         /**
16031          * @event click
16032          * Fires when a template node is clicked.
16033          * @param {Roo.View} this
16034          * @param {Number} index The index of the target node
16035          * @param {HTMLElement} node The target node
16036          * @param {Roo.EventObject} e The raw event object
16037          */
16038             "click" : true,
16039         /**
16040          * @event dblclick
16041          * Fires when a template node is double clicked.
16042          * @param {Roo.View} this
16043          * @param {Number} index The index of the target node
16044          * @param {HTMLElement} node The target node
16045          * @param {Roo.EventObject} e The raw event object
16046          */
16047             "dblclick" : true,
16048         /**
16049          * @event contextmenu
16050          * Fires when a template node is right clicked.
16051          * @param {Roo.View} this
16052          * @param {Number} index The index of the target node
16053          * @param {HTMLElement} node The target node
16054          * @param {Roo.EventObject} e The raw event object
16055          */
16056             "contextmenu" : true,
16057         /**
16058          * @event selectionchange
16059          * Fires when the selected nodes change.
16060          * @param {Roo.View} this
16061          * @param {Array} selections Array of the selected nodes
16062          */
16063             "selectionchange" : true,
16064     
16065         /**
16066          * @event beforeselect
16067          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16068          * @param {Roo.View} this
16069          * @param {HTMLElement} node The node to be selected
16070          * @param {Array} selections Array of currently selected nodes
16071          */
16072             "beforeselect" : true,
16073         /**
16074          * @event preparedata
16075          * Fires on every row to render, to allow you to change the data.
16076          * @param {Roo.View} this
16077          * @param {Object} data to be rendered (change this)
16078          */
16079           "preparedata" : true
16080           
16081           
16082         });
16083
16084
16085
16086     this.el.on({
16087         "click": this.onClick,
16088         "dblclick": this.onDblClick,
16089         "contextmenu": this.onContextMenu,
16090         scope:this
16091     });
16092
16093     this.selections = [];
16094     this.nodes = [];
16095     this.cmp = new Roo.CompositeElementLite([]);
16096     if(this.store){
16097         this.store = Roo.factory(this.store, Roo.data);
16098         this.setStore(this.store, true);
16099     }
16100     
16101     if ( this.footer && this.footer.xtype) {
16102            
16103          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16104         
16105         this.footer.dataSource = this.store;
16106         this.footer.container = fctr;
16107         this.footer = Roo.factory(this.footer, Roo);
16108         fctr.insertFirst(this.el);
16109         
16110         // this is a bit insane - as the paging toolbar seems to detach the el..
16111 //        dom.parentNode.parentNode.parentNode
16112          // they get detached?
16113     }
16114     
16115     
16116     Roo.View.superclass.constructor.call(this);
16117     
16118     
16119 };
16120
16121 Roo.extend(Roo.View, Roo.util.Observable, {
16122     
16123      /**
16124      * @cfg {Roo.data.Store} store Data store to load data from.
16125      */
16126     store : false,
16127     
16128     /**
16129      * @cfg {String|Roo.Element} el The container element.
16130      */
16131     el : '',
16132     
16133     /**
16134      * @cfg {String|Roo.Template} tpl The template used by this View 
16135      */
16136     tpl : false,
16137     /**
16138      * @cfg {String} dataName the named area of the template to use as the data area
16139      *                          Works with domtemplates roo-name="name"
16140      */
16141     dataName: false,
16142     /**
16143      * @cfg {String} selectedClass The css class to add to selected nodes
16144      */
16145     selectedClass : "x-view-selected",
16146      /**
16147      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16148      */
16149     emptyText : "",
16150     
16151     /**
16152      * @cfg {String} text to display on mask (default Loading)
16153      */
16154     mask : false,
16155     /**
16156      * @cfg {Boolean} multiSelect Allow multiple selection
16157      */
16158     multiSelect : false,
16159     /**
16160      * @cfg {Boolean} singleSelect Allow single selection
16161      */
16162     singleSelect:  false,
16163     
16164     /**
16165      * @cfg {Boolean} toggleSelect - selecting 
16166      */
16167     toggleSelect : false,
16168     
16169     /**
16170      * @cfg {Boolean} tickable - selecting 
16171      */
16172     tickable : false,
16173     
16174     /**
16175      * Returns the element this view is bound to.
16176      * @return {Roo.Element}
16177      */
16178     getEl : function(){
16179         return this.wrapEl;
16180     },
16181     
16182     
16183
16184     /**
16185      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16186      */
16187     refresh : function(){
16188         //Roo.log('refresh');
16189         var t = this.tpl;
16190         
16191         // if we are using something like 'domtemplate', then
16192         // the what gets used is:
16193         // t.applySubtemplate(NAME, data, wrapping data..)
16194         // the outer template then get' applied with
16195         //     the store 'extra data'
16196         // and the body get's added to the
16197         //      roo-name="data" node?
16198         //      <span class='roo-tpl-{name}'></span> ?????
16199         
16200         
16201         
16202         this.clearSelections();
16203         this.el.update("");
16204         var html = [];
16205         var records = this.store.getRange();
16206         if(records.length < 1) {
16207             
16208             // is this valid??  = should it render a template??
16209             
16210             this.el.update(this.emptyText);
16211             return;
16212         }
16213         var el = this.el;
16214         if (this.dataName) {
16215             this.el.update(t.apply(this.store.meta)); //????
16216             el = this.el.child('.roo-tpl-' + this.dataName);
16217         }
16218         
16219         for(var i = 0, len = records.length; i < len; i++){
16220             var data = this.prepareData(records[i].data, i, records[i]);
16221             this.fireEvent("preparedata", this, data, i, records[i]);
16222             
16223             var d = Roo.apply({}, data);
16224             
16225             if(this.tickable){
16226                 Roo.apply(d, {'roo-id' : Roo.id()});
16227                 
16228                 var _this = this;
16229             
16230                 Roo.each(this.parent.item, function(item){
16231                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16232                         return;
16233                     }
16234                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16235                 });
16236             }
16237             
16238             html[html.length] = Roo.util.Format.trim(
16239                 this.dataName ?
16240                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16241                     t.apply(d)
16242             );
16243         }
16244         
16245         
16246         
16247         el.update(html.join(""));
16248         this.nodes = el.dom.childNodes;
16249         this.updateIndexes(0);
16250     },
16251     
16252
16253     /**
16254      * Function to override to reformat the data that is sent to
16255      * the template for each node.
16256      * DEPRICATED - use the preparedata event handler.
16257      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16258      * a JSON object for an UpdateManager bound view).
16259      */
16260     prepareData : function(data, index, record)
16261     {
16262         this.fireEvent("preparedata", this, data, index, record);
16263         return data;
16264     },
16265
16266     onUpdate : function(ds, record){
16267         // Roo.log('on update');   
16268         this.clearSelections();
16269         var index = this.store.indexOf(record);
16270         var n = this.nodes[index];
16271         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16272         n.parentNode.removeChild(n);
16273         this.updateIndexes(index, index);
16274     },
16275
16276     
16277     
16278 // --------- FIXME     
16279     onAdd : function(ds, records, index)
16280     {
16281         //Roo.log(['on Add', ds, records, index] );        
16282         this.clearSelections();
16283         if(this.nodes.length == 0){
16284             this.refresh();
16285             return;
16286         }
16287         var n = this.nodes[index];
16288         for(var i = 0, len = records.length; i < len; i++){
16289             var d = this.prepareData(records[i].data, i, records[i]);
16290             if(n){
16291                 this.tpl.insertBefore(n, d);
16292             }else{
16293                 
16294                 this.tpl.append(this.el, d);
16295             }
16296         }
16297         this.updateIndexes(index);
16298     },
16299
16300     onRemove : function(ds, record, index){
16301        // Roo.log('onRemove');
16302         this.clearSelections();
16303         var el = this.dataName  ?
16304             this.el.child('.roo-tpl-' + this.dataName) :
16305             this.el; 
16306         
16307         el.dom.removeChild(this.nodes[index]);
16308         this.updateIndexes(index);
16309     },
16310
16311     /**
16312      * Refresh an individual node.
16313      * @param {Number} index
16314      */
16315     refreshNode : function(index){
16316         this.onUpdate(this.store, this.store.getAt(index));
16317     },
16318
16319     updateIndexes : function(startIndex, endIndex){
16320         var ns = this.nodes;
16321         startIndex = startIndex || 0;
16322         endIndex = endIndex || ns.length - 1;
16323         for(var i = startIndex; i <= endIndex; i++){
16324             ns[i].nodeIndex = i;
16325         }
16326     },
16327
16328     /**
16329      * Changes the data store this view uses and refresh the view.
16330      * @param {Store} store
16331      */
16332     setStore : function(store, initial){
16333         if(!initial && this.store){
16334             this.store.un("datachanged", this.refresh);
16335             this.store.un("add", this.onAdd);
16336             this.store.un("remove", this.onRemove);
16337             this.store.un("update", this.onUpdate);
16338             this.store.un("clear", this.refresh);
16339             this.store.un("beforeload", this.onBeforeLoad);
16340             this.store.un("load", this.onLoad);
16341             this.store.un("loadexception", this.onLoad);
16342         }
16343         if(store){
16344           
16345             store.on("datachanged", this.refresh, this);
16346             store.on("add", this.onAdd, this);
16347             store.on("remove", this.onRemove, this);
16348             store.on("update", this.onUpdate, this);
16349             store.on("clear", this.refresh, this);
16350             store.on("beforeload", this.onBeforeLoad, this);
16351             store.on("load", this.onLoad, this);
16352             store.on("loadexception", this.onLoad, this);
16353         }
16354         
16355         if(store){
16356             this.refresh();
16357         }
16358     },
16359     /**
16360      * onbeforeLoad - masks the loading area.
16361      *
16362      */
16363     onBeforeLoad : function(store,opts)
16364     {
16365          //Roo.log('onBeforeLoad');   
16366         if (!opts.add) {
16367             this.el.update("");
16368         }
16369         this.el.mask(this.mask ? this.mask : "Loading" ); 
16370     },
16371     onLoad : function ()
16372     {
16373         this.el.unmask();
16374     },
16375     
16376
16377     /**
16378      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16379      * @param {HTMLElement} node
16380      * @return {HTMLElement} The template node
16381      */
16382     findItemFromChild : function(node){
16383         var el = this.dataName  ?
16384             this.el.child('.roo-tpl-' + this.dataName,true) :
16385             this.el.dom; 
16386         
16387         if(!node || node.parentNode == el){
16388                     return node;
16389             }
16390             var p = node.parentNode;
16391             while(p && p != el){
16392             if(p.parentNode == el){
16393                 return p;
16394             }
16395             p = p.parentNode;
16396         }
16397             return null;
16398     },
16399
16400     /** @ignore */
16401     onClick : function(e){
16402         var item = this.findItemFromChild(e.getTarget());
16403         if(item){
16404             var index = this.indexOf(item);
16405             if(this.onItemClick(item, index, e) !== false){
16406                 this.fireEvent("click", this, index, item, e);
16407             }
16408         }else{
16409             this.clearSelections();
16410         }
16411     },
16412
16413     /** @ignore */
16414     onContextMenu : function(e){
16415         var item = this.findItemFromChild(e.getTarget());
16416         if(item){
16417             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16418         }
16419     },
16420
16421     /** @ignore */
16422     onDblClick : function(e){
16423         var item = this.findItemFromChild(e.getTarget());
16424         if(item){
16425             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16426         }
16427     },
16428
16429     onItemClick : function(item, index, e)
16430     {
16431         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16432             return false;
16433         }
16434         if (this.toggleSelect) {
16435             var m = this.isSelected(item) ? 'unselect' : 'select';
16436             //Roo.log(m);
16437             var _t = this;
16438             _t[m](item, true, false);
16439             return true;
16440         }
16441         if(this.multiSelect || this.singleSelect){
16442             if(this.multiSelect && e.shiftKey && this.lastSelection){
16443                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16444             }else{
16445                 this.select(item, this.multiSelect && e.ctrlKey);
16446                 this.lastSelection = item;
16447             }
16448             
16449             if(!this.tickable){
16450                 e.preventDefault();
16451             }
16452             
16453         }
16454         return true;
16455     },
16456
16457     /**
16458      * Get the number of selected nodes.
16459      * @return {Number}
16460      */
16461     getSelectionCount : function(){
16462         return this.selections.length;
16463     },
16464
16465     /**
16466      * Get the currently selected nodes.
16467      * @return {Array} An array of HTMLElements
16468      */
16469     getSelectedNodes : function(){
16470         return this.selections;
16471     },
16472
16473     /**
16474      * Get the indexes of the selected nodes.
16475      * @return {Array}
16476      */
16477     getSelectedIndexes : function(){
16478         var indexes = [], s = this.selections;
16479         for(var i = 0, len = s.length; i < len; i++){
16480             indexes.push(s[i].nodeIndex);
16481         }
16482         return indexes;
16483     },
16484
16485     /**
16486      * Clear all selections
16487      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16488      */
16489     clearSelections : function(suppressEvent){
16490         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16491             this.cmp.elements = this.selections;
16492             this.cmp.removeClass(this.selectedClass);
16493             this.selections = [];
16494             if(!suppressEvent){
16495                 this.fireEvent("selectionchange", this, this.selections);
16496             }
16497         }
16498     },
16499
16500     /**
16501      * Returns true if the passed node is selected
16502      * @param {HTMLElement/Number} node The node or node index
16503      * @return {Boolean}
16504      */
16505     isSelected : function(node){
16506         var s = this.selections;
16507         if(s.length < 1){
16508             return false;
16509         }
16510         node = this.getNode(node);
16511         return s.indexOf(node) !== -1;
16512     },
16513
16514     /**
16515      * Selects nodes.
16516      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16517      * @param {Boolean} keepExisting (optional) true to keep existing selections
16518      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16519      */
16520     select : function(nodeInfo, keepExisting, suppressEvent){
16521         if(nodeInfo instanceof Array){
16522             if(!keepExisting){
16523                 this.clearSelections(true);
16524             }
16525             for(var i = 0, len = nodeInfo.length; i < len; i++){
16526                 this.select(nodeInfo[i], true, true);
16527             }
16528             return;
16529         } 
16530         var node = this.getNode(nodeInfo);
16531         if(!node || this.isSelected(node)){
16532             return; // already selected.
16533         }
16534         if(!keepExisting){
16535             this.clearSelections(true);
16536         }
16537         
16538         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16539             Roo.fly(node).addClass(this.selectedClass);
16540             this.selections.push(node);
16541             if(!suppressEvent){
16542                 this.fireEvent("selectionchange", this, this.selections);
16543             }
16544         }
16545         
16546         
16547     },
16548       /**
16549      * Unselects nodes.
16550      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16551      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16553      */
16554     unselect : function(nodeInfo, keepExisting, suppressEvent)
16555     {
16556         if(nodeInfo instanceof Array){
16557             Roo.each(this.selections, function(s) {
16558                 this.unselect(s, nodeInfo);
16559             }, this);
16560             return;
16561         }
16562         var node = this.getNode(nodeInfo);
16563         if(!node || !this.isSelected(node)){
16564             //Roo.log("not selected");
16565             return; // not selected.
16566         }
16567         // fireevent???
16568         var ns = [];
16569         Roo.each(this.selections, function(s) {
16570             if (s == node ) {
16571                 Roo.fly(node).removeClass(this.selectedClass);
16572
16573                 return;
16574             }
16575             ns.push(s);
16576         },this);
16577         
16578         this.selections= ns;
16579         this.fireEvent("selectionchange", this, this.selections);
16580     },
16581
16582     /**
16583      * Gets a template node.
16584      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16585      * @return {HTMLElement} The node or null if it wasn't found
16586      */
16587     getNode : function(nodeInfo){
16588         if(typeof nodeInfo == "string"){
16589             return document.getElementById(nodeInfo);
16590         }else if(typeof nodeInfo == "number"){
16591             return this.nodes[nodeInfo];
16592         }
16593         return nodeInfo;
16594     },
16595
16596     /**
16597      * Gets a range template nodes.
16598      * @param {Number} startIndex
16599      * @param {Number} endIndex
16600      * @return {Array} An array of nodes
16601      */
16602     getNodes : function(start, end){
16603         var ns = this.nodes;
16604         start = start || 0;
16605         end = typeof end == "undefined" ? ns.length - 1 : end;
16606         var nodes = [];
16607         if(start <= end){
16608             for(var i = start; i <= end; i++){
16609                 nodes.push(ns[i]);
16610             }
16611         } else{
16612             for(var i = start; i >= end; i--){
16613                 nodes.push(ns[i]);
16614             }
16615         }
16616         return nodes;
16617     },
16618
16619     /**
16620      * Finds the index of the passed node
16621      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16622      * @return {Number} The index of the node or -1
16623      */
16624     indexOf : function(node){
16625         node = this.getNode(node);
16626         if(typeof node.nodeIndex == "number"){
16627             return node.nodeIndex;
16628         }
16629         var ns = this.nodes;
16630         for(var i = 0, len = ns.length; i < len; i++){
16631             if(ns[i] == node){
16632                 return i;
16633             }
16634         }
16635         return -1;
16636     }
16637 });
16638 /*
16639  * - LGPL
16640  *
16641  * based on jquery fullcalendar
16642  * 
16643  */
16644
16645 Roo.bootstrap = Roo.bootstrap || {};
16646 /**
16647  * @class Roo.bootstrap.Calendar
16648  * @extends Roo.bootstrap.Component
16649  * Bootstrap Calendar class
16650  * @cfg {Boolean} loadMask (true|false) default false
16651  * @cfg {Object} header generate the user specific header of the calendar, default false
16652
16653  * @constructor
16654  * Create a new Container
16655  * @param {Object} config The config object
16656  */
16657
16658
16659
16660 Roo.bootstrap.Calendar = function(config){
16661     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16662      this.addEvents({
16663         /**
16664              * @event select
16665              * Fires when a date is selected
16666              * @param {DatePicker} this
16667              * @param {Date} date The selected date
16668              */
16669         'select': true,
16670         /**
16671              * @event monthchange
16672              * Fires when the displayed month changes 
16673              * @param {DatePicker} this
16674              * @param {Date} date The selected month
16675              */
16676         'monthchange': true,
16677         /**
16678              * @event evententer
16679              * Fires when mouse over an event
16680              * @param {Calendar} this
16681              * @param {event} Event
16682              */
16683         'evententer': true,
16684         /**
16685              * @event eventleave
16686              * Fires when the mouse leaves an
16687              * @param {Calendar} this
16688              * @param {event}
16689              */
16690         'eventleave': true,
16691         /**
16692              * @event eventclick
16693              * Fires when the mouse click an
16694              * @param {Calendar} this
16695              * @param {event}
16696              */
16697         'eventclick': true
16698         
16699     });
16700
16701 };
16702
16703 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16704     
16705      /**
16706      * @cfg {Number} startDay
16707      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16708      */
16709     startDay : 0,
16710     
16711     loadMask : false,
16712     
16713     header : false,
16714       
16715     getAutoCreate : function(){
16716         
16717         
16718         var fc_button = function(name, corner, style, content ) {
16719             return Roo.apply({},{
16720                 tag : 'span',
16721                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16722                          (corner.length ?
16723                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16724                             ''
16725                         ),
16726                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16727                 unselectable: 'on'
16728             });
16729         };
16730         
16731         var header = {};
16732         
16733         if(!this.header){
16734             header = {
16735                 tag : 'table',
16736                 cls : 'fc-header',
16737                 style : 'width:100%',
16738                 cn : [
16739                     {
16740                         tag: 'tr',
16741                         cn : [
16742                             {
16743                                 tag : 'td',
16744                                 cls : 'fc-header-left',
16745                                 cn : [
16746                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16747                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16748                                     { tag: 'span', cls: 'fc-header-space' },
16749                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16750
16751
16752                                 ]
16753                             },
16754
16755                             {
16756                                 tag : 'td',
16757                                 cls : 'fc-header-center',
16758                                 cn : [
16759                                     {
16760                                         tag: 'span',
16761                                         cls: 'fc-header-title',
16762                                         cn : {
16763                                             tag: 'H2',
16764                                             html : 'month / year'
16765                                         }
16766                                     }
16767
16768                                 ]
16769                             },
16770                             {
16771                                 tag : 'td',
16772                                 cls : 'fc-header-right',
16773                                 cn : [
16774                               /*      fc_button('month', 'left', '', 'month' ),
16775                                     fc_button('week', '', '', 'week' ),
16776                                     fc_button('day', 'right', '', 'day' )
16777                                 */    
16778
16779                                 ]
16780                             }
16781
16782                         ]
16783                     }
16784                 ]
16785             };
16786         }
16787         
16788         header = this.header;
16789         
16790        
16791         var cal_heads = function() {
16792             var ret = [];
16793             // fixme - handle this.
16794             
16795             for (var i =0; i < Date.dayNames.length; i++) {
16796                 var d = Date.dayNames[i];
16797                 ret.push({
16798                     tag: 'th',
16799                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16800                     html : d.substring(0,3)
16801                 });
16802                 
16803             }
16804             ret[0].cls += ' fc-first';
16805             ret[6].cls += ' fc-last';
16806             return ret;
16807         };
16808         var cal_cell = function(n) {
16809             return  {
16810                 tag: 'td',
16811                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16812                 cn : [
16813                     {
16814                         cn : [
16815                             {
16816                                 cls: 'fc-day-number',
16817                                 html: 'D'
16818                             },
16819                             {
16820                                 cls: 'fc-day-content',
16821                              
16822                                 cn : [
16823                                      {
16824                                         style: 'position: relative;' // height: 17px;
16825                                     }
16826                                 ]
16827                             }
16828                             
16829                             
16830                         ]
16831                     }
16832                 ]
16833                 
16834             }
16835         };
16836         var cal_rows = function() {
16837             
16838             var ret = [];
16839             for (var r = 0; r < 6; r++) {
16840                 var row= {
16841                     tag : 'tr',
16842                     cls : 'fc-week',
16843                     cn : []
16844                 };
16845                 
16846                 for (var i =0; i < Date.dayNames.length; i++) {
16847                     var d = Date.dayNames[i];
16848                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16849
16850                 }
16851                 row.cn[0].cls+=' fc-first';
16852                 row.cn[0].cn[0].style = 'min-height:90px';
16853                 row.cn[6].cls+=' fc-last';
16854                 ret.push(row);
16855                 
16856             }
16857             ret[0].cls += ' fc-first';
16858             ret[4].cls += ' fc-prev-last';
16859             ret[5].cls += ' fc-last';
16860             return ret;
16861             
16862         };
16863         
16864         var cal_table = {
16865             tag: 'table',
16866             cls: 'fc-border-separate',
16867             style : 'width:100%',
16868             cellspacing  : 0,
16869             cn : [
16870                 { 
16871                     tag: 'thead',
16872                     cn : [
16873                         { 
16874                             tag: 'tr',
16875                             cls : 'fc-first fc-last',
16876                             cn : cal_heads()
16877                         }
16878                     ]
16879                 },
16880                 { 
16881                     tag: 'tbody',
16882                     cn : cal_rows()
16883                 }
16884                   
16885             ]
16886         };
16887          
16888          var cfg = {
16889             cls : 'fc fc-ltr',
16890             cn : [
16891                 header,
16892                 {
16893                     cls : 'fc-content',
16894                     style : "position: relative;",
16895                     cn : [
16896                         {
16897                             cls : 'fc-view fc-view-month fc-grid',
16898                             style : 'position: relative',
16899                             unselectable : 'on',
16900                             cn : [
16901                                 {
16902                                     cls : 'fc-event-container',
16903                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16904                                 },
16905                                 cal_table
16906                             ]
16907                         }
16908                     ]
16909     
16910                 }
16911            ] 
16912             
16913         };
16914         
16915          
16916         
16917         return cfg;
16918     },
16919     
16920     
16921     initEvents : function()
16922     {
16923         if(!this.store){
16924             throw "can not find store for calendar";
16925         }
16926         
16927         var mark = {
16928             tag: "div",
16929             cls:"x-dlg-mask",
16930             style: "text-align:center",
16931             cn: [
16932                 {
16933                     tag: "div",
16934                     style: "background-color:white;width:50%;margin:250 auto",
16935                     cn: [
16936                         {
16937                             tag: "img",
16938                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16939                         },
16940                         {
16941                             tag: "span",
16942                             html: "Loading"
16943                         }
16944                         
16945                     ]
16946                 }
16947             ]
16948         };
16949         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16950         
16951         var size = this.el.select('.fc-content', true).first().getSize();
16952         this.maskEl.setSize(size.width, size.height);
16953         this.maskEl.enableDisplayMode("block");
16954         if(!this.loadMask){
16955             this.maskEl.hide();
16956         }
16957         
16958         this.store = Roo.factory(this.store, Roo.data);
16959         this.store.on('load', this.onLoad, this);
16960         this.store.on('beforeload', this.onBeforeLoad, this);
16961         
16962         this.resize();
16963         
16964         this.cells = this.el.select('.fc-day',true);
16965         //Roo.log(this.cells);
16966         this.textNodes = this.el.query('.fc-day-number');
16967         this.cells.addClassOnOver('fc-state-hover');
16968         
16969         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16970         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16971         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16972         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16973         
16974         this.on('monthchange', this.onMonthChange, this);
16975         
16976         this.update(new Date().clearTime());
16977     },
16978     
16979     resize : function() {
16980         var sz  = this.el.getSize();
16981         
16982         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16983         this.el.select('.fc-day-content div',true).setHeight(34);
16984     },
16985     
16986     
16987     // private
16988     showPrevMonth : function(e){
16989         this.update(this.activeDate.add("mo", -1));
16990     },
16991     showToday : function(e){
16992         this.update(new Date().clearTime());
16993     },
16994     // private
16995     showNextMonth : function(e){
16996         this.update(this.activeDate.add("mo", 1));
16997     },
16998
16999     // private
17000     showPrevYear : function(){
17001         this.update(this.activeDate.add("y", -1));
17002     },
17003
17004     // private
17005     showNextYear : function(){
17006         this.update(this.activeDate.add("y", 1));
17007     },
17008
17009     
17010    // private
17011     update : function(date)
17012     {
17013         var vd = this.activeDate;
17014         this.activeDate = date;
17015 //        if(vd && this.el){
17016 //            var t = date.getTime();
17017 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17018 //                Roo.log('using add remove');
17019 //                
17020 //                this.fireEvent('monthchange', this, date);
17021 //                
17022 //                this.cells.removeClass("fc-state-highlight");
17023 //                this.cells.each(function(c){
17024 //                   if(c.dateValue == t){
17025 //                       c.addClass("fc-state-highlight");
17026 //                       setTimeout(function(){
17027 //                            try{c.dom.firstChild.focus();}catch(e){}
17028 //                       }, 50);
17029 //                       return false;
17030 //                   }
17031 //                   return true;
17032 //                });
17033 //                return;
17034 //            }
17035 //        }
17036         
17037         var days = date.getDaysInMonth();
17038         
17039         var firstOfMonth = date.getFirstDateOfMonth();
17040         var startingPos = firstOfMonth.getDay()-this.startDay;
17041         
17042         if(startingPos < this.startDay){
17043             startingPos += 7;
17044         }
17045         
17046         var pm = date.add(Date.MONTH, -1);
17047         var prevStart = pm.getDaysInMonth()-startingPos;
17048 //        
17049         this.cells = this.el.select('.fc-day',true);
17050         this.textNodes = this.el.query('.fc-day-number');
17051         this.cells.addClassOnOver('fc-state-hover');
17052         
17053         var cells = this.cells.elements;
17054         var textEls = this.textNodes;
17055         
17056         Roo.each(cells, function(cell){
17057             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17058         });
17059         
17060         days += startingPos;
17061
17062         // convert everything to numbers so it's fast
17063         var day = 86400000;
17064         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17065         //Roo.log(d);
17066         //Roo.log(pm);
17067         //Roo.log(prevStart);
17068         
17069         var today = new Date().clearTime().getTime();
17070         var sel = date.clearTime().getTime();
17071         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17072         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17073         var ddMatch = this.disabledDatesRE;
17074         var ddText = this.disabledDatesText;
17075         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17076         var ddaysText = this.disabledDaysText;
17077         var format = this.format;
17078         
17079         var setCellClass = function(cal, cell){
17080             cell.row = 0;
17081             cell.events = [];
17082             cell.more = [];
17083             //Roo.log('set Cell Class');
17084             cell.title = "";
17085             var t = d.getTime();
17086             
17087             //Roo.log(d);
17088             
17089             cell.dateValue = t;
17090             if(t == today){
17091                 cell.className += " fc-today";
17092                 cell.className += " fc-state-highlight";
17093                 cell.title = cal.todayText;
17094             }
17095             if(t == sel){
17096                 // disable highlight in other month..
17097                 //cell.className += " fc-state-highlight";
17098                 
17099             }
17100             // disabling
17101             if(t < min) {
17102                 cell.className = " fc-state-disabled";
17103                 cell.title = cal.minText;
17104                 return;
17105             }
17106             if(t > max) {
17107                 cell.className = " fc-state-disabled";
17108                 cell.title = cal.maxText;
17109                 return;
17110             }
17111             if(ddays){
17112                 if(ddays.indexOf(d.getDay()) != -1){
17113                     cell.title = ddaysText;
17114                     cell.className = " fc-state-disabled";
17115                 }
17116             }
17117             if(ddMatch && format){
17118                 var fvalue = d.dateFormat(format);
17119                 if(ddMatch.test(fvalue)){
17120                     cell.title = ddText.replace("%0", fvalue);
17121                     cell.className = " fc-state-disabled";
17122                 }
17123             }
17124             
17125             if (!cell.initialClassName) {
17126                 cell.initialClassName = cell.dom.className;
17127             }
17128             
17129             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17130         };
17131
17132         var i = 0;
17133         
17134         for(; i < startingPos; i++) {
17135             textEls[i].innerHTML = (++prevStart);
17136             d.setDate(d.getDate()+1);
17137             
17138             cells[i].className = "fc-past fc-other-month";
17139             setCellClass(this, cells[i]);
17140         }
17141         
17142         var intDay = 0;
17143         
17144         for(; i < days; i++){
17145             intDay = i - startingPos + 1;
17146             textEls[i].innerHTML = (intDay);
17147             d.setDate(d.getDate()+1);
17148             
17149             cells[i].className = ''; // "x-date-active";
17150             setCellClass(this, cells[i]);
17151         }
17152         var extraDays = 0;
17153         
17154         for(; i < 42; i++) {
17155             textEls[i].innerHTML = (++extraDays);
17156             d.setDate(d.getDate()+1);
17157             
17158             cells[i].className = "fc-future fc-other-month";
17159             setCellClass(this, cells[i]);
17160         }
17161         
17162         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17163         
17164         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17165         
17166         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17167         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17168         
17169         if(totalRows != 6){
17170             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17171             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17172         }
17173         
17174         this.fireEvent('monthchange', this, date);
17175         
17176         
17177         /*
17178         if(!this.internalRender){
17179             var main = this.el.dom.firstChild;
17180             var w = main.offsetWidth;
17181             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17182             Roo.fly(main).setWidth(w);
17183             this.internalRender = true;
17184             // opera does not respect the auto grow header center column
17185             // then, after it gets a width opera refuses to recalculate
17186             // without a second pass
17187             if(Roo.isOpera && !this.secondPass){
17188                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17189                 this.secondPass = true;
17190                 this.update.defer(10, this, [date]);
17191             }
17192         }
17193         */
17194         
17195     },
17196     
17197     findCell : function(dt) {
17198         dt = dt.clearTime().getTime();
17199         var ret = false;
17200         this.cells.each(function(c){
17201             //Roo.log("check " +c.dateValue + '?=' + dt);
17202             if(c.dateValue == dt){
17203                 ret = c;
17204                 return false;
17205             }
17206             return true;
17207         });
17208         
17209         return ret;
17210     },
17211     
17212     findCells : function(ev) {
17213         var s = ev.start.clone().clearTime().getTime();
17214        // Roo.log(s);
17215         var e= ev.end.clone().clearTime().getTime();
17216        // Roo.log(e);
17217         var ret = [];
17218         this.cells.each(function(c){
17219              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17220             
17221             if(c.dateValue > e){
17222                 return ;
17223             }
17224             if(c.dateValue < s){
17225                 return ;
17226             }
17227             ret.push(c);
17228         });
17229         
17230         return ret;    
17231     },
17232     
17233 //    findBestRow: function(cells)
17234 //    {
17235 //        var ret = 0;
17236 //        
17237 //        for (var i =0 ; i < cells.length;i++) {
17238 //            ret  = Math.max(cells[i].rows || 0,ret);
17239 //        }
17240 //        return ret;
17241 //        
17242 //    },
17243     
17244     
17245     addItem : function(ev)
17246     {
17247         // look for vertical location slot in
17248         var cells = this.findCells(ev);
17249         
17250 //        ev.row = this.findBestRow(cells);
17251         
17252         // work out the location.
17253         
17254         var crow = false;
17255         var rows = [];
17256         for(var i =0; i < cells.length; i++) {
17257             
17258             cells[i].row = cells[0].row;
17259             
17260             if(i == 0){
17261                 cells[i].row = cells[i].row + 1;
17262             }
17263             
17264             if (!crow) {
17265                 crow = {
17266                     start : cells[i],
17267                     end :  cells[i]
17268                 };
17269                 continue;
17270             }
17271             if (crow.start.getY() == cells[i].getY()) {
17272                 // on same row.
17273                 crow.end = cells[i];
17274                 continue;
17275             }
17276             // different row.
17277             rows.push(crow);
17278             crow = {
17279                 start: cells[i],
17280                 end : cells[i]
17281             };
17282             
17283         }
17284         
17285         rows.push(crow);
17286         ev.els = [];
17287         ev.rows = rows;
17288         ev.cells = cells;
17289         
17290         cells[0].events.push(ev);
17291         
17292         this.calevents.push(ev);
17293     },
17294     
17295     clearEvents: function() {
17296         
17297         if(!this.calevents){
17298             return;
17299         }
17300         
17301         Roo.each(this.cells.elements, function(c){
17302             c.row = 0;
17303             c.events = [];
17304             c.more = [];
17305         });
17306         
17307         Roo.each(this.calevents, function(e) {
17308             Roo.each(e.els, function(el) {
17309                 el.un('mouseenter' ,this.onEventEnter, this);
17310                 el.un('mouseleave' ,this.onEventLeave, this);
17311                 el.remove();
17312             },this);
17313         },this);
17314         
17315         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17316             e.remove();
17317         });
17318         
17319     },
17320     
17321     renderEvents: function()
17322     {   
17323         var _this = this;
17324         
17325         this.cells.each(function(c) {
17326             
17327             if(c.row < 5){
17328                 return;
17329             }
17330             
17331             var ev = c.events;
17332             
17333             var r = 4;
17334             if(c.row != c.events.length){
17335                 r = 4 - (4 - (c.row - c.events.length));
17336             }
17337             
17338             c.events = ev.slice(0, r);
17339             c.more = ev.slice(r);
17340             
17341             if(c.more.length && c.more.length == 1){
17342                 c.events.push(c.more.pop());
17343             }
17344             
17345             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17346             
17347         });
17348             
17349         this.cells.each(function(c) {
17350             
17351             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17352             
17353             
17354             for (var e = 0; e < c.events.length; e++){
17355                 var ev = c.events[e];
17356                 var rows = ev.rows;
17357                 
17358                 for(var i = 0; i < rows.length; i++) {
17359                 
17360                     // how many rows should it span..
17361
17362                     var  cfg = {
17363                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17364                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17365
17366                         unselectable : "on",
17367                         cn : [
17368                             {
17369                                 cls: 'fc-event-inner',
17370                                 cn : [
17371     //                                {
17372     //                                  tag:'span',
17373     //                                  cls: 'fc-event-time',
17374     //                                  html : cells.length > 1 ? '' : ev.time
17375     //                                },
17376                                     {
17377                                       tag:'span',
17378                                       cls: 'fc-event-title',
17379                                       html : String.format('{0}', ev.title)
17380                                     }
17381
17382
17383                                 ]
17384                             },
17385                             {
17386                                 cls: 'ui-resizable-handle ui-resizable-e',
17387                                 html : '&nbsp;&nbsp;&nbsp'
17388                             }
17389
17390                         ]
17391                     };
17392
17393                     if (i == 0) {
17394                         cfg.cls += ' fc-event-start';
17395                     }
17396                     if ((i+1) == rows.length) {
17397                         cfg.cls += ' fc-event-end';
17398                     }
17399
17400                     var ctr = _this.el.select('.fc-event-container',true).first();
17401                     var cg = ctr.createChild(cfg);
17402
17403                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17404                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17405
17406                     var r = (c.more.length) ? 1 : 0;
17407                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17408                     cg.setWidth(ebox.right - sbox.x -2);
17409
17410                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17411                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17412                     cg.on('click', _this.onEventClick, _this, ev);
17413
17414                     ev.els.push(cg);
17415                     
17416                 }
17417                 
17418             }
17419             
17420             
17421             if(c.more.length){
17422                 var  cfg = {
17423                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17424                     style : 'position: absolute',
17425                     unselectable : "on",
17426                     cn : [
17427                         {
17428                             cls: 'fc-event-inner',
17429                             cn : [
17430                                 {
17431                                   tag:'span',
17432                                   cls: 'fc-event-title',
17433                                   html : 'More'
17434                                 }
17435
17436
17437                             ]
17438                         },
17439                         {
17440                             cls: 'ui-resizable-handle ui-resizable-e',
17441                             html : '&nbsp;&nbsp;&nbsp'
17442                         }
17443
17444                     ]
17445                 };
17446
17447                 var ctr = _this.el.select('.fc-event-container',true).first();
17448                 var cg = ctr.createChild(cfg);
17449
17450                 var sbox = c.select('.fc-day-content',true).first().getBox();
17451                 var ebox = c.select('.fc-day-content',true).first().getBox();
17452                 //Roo.log(cg);
17453                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17454                 cg.setWidth(ebox.right - sbox.x -2);
17455
17456                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17457                 
17458             }
17459             
17460         });
17461         
17462         
17463         
17464     },
17465     
17466     onEventEnter: function (e, el,event,d) {
17467         this.fireEvent('evententer', this, el, event);
17468     },
17469     
17470     onEventLeave: function (e, el,event,d) {
17471         this.fireEvent('eventleave', this, el, event);
17472     },
17473     
17474     onEventClick: function (e, el,event,d) {
17475         this.fireEvent('eventclick', this, el, event);
17476     },
17477     
17478     onMonthChange: function () {
17479         this.store.load();
17480     },
17481     
17482     onMoreEventClick: function(e, el, more)
17483     {
17484         var _this = this;
17485         
17486         this.calpopover.placement = 'right';
17487         this.calpopover.setTitle('More');
17488         
17489         this.calpopover.setContent('');
17490         
17491         var ctr = this.calpopover.el.select('.popover-content', true).first();
17492         
17493         Roo.each(more, function(m){
17494             var cfg = {
17495                 cls : 'fc-event-hori fc-event-draggable',
17496                 html : m.title
17497             };
17498             var cg = ctr.createChild(cfg);
17499             
17500             cg.on('click', _this.onEventClick, _this, m);
17501         });
17502         
17503         this.calpopover.show(el);
17504         
17505         
17506     },
17507     
17508     onLoad: function () 
17509     {   
17510         this.calevents = [];
17511         var cal = this;
17512         
17513         if(this.store.getCount() > 0){
17514             this.store.data.each(function(d){
17515                cal.addItem({
17516                     id : d.data.id,
17517                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17518                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17519                     time : d.data.start_time,
17520                     title : d.data.title,
17521                     description : d.data.description,
17522                     venue : d.data.venue
17523                 });
17524             });
17525         }
17526         
17527         this.renderEvents();
17528         
17529         if(this.calevents.length && this.loadMask){
17530             this.maskEl.hide();
17531         }
17532     },
17533     
17534     onBeforeLoad: function()
17535     {
17536         this.clearEvents();
17537         if(this.loadMask){
17538             this.maskEl.show();
17539         }
17540     }
17541 });
17542
17543  
17544  /*
17545  * - LGPL
17546  *
17547  * element
17548  * 
17549  */
17550
17551 /**
17552  * @class Roo.bootstrap.Popover
17553  * @extends Roo.bootstrap.Component
17554  * Bootstrap Popover class
17555  * @cfg {String} html contents of the popover   (or false to use children..)
17556  * @cfg {String} title of popover (or false to hide)
17557  * @cfg {String} placement how it is placed
17558  * @cfg {String} trigger click || hover (or false to trigger manually)
17559  * @cfg {String} over what (parent or false to trigger manually.)
17560  * @cfg {Number} delay - delay before showing
17561  
17562  * @constructor
17563  * Create a new Popover
17564  * @param {Object} config The config object
17565  */
17566
17567 Roo.bootstrap.Popover = function(config){
17568     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17569     
17570     this.addEvents({
17571         // raw events
17572          /**
17573          * @event show
17574          * After the popover show
17575          * 
17576          * @param {Roo.bootstrap.Popover} this
17577          */
17578         "show" : true,
17579         /**
17580          * @event hide
17581          * After the popover hide
17582          * 
17583          * @param {Roo.bootstrap.Popover} this
17584          */
17585         "hide" : true
17586     });
17587 };
17588
17589 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17590     
17591     title: 'Fill in a title',
17592     html: false,
17593     
17594     placement : 'right',
17595     trigger : 'hover', // hover
17596     
17597     delay : 0,
17598     
17599     over: 'parent',
17600     
17601     can_build_overlaid : false,
17602     
17603     getChildContainer : function()
17604     {
17605         return this.el.select('.popover-content',true).first();
17606     },
17607     
17608     getAutoCreate : function(){
17609          
17610         var cfg = {
17611            cls : 'popover roo-dynamic',
17612            style: 'display:block',
17613            cn : [
17614                 {
17615                     cls : 'arrow'
17616                 },
17617                 {
17618                     cls : 'popover-inner',
17619                     cn : [
17620                         {
17621                             tag: 'h3',
17622                             cls: 'popover-title',
17623                             html : this.title
17624                         },
17625                         {
17626                             cls : 'popover-content',
17627                             html : this.html
17628                         }
17629                     ]
17630                     
17631                 }
17632            ]
17633         };
17634         
17635         return cfg;
17636     },
17637     setTitle: function(str)
17638     {
17639         this.title = str;
17640         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17641     },
17642     setContent: function(str)
17643     {
17644         this.html = str;
17645         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17646     },
17647     // as it get's added to the bottom of the page.
17648     onRender : function(ct, position)
17649     {
17650         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17651         if(!this.el){
17652             var cfg = Roo.apply({},  this.getAutoCreate());
17653             cfg.id = Roo.id();
17654             
17655             if (this.cls) {
17656                 cfg.cls += ' ' + this.cls;
17657             }
17658             if (this.style) {
17659                 cfg.style = this.style;
17660             }
17661             //Roo.log("adding to ");
17662             this.el = Roo.get(document.body).createChild(cfg, position);
17663 //            Roo.log(this.el);
17664         }
17665         this.initEvents();
17666     },
17667     
17668     initEvents : function()
17669     {
17670         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17671         this.el.enableDisplayMode('block');
17672         this.el.hide();
17673         if (this.over === false) {
17674             return; 
17675         }
17676         if (this.triggers === false) {
17677             return;
17678         }
17679         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17680         var triggers = this.trigger ? this.trigger.split(' ') : [];
17681         Roo.each(triggers, function(trigger) {
17682         
17683             if (trigger == 'click') {
17684                 on_el.on('click', this.toggle, this);
17685             } else if (trigger != 'manual') {
17686                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17687                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17688       
17689                 on_el.on(eventIn  ,this.enter, this);
17690                 on_el.on(eventOut, this.leave, this);
17691             }
17692         }, this);
17693         
17694     },
17695     
17696     
17697     // private
17698     timeout : null,
17699     hoverState : null,
17700     
17701     toggle : function () {
17702         this.hoverState == 'in' ? this.leave() : this.enter();
17703     },
17704     
17705     enter : function () {
17706         
17707         clearTimeout(this.timeout);
17708     
17709         this.hoverState = 'in';
17710     
17711         if (!this.delay || !this.delay.show) {
17712             this.show();
17713             return;
17714         }
17715         var _t = this;
17716         this.timeout = setTimeout(function () {
17717             if (_t.hoverState == 'in') {
17718                 _t.show();
17719             }
17720         }, this.delay.show)
17721     },
17722     
17723     leave : function() {
17724         clearTimeout(this.timeout);
17725     
17726         this.hoverState = 'out';
17727     
17728         if (!this.delay || !this.delay.hide) {
17729             this.hide();
17730             return;
17731         }
17732         var _t = this;
17733         this.timeout = setTimeout(function () {
17734             if (_t.hoverState == 'out') {
17735                 _t.hide();
17736             }
17737         }, this.delay.hide)
17738     },
17739     
17740     show : function (on_el)
17741     {
17742         if (!on_el) {
17743             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17744         }
17745         
17746         // set content.
17747         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17748         if (this.html !== false) {
17749             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17750         }
17751         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17752         if (!this.title.length) {
17753             this.el.select('.popover-title',true).hide();
17754         }
17755         
17756         var placement = typeof this.placement == 'function' ?
17757             this.placement.call(this, this.el, on_el) :
17758             this.placement;
17759             
17760         var autoToken = /\s?auto?\s?/i;
17761         var autoPlace = autoToken.test(placement);
17762         if (autoPlace) {
17763             placement = placement.replace(autoToken, '') || 'top';
17764         }
17765         
17766         //this.el.detach()
17767         //this.el.setXY([0,0]);
17768         this.el.show();
17769         this.el.dom.style.display='block';
17770         this.el.addClass(placement);
17771         
17772         //this.el.appendTo(on_el);
17773         
17774         var p = this.getPosition();
17775         var box = this.el.getBox();
17776         
17777         if (autoPlace) {
17778             // fixme..
17779         }
17780         var align = Roo.bootstrap.Popover.alignment[placement];
17781         
17782 //        Roo.log(align);
17783         this.el.alignTo(on_el, align[0],align[1]);
17784         //var arrow = this.el.select('.arrow',true).first();
17785         //arrow.set(align[2], 
17786         
17787         this.el.addClass('in');
17788         
17789         
17790         if (this.el.hasClass('fade')) {
17791             // fade it?
17792         }
17793         
17794         this.hoverState = 'in';
17795         
17796         this.fireEvent('show', this);
17797         
17798     },
17799     hide : function()
17800     {
17801         this.el.setXY([0,0]);
17802         this.el.removeClass('in');
17803         this.el.hide();
17804         this.hoverState = null;
17805         
17806         this.fireEvent('hide', this);
17807     }
17808     
17809 });
17810
17811 Roo.bootstrap.Popover.alignment = {
17812     'left' : ['r-l', [-10,0], 'right'],
17813     'right' : ['l-r', [10,0], 'left'],
17814     'bottom' : ['t-b', [0,10], 'top'],
17815     'top' : [ 'b-t', [0,-10], 'bottom']
17816 };
17817
17818  /*
17819  * - LGPL
17820  *
17821  * Progress
17822  * 
17823  */
17824
17825 /**
17826  * @class Roo.bootstrap.Progress
17827  * @extends Roo.bootstrap.Component
17828  * Bootstrap Progress class
17829  * @cfg {Boolean} striped striped of the progress bar
17830  * @cfg {Boolean} active animated of the progress bar
17831  * 
17832  * 
17833  * @constructor
17834  * Create a new Progress
17835  * @param {Object} config The config object
17836  */
17837
17838 Roo.bootstrap.Progress = function(config){
17839     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17840 };
17841
17842 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17843     
17844     striped : false,
17845     active: false,
17846     
17847     getAutoCreate : function(){
17848         var cfg = {
17849             tag: 'div',
17850             cls: 'progress'
17851         };
17852         
17853         
17854         if(this.striped){
17855             cfg.cls += ' progress-striped';
17856         }
17857       
17858         if(this.active){
17859             cfg.cls += ' active';
17860         }
17861         
17862         
17863         return cfg;
17864     }
17865    
17866 });
17867
17868  
17869
17870  /*
17871  * - LGPL
17872  *
17873  * ProgressBar
17874  * 
17875  */
17876
17877 /**
17878  * @class Roo.bootstrap.ProgressBar
17879  * @extends Roo.bootstrap.Component
17880  * Bootstrap ProgressBar class
17881  * @cfg {Number} aria_valuenow aria-value now
17882  * @cfg {Number} aria_valuemin aria-value min
17883  * @cfg {Number} aria_valuemax aria-value max
17884  * @cfg {String} label label for the progress bar
17885  * @cfg {String} panel (success | info | warning | danger )
17886  * @cfg {String} role role of the progress bar
17887  * @cfg {String} sr_only text
17888  * 
17889  * 
17890  * @constructor
17891  * Create a new ProgressBar
17892  * @param {Object} config The config object
17893  */
17894
17895 Roo.bootstrap.ProgressBar = function(config){
17896     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17897 };
17898
17899 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17900     
17901     aria_valuenow : 0,
17902     aria_valuemin : 0,
17903     aria_valuemax : 100,
17904     label : false,
17905     panel : false,
17906     role : false,
17907     sr_only: false,
17908     
17909     getAutoCreate : function()
17910     {
17911         
17912         var cfg = {
17913             tag: 'div',
17914             cls: 'progress-bar',
17915             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17916         };
17917         
17918         if(this.sr_only){
17919             cfg.cn = {
17920                 tag: 'span',
17921                 cls: 'sr-only',
17922                 html: this.sr_only
17923             }
17924         }
17925         
17926         if(this.role){
17927             cfg.role = this.role;
17928         }
17929         
17930         if(this.aria_valuenow){
17931             cfg['aria-valuenow'] = this.aria_valuenow;
17932         }
17933         
17934         if(this.aria_valuemin){
17935             cfg['aria-valuemin'] = this.aria_valuemin;
17936         }
17937         
17938         if(this.aria_valuemax){
17939             cfg['aria-valuemax'] = this.aria_valuemax;
17940         }
17941         
17942         if(this.label && !this.sr_only){
17943             cfg.html = this.label;
17944         }
17945         
17946         if(this.panel){
17947             cfg.cls += ' progress-bar-' + this.panel;
17948         }
17949         
17950         return cfg;
17951     },
17952     
17953     update : function(aria_valuenow)
17954     {
17955         this.aria_valuenow = aria_valuenow;
17956         
17957         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17958     }
17959    
17960 });
17961
17962  
17963
17964  /*
17965  * - LGPL
17966  *
17967  * column
17968  * 
17969  */
17970
17971 /**
17972  * @class Roo.bootstrap.TabGroup
17973  * @extends Roo.bootstrap.Column
17974  * Bootstrap Column class
17975  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17976  * @cfg {Boolean} carousel true to make the group behave like a carousel
17977  * @cfg {Boolean} bullets show bullets for the panels
17978  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17979  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17980  * @cfg {Boolean} showarrow (true|false) show arrow default true
17981  * 
17982  * @constructor
17983  * Create a new TabGroup
17984  * @param {Object} config The config object
17985  */
17986
17987 Roo.bootstrap.TabGroup = function(config){
17988     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17989     if (!this.navId) {
17990         this.navId = Roo.id();
17991     }
17992     this.tabs = [];
17993     Roo.bootstrap.TabGroup.register(this);
17994     
17995 };
17996
17997 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17998     
17999     carousel : false,
18000     transition : false,
18001     bullets : 0,
18002     timer : 0,
18003     autoslide : false,
18004     slideFn : false,
18005     slideOnTouch : false,
18006     showarrow : true,
18007     
18008     getAutoCreate : function()
18009     {
18010         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18011         
18012         cfg.cls += ' tab-content';
18013         
18014         if (this.carousel) {
18015             cfg.cls += ' carousel slide';
18016             
18017             cfg.cn = [{
18018                cls : 'carousel-inner',
18019                cn : []
18020             }];
18021         
18022             if(this.bullets  && !Roo.isTouch){
18023                 
18024                 var bullets = {
18025                     cls : 'carousel-bullets',
18026                     cn : []
18027                 };
18028                
18029                 if(this.bullets_cls){
18030                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18031                 }
18032                 
18033                 bullets.cn.push({
18034                     cls : 'clear'
18035                 });
18036                 
18037                 cfg.cn[0].cn.push(bullets);
18038             }
18039             
18040             if(this.showarrow){
18041                 cfg.cn[0].cn.push({
18042                     tag : 'div',
18043                     class : 'carousel-arrow',
18044                     cn : [
18045                         {
18046                             tag : 'div',
18047                             class : 'carousel-prev',
18048                             cn : [
18049                                 {
18050                                     tag : 'i',
18051                                     class : 'fa fa-chevron-left'
18052                                 }
18053                             ]
18054                         },
18055                         {
18056                             tag : 'div',
18057                             class : 'carousel-next',
18058                             cn : [
18059                                 {
18060                                     tag : 'i',
18061                                     class : 'fa fa-chevron-right'
18062                                 }
18063                             ]
18064                         }
18065                     ]
18066                 });
18067             }
18068             
18069         }
18070         
18071         return cfg;
18072     },
18073     
18074     initEvents:  function()
18075     {
18076 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18077 //            this.el.on("touchstart", this.onTouchStart, this);
18078 //        }
18079         
18080         if(this.autoslide){
18081             var _this = this;
18082             
18083             this.slideFn = window.setInterval(function() {
18084                 _this.showPanelNext();
18085             }, this.timer);
18086         }
18087         
18088         if(this.showarrow){
18089             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18090             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18091         }
18092         
18093         
18094     },
18095     
18096 //    onTouchStart : function(e, el, o)
18097 //    {
18098 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18099 //            return;
18100 //        }
18101 //        
18102 //        this.showPanelNext();
18103 //    },
18104     
18105     
18106     getChildContainer : function()
18107     {
18108         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18109     },
18110     
18111     /**
18112     * register a Navigation item
18113     * @param {Roo.bootstrap.NavItem} the navitem to add
18114     */
18115     register : function(item)
18116     {
18117         this.tabs.push( item);
18118         item.navId = this.navId; // not really needed..
18119         this.addBullet();
18120     
18121     },
18122     
18123     getActivePanel : function()
18124     {
18125         var r = false;
18126         Roo.each(this.tabs, function(t) {
18127             if (t.active) {
18128                 r = t;
18129                 return false;
18130             }
18131             return null;
18132         });
18133         return r;
18134         
18135     },
18136     getPanelByName : function(n)
18137     {
18138         var r = false;
18139         Roo.each(this.tabs, function(t) {
18140             if (t.tabId == n) {
18141                 r = t;
18142                 return false;
18143             }
18144             return null;
18145         });
18146         return r;
18147     },
18148     indexOfPanel : function(p)
18149     {
18150         var r = false;
18151         Roo.each(this.tabs, function(t,i) {
18152             if (t.tabId == p.tabId) {
18153                 r = i;
18154                 return false;
18155             }
18156             return null;
18157         });
18158         return r;
18159     },
18160     /**
18161      * show a specific panel
18162      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18163      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18164      */
18165     showPanel : function (pan)
18166     {
18167         if(this.transition || typeof(pan) == 'undefined'){
18168             Roo.log("waiting for the transitionend");
18169             return;
18170         }
18171         
18172         if (typeof(pan) == 'number') {
18173             pan = this.tabs[pan];
18174         }
18175         
18176         if (typeof(pan) == 'string') {
18177             pan = this.getPanelByName(pan);
18178         }
18179         
18180         var cur = this.getActivePanel();
18181         
18182         if(!pan || !cur){
18183             Roo.log('pan or acitve pan is undefined');
18184             return false;
18185         }
18186         
18187         if (pan.tabId == this.getActivePanel().tabId) {
18188             return true;
18189         }
18190         
18191         if (false === cur.fireEvent('beforedeactivate')) {
18192             return false;
18193         }
18194         
18195         if(this.bullets > 0 && !Roo.isTouch){
18196             this.setActiveBullet(this.indexOfPanel(pan));
18197         }
18198         
18199         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18200             
18201             this.transition = true;
18202             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18203             var lr = dir == 'next' ? 'left' : 'right';
18204             pan.el.addClass(dir); // or prev
18205             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18206             cur.el.addClass(lr); // or right
18207             pan.el.addClass(lr);
18208             
18209             var _this = this;
18210             cur.el.on('transitionend', function() {
18211                 Roo.log("trans end?");
18212                 
18213                 pan.el.removeClass([lr,dir]);
18214                 pan.setActive(true);
18215                 
18216                 cur.el.removeClass([lr]);
18217                 cur.setActive(false);
18218                 
18219                 _this.transition = false;
18220                 
18221             }, this, { single:  true } );
18222             
18223             return true;
18224         }
18225         
18226         cur.setActive(false);
18227         pan.setActive(true);
18228         
18229         return true;
18230         
18231     },
18232     showPanelNext : function()
18233     {
18234         var i = this.indexOfPanel(this.getActivePanel());
18235         
18236         if (i >= this.tabs.length - 1 && !this.autoslide) {
18237             return;
18238         }
18239         
18240         if (i >= this.tabs.length - 1 && this.autoslide) {
18241             i = -1;
18242         }
18243         
18244         this.showPanel(this.tabs[i+1]);
18245     },
18246     
18247     showPanelPrev : function()
18248     {
18249         var i = this.indexOfPanel(this.getActivePanel());
18250         
18251         if (i  < 1 && !this.autoslide) {
18252             return;
18253         }
18254         
18255         if (i < 1 && this.autoslide) {
18256             i = this.tabs.length;
18257         }
18258         
18259         this.showPanel(this.tabs[i-1]);
18260     },
18261     
18262     
18263     addBullet: function()
18264     {
18265         if(!this.bullets || Roo.isTouch){
18266             return;
18267         }
18268         var ctr = this.el.select('.carousel-bullets',true).first();
18269         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18270         var bullet = ctr.createChild({
18271             cls : 'bullet bullet-' + i
18272         },ctr.dom.lastChild);
18273         
18274         
18275         var _this = this;
18276         
18277         bullet.on('click', (function(e, el, o, ii, t){
18278
18279             e.preventDefault();
18280
18281             this.showPanel(ii);
18282
18283             if(this.autoslide && this.slideFn){
18284                 clearInterval(this.slideFn);
18285                 this.slideFn = window.setInterval(function() {
18286                     _this.showPanelNext();
18287                 }, this.timer);
18288             }
18289
18290         }).createDelegate(this, [i, bullet], true));
18291                 
18292         
18293     },
18294      
18295     setActiveBullet : function(i)
18296     {
18297         if(Roo.isTouch){
18298             return;
18299         }
18300         
18301         Roo.each(this.el.select('.bullet', true).elements, function(el){
18302             el.removeClass('selected');
18303         });
18304
18305         var bullet = this.el.select('.bullet-' + i, true).first();
18306         
18307         if(!bullet){
18308             return;
18309         }
18310         
18311         bullet.addClass('selected');
18312     }
18313     
18314     
18315   
18316 });
18317
18318  
18319
18320  
18321  
18322 Roo.apply(Roo.bootstrap.TabGroup, {
18323     
18324     groups: {},
18325      /**
18326     * register a Navigation Group
18327     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18328     */
18329     register : function(navgrp)
18330     {
18331         this.groups[navgrp.navId] = navgrp;
18332         
18333     },
18334     /**
18335     * fetch a Navigation Group based on the navigation ID
18336     * if one does not exist , it will get created.
18337     * @param {string} the navgroup to add
18338     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18339     */
18340     get: function(navId) {
18341         if (typeof(this.groups[navId]) == 'undefined') {
18342             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18343         }
18344         return this.groups[navId] ;
18345     }
18346     
18347     
18348     
18349 });
18350
18351  /*
18352  * - LGPL
18353  *
18354  * TabPanel
18355  * 
18356  */
18357
18358 /**
18359  * @class Roo.bootstrap.TabPanel
18360  * @extends Roo.bootstrap.Component
18361  * Bootstrap TabPanel class
18362  * @cfg {Boolean} active panel active
18363  * @cfg {String} html panel content
18364  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18365  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18366  * @cfg {String} href click to link..
18367  * 
18368  * 
18369  * @constructor
18370  * Create a new TabPanel
18371  * @param {Object} config The config object
18372  */
18373
18374 Roo.bootstrap.TabPanel = function(config){
18375     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18376     this.addEvents({
18377         /**
18378              * @event changed
18379              * Fires when the active status changes
18380              * @param {Roo.bootstrap.TabPanel} this
18381              * @param {Boolean} state the new state
18382             
18383          */
18384         'changed': true,
18385         /**
18386              * @event beforedeactivate
18387              * Fires before a tab is de-activated - can be used to do validation on a form.
18388              * @param {Roo.bootstrap.TabPanel} this
18389              * @return {Boolean} false if there is an error
18390             
18391          */
18392         'beforedeactivate': true
18393      });
18394     
18395     this.tabId = this.tabId || Roo.id();
18396   
18397 };
18398
18399 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18400     
18401     active: false,
18402     html: false,
18403     tabId: false,
18404     navId : false,
18405     href : '',
18406     
18407     getAutoCreate : function(){
18408         var cfg = {
18409             tag: 'div',
18410             // item is needed for carousel - not sure if it has any effect otherwise
18411             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18412             html: this.html || ''
18413         };
18414         
18415         if(this.active){
18416             cfg.cls += ' active';
18417         }
18418         
18419         if(this.tabId){
18420             cfg.tabId = this.tabId;
18421         }
18422         
18423         
18424         return cfg;
18425     },
18426     
18427     initEvents:  function()
18428     {
18429         var p = this.parent();
18430         
18431         this.navId = this.navId || p.navId;
18432         
18433         if (typeof(this.navId) != 'undefined') {
18434             // not really needed.. but just in case.. parent should be a NavGroup.
18435             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18436             
18437             tg.register(this);
18438             
18439             var i = tg.tabs.length - 1;
18440             
18441             if(this.active && tg.bullets > 0 && i < tg.bullets){
18442                 tg.setActiveBullet(i);
18443             }
18444         }
18445         
18446         this.el.on('click', this.onClick, this);
18447         
18448         if(Roo.isTouch){
18449             this.el.on("touchstart", this.onTouchStart, this);
18450             this.el.on("touchmove", this.onTouchMove, this);
18451             this.el.on("touchend", this.onTouchEnd, this);
18452         }
18453         
18454     },
18455     
18456     onRender : function(ct, position)
18457     {
18458         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18459     },
18460     
18461     setActive : function(state)
18462     {
18463         Roo.log("panel - set active " + this.tabId + "=" + state);
18464         
18465         this.active = state;
18466         if (!state) {
18467             this.el.removeClass('active');
18468             
18469         } else  if (!this.el.hasClass('active')) {
18470             this.el.addClass('active');
18471         }
18472         
18473         this.fireEvent('changed', this, state);
18474     },
18475     
18476     onClick : function(e)
18477     {
18478         e.preventDefault();
18479         
18480         if(!this.href.length){
18481             return;
18482         }
18483         
18484         window.location.href = this.href;
18485     },
18486     
18487     startX : 0,
18488     startY : 0,
18489     endX : 0,
18490     endY : 0,
18491     swiping : false,
18492     
18493     onTouchStart : function(e)
18494     {
18495         this.swiping = false;
18496         
18497         this.startX = e.browserEvent.touches[0].clientX;
18498         this.startY = e.browserEvent.touches[0].clientY;
18499     },
18500     
18501     onTouchMove : function(e)
18502     {
18503         this.swiping = true;
18504         
18505         this.endX = e.browserEvent.touches[0].clientX;
18506         this.endY = e.browserEvent.touches[0].clientY;
18507     },
18508     
18509     onTouchEnd : function(e)
18510     {
18511         if(!this.swiping){
18512             this.onClick(e);
18513             return;
18514         }
18515         
18516         var tabGroup = this.parent();
18517         
18518         if(this.endX > this.startX){ // swiping right
18519             tabGroup.showPanelPrev();
18520             return;
18521         }
18522         
18523         if(this.startX > this.endX){ // swiping left
18524             tabGroup.showPanelNext();
18525             return;
18526         }
18527     }
18528     
18529     
18530 });
18531  
18532
18533  
18534
18535  /*
18536  * - LGPL
18537  *
18538  * DateField
18539  * 
18540  */
18541
18542 /**
18543  * @class Roo.bootstrap.DateField
18544  * @extends Roo.bootstrap.Input
18545  * Bootstrap DateField class
18546  * @cfg {Number} weekStart default 0
18547  * @cfg {String} viewMode default empty, (months|years)
18548  * @cfg {String} minViewMode default empty, (months|years)
18549  * @cfg {Number} startDate default -Infinity
18550  * @cfg {Number} endDate default Infinity
18551  * @cfg {Boolean} todayHighlight default false
18552  * @cfg {Boolean} todayBtn default false
18553  * @cfg {Boolean} calendarWeeks default false
18554  * @cfg {Object} daysOfWeekDisabled default empty
18555  * @cfg {Boolean} singleMode default false (true | false)
18556  * 
18557  * @cfg {Boolean} keyboardNavigation default true
18558  * @cfg {String} language default en
18559  * 
18560  * @constructor
18561  * Create a new DateField
18562  * @param {Object} config The config object
18563  */
18564
18565 Roo.bootstrap.DateField = function(config){
18566     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18567      this.addEvents({
18568             /**
18569              * @event show
18570              * Fires when this field show.
18571              * @param {Roo.bootstrap.DateField} this
18572              * @param {Mixed} date The date value
18573              */
18574             show : true,
18575             /**
18576              * @event show
18577              * Fires when this field hide.
18578              * @param {Roo.bootstrap.DateField} this
18579              * @param {Mixed} date The date value
18580              */
18581             hide : true,
18582             /**
18583              * @event select
18584              * Fires when select a date.
18585              * @param {Roo.bootstrap.DateField} this
18586              * @param {Mixed} date The date value
18587              */
18588             select : true,
18589             /**
18590              * @event beforeselect
18591              * Fires when before select a date.
18592              * @param {Roo.bootstrap.DateField} this
18593              * @param {Mixed} date The date value
18594              */
18595             beforeselect : true
18596         });
18597 };
18598
18599 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18600     
18601     /**
18602      * @cfg {String} format
18603      * The default date format string which can be overriden for localization support.  The format must be
18604      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18605      */
18606     format : "m/d/y",
18607     /**
18608      * @cfg {String} altFormats
18609      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18610      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18611      */
18612     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18613     
18614     weekStart : 0,
18615     
18616     viewMode : '',
18617     
18618     minViewMode : '',
18619     
18620     todayHighlight : false,
18621     
18622     todayBtn: false,
18623     
18624     language: 'en',
18625     
18626     keyboardNavigation: true,
18627     
18628     calendarWeeks: false,
18629     
18630     startDate: -Infinity,
18631     
18632     endDate: Infinity,
18633     
18634     daysOfWeekDisabled: [],
18635     
18636     _events: [],
18637     
18638     singleMode : false,
18639     
18640     UTCDate: function()
18641     {
18642         return new Date(Date.UTC.apply(Date, arguments));
18643     },
18644     
18645     UTCToday: function()
18646     {
18647         var today = new Date();
18648         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18649     },
18650     
18651     getDate: function() {
18652             var d = this.getUTCDate();
18653             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18654     },
18655     
18656     getUTCDate: function() {
18657             return this.date;
18658     },
18659     
18660     setDate: function(d) {
18661             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18662     },
18663     
18664     setUTCDate: function(d) {
18665             this.date = d;
18666             this.setValue(this.formatDate(this.date));
18667     },
18668         
18669     onRender: function(ct, position)
18670     {
18671         
18672         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18673         
18674         this.language = this.language || 'en';
18675         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18676         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18677         
18678         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18679         this.format = this.format || 'm/d/y';
18680         this.isInline = false;
18681         this.isInput = true;
18682         this.component = this.el.select('.add-on', true).first() || false;
18683         this.component = (this.component && this.component.length === 0) ? false : this.component;
18684         this.hasInput = this.component && this.inputEl().length;
18685         
18686         if (typeof(this.minViewMode === 'string')) {
18687             switch (this.minViewMode) {
18688                 case 'months':
18689                     this.minViewMode = 1;
18690                     break;
18691                 case 'years':
18692                     this.minViewMode = 2;
18693                     break;
18694                 default:
18695                     this.minViewMode = 0;
18696                     break;
18697             }
18698         }
18699         
18700         if (typeof(this.viewMode === 'string')) {
18701             switch (this.viewMode) {
18702                 case 'months':
18703                     this.viewMode = 1;
18704                     break;
18705                 case 'years':
18706                     this.viewMode = 2;
18707                     break;
18708                 default:
18709                     this.viewMode = 0;
18710                     break;
18711             }
18712         }
18713                 
18714         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18715         
18716 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18717         
18718         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18719         
18720         this.picker().on('mousedown', this.onMousedown, this);
18721         this.picker().on('click', this.onClick, this);
18722         
18723         this.picker().addClass('datepicker-dropdown');
18724         
18725         this.startViewMode = this.viewMode;
18726         
18727         if(this.singleMode){
18728             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18729                 v.setVisibilityMode(Roo.Element.DISPLAY);
18730                 v.hide();
18731             });
18732             
18733             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18734                 v.setStyle('width', '189px');
18735             });
18736         }
18737         
18738         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18739             if(!this.calendarWeeks){
18740                 v.remove();
18741                 return;
18742             }
18743             
18744             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18745             v.attr('colspan', function(i, val){
18746                 return parseInt(val) + 1;
18747             });
18748         });
18749                         
18750         
18751         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18752         
18753         this.setStartDate(this.startDate);
18754         this.setEndDate(this.endDate);
18755         
18756         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18757         
18758         this.fillDow();
18759         this.fillMonths();
18760         this.update();
18761         this.showMode();
18762         
18763         if(this.isInline) {
18764             this.showPopup();
18765         }
18766     },
18767     
18768     picker : function()
18769     {
18770         return this.pickerEl;
18771 //        return this.el.select('.datepicker', true).first();
18772     },
18773     
18774     fillDow: function()
18775     {
18776         var dowCnt = this.weekStart;
18777         
18778         var dow = {
18779             tag: 'tr',
18780             cn: [
18781                 
18782             ]
18783         };
18784         
18785         if(this.calendarWeeks){
18786             dow.cn.push({
18787                 tag: 'th',
18788                 cls: 'cw',
18789                 html: '&nbsp;'
18790             })
18791         }
18792         
18793         while (dowCnt < this.weekStart + 7) {
18794             dow.cn.push({
18795                 tag: 'th',
18796                 cls: 'dow',
18797                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18798             });
18799         }
18800         
18801         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18802     },
18803     
18804     fillMonths: function()
18805     {    
18806         var i = 0;
18807         var months = this.picker().select('>.datepicker-months td', true).first();
18808         
18809         months.dom.innerHTML = '';
18810         
18811         while (i < 12) {
18812             var month = {
18813                 tag: 'span',
18814                 cls: 'month',
18815                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18816             };
18817             
18818             months.createChild(month);
18819         }
18820         
18821     },
18822     
18823     update: function()
18824     {
18825         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
18826         
18827         if (this.date < this.startDate) {
18828             this.viewDate = new Date(this.startDate);
18829         } else if (this.date > this.endDate) {
18830             this.viewDate = new Date(this.endDate);
18831         } else {
18832             this.viewDate = new Date(this.date);
18833         }
18834         
18835         this.fill();
18836     },
18837     
18838     fill: function() 
18839     {
18840         var d = new Date(this.viewDate),
18841                 year = d.getUTCFullYear(),
18842                 month = d.getUTCMonth(),
18843                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18844                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18845                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18846                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18847                 currentDate = this.date && this.date.valueOf(),
18848                 today = this.UTCToday();
18849         
18850         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18851         
18852 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18853         
18854 //        this.picker.select('>tfoot th.today').
18855 //                                              .text(dates[this.language].today)
18856 //                                              .toggle(this.todayBtn !== false);
18857     
18858         this.updateNavArrows();
18859         this.fillMonths();
18860                                                 
18861         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18862         
18863         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18864          
18865         prevMonth.setUTCDate(day);
18866         
18867         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18868         
18869         var nextMonth = new Date(prevMonth);
18870         
18871         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18872         
18873         nextMonth = nextMonth.valueOf();
18874         
18875         var fillMonths = false;
18876         
18877         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18878         
18879         while(prevMonth.valueOf() <= nextMonth) {
18880             var clsName = '';
18881             
18882             if (prevMonth.getUTCDay() === this.weekStart) {
18883                 if(fillMonths){
18884                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18885                 }
18886                     
18887                 fillMonths = {
18888                     tag: 'tr',
18889                     cn: []
18890                 };
18891                 
18892                 if(this.calendarWeeks){
18893                     // ISO 8601: First week contains first thursday.
18894                     // ISO also states week starts on Monday, but we can be more abstract here.
18895                     var
18896                     // Start of current week: based on weekstart/current date
18897                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18898                     // Thursday of this week
18899                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18900                     // First Thursday of year, year from thursday
18901                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18902                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18903                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18904                     
18905                     fillMonths.cn.push({
18906                         tag: 'td',
18907                         cls: 'cw',
18908                         html: calWeek
18909                     });
18910                 }
18911             }
18912             
18913             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18914                 clsName += ' old';
18915             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18916                 clsName += ' new';
18917             }
18918             if (this.todayHighlight &&
18919                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18920                 prevMonth.getUTCMonth() == today.getMonth() &&
18921                 prevMonth.getUTCDate() == today.getDate()) {
18922                 clsName += ' today';
18923             }
18924             
18925             if (currentDate && prevMonth.valueOf() === currentDate) {
18926                 clsName += ' active';
18927             }
18928             
18929             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18930                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18931                     clsName += ' disabled';
18932             }
18933             
18934             fillMonths.cn.push({
18935                 tag: 'td',
18936                 cls: 'day ' + clsName,
18937                 html: prevMonth.getDate()
18938             });
18939             
18940             prevMonth.setDate(prevMonth.getDate()+1);
18941         }
18942           
18943         var currentYear = this.date && this.date.getUTCFullYear();
18944         var currentMonth = this.date && this.date.getUTCMonth();
18945         
18946         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18947         
18948         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18949             v.removeClass('active');
18950             
18951             if(currentYear === year && k === currentMonth){
18952                 v.addClass('active');
18953             }
18954             
18955             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18956                 v.addClass('disabled');
18957             }
18958             
18959         });
18960         
18961         
18962         year = parseInt(year/10, 10) * 10;
18963         
18964         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18965         
18966         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18967         
18968         year -= 1;
18969         for (var i = -1; i < 11; i++) {
18970             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18971                 tag: 'span',
18972                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18973                 html: year
18974             });
18975             
18976             year += 1;
18977         }
18978     },
18979     
18980     showMode: function(dir) 
18981     {
18982         if (dir) {
18983             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18984         }
18985         
18986         Roo.each(this.picker().select('>div',true).elements, function(v){
18987             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18988             v.hide();
18989         });
18990         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18991     },
18992     
18993     place: function()
18994     {
18995         if(this.isInline) {
18996             return;
18997         }
18998         
18999         this.picker().removeClass(['bottom', 'top']);
19000         
19001         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19002             /*
19003              * place to the top of element!
19004              *
19005              */
19006             
19007             this.picker().addClass('top');
19008             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19009             
19010             return;
19011         }
19012         
19013         this.picker().addClass('bottom');
19014         
19015         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19016     },
19017     
19018     parseDate : function(value)
19019     {
19020         if(!value || value instanceof Date){
19021             return value;
19022         }
19023         var v = Date.parseDate(value, this.format);
19024         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19025             v = Date.parseDate(value, 'Y-m-d');
19026         }
19027         if(!v && this.altFormats){
19028             if(!this.altFormatsArray){
19029                 this.altFormatsArray = this.altFormats.split("|");
19030             }
19031             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19032                 v = Date.parseDate(value, this.altFormatsArray[i]);
19033             }
19034         }
19035         return v;
19036     },
19037     
19038     formatDate : function(date, fmt)
19039     {   
19040         return (!date || !(date instanceof Date)) ?
19041         date : date.dateFormat(fmt || this.format);
19042     },
19043     
19044     onFocus : function()
19045     {
19046         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19047         this.showPopup();
19048     },
19049     
19050     onBlur : function()
19051     {
19052         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19053         
19054         var d = this.inputEl().getValue();
19055         
19056         this.setValue(d);
19057                 
19058         this.hidePopup();
19059     },
19060     
19061     showPopup : function()
19062     {
19063         this.picker().show();
19064         this.update();
19065         this.place();
19066         
19067         this.fireEvent('showpopup', this, this.date);
19068     },
19069     
19070     hidePopup : function()
19071     {
19072         if(this.isInline) {
19073             return;
19074         }
19075         this.picker().hide();
19076         this.viewMode = this.startViewMode;
19077         this.showMode();
19078         
19079         this.fireEvent('hidepopup', this, this.date);
19080         
19081     },
19082     
19083     onMousedown: function(e)
19084     {
19085         e.stopPropagation();
19086         e.preventDefault();
19087     },
19088     
19089     keyup: function(e)
19090     {
19091         Roo.bootstrap.DateField.superclass.keyup.call(this);
19092         this.update();
19093     },
19094
19095     setValue: function(v)
19096     {
19097         if(this.fireEvent('beforeselect', this, v) !== false){
19098             var d = new Date(this.parseDate(v) ).clearTime();
19099         
19100             if(isNaN(d.getTime())){
19101                 this.date = this.viewDate = '';
19102                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19103                 return;
19104             }
19105
19106             v = this.formatDate(d);
19107
19108             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19109
19110             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19111
19112             this.update();
19113
19114             this.fireEvent('select', this, this.date);
19115         }
19116     },
19117     
19118     getValue: function()
19119     {
19120         return this.formatDate(this.date);
19121     },
19122     
19123     fireKey: function(e)
19124     {
19125         if (!this.picker().isVisible()){
19126             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19127                 this.showPopup();
19128             }
19129             return;
19130         }
19131         
19132         var dateChanged = false,
19133         dir, day, month,
19134         newDate, newViewDate;
19135         
19136         switch(e.keyCode){
19137             case 27: // escape
19138                 this.hidePopup();
19139                 e.preventDefault();
19140                 break;
19141             case 37: // left
19142             case 39: // right
19143                 if (!this.keyboardNavigation) {
19144                     break;
19145                 }
19146                 dir = e.keyCode == 37 ? -1 : 1;
19147                 
19148                 if (e.ctrlKey){
19149                     newDate = this.moveYear(this.date, dir);
19150                     newViewDate = this.moveYear(this.viewDate, dir);
19151                 } else if (e.shiftKey){
19152                     newDate = this.moveMonth(this.date, dir);
19153                     newViewDate = this.moveMonth(this.viewDate, dir);
19154                 } else {
19155                     newDate = new Date(this.date);
19156                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19157                     newViewDate = new Date(this.viewDate);
19158                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19159                 }
19160                 if (this.dateWithinRange(newDate)){
19161                     this.date = newDate;
19162                     this.viewDate = newViewDate;
19163                     this.setValue(this.formatDate(this.date));
19164 //                    this.update();
19165                     e.preventDefault();
19166                     dateChanged = true;
19167                 }
19168                 break;
19169             case 38: // up
19170             case 40: // down
19171                 if (!this.keyboardNavigation) {
19172                     break;
19173                 }
19174                 dir = e.keyCode == 38 ? -1 : 1;
19175                 if (e.ctrlKey){
19176                     newDate = this.moveYear(this.date, dir);
19177                     newViewDate = this.moveYear(this.viewDate, dir);
19178                 } else if (e.shiftKey){
19179                     newDate = this.moveMonth(this.date, dir);
19180                     newViewDate = this.moveMonth(this.viewDate, dir);
19181                 } else {
19182                     newDate = new Date(this.date);
19183                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19184                     newViewDate = new Date(this.viewDate);
19185                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19186                 }
19187                 if (this.dateWithinRange(newDate)){
19188                     this.date = newDate;
19189                     this.viewDate = newViewDate;
19190                     this.setValue(this.formatDate(this.date));
19191 //                    this.update();
19192                     e.preventDefault();
19193                     dateChanged = true;
19194                 }
19195                 break;
19196             case 13: // enter
19197                 this.setValue(this.formatDate(this.date));
19198                 this.hidePopup();
19199                 e.preventDefault();
19200                 break;
19201             case 9: // tab
19202                 this.setValue(this.formatDate(this.date));
19203                 this.hidePopup();
19204                 break;
19205             case 16: // shift
19206             case 17: // ctrl
19207             case 18: // alt
19208                 break;
19209             default :
19210                 this.hidePopup();
19211                 
19212         }
19213     },
19214     
19215     
19216     onClick: function(e) 
19217     {
19218         e.stopPropagation();
19219         e.preventDefault();
19220         
19221         var target = e.getTarget();
19222         
19223         if(target.nodeName.toLowerCase() === 'i'){
19224             target = Roo.get(target).dom.parentNode;
19225         }
19226         
19227         var nodeName = target.nodeName;
19228         var className = target.className;
19229         var html = target.innerHTML;
19230         //Roo.log(nodeName);
19231         
19232         switch(nodeName.toLowerCase()) {
19233             case 'th':
19234                 switch(className) {
19235                     case 'switch':
19236                         this.showMode(1);
19237                         break;
19238                     case 'prev':
19239                     case 'next':
19240                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19241                         switch(this.viewMode){
19242                                 case 0:
19243                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19244                                         break;
19245                                 case 1:
19246                                 case 2:
19247                                         this.viewDate = this.moveYear(this.viewDate, dir);
19248                                         break;
19249                         }
19250                         this.fill();
19251                         break;
19252                     case 'today':
19253                         var date = new Date();
19254                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19255 //                        this.fill()
19256                         this.setValue(this.formatDate(this.date));
19257                         
19258                         this.hidePopup();
19259                         break;
19260                 }
19261                 break;
19262             case 'span':
19263                 if (className.indexOf('disabled') < 0) {
19264                     this.viewDate.setUTCDate(1);
19265                     if (className.indexOf('month') > -1) {
19266                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19267                     } else {
19268                         var year = parseInt(html, 10) || 0;
19269                         this.viewDate.setUTCFullYear(year);
19270                         
19271                     }
19272                     
19273                     if(this.singleMode){
19274                         this.setValue(this.formatDate(this.viewDate));
19275                         this.hidePopup();
19276                         return;
19277                     }
19278                     
19279                     this.showMode(-1);
19280                     this.fill();
19281                 }
19282                 break;
19283                 
19284             case 'td':
19285                 //Roo.log(className);
19286                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19287                     var day = parseInt(html, 10) || 1;
19288                     var year = this.viewDate.getUTCFullYear(),
19289                         month = this.viewDate.getUTCMonth();
19290
19291                     if (className.indexOf('old') > -1) {
19292                         if(month === 0 ){
19293                             month = 11;
19294                             year -= 1;
19295                         }else{
19296                             month -= 1;
19297                         }
19298                     } else if (className.indexOf('new') > -1) {
19299                         if (month == 11) {
19300                             month = 0;
19301                             year += 1;
19302                         } else {
19303                             month += 1;
19304                         }
19305                     }
19306                     //Roo.log([year,month,day]);
19307                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19308                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19309 //                    this.fill();
19310                     //Roo.log(this.formatDate(this.date));
19311                     this.setValue(this.formatDate(this.date));
19312                     this.hidePopup();
19313                 }
19314                 break;
19315         }
19316     },
19317     
19318     setStartDate: function(startDate)
19319     {
19320         this.startDate = startDate || -Infinity;
19321         if (this.startDate !== -Infinity) {
19322             this.startDate = this.parseDate(this.startDate);
19323         }
19324         this.update();
19325         this.updateNavArrows();
19326     },
19327
19328     setEndDate: function(endDate)
19329     {
19330         this.endDate = endDate || Infinity;
19331         if (this.endDate !== Infinity) {
19332             this.endDate = this.parseDate(this.endDate);
19333         }
19334         this.update();
19335         this.updateNavArrows();
19336     },
19337     
19338     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19339     {
19340         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19341         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19342             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19343         }
19344         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19345             return parseInt(d, 10);
19346         });
19347         this.update();
19348         this.updateNavArrows();
19349     },
19350     
19351     updateNavArrows: function() 
19352     {
19353         if(this.singleMode){
19354             return;
19355         }
19356         
19357         var d = new Date(this.viewDate),
19358         year = d.getUTCFullYear(),
19359         month = d.getUTCMonth();
19360         
19361         Roo.each(this.picker().select('.prev', true).elements, function(v){
19362             v.show();
19363             switch (this.viewMode) {
19364                 case 0:
19365
19366                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19367                         v.hide();
19368                     }
19369                     break;
19370                 case 1:
19371                 case 2:
19372                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19373                         v.hide();
19374                     }
19375                     break;
19376             }
19377         });
19378         
19379         Roo.each(this.picker().select('.next', true).elements, function(v){
19380             v.show();
19381             switch (this.viewMode) {
19382                 case 0:
19383
19384                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19385                         v.hide();
19386                     }
19387                     break;
19388                 case 1:
19389                 case 2:
19390                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19391                         v.hide();
19392                     }
19393                     break;
19394             }
19395         })
19396     },
19397     
19398     moveMonth: function(date, dir)
19399     {
19400         if (!dir) {
19401             return date;
19402         }
19403         var new_date = new Date(date.valueOf()),
19404         day = new_date.getUTCDate(),
19405         month = new_date.getUTCMonth(),
19406         mag = Math.abs(dir),
19407         new_month, test;
19408         dir = dir > 0 ? 1 : -1;
19409         if (mag == 1){
19410             test = dir == -1
19411             // If going back one month, make sure month is not current month
19412             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19413             ? function(){
19414                 return new_date.getUTCMonth() == month;
19415             }
19416             // If going forward one month, make sure month is as expected
19417             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19418             : function(){
19419                 return new_date.getUTCMonth() != new_month;
19420             };
19421             new_month = month + dir;
19422             new_date.setUTCMonth(new_month);
19423             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19424             if (new_month < 0 || new_month > 11) {
19425                 new_month = (new_month + 12) % 12;
19426             }
19427         } else {
19428             // For magnitudes >1, move one month at a time...
19429             for (var i=0; i<mag; i++) {
19430                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19431                 new_date = this.moveMonth(new_date, dir);
19432             }
19433             // ...then reset the day, keeping it in the new month
19434             new_month = new_date.getUTCMonth();
19435             new_date.setUTCDate(day);
19436             test = function(){
19437                 return new_month != new_date.getUTCMonth();
19438             };
19439         }
19440         // Common date-resetting loop -- if date is beyond end of month, make it
19441         // end of month
19442         while (test()){
19443             new_date.setUTCDate(--day);
19444             new_date.setUTCMonth(new_month);
19445         }
19446         return new_date;
19447     },
19448
19449     moveYear: function(date, dir)
19450     {
19451         return this.moveMonth(date, dir*12);
19452     },
19453
19454     dateWithinRange: function(date)
19455     {
19456         return date >= this.startDate && date <= this.endDate;
19457     },
19458
19459     
19460     remove: function() 
19461     {
19462         this.picker().remove();
19463     },
19464     
19465     validateValue : function(value)
19466     {
19467         if(this.getVisibilityEl().hasClass('hidden')){
19468             return true;
19469         }
19470         
19471         if(value.length < 1)  {
19472             if(this.allowBlank){
19473                 return true;
19474             }
19475             return false;
19476         }
19477         
19478         if(value.length < this.minLength){
19479             return false;
19480         }
19481         if(value.length > this.maxLength){
19482             return false;
19483         }
19484         if(this.vtype){
19485             var vt = Roo.form.VTypes;
19486             if(!vt[this.vtype](value, this)){
19487                 return false;
19488             }
19489         }
19490         if(typeof this.validator == "function"){
19491             var msg = this.validator(value);
19492             if(msg !== true){
19493                 return false;
19494             }
19495         }
19496         
19497         if(this.regex && !this.regex.test(value)){
19498             return false;
19499         }
19500         
19501         if(typeof(this.parseDate(value)) == 'undefined'){
19502             return false;
19503         }
19504         
19505         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19506             return false;
19507         }      
19508         
19509         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19510             return false;
19511         } 
19512         
19513         
19514         return true;
19515     },
19516     
19517     reset : function()
19518     {
19519         this.date = this.viewDate = '';
19520         
19521         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19522     }
19523    
19524 });
19525
19526 Roo.apply(Roo.bootstrap.DateField,  {
19527     
19528     head : {
19529         tag: 'thead',
19530         cn: [
19531         {
19532             tag: 'tr',
19533             cn: [
19534             {
19535                 tag: 'th',
19536                 cls: 'prev',
19537                 html: '<i class="fa fa-arrow-left"/>'
19538             },
19539             {
19540                 tag: 'th',
19541                 cls: 'switch',
19542                 colspan: '5'
19543             },
19544             {
19545                 tag: 'th',
19546                 cls: 'next',
19547                 html: '<i class="fa fa-arrow-right"/>'
19548             }
19549
19550             ]
19551         }
19552         ]
19553     },
19554     
19555     content : {
19556         tag: 'tbody',
19557         cn: [
19558         {
19559             tag: 'tr',
19560             cn: [
19561             {
19562                 tag: 'td',
19563                 colspan: '7'
19564             }
19565             ]
19566         }
19567         ]
19568     },
19569     
19570     footer : {
19571         tag: 'tfoot',
19572         cn: [
19573         {
19574             tag: 'tr',
19575             cn: [
19576             {
19577                 tag: 'th',
19578                 colspan: '7',
19579                 cls: 'today'
19580             }
19581                     
19582             ]
19583         }
19584         ]
19585     },
19586     
19587     dates:{
19588         en: {
19589             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19590             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19591             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19592             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19593             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19594             today: "Today"
19595         }
19596     },
19597     
19598     modes: [
19599     {
19600         clsName: 'days',
19601         navFnc: 'Month',
19602         navStep: 1
19603     },
19604     {
19605         clsName: 'months',
19606         navFnc: 'FullYear',
19607         navStep: 1
19608     },
19609     {
19610         clsName: 'years',
19611         navFnc: 'FullYear',
19612         navStep: 10
19613     }]
19614 });
19615
19616 Roo.apply(Roo.bootstrap.DateField,  {
19617   
19618     template : {
19619         tag: 'div',
19620         cls: 'datepicker dropdown-menu roo-dynamic',
19621         cn: [
19622         {
19623             tag: 'div',
19624             cls: 'datepicker-days',
19625             cn: [
19626             {
19627                 tag: 'table',
19628                 cls: 'table-condensed',
19629                 cn:[
19630                 Roo.bootstrap.DateField.head,
19631                 {
19632                     tag: 'tbody'
19633                 },
19634                 Roo.bootstrap.DateField.footer
19635                 ]
19636             }
19637             ]
19638         },
19639         {
19640             tag: 'div',
19641             cls: 'datepicker-months',
19642             cn: [
19643             {
19644                 tag: 'table',
19645                 cls: 'table-condensed',
19646                 cn:[
19647                 Roo.bootstrap.DateField.head,
19648                 Roo.bootstrap.DateField.content,
19649                 Roo.bootstrap.DateField.footer
19650                 ]
19651             }
19652             ]
19653         },
19654         {
19655             tag: 'div',
19656             cls: 'datepicker-years',
19657             cn: [
19658             {
19659                 tag: 'table',
19660                 cls: 'table-condensed',
19661                 cn:[
19662                 Roo.bootstrap.DateField.head,
19663                 Roo.bootstrap.DateField.content,
19664                 Roo.bootstrap.DateField.footer
19665                 ]
19666             }
19667             ]
19668         }
19669         ]
19670     }
19671 });
19672
19673  
19674
19675  /*
19676  * - LGPL
19677  *
19678  * TimeField
19679  * 
19680  */
19681
19682 /**
19683  * @class Roo.bootstrap.TimeField
19684  * @extends Roo.bootstrap.Input
19685  * Bootstrap DateField class
19686  * 
19687  * 
19688  * @constructor
19689  * Create a new TimeField
19690  * @param {Object} config The config object
19691  */
19692
19693 Roo.bootstrap.TimeField = function(config){
19694     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19695     this.addEvents({
19696             /**
19697              * @event show
19698              * Fires when this field show.
19699              * @param {Roo.bootstrap.DateField} thisthis
19700              * @param {Mixed} date The date value
19701              */
19702             show : true,
19703             /**
19704              * @event show
19705              * Fires when this field hide.
19706              * @param {Roo.bootstrap.DateField} this
19707              * @param {Mixed} date The date value
19708              */
19709             hide : true,
19710             /**
19711              * @event select
19712              * Fires when select a date.
19713              * @param {Roo.bootstrap.DateField} this
19714              * @param {Mixed} date The date value
19715              */
19716             select : true
19717         });
19718 };
19719
19720 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19721     
19722     /**
19723      * @cfg {String} format
19724      * The default time format string which can be overriden for localization support.  The format must be
19725      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19726      */
19727     format : "H:i",
19728        
19729     onRender: function(ct, position)
19730     {
19731         
19732         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19733                 
19734         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19735         
19736         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19737         
19738         this.pop = this.picker().select('>.datepicker-time',true).first();
19739         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19740         
19741         this.picker().on('mousedown', this.onMousedown, this);
19742         this.picker().on('click', this.onClick, this);
19743         
19744         this.picker().addClass('datepicker-dropdown');
19745     
19746         this.fillTime();
19747         this.update();
19748             
19749         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19750         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19751         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19752         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19753         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19754         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19755
19756     },
19757     
19758     fireKey: function(e){
19759         if (!this.picker().isVisible()){
19760             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19761                 this.show();
19762             }
19763             return;
19764         }
19765
19766         e.preventDefault();
19767         
19768         switch(e.keyCode){
19769             case 27: // escape
19770                 this.hide();
19771                 break;
19772             case 37: // left
19773             case 39: // right
19774                 this.onTogglePeriod();
19775                 break;
19776             case 38: // up
19777                 this.onIncrementMinutes();
19778                 break;
19779             case 40: // down
19780                 this.onDecrementMinutes();
19781                 break;
19782             case 13: // enter
19783             case 9: // tab
19784                 this.setTime();
19785                 break;
19786         }
19787     },
19788     
19789     onClick: function(e) {
19790         e.stopPropagation();
19791         e.preventDefault();
19792     },
19793     
19794     picker : function()
19795     {
19796         return this.el.select('.datepicker', true).first();
19797     },
19798     
19799     fillTime: function()
19800     {    
19801         var time = this.pop.select('tbody', true).first();
19802         
19803         time.dom.innerHTML = '';
19804         
19805         time.createChild({
19806             tag: 'tr',
19807             cn: [
19808                 {
19809                     tag: 'td',
19810                     cn: [
19811                         {
19812                             tag: 'a',
19813                             href: '#',
19814                             cls: 'btn',
19815                             cn: [
19816                                 {
19817                                     tag: 'span',
19818                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19819                                 }
19820                             ]
19821                         } 
19822                     ]
19823                 },
19824                 {
19825                     tag: 'td',
19826                     cls: 'separator'
19827                 },
19828                 {
19829                     tag: 'td',
19830                     cn: [
19831                         {
19832                             tag: 'a',
19833                             href: '#',
19834                             cls: 'btn',
19835                             cn: [
19836                                 {
19837                                     tag: 'span',
19838                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19839                                 }
19840                             ]
19841                         }
19842                     ]
19843                 },
19844                 {
19845                     tag: 'td',
19846                     cls: 'separator'
19847                 }
19848             ]
19849         });
19850         
19851         time.createChild({
19852             tag: 'tr',
19853             cn: [
19854                 {
19855                     tag: 'td',
19856                     cn: [
19857                         {
19858                             tag: 'span',
19859                             cls: 'timepicker-hour',
19860                             html: '00'
19861                         }  
19862                     ]
19863                 },
19864                 {
19865                     tag: 'td',
19866                     cls: 'separator',
19867                     html: ':'
19868                 },
19869                 {
19870                     tag: 'td',
19871                     cn: [
19872                         {
19873                             tag: 'span',
19874                             cls: 'timepicker-minute',
19875                             html: '00'
19876                         }  
19877                     ]
19878                 },
19879                 {
19880                     tag: 'td',
19881                     cls: 'separator'
19882                 },
19883                 {
19884                     tag: 'td',
19885                     cn: [
19886                         {
19887                             tag: 'button',
19888                             type: 'button',
19889                             cls: 'btn btn-primary period',
19890                             html: 'AM'
19891                             
19892                         }
19893                     ]
19894                 }
19895             ]
19896         });
19897         
19898         time.createChild({
19899             tag: 'tr',
19900             cn: [
19901                 {
19902                     tag: 'td',
19903                     cn: [
19904                         {
19905                             tag: 'a',
19906                             href: '#',
19907                             cls: 'btn',
19908                             cn: [
19909                                 {
19910                                     tag: 'span',
19911                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19912                                 }
19913                             ]
19914                         }
19915                     ]
19916                 },
19917                 {
19918                     tag: 'td',
19919                     cls: 'separator'
19920                 },
19921                 {
19922                     tag: 'td',
19923                     cn: [
19924                         {
19925                             tag: 'a',
19926                             href: '#',
19927                             cls: 'btn',
19928                             cn: [
19929                                 {
19930                                     tag: 'span',
19931                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19932                                 }
19933                             ]
19934                         }
19935                     ]
19936                 },
19937                 {
19938                     tag: 'td',
19939                     cls: 'separator'
19940                 }
19941             ]
19942         });
19943         
19944     },
19945     
19946     update: function()
19947     {
19948         
19949         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19950         
19951         this.fill();
19952     },
19953     
19954     fill: function() 
19955     {
19956         var hours = this.time.getHours();
19957         var minutes = this.time.getMinutes();
19958         var period = 'AM';
19959         
19960         if(hours > 11){
19961             period = 'PM';
19962         }
19963         
19964         if(hours == 0){
19965             hours = 12;
19966         }
19967         
19968         
19969         if(hours > 12){
19970             hours = hours - 12;
19971         }
19972         
19973         if(hours < 10){
19974             hours = '0' + hours;
19975         }
19976         
19977         if(minutes < 10){
19978             minutes = '0' + minutes;
19979         }
19980         
19981         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19982         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19983         this.pop.select('button', true).first().dom.innerHTML = period;
19984         
19985     },
19986     
19987     place: function()
19988     {   
19989         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19990         
19991         var cls = ['bottom'];
19992         
19993         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19994             cls.pop();
19995             cls.push('top');
19996         }
19997         
19998         cls.push('right');
19999         
20000         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20001             cls.pop();
20002             cls.push('left');
20003         }
20004         
20005         this.picker().addClass(cls.join('-'));
20006         
20007         var _this = this;
20008         
20009         Roo.each(cls, function(c){
20010             if(c == 'bottom'){
20011                 _this.picker().setTop(_this.inputEl().getHeight());
20012                 return;
20013             }
20014             if(c == 'top'){
20015                 _this.picker().setTop(0 - _this.picker().getHeight());
20016                 return;
20017             }
20018             
20019             if(c == 'left'){
20020                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20021                 return;
20022             }
20023             if(c == 'right'){
20024                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20025                 return;
20026             }
20027         });
20028         
20029     },
20030   
20031     onFocus : function()
20032     {
20033         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20034         this.show();
20035     },
20036     
20037     onBlur : function()
20038     {
20039         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20040         this.hide();
20041     },
20042     
20043     show : function()
20044     {
20045         this.picker().show();
20046         this.pop.show();
20047         this.update();
20048         this.place();
20049         
20050         this.fireEvent('show', this, this.date);
20051     },
20052     
20053     hide : function()
20054     {
20055         this.picker().hide();
20056         this.pop.hide();
20057         
20058         this.fireEvent('hide', this, this.date);
20059     },
20060     
20061     setTime : function()
20062     {
20063         this.hide();
20064         this.setValue(this.time.format(this.format));
20065         
20066         this.fireEvent('select', this, this.date);
20067         
20068         
20069     },
20070     
20071     onMousedown: function(e){
20072         e.stopPropagation();
20073         e.preventDefault();
20074     },
20075     
20076     onIncrementHours: function()
20077     {
20078         Roo.log('onIncrementHours');
20079         this.time = this.time.add(Date.HOUR, 1);
20080         this.update();
20081         
20082     },
20083     
20084     onDecrementHours: function()
20085     {
20086         Roo.log('onDecrementHours');
20087         this.time = this.time.add(Date.HOUR, -1);
20088         this.update();
20089     },
20090     
20091     onIncrementMinutes: function()
20092     {
20093         Roo.log('onIncrementMinutes');
20094         this.time = this.time.add(Date.MINUTE, 1);
20095         this.update();
20096     },
20097     
20098     onDecrementMinutes: function()
20099     {
20100         Roo.log('onDecrementMinutes');
20101         this.time = this.time.add(Date.MINUTE, -1);
20102         this.update();
20103     },
20104     
20105     onTogglePeriod: function()
20106     {
20107         Roo.log('onTogglePeriod');
20108         this.time = this.time.add(Date.HOUR, 12);
20109         this.update();
20110     }
20111     
20112    
20113 });
20114
20115 Roo.apply(Roo.bootstrap.TimeField,  {
20116     
20117     content : {
20118         tag: 'tbody',
20119         cn: [
20120             {
20121                 tag: 'tr',
20122                 cn: [
20123                 {
20124                     tag: 'td',
20125                     colspan: '7'
20126                 }
20127                 ]
20128             }
20129         ]
20130     },
20131     
20132     footer : {
20133         tag: 'tfoot',
20134         cn: [
20135             {
20136                 tag: 'tr',
20137                 cn: [
20138                 {
20139                     tag: 'th',
20140                     colspan: '7',
20141                     cls: '',
20142                     cn: [
20143                         {
20144                             tag: 'button',
20145                             cls: 'btn btn-info ok',
20146                             html: 'OK'
20147                         }
20148                     ]
20149                 }
20150
20151                 ]
20152             }
20153         ]
20154     }
20155 });
20156
20157 Roo.apply(Roo.bootstrap.TimeField,  {
20158   
20159     template : {
20160         tag: 'div',
20161         cls: 'datepicker dropdown-menu',
20162         cn: [
20163             {
20164                 tag: 'div',
20165                 cls: 'datepicker-time',
20166                 cn: [
20167                 {
20168                     tag: 'table',
20169                     cls: 'table-condensed',
20170                     cn:[
20171                     Roo.bootstrap.TimeField.content,
20172                     Roo.bootstrap.TimeField.footer
20173                     ]
20174                 }
20175                 ]
20176             }
20177         ]
20178     }
20179 });
20180
20181  
20182
20183  /*
20184  * - LGPL
20185  *
20186  * MonthField
20187  * 
20188  */
20189
20190 /**
20191  * @class Roo.bootstrap.MonthField
20192  * @extends Roo.bootstrap.Input
20193  * Bootstrap MonthField class
20194  * 
20195  * @cfg {String} language default en
20196  * 
20197  * @constructor
20198  * Create a new MonthField
20199  * @param {Object} config The config object
20200  */
20201
20202 Roo.bootstrap.MonthField = function(config){
20203     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20204     
20205     this.addEvents({
20206         /**
20207          * @event show
20208          * Fires when this field show.
20209          * @param {Roo.bootstrap.MonthField} this
20210          * @param {Mixed} date The date value
20211          */
20212         show : true,
20213         /**
20214          * @event show
20215          * Fires when this field hide.
20216          * @param {Roo.bootstrap.MonthField} this
20217          * @param {Mixed} date The date value
20218          */
20219         hide : true,
20220         /**
20221          * @event select
20222          * Fires when select a date.
20223          * @param {Roo.bootstrap.MonthField} this
20224          * @param {String} oldvalue The old value
20225          * @param {String} newvalue The new value
20226          */
20227         select : true
20228     });
20229 };
20230
20231 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20232     
20233     onRender: function(ct, position)
20234     {
20235         
20236         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20237         
20238         this.language = this.language || 'en';
20239         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20240         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20241         
20242         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20243         this.isInline = false;
20244         this.isInput = true;
20245         this.component = this.el.select('.add-on', true).first() || false;
20246         this.component = (this.component && this.component.length === 0) ? false : this.component;
20247         this.hasInput = this.component && this.inputEL().length;
20248         
20249         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20250         
20251         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20252         
20253         this.picker().on('mousedown', this.onMousedown, this);
20254         this.picker().on('click', this.onClick, this);
20255         
20256         this.picker().addClass('datepicker-dropdown');
20257         
20258         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20259             v.setStyle('width', '189px');
20260         });
20261         
20262         this.fillMonths();
20263         
20264         this.update();
20265         
20266         if(this.isInline) {
20267             this.show();
20268         }
20269         
20270     },
20271     
20272     setValue: function(v, suppressEvent)
20273     {   
20274         var o = this.getValue();
20275         
20276         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20277         
20278         this.update();
20279
20280         if(suppressEvent !== true){
20281             this.fireEvent('select', this, o, v);
20282         }
20283         
20284     },
20285     
20286     getValue: function()
20287     {
20288         return this.value;
20289     },
20290     
20291     onClick: function(e) 
20292     {
20293         e.stopPropagation();
20294         e.preventDefault();
20295         
20296         var target = e.getTarget();
20297         
20298         if(target.nodeName.toLowerCase() === 'i'){
20299             target = Roo.get(target).dom.parentNode;
20300         }
20301         
20302         var nodeName = target.nodeName;
20303         var className = target.className;
20304         var html = target.innerHTML;
20305         
20306         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20307             return;
20308         }
20309         
20310         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20311         
20312         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20313         
20314         this.hide();
20315                         
20316     },
20317     
20318     picker : function()
20319     {
20320         return this.pickerEl;
20321     },
20322     
20323     fillMonths: function()
20324     {    
20325         var i = 0;
20326         var months = this.picker().select('>.datepicker-months td', true).first();
20327         
20328         months.dom.innerHTML = '';
20329         
20330         while (i < 12) {
20331             var month = {
20332                 tag: 'span',
20333                 cls: 'month',
20334                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20335             };
20336             
20337             months.createChild(month);
20338         }
20339         
20340     },
20341     
20342     update: function()
20343     {
20344         var _this = this;
20345         
20346         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20347             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20348         }
20349         
20350         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20351             e.removeClass('active');
20352             
20353             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20354                 e.addClass('active');
20355             }
20356         })
20357     },
20358     
20359     place: function()
20360     {
20361         if(this.isInline) {
20362             return;
20363         }
20364         
20365         this.picker().removeClass(['bottom', 'top']);
20366         
20367         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20368             /*
20369              * place to the top of element!
20370              *
20371              */
20372             
20373             this.picker().addClass('top');
20374             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20375             
20376             return;
20377         }
20378         
20379         this.picker().addClass('bottom');
20380         
20381         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20382     },
20383     
20384     onFocus : function()
20385     {
20386         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20387         this.show();
20388     },
20389     
20390     onBlur : function()
20391     {
20392         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20393         
20394         var d = this.inputEl().getValue();
20395         
20396         this.setValue(d);
20397                 
20398         this.hide();
20399     },
20400     
20401     show : function()
20402     {
20403         this.picker().show();
20404         this.picker().select('>.datepicker-months', true).first().show();
20405         this.update();
20406         this.place();
20407         
20408         this.fireEvent('show', this, this.date);
20409     },
20410     
20411     hide : function()
20412     {
20413         if(this.isInline) {
20414             return;
20415         }
20416         this.picker().hide();
20417         this.fireEvent('hide', this, this.date);
20418         
20419     },
20420     
20421     onMousedown: function(e)
20422     {
20423         e.stopPropagation();
20424         e.preventDefault();
20425     },
20426     
20427     keyup: function(e)
20428     {
20429         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20430         this.update();
20431     },
20432
20433     fireKey: function(e)
20434     {
20435         if (!this.picker().isVisible()){
20436             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20437                 this.show();
20438             }
20439             return;
20440         }
20441         
20442         var dir;
20443         
20444         switch(e.keyCode){
20445             case 27: // escape
20446                 this.hide();
20447                 e.preventDefault();
20448                 break;
20449             case 37: // left
20450             case 39: // right
20451                 dir = e.keyCode == 37 ? -1 : 1;
20452                 
20453                 this.vIndex = this.vIndex + dir;
20454                 
20455                 if(this.vIndex < 0){
20456                     this.vIndex = 0;
20457                 }
20458                 
20459                 if(this.vIndex > 11){
20460                     this.vIndex = 11;
20461                 }
20462                 
20463                 if(isNaN(this.vIndex)){
20464                     this.vIndex = 0;
20465                 }
20466                 
20467                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20468                 
20469                 break;
20470             case 38: // up
20471             case 40: // down
20472                 
20473                 dir = e.keyCode == 38 ? -1 : 1;
20474                 
20475                 this.vIndex = this.vIndex + dir * 4;
20476                 
20477                 if(this.vIndex < 0){
20478                     this.vIndex = 0;
20479                 }
20480                 
20481                 if(this.vIndex > 11){
20482                     this.vIndex = 11;
20483                 }
20484                 
20485                 if(isNaN(this.vIndex)){
20486                     this.vIndex = 0;
20487                 }
20488                 
20489                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20490                 break;
20491                 
20492             case 13: // enter
20493                 
20494                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20495                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20496                 }
20497                 
20498                 this.hide();
20499                 e.preventDefault();
20500                 break;
20501             case 9: // tab
20502                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20503                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20504                 }
20505                 this.hide();
20506                 break;
20507             case 16: // shift
20508             case 17: // ctrl
20509             case 18: // alt
20510                 break;
20511             default :
20512                 this.hide();
20513                 
20514         }
20515     },
20516     
20517     remove: function() 
20518     {
20519         this.picker().remove();
20520     }
20521    
20522 });
20523
20524 Roo.apply(Roo.bootstrap.MonthField,  {
20525     
20526     content : {
20527         tag: 'tbody',
20528         cn: [
20529         {
20530             tag: 'tr',
20531             cn: [
20532             {
20533                 tag: 'td',
20534                 colspan: '7'
20535             }
20536             ]
20537         }
20538         ]
20539     },
20540     
20541     dates:{
20542         en: {
20543             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20544             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20545         }
20546     }
20547 });
20548
20549 Roo.apply(Roo.bootstrap.MonthField,  {
20550   
20551     template : {
20552         tag: 'div',
20553         cls: 'datepicker dropdown-menu roo-dynamic',
20554         cn: [
20555             {
20556                 tag: 'div',
20557                 cls: 'datepicker-months',
20558                 cn: [
20559                 {
20560                     tag: 'table',
20561                     cls: 'table-condensed',
20562                     cn:[
20563                         Roo.bootstrap.DateField.content
20564                     ]
20565                 }
20566                 ]
20567             }
20568         ]
20569     }
20570 });
20571
20572  
20573
20574  
20575  /*
20576  * - LGPL
20577  *
20578  * CheckBox
20579  * 
20580  */
20581
20582 /**
20583  * @class Roo.bootstrap.CheckBox
20584  * @extends Roo.bootstrap.Input
20585  * Bootstrap CheckBox class
20586  * 
20587  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20588  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20589  * @cfg {String} boxLabel The text that appears beside the checkbox
20590  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20591  * @cfg {Boolean} checked initnal the element
20592  * @cfg {Boolean} inline inline the element (default false)
20593  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20594  * @cfg {String} tooltip label tooltip
20595  * 
20596  * @constructor
20597  * Create a new CheckBox
20598  * @param {Object} config The config object
20599  */
20600
20601 Roo.bootstrap.CheckBox = function(config){
20602     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20603    
20604     this.addEvents({
20605         /**
20606         * @event check
20607         * Fires when the element is checked or unchecked.
20608         * @param {Roo.bootstrap.CheckBox} this This input
20609         * @param {Boolean} checked The new checked value
20610         */
20611        check : true,
20612        /**
20613         * @event click
20614         * Fires when the element is click.
20615         * @param {Roo.bootstrap.CheckBox} this This input
20616         */
20617        click : true
20618     });
20619     
20620 };
20621
20622 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20623   
20624     inputType: 'checkbox',
20625     inputValue: 1,
20626     valueOff: 0,
20627     boxLabel: false,
20628     checked: false,
20629     weight : false,
20630     inline: false,
20631     tooltip : '',
20632     
20633     getAutoCreate : function()
20634     {
20635         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20636         
20637         var id = Roo.id();
20638         
20639         var cfg = {};
20640         
20641         cfg.cls = 'form-group ' + this.inputType; //input-group
20642         
20643         if(this.inline){
20644             cfg.cls += ' ' + this.inputType + '-inline';
20645         }
20646         
20647         var input =  {
20648             tag: 'input',
20649             id : id,
20650             type : this.inputType,
20651             value : this.inputValue,
20652             cls : 'roo-' + this.inputType, //'form-box',
20653             placeholder : this.placeholder || ''
20654             
20655         };
20656         
20657         if(this.inputType != 'radio'){
20658             var hidden =  {
20659                 tag: 'input',
20660                 type : 'hidden',
20661                 cls : 'roo-hidden-value',
20662                 value : this.checked ? this.inputValue : this.valueOff
20663             };
20664         }
20665         
20666             
20667         if (this.weight) { // Validity check?
20668             cfg.cls += " " + this.inputType + "-" + this.weight;
20669         }
20670         
20671         if (this.disabled) {
20672             input.disabled=true;
20673         }
20674         
20675         if(this.checked){
20676             input.checked = this.checked;
20677         }
20678         
20679         if (this.name) {
20680             
20681             input.name = this.name;
20682             
20683             if(this.inputType != 'radio'){
20684                 hidden.name = this.name;
20685                 input.name = '_hidden_' + this.name;
20686             }
20687         }
20688         
20689         if (this.size) {
20690             input.cls += ' input-' + this.size;
20691         }
20692         
20693         var settings=this;
20694         
20695         ['xs','sm','md','lg'].map(function(size){
20696             if (settings[size]) {
20697                 cfg.cls += ' col-' + size + '-' + settings[size];
20698             }
20699         });
20700         
20701         var inputblock = input;
20702          
20703         if (this.before || this.after) {
20704             
20705             inputblock = {
20706                 cls : 'input-group',
20707                 cn :  [] 
20708             };
20709             
20710             if (this.before) {
20711                 inputblock.cn.push({
20712                     tag :'span',
20713                     cls : 'input-group-addon',
20714                     html : this.before
20715                 });
20716             }
20717             
20718             inputblock.cn.push(input);
20719             
20720             if(this.inputType != 'radio'){
20721                 inputblock.cn.push(hidden);
20722             }
20723             
20724             if (this.after) {
20725                 inputblock.cn.push({
20726                     tag :'span',
20727                     cls : 'input-group-addon',
20728                     html : this.after
20729                 });
20730             }
20731             
20732         }
20733         
20734         if (align ==='left' && this.fieldLabel.length) {
20735 //                Roo.log("left and has label");
20736             cfg.cn = [
20737                 {
20738                     tag: 'label',
20739                     'for' :  id,
20740                     cls : 'control-label',
20741                     html : this.fieldLabel
20742                 },
20743                 {
20744                     cls : "", 
20745                     cn: [
20746                         inputblock
20747                     ]
20748                 }
20749             ];
20750             
20751             if(this.labelWidth > 12){
20752                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20753             }
20754             
20755             if(this.labelWidth < 13 && this.labelmd == 0){
20756                 this.labelmd = this.labelWidth;
20757             }
20758             
20759             if(this.labellg > 0){
20760                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20761                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20762             }
20763             
20764             if(this.labelmd > 0){
20765                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20766                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20767             }
20768             
20769             if(this.labelsm > 0){
20770                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20771                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20772             }
20773             
20774             if(this.labelxs > 0){
20775                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20776                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20777             }
20778             
20779         } else if ( this.fieldLabel.length) {
20780 //                Roo.log(" label");
20781                 cfg.cn = [
20782                    
20783                     {
20784                         tag: this.boxLabel ? 'span' : 'label',
20785                         'for': id,
20786                         cls: 'control-label box-input-label',
20787                         //cls : 'input-group-addon',
20788                         html : this.fieldLabel
20789                     },
20790                     
20791                     inputblock
20792                     
20793                 ];
20794
20795         } else {
20796             
20797 //                Roo.log(" no label && no align");
20798                 cfg.cn = [  inputblock ] ;
20799                 
20800                 
20801         }
20802         
20803         if(this.boxLabel){
20804              var boxLabelCfg = {
20805                 tag: 'label',
20806                 //'for': id, // box label is handled by onclick - so no for...
20807                 cls: 'box-label',
20808                 html: this.boxLabel
20809             };
20810             
20811             if(this.tooltip){
20812                 boxLabelCfg.tooltip = this.tooltip;
20813             }
20814              
20815             cfg.cn.push(boxLabelCfg);
20816         }
20817         
20818         if(this.inputType != 'radio'){
20819             cfg.cn.push(hidden);
20820         }
20821         
20822         return cfg;
20823         
20824     },
20825     
20826     /**
20827      * return the real input element.
20828      */
20829     inputEl: function ()
20830     {
20831         return this.el.select('input.roo-' + this.inputType,true).first();
20832     },
20833     hiddenEl: function ()
20834     {
20835         return this.el.select('input.roo-hidden-value',true).first();
20836     },
20837     
20838     labelEl: function()
20839     {
20840         return this.el.select('label.control-label',true).first();
20841     },
20842     /* depricated... */
20843     
20844     label: function()
20845     {
20846         return this.labelEl();
20847     },
20848     
20849     boxLabelEl: function()
20850     {
20851         return this.el.select('label.box-label',true).first();
20852     },
20853     
20854     initEvents : function()
20855     {
20856 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20857         
20858         this.inputEl().on('click', this.onClick,  this);
20859         
20860         if (this.boxLabel) { 
20861             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20862         }
20863         
20864         this.startValue = this.getValue();
20865         
20866         if(this.groupId){
20867             Roo.bootstrap.CheckBox.register(this);
20868         }
20869     },
20870     
20871     onClick : function(e)
20872     {   
20873         if(this.fireEvent('click', this, e) !== false){
20874             this.setChecked(!this.checked);
20875         }
20876         
20877     },
20878     
20879     setChecked : function(state,suppressEvent)
20880     {
20881         this.startValue = this.getValue();
20882
20883         if(this.inputType == 'radio'){
20884             
20885             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20886                 e.dom.checked = false;
20887             });
20888             
20889             this.inputEl().dom.checked = true;
20890             
20891             this.inputEl().dom.value = this.inputValue;
20892             
20893             if(suppressEvent !== true){
20894                 this.fireEvent('check', this, true);
20895             }
20896             
20897             this.validate();
20898             
20899             return;
20900         }
20901         
20902         this.checked = state;
20903         
20904         this.inputEl().dom.checked = state;
20905         
20906         
20907         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20908         
20909         if(suppressEvent !== true){
20910             this.fireEvent('check', this, state);
20911         }
20912         
20913         this.validate();
20914     },
20915     
20916     getValue : function()
20917     {
20918         if(this.inputType == 'radio'){
20919             return this.getGroupValue();
20920         }
20921         
20922         return this.hiddenEl().dom.value;
20923         
20924     },
20925     
20926     getGroupValue : function()
20927     {
20928         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20929             return '';
20930         }
20931         
20932         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20933     },
20934     
20935     setValue : function(v,suppressEvent)
20936     {
20937         if(this.inputType == 'radio'){
20938             this.setGroupValue(v, suppressEvent);
20939             return;
20940         }
20941         
20942         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20943         
20944         this.validate();
20945     },
20946     
20947     setGroupValue : function(v, suppressEvent)
20948     {
20949         this.startValue = this.getValue();
20950         
20951         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20952             e.dom.checked = false;
20953             
20954             if(e.dom.value == v){
20955                 e.dom.checked = true;
20956             }
20957         });
20958         
20959         if(suppressEvent !== true){
20960             this.fireEvent('check', this, true);
20961         }
20962
20963         this.validate();
20964         
20965         return;
20966     },
20967     
20968     validate : function()
20969     {
20970         if(this.getVisibilityEl().hasClass('hidden')){
20971             return true;
20972         }
20973         
20974         if(
20975                 this.disabled || 
20976                 (this.inputType == 'radio' && this.validateRadio()) ||
20977                 (this.inputType == 'checkbox' && this.validateCheckbox())
20978         ){
20979             this.markValid();
20980             return true;
20981         }
20982         
20983         this.markInvalid();
20984         return false;
20985     },
20986     
20987     validateRadio : function()
20988     {
20989         if(this.getVisibilityEl().hasClass('hidden')){
20990             return true;
20991         }
20992         
20993         if(this.allowBlank){
20994             return true;
20995         }
20996         
20997         var valid = false;
20998         
20999         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21000             if(!e.dom.checked){
21001                 return;
21002             }
21003             
21004             valid = true;
21005             
21006             return false;
21007         });
21008         
21009         return valid;
21010     },
21011     
21012     validateCheckbox : function()
21013     {
21014         if(!this.groupId){
21015             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21016             //return (this.getValue() == this.inputValue) ? true : false;
21017         }
21018         
21019         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21020         
21021         if(!group){
21022             return false;
21023         }
21024         
21025         var r = false;
21026         
21027         for(var i in group){
21028             if(group[i].el.isVisible(true)){
21029                 r = false;
21030                 break;
21031             }
21032             
21033             r = true;
21034         }
21035         
21036         for(var i in group){
21037             if(r){
21038                 break;
21039             }
21040             
21041             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21042         }
21043         
21044         return r;
21045     },
21046     
21047     /**
21048      * Mark this field as valid
21049      */
21050     markValid : function()
21051     {
21052         var _this = this;
21053         
21054         this.fireEvent('valid', this);
21055         
21056         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21057         
21058         if(this.groupId){
21059             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21060         }
21061         
21062         if(label){
21063             label.markValid();
21064         }
21065
21066         if(this.inputType == 'radio'){
21067             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21068                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21069                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21070             });
21071             
21072             return;
21073         }
21074
21075         if(!this.groupId){
21076             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21077             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21078             return;
21079         }
21080         
21081         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21082         
21083         if(!group){
21084             return;
21085         }
21086         
21087         for(var i in group){
21088             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21089             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21090         }
21091     },
21092     
21093      /**
21094      * Mark this field as invalid
21095      * @param {String} msg The validation message
21096      */
21097     markInvalid : function(msg)
21098     {
21099         if(this.allowBlank){
21100             return;
21101         }
21102         
21103         var _this = this;
21104         
21105         this.fireEvent('invalid', this, msg);
21106         
21107         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21108         
21109         if(this.groupId){
21110             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21111         }
21112         
21113         if(label){
21114             label.markInvalid();
21115         }
21116             
21117         if(this.inputType == 'radio'){
21118             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21119                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21120                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21121             });
21122             
21123             return;
21124         }
21125         
21126         if(!this.groupId){
21127             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21128             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21129             return;
21130         }
21131         
21132         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21133         
21134         if(!group){
21135             return;
21136         }
21137         
21138         for(var i in group){
21139             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21140             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21141         }
21142         
21143     },
21144     
21145     clearInvalid : function()
21146     {
21147         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21148         
21149         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21150         
21151         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21152         
21153         if (label && label.iconEl) {
21154             label.iconEl.removeClass(label.validClass);
21155             label.iconEl.removeClass(label.invalidClass);
21156         }
21157     },
21158     
21159     disable : function()
21160     {
21161         if(this.inputType != 'radio'){
21162             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21163             return;
21164         }
21165         
21166         var _this = this;
21167         
21168         if(this.rendered){
21169             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21170                 _this.getActionEl().addClass(this.disabledClass);
21171                 e.dom.disabled = true;
21172             });
21173         }
21174         
21175         this.disabled = true;
21176         this.fireEvent("disable", this);
21177         return this;
21178     },
21179
21180     enable : function()
21181     {
21182         if(this.inputType != 'radio'){
21183             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21184             return;
21185         }
21186         
21187         var _this = this;
21188         
21189         if(this.rendered){
21190             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21191                 _this.getActionEl().removeClass(this.disabledClass);
21192                 e.dom.disabled = false;
21193             });
21194         }
21195         
21196         this.disabled = false;
21197         this.fireEvent("enable", this);
21198         return this;
21199     },
21200     
21201     setBoxLabel : function(v)
21202     {
21203         this.boxLabel = v;
21204         
21205         if(this.rendered){
21206             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21207         }
21208     }
21209
21210 });
21211
21212 Roo.apply(Roo.bootstrap.CheckBox, {
21213     
21214     groups: {},
21215     
21216      /**
21217     * register a CheckBox Group
21218     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21219     */
21220     register : function(checkbox)
21221     {
21222         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21223             this.groups[checkbox.groupId] = {};
21224         }
21225         
21226         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21227             return;
21228         }
21229         
21230         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21231         
21232     },
21233     /**
21234     * fetch a CheckBox Group based on the group ID
21235     * @param {string} the group ID
21236     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21237     */
21238     get: function(groupId) {
21239         if (typeof(this.groups[groupId]) == 'undefined') {
21240             return false;
21241         }
21242         
21243         return this.groups[groupId] ;
21244     }
21245     
21246     
21247 });
21248 /*
21249  * - LGPL
21250  *
21251  * RadioItem
21252  * 
21253  */
21254
21255 /**
21256  * @class Roo.bootstrap.Radio
21257  * @extends Roo.bootstrap.Component
21258  * Bootstrap Radio class
21259  * @cfg {String} boxLabel - the label associated
21260  * @cfg {String} value - the value of radio
21261  * 
21262  * @constructor
21263  * Create a new Radio
21264  * @param {Object} config The config object
21265  */
21266 Roo.bootstrap.Radio = function(config){
21267     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21268     
21269 };
21270
21271 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21272     
21273     boxLabel : '',
21274     
21275     value : '',
21276     
21277     getAutoCreate : function()
21278     {
21279         var cfg = {
21280             tag : 'div',
21281             cls : 'form-group radio',
21282             cn : [
21283                 {
21284                     tag : 'label',
21285                     cls : 'box-label',
21286                     html : this.boxLabel
21287                 }
21288             ]
21289         };
21290         
21291         return cfg;
21292     },
21293     
21294     initEvents : function() 
21295     {
21296         this.parent().register(this);
21297         
21298         this.el.on('click', this.onClick, this);
21299         
21300     },
21301     
21302     onClick : function(e)
21303     {
21304         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21305             this.setChecked(true);
21306         }
21307     },
21308     
21309     setChecked : function(state, suppressEvent)
21310     {
21311         this.parent().setValue(this.value, suppressEvent);
21312         
21313     },
21314     
21315     setBoxLabel : function(v)
21316     {
21317         this.boxLabel = v;
21318         
21319         if(this.rendered){
21320             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21321         }
21322     }
21323     
21324 });
21325  
21326
21327  /*
21328  * - LGPL
21329  *
21330  * Input
21331  * 
21332  */
21333
21334 /**
21335  * @class Roo.bootstrap.SecurePass
21336  * @extends Roo.bootstrap.Input
21337  * Bootstrap SecurePass class
21338  *
21339  * 
21340  * @constructor
21341  * Create a new SecurePass
21342  * @param {Object} config The config object
21343  */
21344  
21345 Roo.bootstrap.SecurePass = function (config) {
21346     // these go here, so the translation tool can replace them..
21347     this.errors = {
21348         PwdEmpty: "Please type a password, and then retype it to confirm.",
21349         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21350         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21351         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21352         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21353         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21354         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21355         TooWeak: "Your password is Too Weak."
21356     },
21357     this.meterLabel = "Password strength:";
21358     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21359     this.meterClass = [
21360         "roo-password-meter-tooweak", 
21361         "roo-password-meter-weak", 
21362         "roo-password-meter-medium", 
21363         "roo-password-meter-strong", 
21364         "roo-password-meter-grey"
21365     ];
21366     
21367     this.errors = {};
21368     
21369     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21370 }
21371
21372 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21373     /**
21374      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21375      * {
21376      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21377      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21378      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21379      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21380      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21381      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21382      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21383      * })
21384      */
21385     // private
21386     
21387     meterWidth: 300,
21388     errorMsg :'',    
21389     errors: false,
21390     imageRoot: '/',
21391     /**
21392      * @cfg {String/Object} Label for the strength meter (defaults to
21393      * 'Password strength:')
21394      */
21395     // private
21396     meterLabel: '',
21397     /**
21398      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21399      * ['Weak', 'Medium', 'Strong'])
21400      */
21401     // private    
21402     pwdStrengths: false,    
21403     // private
21404     strength: 0,
21405     // private
21406     _lastPwd: null,
21407     // private
21408     kCapitalLetter: 0,
21409     kSmallLetter: 1,
21410     kDigit: 2,
21411     kPunctuation: 3,
21412     
21413     insecure: false,
21414     // private
21415     initEvents: function ()
21416     {
21417         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21418
21419         if (this.el.is('input[type=password]') && Roo.isSafari) {
21420             this.el.on('keydown', this.SafariOnKeyDown, this);
21421         }
21422
21423         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21424     },
21425     // private
21426     onRender: function (ct, position)
21427     {
21428         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21429         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21430         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21431
21432         this.trigger.createChild({
21433                    cn: [
21434                     {
21435                     //id: 'PwdMeter',
21436                     tag: 'div',
21437                     cls: 'roo-password-meter-grey col-xs-12',
21438                     style: {
21439                         //width: 0,
21440                         //width: this.meterWidth + 'px'                                                
21441                         }
21442                     },
21443                     {                            
21444                          cls: 'roo-password-meter-text'                          
21445                     }
21446                 ]            
21447         });
21448
21449          
21450         if (this.hideTrigger) {
21451             this.trigger.setDisplayed(false);
21452         }
21453         this.setSize(this.width || '', this.height || '');
21454     },
21455     // private
21456     onDestroy: function ()
21457     {
21458         if (this.trigger) {
21459             this.trigger.removeAllListeners();
21460             this.trigger.remove();
21461         }
21462         if (this.wrap) {
21463             this.wrap.remove();
21464         }
21465         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21466     },
21467     // private
21468     checkStrength: function ()
21469     {
21470         var pwd = this.inputEl().getValue();
21471         if (pwd == this._lastPwd) {
21472             return;
21473         }
21474
21475         var strength;
21476         if (this.ClientSideStrongPassword(pwd)) {
21477             strength = 3;
21478         } else if (this.ClientSideMediumPassword(pwd)) {
21479             strength = 2;
21480         } else if (this.ClientSideWeakPassword(pwd)) {
21481             strength = 1;
21482         } else {
21483             strength = 0;
21484         }
21485         
21486         Roo.log('strength1: ' + strength);
21487         
21488         //var pm = this.trigger.child('div/div/div').dom;
21489         var pm = this.trigger.child('div/div');
21490         pm.removeClass(this.meterClass);
21491         pm.addClass(this.meterClass[strength]);
21492                 
21493         
21494         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21495                 
21496         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21497         
21498         this._lastPwd = pwd;
21499     },
21500     reset: function ()
21501     {
21502         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21503         
21504         this._lastPwd = '';
21505         
21506         var pm = this.trigger.child('div/div');
21507         pm.removeClass(this.meterClass);
21508         pm.addClass('roo-password-meter-grey');        
21509         
21510         
21511         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21512         
21513         pt.innerHTML = '';
21514         this.inputEl().dom.type='password';
21515     },
21516     // private
21517     validateValue: function (value)
21518     {
21519         
21520         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21521             return false;
21522         }
21523         if (value.length == 0) {
21524             if (this.allowBlank) {
21525                 this.clearInvalid();
21526                 return true;
21527             }
21528
21529             this.markInvalid(this.errors.PwdEmpty);
21530             this.errorMsg = this.errors.PwdEmpty;
21531             return false;
21532         }
21533         
21534         if(this.insecure){
21535             return true;
21536         }
21537         
21538         if ('[\x21-\x7e]*'.match(value)) {
21539             this.markInvalid(this.errors.PwdBadChar);
21540             this.errorMsg = this.errors.PwdBadChar;
21541             return false;
21542         }
21543         if (value.length < 6) {
21544             this.markInvalid(this.errors.PwdShort);
21545             this.errorMsg = this.errors.PwdShort;
21546             return false;
21547         }
21548         if (value.length > 16) {
21549             this.markInvalid(this.errors.PwdLong);
21550             this.errorMsg = this.errors.PwdLong;
21551             return false;
21552         }
21553         var strength;
21554         if (this.ClientSideStrongPassword(value)) {
21555             strength = 3;
21556         } else if (this.ClientSideMediumPassword(value)) {
21557             strength = 2;
21558         } else if (this.ClientSideWeakPassword(value)) {
21559             strength = 1;
21560         } else {
21561             strength = 0;
21562         }
21563
21564         
21565         if (strength < 2) {
21566             //this.markInvalid(this.errors.TooWeak);
21567             this.errorMsg = this.errors.TooWeak;
21568             //return false;
21569         }
21570         
21571         
21572         console.log('strength2: ' + strength);
21573         
21574         //var pm = this.trigger.child('div/div/div').dom;
21575         
21576         var pm = this.trigger.child('div/div');
21577         pm.removeClass(this.meterClass);
21578         pm.addClass(this.meterClass[strength]);
21579                 
21580         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21581                 
21582         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21583         
21584         this.errorMsg = ''; 
21585         return true;
21586     },
21587     // private
21588     CharacterSetChecks: function (type)
21589     {
21590         this.type = type;
21591         this.fResult = false;
21592     },
21593     // private
21594     isctype: function (character, type)
21595     {
21596         switch (type) {  
21597             case this.kCapitalLetter:
21598                 if (character >= 'A' && character <= 'Z') {
21599                     return true;
21600                 }
21601                 break;
21602             
21603             case this.kSmallLetter:
21604                 if (character >= 'a' && character <= 'z') {
21605                     return true;
21606                 }
21607                 break;
21608             
21609             case this.kDigit:
21610                 if (character >= '0' && character <= '9') {
21611                     return true;
21612                 }
21613                 break;
21614             
21615             case this.kPunctuation:
21616                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21617                     return true;
21618                 }
21619                 break;
21620             
21621             default:
21622                 return false;
21623         }
21624
21625     },
21626     // private
21627     IsLongEnough: function (pwd, size)
21628     {
21629         return !(pwd == null || isNaN(size) || pwd.length < size);
21630     },
21631     // private
21632     SpansEnoughCharacterSets: function (word, nb)
21633     {
21634         if (!this.IsLongEnough(word, nb))
21635         {
21636             return false;
21637         }
21638
21639         var characterSetChecks = new Array(
21640             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21641             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21642         );
21643         
21644         for (var index = 0; index < word.length; ++index) {
21645             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21646                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21647                     characterSetChecks[nCharSet].fResult = true;
21648                     break;
21649                 }
21650             }
21651         }
21652
21653         var nCharSets = 0;
21654         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21655             if (characterSetChecks[nCharSet].fResult) {
21656                 ++nCharSets;
21657             }
21658         }
21659
21660         if (nCharSets < nb) {
21661             return false;
21662         }
21663         return true;
21664     },
21665     // private
21666     ClientSideStrongPassword: function (pwd)
21667     {
21668         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21669     },
21670     // private
21671     ClientSideMediumPassword: function (pwd)
21672     {
21673         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21674     },
21675     // private
21676     ClientSideWeakPassword: function (pwd)
21677     {
21678         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21679     }
21680           
21681 })//<script type="text/javascript">
21682
21683 /*
21684  * Based  Ext JS Library 1.1.1
21685  * Copyright(c) 2006-2007, Ext JS, LLC.
21686  * LGPL
21687  *
21688  */
21689  
21690 /**
21691  * @class Roo.HtmlEditorCore
21692  * @extends Roo.Component
21693  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21694  *
21695  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21696  */
21697
21698 Roo.HtmlEditorCore = function(config){
21699     
21700     
21701     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21702     
21703     
21704     this.addEvents({
21705         /**
21706          * @event initialize
21707          * Fires when the editor is fully initialized (including the iframe)
21708          * @param {Roo.HtmlEditorCore} this
21709          */
21710         initialize: true,
21711         /**
21712          * @event activate
21713          * Fires when the editor is first receives the focus. Any insertion must wait
21714          * until after this event.
21715          * @param {Roo.HtmlEditorCore} this
21716          */
21717         activate: true,
21718          /**
21719          * @event beforesync
21720          * Fires before the textarea is updated with content from the editor iframe. Return false
21721          * to cancel the sync.
21722          * @param {Roo.HtmlEditorCore} this
21723          * @param {String} html
21724          */
21725         beforesync: true,
21726          /**
21727          * @event beforepush
21728          * Fires before the iframe editor is updated with content from the textarea. Return false
21729          * to cancel the push.
21730          * @param {Roo.HtmlEditorCore} this
21731          * @param {String} html
21732          */
21733         beforepush: true,
21734          /**
21735          * @event sync
21736          * Fires when the textarea is updated with content from the editor iframe.
21737          * @param {Roo.HtmlEditorCore} this
21738          * @param {String} html
21739          */
21740         sync: true,
21741          /**
21742          * @event push
21743          * Fires when the iframe editor is updated with content from the textarea.
21744          * @param {Roo.HtmlEditorCore} this
21745          * @param {String} html
21746          */
21747         push: true,
21748         
21749         /**
21750          * @event editorevent
21751          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21752          * @param {Roo.HtmlEditorCore} this
21753          */
21754         editorevent: true
21755         
21756     });
21757     
21758     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21759     
21760     // defaults : white / black...
21761     this.applyBlacklists();
21762     
21763     
21764     
21765 };
21766
21767
21768 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21769
21770
21771      /**
21772      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21773      */
21774     
21775     owner : false,
21776     
21777      /**
21778      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21779      *                        Roo.resizable.
21780      */
21781     resizable : false,
21782      /**
21783      * @cfg {Number} height (in pixels)
21784      */   
21785     height: 300,
21786    /**
21787      * @cfg {Number} width (in pixels)
21788      */   
21789     width: 500,
21790     
21791     /**
21792      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21793      * 
21794      */
21795     stylesheets: false,
21796     
21797     // id of frame..
21798     frameId: false,
21799     
21800     // private properties
21801     validationEvent : false,
21802     deferHeight: true,
21803     initialized : false,
21804     activated : false,
21805     sourceEditMode : false,
21806     onFocus : Roo.emptyFn,
21807     iframePad:3,
21808     hideMode:'offsets',
21809     
21810     clearUp: true,
21811     
21812     // blacklist + whitelisted elements..
21813     black: false,
21814     white: false,
21815      
21816     bodyCls : '',
21817
21818     /**
21819      * Protected method that will not generally be called directly. It
21820      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21821      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21822      */
21823     getDocMarkup : function(){
21824         // body styles..
21825         var st = '';
21826         
21827         // inherit styels from page...?? 
21828         if (this.stylesheets === false) {
21829             
21830             Roo.get(document.head).select('style').each(function(node) {
21831                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21832             });
21833             
21834             Roo.get(document.head).select('link').each(function(node) { 
21835                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21836             });
21837             
21838         } else if (!this.stylesheets.length) {
21839                 // simple..
21840                 st = '<style type="text/css">' +
21841                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21842                    '</style>';
21843         } else { 
21844             st = '<style type="text/css">' +
21845                     this.stylesheets +
21846                 '</style>';
21847         }
21848         
21849         st +=  '<style type="text/css">' +
21850             'IMG { cursor: pointer } ' +
21851         '</style>';
21852
21853         var cls = 'roo-htmleditor-body';
21854         
21855         if(this.bodyCls.length){
21856             cls += ' ' + this.bodyCls;
21857         }
21858         
21859         return '<html><head>' + st  +
21860             //<style type="text/css">' +
21861             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21862             //'</style>' +
21863             ' </head><body class="' +  cls + '"></body></html>';
21864     },
21865
21866     // private
21867     onRender : function(ct, position)
21868     {
21869         var _t = this;
21870         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21871         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21872         
21873         
21874         this.el.dom.style.border = '0 none';
21875         this.el.dom.setAttribute('tabIndex', -1);
21876         this.el.addClass('x-hidden hide');
21877         
21878         
21879         
21880         if(Roo.isIE){ // fix IE 1px bogus margin
21881             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21882         }
21883        
21884         
21885         this.frameId = Roo.id();
21886         
21887          
21888         
21889         var iframe = this.owner.wrap.createChild({
21890             tag: 'iframe',
21891             cls: 'form-control', // bootstrap..
21892             id: this.frameId,
21893             name: this.frameId,
21894             frameBorder : 'no',
21895             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21896         }, this.el
21897         );
21898         
21899         
21900         this.iframe = iframe.dom;
21901
21902          this.assignDocWin();
21903         
21904         this.doc.designMode = 'on';
21905        
21906         this.doc.open();
21907         this.doc.write(this.getDocMarkup());
21908         this.doc.close();
21909
21910         
21911         var task = { // must defer to wait for browser to be ready
21912             run : function(){
21913                 //console.log("run task?" + this.doc.readyState);
21914                 this.assignDocWin();
21915                 if(this.doc.body || this.doc.readyState == 'complete'){
21916                     try {
21917                         this.doc.designMode="on";
21918                     } catch (e) {
21919                         return;
21920                     }
21921                     Roo.TaskMgr.stop(task);
21922                     this.initEditor.defer(10, this);
21923                 }
21924             },
21925             interval : 10,
21926             duration: 10000,
21927             scope: this
21928         };
21929         Roo.TaskMgr.start(task);
21930
21931     },
21932
21933     // private
21934     onResize : function(w, h)
21935     {
21936          Roo.log('resize: ' +w + ',' + h );
21937         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21938         if(!this.iframe){
21939             return;
21940         }
21941         if(typeof w == 'number'){
21942             
21943             this.iframe.style.width = w + 'px';
21944         }
21945         if(typeof h == 'number'){
21946             
21947             this.iframe.style.height = h + 'px';
21948             if(this.doc){
21949                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21950             }
21951         }
21952         
21953     },
21954
21955     /**
21956      * Toggles the editor between standard and source edit mode.
21957      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21958      */
21959     toggleSourceEdit : function(sourceEditMode){
21960         
21961         this.sourceEditMode = sourceEditMode === true;
21962         
21963         if(this.sourceEditMode){
21964  
21965             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21966             
21967         }else{
21968             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21969             //this.iframe.className = '';
21970             this.deferFocus();
21971         }
21972         //this.setSize(this.owner.wrap.getSize());
21973         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21974     },
21975
21976     
21977   
21978
21979     /**
21980      * Protected method that will not generally be called directly. If you need/want
21981      * custom HTML cleanup, this is the method you should override.
21982      * @param {String} html The HTML to be cleaned
21983      * return {String} The cleaned HTML
21984      */
21985     cleanHtml : function(html){
21986         html = String(html);
21987         if(html.length > 5){
21988             if(Roo.isSafari){ // strip safari nonsense
21989                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21990             }
21991         }
21992         if(html == '&nbsp;'){
21993             html = '';
21994         }
21995         return html;
21996     },
21997
21998     /**
21999      * HTML Editor -> Textarea
22000      * Protected method that will not generally be called directly. Syncs the contents
22001      * of the editor iframe with the textarea.
22002      */
22003     syncValue : function(){
22004         if(this.initialized){
22005             var bd = (this.doc.body || this.doc.documentElement);
22006             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22007             var html = bd.innerHTML;
22008             if(Roo.isSafari){
22009                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22010                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22011                 if(m && m[1]){
22012                     html = '<div style="'+m[0]+'">' + html + '</div>';
22013                 }
22014             }
22015             html = this.cleanHtml(html);
22016             // fix up the special chars.. normaly like back quotes in word...
22017             // however we do not want to do this with chinese..
22018             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22019                 var cc = b.charCodeAt();
22020                 if (
22021                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22022                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22023                     (cc >= 0xf900 && cc < 0xfb00 )
22024                 ) {
22025                         return b;
22026                 }
22027                 return "&#"+cc+";" 
22028             });
22029             if(this.owner.fireEvent('beforesync', this, html) !== false){
22030                 this.el.dom.value = html;
22031                 this.owner.fireEvent('sync', this, html);
22032             }
22033         }
22034     },
22035
22036     /**
22037      * Protected method that will not generally be called directly. Pushes the value of the textarea
22038      * into the iframe editor.
22039      */
22040     pushValue : function(){
22041         if(this.initialized){
22042             var v = this.el.dom.value.trim();
22043             
22044 //            if(v.length < 1){
22045 //                v = '&#160;';
22046 //            }
22047             
22048             if(this.owner.fireEvent('beforepush', this, v) !== false){
22049                 var d = (this.doc.body || this.doc.documentElement);
22050                 d.innerHTML = v;
22051                 this.cleanUpPaste();
22052                 this.el.dom.value = d.innerHTML;
22053                 this.owner.fireEvent('push', this, v);
22054             }
22055         }
22056     },
22057
22058     // private
22059     deferFocus : function(){
22060         this.focus.defer(10, this);
22061     },
22062
22063     // doc'ed in Field
22064     focus : function(){
22065         if(this.win && !this.sourceEditMode){
22066             this.win.focus();
22067         }else{
22068             this.el.focus();
22069         }
22070     },
22071     
22072     assignDocWin: function()
22073     {
22074         var iframe = this.iframe;
22075         
22076          if(Roo.isIE){
22077             this.doc = iframe.contentWindow.document;
22078             this.win = iframe.contentWindow;
22079         } else {
22080 //            if (!Roo.get(this.frameId)) {
22081 //                return;
22082 //            }
22083 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22084 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22085             
22086             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22087                 return;
22088             }
22089             
22090             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22091             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22092         }
22093     },
22094     
22095     // private
22096     initEditor : function(){
22097         //console.log("INIT EDITOR");
22098         this.assignDocWin();
22099         
22100         
22101         
22102         this.doc.designMode="on";
22103         this.doc.open();
22104         this.doc.write(this.getDocMarkup());
22105         this.doc.close();
22106         
22107         var dbody = (this.doc.body || this.doc.documentElement);
22108         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22109         // this copies styles from the containing element into thsi one..
22110         // not sure why we need all of this..
22111         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22112         
22113         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22114         //ss['background-attachment'] = 'fixed'; // w3c
22115         dbody.bgProperties = 'fixed'; // ie
22116         //Roo.DomHelper.applyStyles(dbody, ss);
22117         Roo.EventManager.on(this.doc, {
22118             //'mousedown': this.onEditorEvent,
22119             'mouseup': this.onEditorEvent,
22120             'dblclick': this.onEditorEvent,
22121             'click': this.onEditorEvent,
22122             'keyup': this.onEditorEvent,
22123             buffer:100,
22124             scope: this
22125         });
22126         if(Roo.isGecko){
22127             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22128         }
22129         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22130             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22131         }
22132         this.initialized = true;
22133
22134         this.owner.fireEvent('initialize', this);
22135         this.pushValue();
22136     },
22137
22138     // private
22139     onDestroy : function(){
22140         
22141         
22142         
22143         if(this.rendered){
22144             
22145             //for (var i =0; i < this.toolbars.length;i++) {
22146             //    // fixme - ask toolbars for heights?
22147             //    this.toolbars[i].onDestroy();
22148            // }
22149             
22150             //this.wrap.dom.innerHTML = '';
22151             //this.wrap.remove();
22152         }
22153     },
22154
22155     // private
22156     onFirstFocus : function(){
22157         
22158         this.assignDocWin();
22159         
22160         
22161         this.activated = true;
22162          
22163     
22164         if(Roo.isGecko){ // prevent silly gecko errors
22165             this.win.focus();
22166             var s = this.win.getSelection();
22167             if(!s.focusNode || s.focusNode.nodeType != 3){
22168                 var r = s.getRangeAt(0);
22169                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22170                 r.collapse(true);
22171                 this.deferFocus();
22172             }
22173             try{
22174                 this.execCmd('useCSS', true);
22175                 this.execCmd('styleWithCSS', false);
22176             }catch(e){}
22177         }
22178         this.owner.fireEvent('activate', this);
22179     },
22180
22181     // private
22182     adjustFont: function(btn){
22183         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22184         //if(Roo.isSafari){ // safari
22185         //    adjust *= 2;
22186        // }
22187         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22188         if(Roo.isSafari){ // safari
22189             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22190             v =  (v < 10) ? 10 : v;
22191             v =  (v > 48) ? 48 : v;
22192             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22193             
22194         }
22195         
22196         
22197         v = Math.max(1, v+adjust);
22198         
22199         this.execCmd('FontSize', v  );
22200     },
22201
22202     onEditorEvent : function(e)
22203     {
22204         this.owner.fireEvent('editorevent', this, e);
22205       //  this.updateToolbar();
22206         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22207     },
22208
22209     insertTag : function(tg)
22210     {
22211         // could be a bit smarter... -> wrap the current selected tRoo..
22212         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22213             
22214             range = this.createRange(this.getSelection());
22215             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22216             wrappingNode.appendChild(range.extractContents());
22217             range.insertNode(wrappingNode);
22218
22219             return;
22220             
22221             
22222             
22223         }
22224         this.execCmd("formatblock",   tg);
22225         
22226     },
22227     
22228     insertText : function(txt)
22229     {
22230         
22231         
22232         var range = this.createRange();
22233         range.deleteContents();
22234                //alert(Sender.getAttribute('label'));
22235                
22236         range.insertNode(this.doc.createTextNode(txt));
22237     } ,
22238     
22239      
22240
22241     /**
22242      * Executes a Midas editor command on the editor document and performs necessary focus and
22243      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22244      * @param {String} cmd The Midas command
22245      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22246      */
22247     relayCmd : function(cmd, value){
22248         this.win.focus();
22249         this.execCmd(cmd, value);
22250         this.owner.fireEvent('editorevent', this);
22251         //this.updateToolbar();
22252         this.owner.deferFocus();
22253     },
22254
22255     /**
22256      * Executes a Midas editor command directly on the editor document.
22257      * For visual commands, you should use {@link #relayCmd} instead.
22258      * <b>This should only be called after the editor is initialized.</b>
22259      * @param {String} cmd The Midas command
22260      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22261      */
22262     execCmd : function(cmd, value){
22263         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22264         this.syncValue();
22265     },
22266  
22267  
22268    
22269     /**
22270      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22271      * to insert tRoo.
22272      * @param {String} text | dom node.. 
22273      */
22274     insertAtCursor : function(text)
22275     {
22276         
22277         if(!this.activated){
22278             return;
22279         }
22280         /*
22281         if(Roo.isIE){
22282             this.win.focus();
22283             var r = this.doc.selection.createRange();
22284             if(r){
22285                 r.collapse(true);
22286                 r.pasteHTML(text);
22287                 this.syncValue();
22288                 this.deferFocus();
22289             
22290             }
22291             return;
22292         }
22293         */
22294         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22295             this.win.focus();
22296             
22297             
22298             // from jquery ui (MIT licenced)
22299             var range, node;
22300             var win = this.win;
22301             
22302             if (win.getSelection && win.getSelection().getRangeAt) {
22303                 range = win.getSelection().getRangeAt(0);
22304                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22305                 range.insertNode(node);
22306             } else if (win.document.selection && win.document.selection.createRange) {
22307                 // no firefox support
22308                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22309                 win.document.selection.createRange().pasteHTML(txt);
22310             } else {
22311                 // no firefox support
22312                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22313                 this.execCmd('InsertHTML', txt);
22314             } 
22315             
22316             this.syncValue();
22317             
22318             this.deferFocus();
22319         }
22320     },
22321  // private
22322     mozKeyPress : function(e){
22323         if(e.ctrlKey){
22324             var c = e.getCharCode(), cmd;
22325           
22326             if(c > 0){
22327                 c = String.fromCharCode(c).toLowerCase();
22328                 switch(c){
22329                     case 'b':
22330                         cmd = 'bold';
22331                         break;
22332                     case 'i':
22333                         cmd = 'italic';
22334                         break;
22335                     
22336                     case 'u':
22337                         cmd = 'underline';
22338                         break;
22339                     
22340                     case 'v':
22341                         this.cleanUpPaste.defer(100, this);
22342                         return;
22343                         
22344                 }
22345                 if(cmd){
22346                     this.win.focus();
22347                     this.execCmd(cmd);
22348                     this.deferFocus();
22349                     e.preventDefault();
22350                 }
22351                 
22352             }
22353         }
22354     },
22355
22356     // private
22357     fixKeys : function(){ // load time branching for fastest keydown performance
22358         if(Roo.isIE){
22359             return function(e){
22360                 var k = e.getKey(), r;
22361                 if(k == e.TAB){
22362                     e.stopEvent();
22363                     r = this.doc.selection.createRange();
22364                     if(r){
22365                         r.collapse(true);
22366                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22367                         this.deferFocus();
22368                     }
22369                     return;
22370                 }
22371                 
22372                 if(k == e.ENTER){
22373                     r = this.doc.selection.createRange();
22374                     if(r){
22375                         var target = r.parentElement();
22376                         if(!target || target.tagName.toLowerCase() != 'li'){
22377                             e.stopEvent();
22378                             r.pasteHTML('<br />');
22379                             r.collapse(false);
22380                             r.select();
22381                         }
22382                     }
22383                 }
22384                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22385                     this.cleanUpPaste.defer(100, this);
22386                     return;
22387                 }
22388                 
22389                 
22390             };
22391         }else if(Roo.isOpera){
22392             return function(e){
22393                 var k = e.getKey();
22394                 if(k == e.TAB){
22395                     e.stopEvent();
22396                     this.win.focus();
22397                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22398                     this.deferFocus();
22399                 }
22400                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22401                     this.cleanUpPaste.defer(100, this);
22402                     return;
22403                 }
22404                 
22405             };
22406         }else if(Roo.isSafari){
22407             return function(e){
22408                 var k = e.getKey();
22409                 
22410                 if(k == e.TAB){
22411                     e.stopEvent();
22412                     this.execCmd('InsertText','\t');
22413                     this.deferFocus();
22414                     return;
22415                 }
22416                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22417                     this.cleanUpPaste.defer(100, this);
22418                     return;
22419                 }
22420                 
22421              };
22422         }
22423     }(),
22424     
22425     getAllAncestors: function()
22426     {
22427         var p = this.getSelectedNode();
22428         var a = [];
22429         if (!p) {
22430             a.push(p); // push blank onto stack..
22431             p = this.getParentElement();
22432         }
22433         
22434         
22435         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22436             a.push(p);
22437             p = p.parentNode;
22438         }
22439         a.push(this.doc.body);
22440         return a;
22441     },
22442     lastSel : false,
22443     lastSelNode : false,
22444     
22445     
22446     getSelection : function() 
22447     {
22448         this.assignDocWin();
22449         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22450     },
22451     
22452     getSelectedNode: function() 
22453     {
22454         // this may only work on Gecko!!!
22455         
22456         // should we cache this!!!!
22457         
22458         
22459         
22460          
22461         var range = this.createRange(this.getSelection()).cloneRange();
22462         
22463         if (Roo.isIE) {
22464             var parent = range.parentElement();
22465             while (true) {
22466                 var testRange = range.duplicate();
22467                 testRange.moveToElementText(parent);
22468                 if (testRange.inRange(range)) {
22469                     break;
22470                 }
22471                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22472                     break;
22473                 }
22474                 parent = parent.parentElement;
22475             }
22476             return parent;
22477         }
22478         
22479         // is ancestor a text element.
22480         var ac =  range.commonAncestorContainer;
22481         if (ac.nodeType == 3) {
22482             ac = ac.parentNode;
22483         }
22484         
22485         var ar = ac.childNodes;
22486          
22487         var nodes = [];
22488         var other_nodes = [];
22489         var has_other_nodes = false;
22490         for (var i=0;i<ar.length;i++) {
22491             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22492                 continue;
22493             }
22494             // fullly contained node.
22495             
22496             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22497                 nodes.push(ar[i]);
22498                 continue;
22499             }
22500             
22501             // probably selected..
22502             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22503                 other_nodes.push(ar[i]);
22504                 continue;
22505             }
22506             // outer..
22507             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22508                 continue;
22509             }
22510             
22511             
22512             has_other_nodes = true;
22513         }
22514         if (!nodes.length && other_nodes.length) {
22515             nodes= other_nodes;
22516         }
22517         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22518             return false;
22519         }
22520         
22521         return nodes[0];
22522     },
22523     createRange: function(sel)
22524     {
22525         // this has strange effects when using with 
22526         // top toolbar - not sure if it's a great idea.
22527         //this.editor.contentWindow.focus();
22528         if (typeof sel != "undefined") {
22529             try {
22530                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22531             } catch(e) {
22532                 return this.doc.createRange();
22533             }
22534         } else {
22535             return this.doc.createRange();
22536         }
22537     },
22538     getParentElement: function()
22539     {
22540         
22541         this.assignDocWin();
22542         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22543         
22544         var range = this.createRange(sel);
22545          
22546         try {
22547             var p = range.commonAncestorContainer;
22548             while (p.nodeType == 3) { // text node
22549                 p = p.parentNode;
22550             }
22551             return p;
22552         } catch (e) {
22553             return null;
22554         }
22555     
22556     },
22557     /***
22558      *
22559      * Range intersection.. the hard stuff...
22560      *  '-1' = before
22561      *  '0' = hits..
22562      *  '1' = after.
22563      *         [ -- selected range --- ]
22564      *   [fail]                        [fail]
22565      *
22566      *    basically..
22567      *      if end is before start or  hits it. fail.
22568      *      if start is after end or hits it fail.
22569      *
22570      *   if either hits (but other is outside. - then it's not 
22571      *   
22572      *    
22573      **/
22574     
22575     
22576     // @see http://www.thismuchiknow.co.uk/?p=64.
22577     rangeIntersectsNode : function(range, node)
22578     {
22579         var nodeRange = node.ownerDocument.createRange();
22580         try {
22581             nodeRange.selectNode(node);
22582         } catch (e) {
22583             nodeRange.selectNodeContents(node);
22584         }
22585     
22586         var rangeStartRange = range.cloneRange();
22587         rangeStartRange.collapse(true);
22588     
22589         var rangeEndRange = range.cloneRange();
22590         rangeEndRange.collapse(false);
22591     
22592         var nodeStartRange = nodeRange.cloneRange();
22593         nodeStartRange.collapse(true);
22594     
22595         var nodeEndRange = nodeRange.cloneRange();
22596         nodeEndRange.collapse(false);
22597     
22598         return rangeStartRange.compareBoundaryPoints(
22599                  Range.START_TO_START, nodeEndRange) == -1 &&
22600                rangeEndRange.compareBoundaryPoints(
22601                  Range.START_TO_START, nodeStartRange) == 1;
22602         
22603          
22604     },
22605     rangeCompareNode : function(range, node)
22606     {
22607         var nodeRange = node.ownerDocument.createRange();
22608         try {
22609             nodeRange.selectNode(node);
22610         } catch (e) {
22611             nodeRange.selectNodeContents(node);
22612         }
22613         
22614         
22615         range.collapse(true);
22616     
22617         nodeRange.collapse(true);
22618      
22619         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22620         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22621          
22622         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22623         
22624         var nodeIsBefore   =  ss == 1;
22625         var nodeIsAfter    = ee == -1;
22626         
22627         if (nodeIsBefore && nodeIsAfter) {
22628             return 0; // outer
22629         }
22630         if (!nodeIsBefore && nodeIsAfter) {
22631             return 1; //right trailed.
22632         }
22633         
22634         if (nodeIsBefore && !nodeIsAfter) {
22635             return 2;  // left trailed.
22636         }
22637         // fully contined.
22638         return 3;
22639     },
22640
22641     // private? - in a new class?
22642     cleanUpPaste :  function()
22643     {
22644         // cleans up the whole document..
22645         Roo.log('cleanuppaste');
22646         
22647         this.cleanUpChildren(this.doc.body);
22648         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22649         if (clean != this.doc.body.innerHTML) {
22650             this.doc.body.innerHTML = clean;
22651         }
22652         
22653     },
22654     
22655     cleanWordChars : function(input) {// change the chars to hex code
22656         var he = Roo.HtmlEditorCore;
22657         
22658         var output = input;
22659         Roo.each(he.swapCodes, function(sw) { 
22660             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22661             
22662             output = output.replace(swapper, sw[1]);
22663         });
22664         
22665         return output;
22666     },
22667     
22668     
22669     cleanUpChildren : function (n)
22670     {
22671         if (!n.childNodes.length) {
22672             return;
22673         }
22674         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22675            this.cleanUpChild(n.childNodes[i]);
22676         }
22677     },
22678     
22679     
22680         
22681     
22682     cleanUpChild : function (node)
22683     {
22684         var ed = this;
22685         //console.log(node);
22686         if (node.nodeName == "#text") {
22687             // clean up silly Windows -- stuff?
22688             return; 
22689         }
22690         if (node.nodeName == "#comment") {
22691             node.parentNode.removeChild(node);
22692             // clean up silly Windows -- stuff?
22693             return; 
22694         }
22695         var lcname = node.tagName.toLowerCase();
22696         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22697         // whitelist of tags..
22698         
22699         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22700             // remove node.
22701             node.parentNode.removeChild(node);
22702             return;
22703             
22704         }
22705         
22706         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22707         
22708         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22709         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22710         
22711         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22712         //    remove_keep_children = true;
22713         //}
22714         
22715         if (remove_keep_children) {
22716             this.cleanUpChildren(node);
22717             // inserts everything just before this node...
22718             while (node.childNodes.length) {
22719                 var cn = node.childNodes[0];
22720                 node.removeChild(cn);
22721                 node.parentNode.insertBefore(cn, node);
22722             }
22723             node.parentNode.removeChild(node);
22724             return;
22725         }
22726         
22727         if (!node.attributes || !node.attributes.length) {
22728             this.cleanUpChildren(node);
22729             return;
22730         }
22731         
22732         function cleanAttr(n,v)
22733         {
22734             
22735             if (v.match(/^\./) || v.match(/^\//)) {
22736                 return;
22737             }
22738             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22739                 return;
22740             }
22741             if (v.match(/^#/)) {
22742                 return;
22743             }
22744 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22745             node.removeAttribute(n);
22746             
22747         }
22748         
22749         var cwhite = this.cwhite;
22750         var cblack = this.cblack;
22751             
22752         function cleanStyle(n,v)
22753         {
22754             if (v.match(/expression/)) { //XSS?? should we even bother..
22755                 node.removeAttribute(n);
22756                 return;
22757             }
22758             
22759             var parts = v.split(/;/);
22760             var clean = [];
22761             
22762             Roo.each(parts, function(p) {
22763                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22764                 if (!p.length) {
22765                     return true;
22766                 }
22767                 var l = p.split(':').shift().replace(/\s+/g,'');
22768                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22769                 
22770                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22771 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22772                     //node.removeAttribute(n);
22773                     return true;
22774                 }
22775                 //Roo.log()
22776                 // only allow 'c whitelisted system attributes'
22777                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22778 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22779                     //node.removeAttribute(n);
22780                     return true;
22781                 }
22782                 
22783                 
22784                  
22785                 
22786                 clean.push(p);
22787                 return true;
22788             });
22789             if (clean.length) { 
22790                 node.setAttribute(n, clean.join(';'));
22791             } else {
22792                 node.removeAttribute(n);
22793             }
22794             
22795         }
22796         
22797         
22798         for (var i = node.attributes.length-1; i > -1 ; i--) {
22799             var a = node.attributes[i];
22800             //console.log(a);
22801             
22802             if (a.name.toLowerCase().substr(0,2)=='on')  {
22803                 node.removeAttribute(a.name);
22804                 continue;
22805             }
22806             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22807                 node.removeAttribute(a.name);
22808                 continue;
22809             }
22810             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22811                 cleanAttr(a.name,a.value); // fixme..
22812                 continue;
22813             }
22814             if (a.name == 'style') {
22815                 cleanStyle(a.name,a.value);
22816                 continue;
22817             }
22818             /// clean up MS crap..
22819             // tecnically this should be a list of valid class'es..
22820             
22821             
22822             if (a.name == 'class') {
22823                 if (a.value.match(/^Mso/)) {
22824                     node.className = '';
22825                 }
22826                 
22827                 if (a.value.match(/^body$/)) {
22828                     node.className = '';
22829                 }
22830                 continue;
22831             }
22832             
22833             // style cleanup!?
22834             // class cleanup?
22835             
22836         }
22837         
22838         
22839         this.cleanUpChildren(node);
22840         
22841         
22842     },
22843     
22844     /**
22845      * Clean up MS wordisms...
22846      */
22847     cleanWord : function(node)
22848     {
22849         
22850         
22851         if (!node) {
22852             this.cleanWord(this.doc.body);
22853             return;
22854         }
22855         if (node.nodeName == "#text") {
22856             // clean up silly Windows -- stuff?
22857             return; 
22858         }
22859         if (node.nodeName == "#comment") {
22860             node.parentNode.removeChild(node);
22861             // clean up silly Windows -- stuff?
22862             return; 
22863         }
22864         
22865         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22866             node.parentNode.removeChild(node);
22867             return;
22868         }
22869         
22870         // remove - but keep children..
22871         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22872             while (node.childNodes.length) {
22873                 var cn = node.childNodes[0];
22874                 node.removeChild(cn);
22875                 node.parentNode.insertBefore(cn, node);
22876             }
22877             node.parentNode.removeChild(node);
22878             this.iterateChildren(node, this.cleanWord);
22879             return;
22880         }
22881         // clean styles
22882         if (node.className.length) {
22883             
22884             var cn = node.className.split(/\W+/);
22885             var cna = [];
22886             Roo.each(cn, function(cls) {
22887                 if (cls.match(/Mso[a-zA-Z]+/)) {
22888                     return;
22889                 }
22890                 cna.push(cls);
22891             });
22892             node.className = cna.length ? cna.join(' ') : '';
22893             if (!cna.length) {
22894                 node.removeAttribute("class");
22895             }
22896         }
22897         
22898         if (node.hasAttribute("lang")) {
22899             node.removeAttribute("lang");
22900         }
22901         
22902         if (node.hasAttribute("style")) {
22903             
22904             var styles = node.getAttribute("style").split(";");
22905             var nstyle = [];
22906             Roo.each(styles, function(s) {
22907                 if (!s.match(/:/)) {
22908                     return;
22909                 }
22910                 var kv = s.split(":");
22911                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22912                     return;
22913                 }
22914                 // what ever is left... we allow.
22915                 nstyle.push(s);
22916             });
22917             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22918             if (!nstyle.length) {
22919                 node.removeAttribute('style');
22920             }
22921         }
22922         this.iterateChildren(node, this.cleanWord);
22923         
22924         
22925         
22926     },
22927     /**
22928      * iterateChildren of a Node, calling fn each time, using this as the scole..
22929      * @param {DomNode} node node to iterate children of.
22930      * @param {Function} fn method of this class to call on each item.
22931      */
22932     iterateChildren : function(node, fn)
22933     {
22934         if (!node.childNodes.length) {
22935                 return;
22936         }
22937         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22938            fn.call(this, node.childNodes[i])
22939         }
22940     },
22941     
22942     
22943     /**
22944      * cleanTableWidths.
22945      *
22946      * Quite often pasting from word etc.. results in tables with column and widths.
22947      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22948      *
22949      */
22950     cleanTableWidths : function(node)
22951     {
22952          
22953          
22954         if (!node) {
22955             this.cleanTableWidths(this.doc.body);
22956             return;
22957         }
22958         
22959         // ignore list...
22960         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22961             return; 
22962         }
22963         Roo.log(node.tagName);
22964         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22965             this.iterateChildren(node, this.cleanTableWidths);
22966             return;
22967         }
22968         if (node.hasAttribute('width')) {
22969             node.removeAttribute('width');
22970         }
22971         
22972          
22973         if (node.hasAttribute("style")) {
22974             // pretty basic...
22975             
22976             var styles = node.getAttribute("style").split(";");
22977             var nstyle = [];
22978             Roo.each(styles, function(s) {
22979                 if (!s.match(/:/)) {
22980                     return;
22981                 }
22982                 var kv = s.split(":");
22983                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22984                     return;
22985                 }
22986                 // what ever is left... we allow.
22987                 nstyle.push(s);
22988             });
22989             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22990             if (!nstyle.length) {
22991                 node.removeAttribute('style');
22992             }
22993         }
22994         
22995         this.iterateChildren(node, this.cleanTableWidths);
22996         
22997         
22998     },
22999     
23000     
23001     
23002     
23003     domToHTML : function(currentElement, depth, nopadtext) {
23004         
23005         depth = depth || 0;
23006         nopadtext = nopadtext || false;
23007     
23008         if (!currentElement) {
23009             return this.domToHTML(this.doc.body);
23010         }
23011         
23012         //Roo.log(currentElement);
23013         var j;
23014         var allText = false;
23015         var nodeName = currentElement.nodeName;
23016         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23017         
23018         if  (nodeName == '#text') {
23019             
23020             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23021         }
23022         
23023         
23024         var ret = '';
23025         if (nodeName != 'BODY') {
23026              
23027             var i = 0;
23028             // Prints the node tagName, such as <A>, <IMG>, etc
23029             if (tagName) {
23030                 var attr = [];
23031                 for(i = 0; i < currentElement.attributes.length;i++) {
23032                     // quoting?
23033                     var aname = currentElement.attributes.item(i).name;
23034                     if (!currentElement.attributes.item(i).value.length) {
23035                         continue;
23036                     }
23037                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23038                 }
23039                 
23040                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23041             } 
23042             else {
23043                 
23044                 // eack
23045             }
23046         } else {
23047             tagName = false;
23048         }
23049         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23050             return ret;
23051         }
23052         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23053             nopadtext = true;
23054         }
23055         
23056         
23057         // Traverse the tree
23058         i = 0;
23059         var currentElementChild = currentElement.childNodes.item(i);
23060         var allText = true;
23061         var innerHTML  = '';
23062         lastnode = '';
23063         while (currentElementChild) {
23064             // Formatting code (indent the tree so it looks nice on the screen)
23065             var nopad = nopadtext;
23066             if (lastnode == 'SPAN') {
23067                 nopad  = true;
23068             }
23069             // text
23070             if  (currentElementChild.nodeName == '#text') {
23071                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23072                 toadd = nopadtext ? toadd : toadd.trim();
23073                 if (!nopad && toadd.length > 80) {
23074                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23075                 }
23076                 innerHTML  += toadd;
23077                 
23078                 i++;
23079                 currentElementChild = currentElement.childNodes.item(i);
23080                 lastNode = '';
23081                 continue;
23082             }
23083             allText = false;
23084             
23085             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23086                 
23087             // Recursively traverse the tree structure of the child node
23088             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23089             lastnode = currentElementChild.nodeName;
23090             i++;
23091             currentElementChild=currentElement.childNodes.item(i);
23092         }
23093         
23094         ret += innerHTML;
23095         
23096         if (!allText) {
23097                 // The remaining code is mostly for formatting the tree
23098             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23099         }
23100         
23101         
23102         if (tagName) {
23103             ret+= "</"+tagName+">";
23104         }
23105         return ret;
23106         
23107     },
23108         
23109     applyBlacklists : function()
23110     {
23111         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23112         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23113         
23114         this.white = [];
23115         this.black = [];
23116         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23117             if (b.indexOf(tag) > -1) {
23118                 return;
23119             }
23120             this.white.push(tag);
23121             
23122         }, this);
23123         
23124         Roo.each(w, function(tag) {
23125             if (b.indexOf(tag) > -1) {
23126                 return;
23127             }
23128             if (this.white.indexOf(tag) > -1) {
23129                 return;
23130             }
23131             this.white.push(tag);
23132             
23133         }, this);
23134         
23135         
23136         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23137             if (w.indexOf(tag) > -1) {
23138                 return;
23139             }
23140             this.black.push(tag);
23141             
23142         }, this);
23143         
23144         Roo.each(b, function(tag) {
23145             if (w.indexOf(tag) > -1) {
23146                 return;
23147             }
23148             if (this.black.indexOf(tag) > -1) {
23149                 return;
23150             }
23151             this.black.push(tag);
23152             
23153         }, this);
23154         
23155         
23156         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23157         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23158         
23159         this.cwhite = [];
23160         this.cblack = [];
23161         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23162             if (b.indexOf(tag) > -1) {
23163                 return;
23164             }
23165             this.cwhite.push(tag);
23166             
23167         }, this);
23168         
23169         Roo.each(w, function(tag) {
23170             if (b.indexOf(tag) > -1) {
23171                 return;
23172             }
23173             if (this.cwhite.indexOf(tag) > -1) {
23174                 return;
23175             }
23176             this.cwhite.push(tag);
23177             
23178         }, this);
23179         
23180         
23181         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23182             if (w.indexOf(tag) > -1) {
23183                 return;
23184             }
23185             this.cblack.push(tag);
23186             
23187         }, this);
23188         
23189         Roo.each(b, function(tag) {
23190             if (w.indexOf(tag) > -1) {
23191                 return;
23192             }
23193             if (this.cblack.indexOf(tag) > -1) {
23194                 return;
23195             }
23196             this.cblack.push(tag);
23197             
23198         }, this);
23199     },
23200     
23201     setStylesheets : function(stylesheets)
23202     {
23203         if(typeof(stylesheets) == 'string'){
23204             Roo.get(this.iframe.contentDocument.head).createChild({
23205                 tag : 'link',
23206                 rel : 'stylesheet',
23207                 type : 'text/css',
23208                 href : stylesheets
23209             });
23210             
23211             return;
23212         }
23213         var _this = this;
23214      
23215         Roo.each(stylesheets, function(s) {
23216             if(!s.length){
23217                 return;
23218             }
23219             
23220             Roo.get(_this.iframe.contentDocument.head).createChild({
23221                 tag : 'link',
23222                 rel : 'stylesheet',
23223                 type : 'text/css',
23224                 href : s
23225             });
23226         });
23227
23228         
23229     },
23230     
23231     removeStylesheets : function()
23232     {
23233         var _this = this;
23234         
23235         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23236             s.remove();
23237         });
23238     },
23239     
23240     setStyle : function(style)
23241     {
23242         Roo.get(this.iframe.contentDocument.head).createChild({
23243             tag : 'style',
23244             type : 'text/css',
23245             html : style
23246         });
23247
23248         return;
23249     }
23250     
23251     // hide stuff that is not compatible
23252     /**
23253      * @event blur
23254      * @hide
23255      */
23256     /**
23257      * @event change
23258      * @hide
23259      */
23260     /**
23261      * @event focus
23262      * @hide
23263      */
23264     /**
23265      * @event specialkey
23266      * @hide
23267      */
23268     /**
23269      * @cfg {String} fieldClass @hide
23270      */
23271     /**
23272      * @cfg {String} focusClass @hide
23273      */
23274     /**
23275      * @cfg {String} autoCreate @hide
23276      */
23277     /**
23278      * @cfg {String} inputType @hide
23279      */
23280     /**
23281      * @cfg {String} invalidClass @hide
23282      */
23283     /**
23284      * @cfg {String} invalidText @hide
23285      */
23286     /**
23287      * @cfg {String} msgFx @hide
23288      */
23289     /**
23290      * @cfg {String} validateOnBlur @hide
23291      */
23292 });
23293
23294 Roo.HtmlEditorCore.white = [
23295         'area', 'br', 'img', 'input', 'hr', 'wbr',
23296         
23297        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23298        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23299        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23300        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23301        'table',   'ul',         'xmp', 
23302        
23303        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23304       'thead',   'tr', 
23305      
23306       'dir', 'menu', 'ol', 'ul', 'dl',
23307        
23308       'embed',  'object'
23309 ];
23310
23311
23312 Roo.HtmlEditorCore.black = [
23313     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23314         'applet', // 
23315         'base',   'basefont', 'bgsound', 'blink',  'body', 
23316         'frame',  'frameset', 'head',    'html',   'ilayer', 
23317         'iframe', 'layer',  'link',     'meta',    'object',   
23318         'script', 'style' ,'title',  'xml' // clean later..
23319 ];
23320 Roo.HtmlEditorCore.clean = [
23321     'script', 'style', 'title', 'xml'
23322 ];
23323 Roo.HtmlEditorCore.remove = [
23324     'font'
23325 ];
23326 // attributes..
23327
23328 Roo.HtmlEditorCore.ablack = [
23329     'on'
23330 ];
23331     
23332 Roo.HtmlEditorCore.aclean = [ 
23333     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23334 ];
23335
23336 // protocols..
23337 Roo.HtmlEditorCore.pwhite= [
23338         'http',  'https',  'mailto'
23339 ];
23340
23341 // white listed style attributes.
23342 Roo.HtmlEditorCore.cwhite= [
23343       //  'text-align', /// default is to allow most things..
23344       
23345          
23346 //        'font-size'//??
23347 ];
23348
23349 // black listed style attributes.
23350 Roo.HtmlEditorCore.cblack= [
23351       //  'font-size' -- this can be set by the project 
23352 ];
23353
23354
23355 Roo.HtmlEditorCore.swapCodes   =[ 
23356     [    8211, "--" ], 
23357     [    8212, "--" ], 
23358     [    8216,  "'" ],  
23359     [    8217, "'" ],  
23360     [    8220, '"' ],  
23361     [    8221, '"' ],  
23362     [    8226, "*" ],  
23363     [    8230, "..." ]
23364 ]; 
23365
23366     /*
23367  * - LGPL
23368  *
23369  * HtmlEditor
23370  * 
23371  */
23372
23373 /**
23374  * @class Roo.bootstrap.HtmlEditor
23375  * @extends Roo.bootstrap.TextArea
23376  * Bootstrap HtmlEditor class
23377
23378  * @constructor
23379  * Create a new HtmlEditor
23380  * @param {Object} config The config object
23381  */
23382
23383 Roo.bootstrap.HtmlEditor = function(config){
23384     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23385     if (!this.toolbars) {
23386         this.toolbars = [];
23387     }
23388     
23389     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23390     this.addEvents({
23391             /**
23392              * @event initialize
23393              * Fires when the editor is fully initialized (including the iframe)
23394              * @param {HtmlEditor} this
23395              */
23396             initialize: true,
23397             /**
23398              * @event activate
23399              * Fires when the editor is first receives the focus. Any insertion must wait
23400              * until after this event.
23401              * @param {HtmlEditor} this
23402              */
23403             activate: true,
23404              /**
23405              * @event beforesync
23406              * Fires before the textarea is updated with content from the editor iframe. Return false
23407              * to cancel the sync.
23408              * @param {HtmlEditor} this
23409              * @param {String} html
23410              */
23411             beforesync: true,
23412              /**
23413              * @event beforepush
23414              * Fires before the iframe editor is updated with content from the textarea. Return false
23415              * to cancel the push.
23416              * @param {HtmlEditor} this
23417              * @param {String} html
23418              */
23419             beforepush: true,
23420              /**
23421              * @event sync
23422              * Fires when the textarea is updated with content from the editor iframe.
23423              * @param {HtmlEditor} this
23424              * @param {String} html
23425              */
23426             sync: true,
23427              /**
23428              * @event push
23429              * Fires when the iframe editor is updated with content from the textarea.
23430              * @param {HtmlEditor} this
23431              * @param {String} html
23432              */
23433             push: true,
23434              /**
23435              * @event editmodechange
23436              * Fires when the editor switches edit modes
23437              * @param {HtmlEditor} this
23438              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23439              */
23440             editmodechange: true,
23441             /**
23442              * @event editorevent
23443              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23444              * @param {HtmlEditor} this
23445              */
23446             editorevent: true,
23447             /**
23448              * @event firstfocus
23449              * Fires when on first focus - needed by toolbars..
23450              * @param {HtmlEditor} this
23451              */
23452             firstfocus: true,
23453             /**
23454              * @event autosave
23455              * Auto save the htmlEditor value as a file into Events
23456              * @param {HtmlEditor} this
23457              */
23458             autosave: true,
23459             /**
23460              * @event savedpreview
23461              * preview the saved version of htmlEditor
23462              * @param {HtmlEditor} this
23463              */
23464             savedpreview: true
23465         });
23466 };
23467
23468
23469 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23470     
23471     
23472       /**
23473      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23474      */
23475     toolbars : false,
23476     
23477      /**
23478     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23479     */
23480     btns : [],
23481    
23482      /**
23483      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23484      *                        Roo.resizable.
23485      */
23486     resizable : false,
23487      /**
23488      * @cfg {Number} height (in pixels)
23489      */   
23490     height: 300,
23491    /**
23492      * @cfg {Number} width (in pixels)
23493      */   
23494     width: false,
23495     
23496     /**
23497      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23498      * 
23499      */
23500     stylesheets: false,
23501     
23502     // id of frame..
23503     frameId: false,
23504     
23505     // private properties
23506     validationEvent : false,
23507     deferHeight: true,
23508     initialized : false,
23509     activated : false,
23510     
23511     onFocus : Roo.emptyFn,
23512     iframePad:3,
23513     hideMode:'offsets',
23514     
23515     tbContainer : false,
23516     
23517     bodyCls : '',
23518     
23519     toolbarContainer :function() {
23520         return this.wrap.select('.x-html-editor-tb',true).first();
23521     },
23522
23523     /**
23524      * Protected method that will not generally be called directly. It
23525      * is called when the editor creates its toolbar. Override this method if you need to
23526      * add custom toolbar buttons.
23527      * @param {HtmlEditor} editor
23528      */
23529     createToolbar : function(){
23530         Roo.log('renewing');
23531         Roo.log("create toolbars");
23532         
23533         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23534         this.toolbars[0].render(this.toolbarContainer());
23535         
23536         return;
23537         
23538 //        if (!editor.toolbars || !editor.toolbars.length) {
23539 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23540 //        }
23541 //        
23542 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23543 //            editor.toolbars[i] = Roo.factory(
23544 //                    typeof(editor.toolbars[i]) == 'string' ?
23545 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23546 //                Roo.bootstrap.HtmlEditor);
23547 //            editor.toolbars[i].init(editor);
23548 //        }
23549     },
23550
23551      
23552     // private
23553     onRender : function(ct, position)
23554     {
23555        // Roo.log("Call onRender: " + this.xtype);
23556         var _t = this;
23557         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23558       
23559         this.wrap = this.inputEl().wrap({
23560             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23561         });
23562         
23563         this.editorcore.onRender(ct, position);
23564          
23565         if (this.resizable) {
23566             this.resizeEl = new Roo.Resizable(this.wrap, {
23567                 pinned : true,
23568                 wrap: true,
23569                 dynamic : true,
23570                 minHeight : this.height,
23571                 height: this.height,
23572                 handles : this.resizable,
23573                 width: this.width,
23574                 listeners : {
23575                     resize : function(r, w, h) {
23576                         _t.onResize(w,h); // -something
23577                     }
23578                 }
23579             });
23580             
23581         }
23582         this.createToolbar(this);
23583        
23584         
23585         if(!this.width && this.resizable){
23586             this.setSize(this.wrap.getSize());
23587         }
23588         if (this.resizeEl) {
23589             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23590             // should trigger onReize..
23591         }
23592         
23593     },
23594
23595     // private
23596     onResize : function(w, h)
23597     {
23598         Roo.log('resize: ' +w + ',' + h );
23599         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23600         var ew = false;
23601         var eh = false;
23602         
23603         if(this.inputEl() ){
23604             if(typeof w == 'number'){
23605                 var aw = w - this.wrap.getFrameWidth('lr');
23606                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23607                 ew = aw;
23608             }
23609             if(typeof h == 'number'){
23610                  var tbh = -11;  // fixme it needs to tool bar size!
23611                 for (var i =0; i < this.toolbars.length;i++) {
23612                     // fixme - ask toolbars for heights?
23613                     tbh += this.toolbars[i].el.getHeight();
23614                     //if (this.toolbars[i].footer) {
23615                     //    tbh += this.toolbars[i].footer.el.getHeight();
23616                     //}
23617                 }
23618               
23619                 
23620                 
23621                 
23622                 
23623                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23624                 ah -= 5; // knock a few pixes off for look..
23625                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23626                 var eh = ah;
23627             }
23628         }
23629         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23630         this.editorcore.onResize(ew,eh);
23631         
23632     },
23633
23634     /**
23635      * Toggles the editor between standard and source edit mode.
23636      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23637      */
23638     toggleSourceEdit : function(sourceEditMode)
23639     {
23640         this.editorcore.toggleSourceEdit(sourceEditMode);
23641         
23642         if(this.editorcore.sourceEditMode){
23643             Roo.log('editor - showing textarea');
23644             
23645 //            Roo.log('in');
23646 //            Roo.log(this.syncValue());
23647             this.syncValue();
23648             this.inputEl().removeClass(['hide', 'x-hidden']);
23649             this.inputEl().dom.removeAttribute('tabIndex');
23650             this.inputEl().focus();
23651         }else{
23652             Roo.log('editor - hiding textarea');
23653 //            Roo.log('out')
23654 //            Roo.log(this.pushValue()); 
23655             this.pushValue();
23656             
23657             this.inputEl().addClass(['hide', 'x-hidden']);
23658             this.inputEl().dom.setAttribute('tabIndex', -1);
23659             //this.deferFocus();
23660         }
23661          
23662         if(this.resizable){
23663             this.setSize(this.wrap.getSize());
23664         }
23665         
23666         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23667     },
23668  
23669     // private (for BoxComponent)
23670     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23671
23672     // private (for BoxComponent)
23673     getResizeEl : function(){
23674         return this.wrap;
23675     },
23676
23677     // private (for BoxComponent)
23678     getPositionEl : function(){
23679         return this.wrap;
23680     },
23681
23682     // private
23683     initEvents : function(){
23684         this.originalValue = this.getValue();
23685     },
23686
23687 //    /**
23688 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23689 //     * @method
23690 //     */
23691 //    markInvalid : Roo.emptyFn,
23692 //    /**
23693 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23694 //     * @method
23695 //     */
23696 //    clearInvalid : Roo.emptyFn,
23697
23698     setValue : function(v){
23699         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23700         this.editorcore.pushValue();
23701     },
23702
23703      
23704     // private
23705     deferFocus : function(){
23706         this.focus.defer(10, this);
23707     },
23708
23709     // doc'ed in Field
23710     focus : function(){
23711         this.editorcore.focus();
23712         
23713     },
23714       
23715
23716     // private
23717     onDestroy : function(){
23718         
23719         
23720         
23721         if(this.rendered){
23722             
23723             for (var i =0; i < this.toolbars.length;i++) {
23724                 // fixme - ask toolbars for heights?
23725                 this.toolbars[i].onDestroy();
23726             }
23727             
23728             this.wrap.dom.innerHTML = '';
23729             this.wrap.remove();
23730         }
23731     },
23732
23733     // private
23734     onFirstFocus : function(){
23735         //Roo.log("onFirstFocus");
23736         this.editorcore.onFirstFocus();
23737          for (var i =0; i < this.toolbars.length;i++) {
23738             this.toolbars[i].onFirstFocus();
23739         }
23740         
23741     },
23742     
23743     // private
23744     syncValue : function()
23745     {   
23746         this.editorcore.syncValue();
23747     },
23748     
23749     pushValue : function()
23750     {   
23751         this.editorcore.pushValue();
23752     }
23753      
23754     
23755     // hide stuff that is not compatible
23756     /**
23757      * @event blur
23758      * @hide
23759      */
23760     /**
23761      * @event change
23762      * @hide
23763      */
23764     /**
23765      * @event focus
23766      * @hide
23767      */
23768     /**
23769      * @event specialkey
23770      * @hide
23771      */
23772     /**
23773      * @cfg {String} fieldClass @hide
23774      */
23775     /**
23776      * @cfg {String} focusClass @hide
23777      */
23778     /**
23779      * @cfg {String} autoCreate @hide
23780      */
23781     /**
23782      * @cfg {String} inputType @hide
23783      */
23784     /**
23785      * @cfg {String} invalidClass @hide
23786      */
23787     /**
23788      * @cfg {String} invalidText @hide
23789      */
23790     /**
23791      * @cfg {String} msgFx @hide
23792      */
23793     /**
23794      * @cfg {String} validateOnBlur @hide
23795      */
23796 });
23797  
23798     
23799    
23800    
23801    
23802       
23803 Roo.namespace('Roo.bootstrap.htmleditor');
23804 /**
23805  * @class Roo.bootstrap.HtmlEditorToolbar1
23806  * Basic Toolbar
23807  * 
23808  * Usage:
23809  *
23810  new Roo.bootstrap.HtmlEditor({
23811     ....
23812     toolbars : [
23813         new Roo.bootstrap.HtmlEditorToolbar1({
23814             disable : { fonts: 1 , format: 1, ..., ... , ...],
23815             btns : [ .... ]
23816         })
23817     }
23818      
23819  * 
23820  * @cfg {Object} disable List of elements to disable..
23821  * @cfg {Array} btns List of additional buttons.
23822  * 
23823  * 
23824  * NEEDS Extra CSS? 
23825  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23826  */
23827  
23828 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23829 {
23830     
23831     Roo.apply(this, config);
23832     
23833     // default disabled, based on 'good practice'..
23834     this.disable = this.disable || {};
23835     Roo.applyIf(this.disable, {
23836         fontSize : true,
23837         colors : true,
23838         specialElements : true
23839     });
23840     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23841     
23842     this.editor = config.editor;
23843     this.editorcore = config.editor.editorcore;
23844     
23845     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23846     
23847     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23848     // dont call parent... till later.
23849 }
23850 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23851      
23852     bar : true,
23853     
23854     editor : false,
23855     editorcore : false,
23856     
23857     
23858     formats : [
23859         "p" ,  
23860         "h1","h2","h3","h4","h5","h6", 
23861         "pre", "code", 
23862         "abbr", "acronym", "address", "cite", "samp", "var",
23863         'div','span'
23864     ],
23865     
23866     onRender : function(ct, position)
23867     {
23868        // Roo.log("Call onRender: " + this.xtype);
23869         
23870        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23871        Roo.log(this.el);
23872        this.el.dom.style.marginBottom = '0';
23873        var _this = this;
23874        var editorcore = this.editorcore;
23875        var editor= this.editor;
23876        
23877        var children = [];
23878        var btn = function(id,cmd , toggle, handler, html){
23879        
23880             var  event = toggle ? 'toggle' : 'click';
23881        
23882             var a = {
23883                 size : 'sm',
23884                 xtype: 'Button',
23885                 xns: Roo.bootstrap,
23886                 glyphicon : id,
23887                 cmd : id || cmd,
23888                 enableToggle:toggle !== false,
23889                 html : html || '',
23890                 pressed : toggle ? false : null,
23891                 listeners : {}
23892             };
23893             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23894                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23895             };
23896             children.push(a);
23897             return a;
23898        }
23899        
23900     //    var cb_box = function...
23901         
23902         var style = {
23903                 xtype: 'Button',
23904                 size : 'sm',
23905                 xns: Roo.bootstrap,
23906                 glyphicon : 'font',
23907                 //html : 'submit'
23908                 menu : {
23909                     xtype: 'Menu',
23910                     xns: Roo.bootstrap,
23911                     items:  []
23912                 }
23913         };
23914         Roo.each(this.formats, function(f) {
23915             style.menu.items.push({
23916                 xtype :'MenuItem',
23917                 xns: Roo.bootstrap,
23918                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23919                 tagname : f,
23920                 listeners : {
23921                     click : function()
23922                     {
23923                         editorcore.insertTag(this.tagname);
23924                         editor.focus();
23925                     }
23926                 }
23927                 
23928             });
23929         });
23930         children.push(style);   
23931         
23932         btn('bold',false,true);
23933         btn('italic',false,true);
23934         btn('align-left', 'justifyleft',true);
23935         btn('align-center', 'justifycenter',true);
23936         btn('align-right' , 'justifyright',true);
23937         btn('link', false, false, function(btn) {
23938             //Roo.log("create link?");
23939             var url = prompt(this.createLinkText, this.defaultLinkValue);
23940             if(url && url != 'http:/'+'/'){
23941                 this.editorcore.relayCmd('createlink', url);
23942             }
23943         }),
23944         btn('list','insertunorderedlist',true);
23945         btn('pencil', false,true, function(btn){
23946                 Roo.log(this);
23947                 this.toggleSourceEdit(btn.pressed);
23948         });
23949         
23950         if (this.editor.btns.length > 0) {
23951             for (var i = 0; i<this.editor.btns.length; i++) {
23952                 children.push(this.editor.btns[i]);
23953             }
23954         }
23955         
23956         /*
23957         var cog = {
23958                 xtype: 'Button',
23959                 size : 'sm',
23960                 xns: Roo.bootstrap,
23961                 glyphicon : 'cog',
23962                 //html : 'submit'
23963                 menu : {
23964                     xtype: 'Menu',
23965                     xns: Roo.bootstrap,
23966                     items:  []
23967                 }
23968         };
23969         
23970         cog.menu.items.push({
23971             xtype :'MenuItem',
23972             xns: Roo.bootstrap,
23973             html : Clean styles,
23974             tagname : f,
23975             listeners : {
23976                 click : function()
23977                 {
23978                     editorcore.insertTag(this.tagname);
23979                     editor.focus();
23980                 }
23981             }
23982             
23983         });
23984        */
23985         
23986          
23987        this.xtype = 'NavSimplebar';
23988         
23989         for(var i=0;i< children.length;i++) {
23990             
23991             this.buttons.add(this.addxtypeChild(children[i]));
23992             
23993         }
23994         
23995         editor.on('editorevent', this.updateToolbar, this);
23996     },
23997     onBtnClick : function(id)
23998     {
23999        this.editorcore.relayCmd(id);
24000        this.editorcore.focus();
24001     },
24002     
24003     /**
24004      * Protected method that will not generally be called directly. It triggers
24005      * a toolbar update by reading the markup state of the current selection in the editor.
24006      */
24007     updateToolbar: function(){
24008
24009         if(!this.editorcore.activated){
24010             this.editor.onFirstFocus(); // is this neeed?
24011             return;
24012         }
24013
24014         var btns = this.buttons; 
24015         var doc = this.editorcore.doc;
24016         btns.get('bold').setActive(doc.queryCommandState('bold'));
24017         btns.get('italic').setActive(doc.queryCommandState('italic'));
24018         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24019         
24020         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24021         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24022         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24023         
24024         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24025         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24026          /*
24027         
24028         var ans = this.editorcore.getAllAncestors();
24029         if (this.formatCombo) {
24030             
24031             
24032             var store = this.formatCombo.store;
24033             this.formatCombo.setValue("");
24034             for (var i =0; i < ans.length;i++) {
24035                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24036                     // select it..
24037                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24038                     break;
24039                 }
24040             }
24041         }
24042         
24043         
24044         
24045         // hides menus... - so this cant be on a menu...
24046         Roo.bootstrap.MenuMgr.hideAll();
24047         */
24048         Roo.bootstrap.MenuMgr.hideAll();
24049         //this.editorsyncValue();
24050     },
24051     onFirstFocus: function() {
24052         this.buttons.each(function(item){
24053            item.enable();
24054         });
24055     },
24056     toggleSourceEdit : function(sourceEditMode){
24057         
24058           
24059         if(sourceEditMode){
24060             Roo.log("disabling buttons");
24061            this.buttons.each( function(item){
24062                 if(item.cmd != 'pencil'){
24063                     item.disable();
24064                 }
24065             });
24066           
24067         }else{
24068             Roo.log("enabling buttons");
24069             if(this.editorcore.initialized){
24070                 this.buttons.each( function(item){
24071                     item.enable();
24072                 });
24073             }
24074             
24075         }
24076         Roo.log("calling toggole on editor");
24077         // tell the editor that it's been pressed..
24078         this.editor.toggleSourceEdit(sourceEditMode);
24079        
24080     }
24081 });
24082
24083
24084
24085
24086
24087 /**
24088  * @class Roo.bootstrap.Table.AbstractSelectionModel
24089  * @extends Roo.util.Observable
24090  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24091  * implemented by descendant classes.  This class should not be directly instantiated.
24092  * @constructor
24093  */
24094 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24095     this.locked = false;
24096     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24097 };
24098
24099
24100 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24101     /** @ignore Called by the grid automatically. Do not call directly. */
24102     init : function(grid){
24103         this.grid = grid;
24104         this.initEvents();
24105     },
24106
24107     /**
24108      * Locks the selections.
24109      */
24110     lock : function(){
24111         this.locked = true;
24112     },
24113
24114     /**
24115      * Unlocks the selections.
24116      */
24117     unlock : function(){
24118         this.locked = false;
24119     },
24120
24121     /**
24122      * Returns true if the selections are locked.
24123      * @return {Boolean}
24124      */
24125     isLocked : function(){
24126         return this.locked;
24127     }
24128 });
24129 /**
24130  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24131  * @class Roo.bootstrap.Table.RowSelectionModel
24132  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24133  * It supports multiple selections and keyboard selection/navigation. 
24134  * @constructor
24135  * @param {Object} config
24136  */
24137
24138 Roo.bootstrap.Table.RowSelectionModel = function(config){
24139     Roo.apply(this, config);
24140     this.selections = new Roo.util.MixedCollection(false, function(o){
24141         return o.id;
24142     });
24143
24144     this.last = false;
24145     this.lastActive = false;
24146
24147     this.addEvents({
24148         /**
24149              * @event selectionchange
24150              * Fires when the selection changes
24151              * @param {SelectionModel} this
24152              */
24153             "selectionchange" : true,
24154         /**
24155              * @event afterselectionchange
24156              * Fires after the selection changes (eg. by key press or clicking)
24157              * @param {SelectionModel} this
24158              */
24159             "afterselectionchange" : true,
24160         /**
24161              * @event beforerowselect
24162              * Fires when a row is selected being selected, return false to cancel.
24163              * @param {SelectionModel} this
24164              * @param {Number} rowIndex The selected index
24165              * @param {Boolean} keepExisting False if other selections will be cleared
24166              */
24167             "beforerowselect" : true,
24168         /**
24169              * @event rowselect
24170              * Fires when a row is selected.
24171              * @param {SelectionModel} this
24172              * @param {Number} rowIndex The selected index
24173              * @param {Roo.data.Record} r The record
24174              */
24175             "rowselect" : true,
24176         /**
24177              * @event rowdeselect
24178              * Fires when a row is deselected.
24179              * @param {SelectionModel} this
24180              * @param {Number} rowIndex The selected index
24181              */
24182         "rowdeselect" : true
24183     });
24184     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24185     this.locked = false;
24186  };
24187
24188 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24189     /**
24190      * @cfg {Boolean} singleSelect
24191      * True to allow selection of only one row at a time (defaults to false)
24192      */
24193     singleSelect : false,
24194
24195     // private
24196     initEvents : function()
24197     {
24198
24199         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24200         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24201         //}else{ // allow click to work like normal
24202          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24203         //}
24204         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24205         this.grid.on("rowclick", this.handleMouseDown, this);
24206         
24207         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24208             "up" : function(e){
24209                 if(!e.shiftKey){
24210                     this.selectPrevious(e.shiftKey);
24211                 }else if(this.last !== false && this.lastActive !== false){
24212                     var last = this.last;
24213                     this.selectRange(this.last,  this.lastActive-1);
24214                     this.grid.getView().focusRow(this.lastActive);
24215                     if(last !== false){
24216                         this.last = last;
24217                     }
24218                 }else{
24219                     this.selectFirstRow();
24220                 }
24221                 this.fireEvent("afterselectionchange", this);
24222             },
24223             "down" : function(e){
24224                 if(!e.shiftKey){
24225                     this.selectNext(e.shiftKey);
24226                 }else if(this.last !== false && this.lastActive !== false){
24227                     var last = this.last;
24228                     this.selectRange(this.last,  this.lastActive+1);
24229                     this.grid.getView().focusRow(this.lastActive);
24230                     if(last !== false){
24231                         this.last = last;
24232                     }
24233                 }else{
24234                     this.selectFirstRow();
24235                 }
24236                 this.fireEvent("afterselectionchange", this);
24237             },
24238             scope: this
24239         });
24240         this.grid.store.on('load', function(){
24241             this.selections.clear();
24242         },this);
24243         /*
24244         var view = this.grid.view;
24245         view.on("refresh", this.onRefresh, this);
24246         view.on("rowupdated", this.onRowUpdated, this);
24247         view.on("rowremoved", this.onRemove, this);
24248         */
24249     },
24250
24251     // private
24252     onRefresh : function()
24253     {
24254         var ds = this.grid.store, i, v = this.grid.view;
24255         var s = this.selections;
24256         s.each(function(r){
24257             if((i = ds.indexOfId(r.id)) != -1){
24258                 v.onRowSelect(i);
24259             }else{
24260                 s.remove(r);
24261             }
24262         });
24263     },
24264
24265     // private
24266     onRemove : function(v, index, r){
24267         this.selections.remove(r);
24268     },
24269
24270     // private
24271     onRowUpdated : function(v, index, r){
24272         if(this.isSelected(r)){
24273             v.onRowSelect(index);
24274         }
24275     },
24276
24277     /**
24278      * Select records.
24279      * @param {Array} records The records to select
24280      * @param {Boolean} keepExisting (optional) True to keep existing selections
24281      */
24282     selectRecords : function(records, keepExisting)
24283     {
24284         if(!keepExisting){
24285             this.clearSelections();
24286         }
24287             var ds = this.grid.store;
24288         for(var i = 0, len = records.length; i < len; i++){
24289             this.selectRow(ds.indexOf(records[i]), true);
24290         }
24291     },
24292
24293     /**
24294      * Gets the number of selected rows.
24295      * @return {Number}
24296      */
24297     getCount : function(){
24298         return this.selections.length;
24299     },
24300
24301     /**
24302      * Selects the first row in the grid.
24303      */
24304     selectFirstRow : function(){
24305         this.selectRow(0);
24306     },
24307
24308     /**
24309      * Select the last row.
24310      * @param {Boolean} keepExisting (optional) True to keep existing selections
24311      */
24312     selectLastRow : function(keepExisting){
24313         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24314         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24315     },
24316
24317     /**
24318      * Selects the row immediately following the last selected row.
24319      * @param {Boolean} keepExisting (optional) True to keep existing selections
24320      */
24321     selectNext : function(keepExisting)
24322     {
24323             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24324             this.selectRow(this.last+1, keepExisting);
24325             this.grid.getView().focusRow(this.last);
24326         }
24327     },
24328
24329     /**
24330      * Selects the row that precedes the last selected row.
24331      * @param {Boolean} keepExisting (optional) True to keep existing selections
24332      */
24333     selectPrevious : function(keepExisting){
24334         if(this.last){
24335             this.selectRow(this.last-1, keepExisting);
24336             this.grid.getView().focusRow(this.last);
24337         }
24338     },
24339
24340     /**
24341      * Returns the selected records
24342      * @return {Array} Array of selected records
24343      */
24344     getSelections : function(){
24345         return [].concat(this.selections.items);
24346     },
24347
24348     /**
24349      * Returns the first selected record.
24350      * @return {Record}
24351      */
24352     getSelected : function(){
24353         return this.selections.itemAt(0);
24354     },
24355
24356
24357     /**
24358      * Clears all selections.
24359      */
24360     clearSelections : function(fast)
24361     {
24362         if(this.locked) {
24363             return;
24364         }
24365         if(fast !== true){
24366                 var ds = this.grid.store;
24367             var s = this.selections;
24368             s.each(function(r){
24369                 this.deselectRow(ds.indexOfId(r.id));
24370             }, this);
24371             s.clear();
24372         }else{
24373             this.selections.clear();
24374         }
24375         this.last = false;
24376     },
24377
24378
24379     /**
24380      * Selects all rows.
24381      */
24382     selectAll : function(){
24383         if(this.locked) {
24384             return;
24385         }
24386         this.selections.clear();
24387         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24388             this.selectRow(i, true);
24389         }
24390     },
24391
24392     /**
24393      * Returns True if there is a selection.
24394      * @return {Boolean}
24395      */
24396     hasSelection : function(){
24397         return this.selections.length > 0;
24398     },
24399
24400     /**
24401      * Returns True if the specified row is selected.
24402      * @param {Number/Record} record The record or index of the record to check
24403      * @return {Boolean}
24404      */
24405     isSelected : function(index){
24406             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24407         return (r && this.selections.key(r.id) ? true : false);
24408     },
24409
24410     /**
24411      * Returns True if the specified record id is selected.
24412      * @param {String} id The id of record to check
24413      * @return {Boolean}
24414      */
24415     isIdSelected : function(id){
24416         return (this.selections.key(id) ? true : false);
24417     },
24418
24419
24420     // private
24421     handleMouseDBClick : function(e, t){
24422         
24423     },
24424     // private
24425     handleMouseDown : function(e, t)
24426     {
24427             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24428         if(this.isLocked() || rowIndex < 0 ){
24429             return;
24430         };
24431         if(e.shiftKey && this.last !== false){
24432             var last = this.last;
24433             this.selectRange(last, rowIndex, e.ctrlKey);
24434             this.last = last; // reset the last
24435             t.focus();
24436     
24437         }else{
24438             var isSelected = this.isSelected(rowIndex);
24439             //Roo.log("select row:" + rowIndex);
24440             if(isSelected){
24441                 this.deselectRow(rowIndex);
24442             } else {
24443                         this.selectRow(rowIndex, true);
24444             }
24445     
24446             /*
24447                 if(e.button !== 0 && isSelected){
24448                 alert('rowIndex 2: ' + rowIndex);
24449                     view.focusRow(rowIndex);
24450                 }else if(e.ctrlKey && isSelected){
24451                     this.deselectRow(rowIndex);
24452                 }else if(!isSelected){
24453                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24454                     view.focusRow(rowIndex);
24455                 }
24456             */
24457         }
24458         this.fireEvent("afterselectionchange", this);
24459     },
24460     // private
24461     handleDragableRowClick :  function(grid, rowIndex, e) 
24462     {
24463         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24464             this.selectRow(rowIndex, false);
24465             grid.view.focusRow(rowIndex);
24466              this.fireEvent("afterselectionchange", this);
24467         }
24468     },
24469     
24470     /**
24471      * Selects multiple rows.
24472      * @param {Array} rows Array of the indexes of the row to select
24473      * @param {Boolean} keepExisting (optional) True to keep existing selections
24474      */
24475     selectRows : function(rows, keepExisting){
24476         if(!keepExisting){
24477             this.clearSelections();
24478         }
24479         for(var i = 0, len = rows.length; i < len; i++){
24480             this.selectRow(rows[i], true);
24481         }
24482     },
24483
24484     /**
24485      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24486      * @param {Number} startRow The index of the first row in the range
24487      * @param {Number} endRow The index of the last row in the range
24488      * @param {Boolean} keepExisting (optional) True to retain existing selections
24489      */
24490     selectRange : function(startRow, endRow, keepExisting){
24491         if(this.locked) {
24492             return;
24493         }
24494         if(!keepExisting){
24495             this.clearSelections();
24496         }
24497         if(startRow <= endRow){
24498             for(var i = startRow; i <= endRow; i++){
24499                 this.selectRow(i, true);
24500             }
24501         }else{
24502             for(var i = startRow; i >= endRow; i--){
24503                 this.selectRow(i, true);
24504             }
24505         }
24506     },
24507
24508     /**
24509      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24510      * @param {Number} startRow The index of the first row in the range
24511      * @param {Number} endRow The index of the last row in the range
24512      */
24513     deselectRange : function(startRow, endRow, preventViewNotify){
24514         if(this.locked) {
24515             return;
24516         }
24517         for(var i = startRow; i <= endRow; i++){
24518             this.deselectRow(i, preventViewNotify);
24519         }
24520     },
24521
24522     /**
24523      * Selects a row.
24524      * @param {Number} row The index of the row to select
24525      * @param {Boolean} keepExisting (optional) True to keep existing selections
24526      */
24527     selectRow : function(index, keepExisting, preventViewNotify)
24528     {
24529             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24530             return;
24531         }
24532         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24533             if(!keepExisting || this.singleSelect){
24534                 this.clearSelections();
24535             }
24536             
24537             var r = this.grid.store.getAt(index);
24538             //console.log('selectRow - record id :' + r.id);
24539             
24540             this.selections.add(r);
24541             this.last = this.lastActive = index;
24542             if(!preventViewNotify){
24543                 var proxy = new Roo.Element(
24544                                 this.grid.getRowDom(index)
24545                 );
24546                 proxy.addClass('bg-info info');
24547             }
24548             this.fireEvent("rowselect", this, index, r);
24549             this.fireEvent("selectionchange", this);
24550         }
24551     },
24552
24553     /**
24554      * Deselects a row.
24555      * @param {Number} row The index of the row to deselect
24556      */
24557     deselectRow : function(index, preventViewNotify)
24558     {
24559         if(this.locked) {
24560             return;
24561         }
24562         if(this.last == index){
24563             this.last = false;
24564         }
24565         if(this.lastActive == index){
24566             this.lastActive = false;
24567         }
24568         
24569         var r = this.grid.store.getAt(index);
24570         if (!r) {
24571             return;
24572         }
24573         
24574         this.selections.remove(r);
24575         //.console.log('deselectRow - record id :' + r.id);
24576         if(!preventViewNotify){
24577         
24578             var proxy = new Roo.Element(
24579                 this.grid.getRowDom(index)
24580             );
24581             proxy.removeClass('bg-info info');
24582         }
24583         this.fireEvent("rowdeselect", this, index);
24584         this.fireEvent("selectionchange", this);
24585     },
24586
24587     // private
24588     restoreLast : function(){
24589         if(this._last){
24590             this.last = this._last;
24591         }
24592     },
24593
24594     // private
24595     acceptsNav : function(row, col, cm){
24596         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24597     },
24598
24599     // private
24600     onEditorKey : function(field, e){
24601         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24602         if(k == e.TAB){
24603             e.stopEvent();
24604             ed.completeEdit();
24605             if(e.shiftKey){
24606                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24607             }else{
24608                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24609             }
24610         }else if(k == e.ENTER && !e.ctrlKey){
24611             e.stopEvent();
24612             ed.completeEdit();
24613             if(e.shiftKey){
24614                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24615             }else{
24616                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24617             }
24618         }else if(k == e.ESC){
24619             ed.cancelEdit();
24620         }
24621         if(newCell){
24622             g.startEditing(newCell[0], newCell[1]);
24623         }
24624     }
24625 });
24626 /*
24627  * Based on:
24628  * Ext JS Library 1.1.1
24629  * Copyright(c) 2006-2007, Ext JS, LLC.
24630  *
24631  * Originally Released Under LGPL - original licence link has changed is not relivant.
24632  *
24633  * Fork - LGPL
24634  * <script type="text/javascript">
24635  */
24636  
24637 /**
24638  * @class Roo.bootstrap.PagingToolbar
24639  * @extends Roo.bootstrap.NavSimplebar
24640  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24641  * @constructor
24642  * Create a new PagingToolbar
24643  * @param {Object} config The config object
24644  * @param {Roo.data.Store} store
24645  */
24646 Roo.bootstrap.PagingToolbar = function(config)
24647 {
24648     // old args format still supported... - xtype is prefered..
24649         // created from xtype...
24650     
24651     this.ds = config.dataSource;
24652     
24653     if (config.store && !this.ds) {
24654         this.store= Roo.factory(config.store, Roo.data);
24655         this.ds = this.store;
24656         this.ds.xmodule = this.xmodule || false;
24657     }
24658     
24659     this.toolbarItems = [];
24660     if (config.items) {
24661         this.toolbarItems = config.items;
24662     }
24663     
24664     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24665     
24666     this.cursor = 0;
24667     
24668     if (this.ds) { 
24669         this.bind(this.ds);
24670     }
24671     
24672     if (Roo.bootstrap.version == 4) {
24673         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24674     } else {
24675         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24676     }
24677     
24678 };
24679
24680 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24681     /**
24682      * @cfg {Roo.data.Store} dataSource
24683      * The underlying data store providing the paged data
24684      */
24685     /**
24686      * @cfg {String/HTMLElement/Element} container
24687      * container The id or element that will contain the toolbar
24688      */
24689     /**
24690      * @cfg {Boolean} displayInfo
24691      * True to display the displayMsg (defaults to false)
24692      */
24693     /**
24694      * @cfg {Number} pageSize
24695      * The number of records to display per page (defaults to 20)
24696      */
24697     pageSize: 20,
24698     /**
24699      * @cfg {String} displayMsg
24700      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24701      */
24702     displayMsg : 'Displaying {0} - {1} of {2}',
24703     /**
24704      * @cfg {String} emptyMsg
24705      * The message to display when no records are found (defaults to "No data to display")
24706      */
24707     emptyMsg : 'No data to display',
24708     /**
24709      * Customizable piece of the default paging text (defaults to "Page")
24710      * @type String
24711      */
24712     beforePageText : "Page",
24713     /**
24714      * Customizable piece of the default paging text (defaults to "of %0")
24715      * @type String
24716      */
24717     afterPageText : "of {0}",
24718     /**
24719      * Customizable piece of the default paging text (defaults to "First Page")
24720      * @type String
24721      */
24722     firstText : "First Page",
24723     /**
24724      * Customizable piece of the default paging text (defaults to "Previous Page")
24725      * @type String
24726      */
24727     prevText : "Previous Page",
24728     /**
24729      * Customizable piece of the default paging text (defaults to "Next Page")
24730      * @type String
24731      */
24732     nextText : "Next Page",
24733     /**
24734      * Customizable piece of the default paging text (defaults to "Last Page")
24735      * @type String
24736      */
24737     lastText : "Last Page",
24738     /**
24739      * Customizable piece of the default paging text (defaults to "Refresh")
24740      * @type String
24741      */
24742     refreshText : "Refresh",
24743
24744     buttons : false,
24745     // private
24746     onRender : function(ct, position) 
24747     {
24748         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24749         this.navgroup.parentId = this.id;
24750         this.navgroup.onRender(this.el, null);
24751         // add the buttons to the navgroup
24752         
24753         if(this.displayInfo){
24754             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24755             this.displayEl = this.el.select('.x-paging-info', true).first();
24756 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24757 //            this.displayEl = navel.el.select('span',true).first();
24758         }
24759         
24760         var _this = this;
24761         
24762         if(this.buttons){
24763             Roo.each(_this.buttons, function(e){ // this might need to use render????
24764                Roo.factory(e).render(_this.el);
24765             });
24766         }
24767             
24768         Roo.each(_this.toolbarItems, function(e) {
24769             _this.navgroup.addItem(e);
24770         });
24771         
24772         
24773         this.first = this.navgroup.addItem({
24774             tooltip: this.firstText,
24775             cls: "prev btn-outline-secondary",
24776             html : ' <i class="fa fa-step-backward"></i>',
24777             disabled: true,
24778             preventDefault: true,
24779             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24780         });
24781         
24782         this.prev =  this.navgroup.addItem({
24783             tooltip: this.prevText,
24784             cls: "prev btn-outline-secondary",
24785             html : ' <i class="fa fa-backward"></i>',
24786             disabled: true,
24787             preventDefault: true,
24788             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24789         });
24790     //this.addSeparator();
24791         
24792         
24793         var field = this.navgroup.addItem( {
24794             tagtype : 'span',
24795             cls : 'x-paging-position  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-step-refresh"></i>',
24831
24832             preventDefault: true,
24833             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24834         });
24835         
24836     },
24837
24838     // private
24839     updateInfo : function(){
24840         if(this.displayEl){
24841             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24842             var msg = count == 0 ?
24843                 this.emptyMsg :
24844                 String.format(
24845                     this.displayMsg,
24846                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24847                 );
24848             this.displayEl.update(msg);
24849         }
24850     },
24851
24852     // private
24853     onLoad : function(ds, r, o)
24854     {
24855         this.cursor = o.params.start ? o.params.start : 0;
24856         
24857         var d = this.getPageData(),
24858             ap = d.activePage,
24859             ps = d.pages;
24860         
24861         
24862         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24863         this.field.dom.value = ap;
24864         this.first.setDisabled(ap == 1);
24865         this.prev.setDisabled(ap == 1);
24866         this.next.setDisabled(ap == ps);
24867         this.last.setDisabled(ap == ps);
24868         this.loading.enable();
24869         this.updateInfo();
24870     },
24871
24872     // private
24873     getPageData : function(){
24874         var total = this.ds.getTotalCount();
24875         return {
24876             total : total,
24877             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24878             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24879         };
24880     },
24881
24882     // private
24883     onLoadError : function(){
24884         this.loading.enable();
24885     },
24886
24887     // private
24888     onPagingKeydown : function(e){
24889         var k = e.getKey();
24890         var d = this.getPageData();
24891         if(k == e.RETURN){
24892             var v = this.field.dom.value, pageNum;
24893             if(!v || isNaN(pageNum = parseInt(v, 10))){
24894                 this.field.dom.value = d.activePage;
24895                 return;
24896             }
24897             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24898             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24899             e.stopEvent();
24900         }
24901         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))
24902         {
24903           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24904           this.field.dom.value = pageNum;
24905           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24906           e.stopEvent();
24907         }
24908         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24909         {
24910           var v = this.field.dom.value, pageNum; 
24911           var increment = (e.shiftKey) ? 10 : 1;
24912           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24913                 increment *= -1;
24914           }
24915           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24916             this.field.dom.value = d.activePage;
24917             return;
24918           }
24919           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24920           {
24921             this.field.dom.value = parseInt(v, 10) + increment;
24922             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24923             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24924           }
24925           e.stopEvent();
24926         }
24927     },
24928
24929     // private
24930     beforeLoad : function(){
24931         if(this.loading){
24932             this.loading.disable();
24933         }
24934     },
24935
24936     // private
24937     onClick : function(which){
24938         
24939         var ds = this.ds;
24940         if (!ds) {
24941             return;
24942         }
24943         
24944         switch(which){
24945             case "first":
24946                 ds.load({params:{start: 0, limit: this.pageSize}});
24947             break;
24948             case "prev":
24949                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24950             break;
24951             case "next":
24952                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24953             break;
24954             case "last":
24955                 var total = ds.getTotalCount();
24956                 var extra = total % this.pageSize;
24957                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24958                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24959             break;
24960             case "refresh":
24961                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24962             break;
24963         }
24964     },
24965
24966     /**
24967      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24968      * @param {Roo.data.Store} store The data store to unbind
24969      */
24970     unbind : function(ds){
24971         ds.un("beforeload", this.beforeLoad, this);
24972         ds.un("load", this.onLoad, this);
24973         ds.un("loadexception", this.onLoadError, this);
24974         ds.un("remove", this.updateInfo, this);
24975         ds.un("add", this.updateInfo, this);
24976         this.ds = undefined;
24977     },
24978
24979     /**
24980      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24981      * @param {Roo.data.Store} store The data store to bind
24982      */
24983     bind : function(ds){
24984         ds.on("beforeload", this.beforeLoad, this);
24985         ds.on("load", this.onLoad, this);
24986         ds.on("loadexception", this.onLoadError, this);
24987         ds.on("remove", this.updateInfo, this);
24988         ds.on("add", this.updateInfo, this);
24989         this.ds = ds;
24990     }
24991 });/*
24992  * - LGPL
24993  *
24994  * element
24995  * 
24996  */
24997
24998 /**
24999  * @class Roo.bootstrap.MessageBar
25000  * @extends Roo.bootstrap.Component
25001  * Bootstrap MessageBar class
25002  * @cfg {String} html contents of the MessageBar
25003  * @cfg {String} weight (info | success | warning | danger) default info
25004  * @cfg {String} beforeClass insert the bar before the given class
25005  * @cfg {Boolean} closable (true | false) default false
25006  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25007  * 
25008  * @constructor
25009  * Create a new Element
25010  * @param {Object} config The config object
25011  */
25012
25013 Roo.bootstrap.MessageBar = function(config){
25014     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25015 };
25016
25017 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25018     
25019     html: '',
25020     weight: 'info',
25021     closable: false,
25022     fixed: false,
25023     beforeClass: 'bootstrap-sticky-wrap',
25024     
25025     getAutoCreate : function(){
25026         
25027         var cfg = {
25028             tag: 'div',
25029             cls: 'alert alert-dismissable alert-' + this.weight,
25030             cn: [
25031                 {
25032                     tag: 'span',
25033                     cls: 'message',
25034                     html: this.html || ''
25035                 }
25036             ]
25037         };
25038         
25039         if(this.fixed){
25040             cfg.cls += ' alert-messages-fixed';
25041         }
25042         
25043         if(this.closable){
25044             cfg.cn.push({
25045                 tag: 'button',
25046                 cls: 'close',
25047                 html: 'x'
25048             });
25049         }
25050         
25051         return cfg;
25052     },
25053     
25054     onRender : function(ct, position)
25055     {
25056         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25057         
25058         if(!this.el){
25059             var cfg = Roo.apply({},  this.getAutoCreate());
25060             cfg.id = Roo.id();
25061             
25062             if (this.cls) {
25063                 cfg.cls += ' ' + this.cls;
25064             }
25065             if (this.style) {
25066                 cfg.style = this.style;
25067             }
25068             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25069             
25070             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25071         }
25072         
25073         this.el.select('>button.close').on('click', this.hide, this);
25074         
25075     },
25076     
25077     show : function()
25078     {
25079         if (!this.rendered) {
25080             this.render();
25081         }
25082         
25083         this.el.show();
25084         
25085         this.fireEvent('show', this);
25086         
25087     },
25088     
25089     hide : function()
25090     {
25091         if (!this.rendered) {
25092             this.render();
25093         }
25094         
25095         this.el.hide();
25096         
25097         this.fireEvent('hide', this);
25098     },
25099     
25100     update : function()
25101     {
25102 //        var e = this.el.dom.firstChild;
25103 //        
25104 //        if(this.closable){
25105 //            e = e.nextSibling;
25106 //        }
25107 //        
25108 //        e.data = this.html || '';
25109
25110         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25111     }
25112    
25113 });
25114
25115  
25116
25117      /*
25118  * - LGPL
25119  *
25120  * Graph
25121  * 
25122  */
25123
25124
25125 /**
25126  * @class Roo.bootstrap.Graph
25127  * @extends Roo.bootstrap.Component
25128  * Bootstrap Graph class
25129 > Prameters
25130  -sm {number} sm 4
25131  -md {number} md 5
25132  @cfg {String} graphtype  bar | vbar | pie
25133  @cfg {number} g_x coodinator | centre x (pie)
25134  @cfg {number} g_y coodinator | centre y (pie)
25135  @cfg {number} g_r radius (pie)
25136  @cfg {number} g_height height of the chart (respected by all elements in the set)
25137  @cfg {number} g_width width of the chart (respected by all elements in the set)
25138  @cfg {Object} title The title of the chart
25139     
25140  -{Array}  values
25141  -opts (object) options for the chart 
25142      o {
25143      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25144      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25145      o vgutter (number)
25146      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.
25147      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25148      o to
25149      o stretch (boolean)
25150      o }
25151  -opts (object) options for the pie
25152      o{
25153      o cut
25154      o startAngle (number)
25155      o endAngle (number)
25156      } 
25157  *
25158  * @constructor
25159  * Create a new Input
25160  * @param {Object} config The config object
25161  */
25162
25163 Roo.bootstrap.Graph = function(config){
25164     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25165     
25166     this.addEvents({
25167         // img events
25168         /**
25169          * @event click
25170          * The img click event for the img.
25171          * @param {Roo.EventObject} e
25172          */
25173         "click" : true
25174     });
25175 };
25176
25177 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25178     
25179     sm: 4,
25180     md: 5,
25181     graphtype: 'bar',
25182     g_height: 250,
25183     g_width: 400,
25184     g_x: 50,
25185     g_y: 50,
25186     g_r: 30,
25187     opts:{
25188         //g_colors: this.colors,
25189         g_type: 'soft',
25190         g_gutter: '20%'
25191
25192     },
25193     title : false,
25194
25195     getAutoCreate : function(){
25196         
25197         var cfg = {
25198             tag: 'div',
25199             html : null
25200         };
25201         
25202         
25203         return  cfg;
25204     },
25205
25206     onRender : function(ct,position){
25207         
25208         
25209         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25210         
25211         if (typeof(Raphael) == 'undefined') {
25212             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25213             return;
25214         }
25215         
25216         this.raphael = Raphael(this.el.dom);
25217         
25218                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25219                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25220                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25221                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25222                 /*
25223                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25224                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25225                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25226                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25227                 
25228                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25229                 r.barchart(330, 10, 300, 220, data1);
25230                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25231                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25232                 */
25233                 
25234                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25235                 // r.barchart(30, 30, 560, 250,  xdata, {
25236                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25237                 //     axis : "0 0 1 1",
25238                 //     axisxlabels :  xdata
25239                 //     //yvalues : cols,
25240                    
25241                 // });
25242 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25243 //        
25244 //        this.load(null,xdata,{
25245 //                axis : "0 0 1 1",
25246 //                axisxlabels :  xdata
25247 //                });
25248
25249     },
25250
25251     load : function(graphtype,xdata,opts)
25252     {
25253         this.raphael.clear();
25254         if(!graphtype) {
25255             graphtype = this.graphtype;
25256         }
25257         if(!opts){
25258             opts = this.opts;
25259         }
25260         var r = this.raphael,
25261             fin = function () {
25262                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25263             },
25264             fout = function () {
25265                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25266             },
25267             pfin = function() {
25268                 this.sector.stop();
25269                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25270
25271                 if (this.label) {
25272                     this.label[0].stop();
25273                     this.label[0].attr({ r: 7.5 });
25274                     this.label[1].attr({ "font-weight": 800 });
25275                 }
25276             },
25277             pfout = function() {
25278                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25279
25280                 if (this.label) {
25281                     this.label[0].animate({ r: 5 }, 500, "bounce");
25282                     this.label[1].attr({ "font-weight": 400 });
25283                 }
25284             };
25285
25286         switch(graphtype){
25287             case 'bar':
25288                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25289                 break;
25290             case 'hbar':
25291                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25292                 break;
25293             case 'pie':
25294 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25295 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25296 //            
25297                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25298                 
25299                 break;
25300
25301         }
25302         
25303         if(this.title){
25304             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25305         }
25306         
25307     },
25308     
25309     setTitle: function(o)
25310     {
25311         this.title = o;
25312     },
25313     
25314     initEvents: function() {
25315         
25316         if(!this.href){
25317             this.el.on('click', this.onClick, this);
25318         }
25319     },
25320     
25321     onClick : function(e)
25322     {
25323         Roo.log('img onclick');
25324         this.fireEvent('click', this, e);
25325     }
25326    
25327 });
25328
25329  
25330 /*
25331  * - LGPL
25332  *
25333  * numberBox
25334  * 
25335  */
25336 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25337
25338 /**
25339  * @class Roo.bootstrap.dash.NumberBox
25340  * @extends Roo.bootstrap.Component
25341  * Bootstrap NumberBox class
25342  * @cfg {String} headline Box headline
25343  * @cfg {String} content Box content
25344  * @cfg {String} icon Box icon
25345  * @cfg {String} footer Footer text
25346  * @cfg {String} fhref Footer href
25347  * 
25348  * @constructor
25349  * Create a new NumberBox
25350  * @param {Object} config The config object
25351  */
25352
25353
25354 Roo.bootstrap.dash.NumberBox = function(config){
25355     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25356     
25357 };
25358
25359 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25360     
25361     headline : '',
25362     content : '',
25363     icon : '',
25364     footer : '',
25365     fhref : '',
25366     ficon : '',
25367     
25368     getAutoCreate : function(){
25369         
25370         var cfg = {
25371             tag : 'div',
25372             cls : 'small-box ',
25373             cn : [
25374                 {
25375                     tag : 'div',
25376                     cls : 'inner',
25377                     cn :[
25378                         {
25379                             tag : 'h3',
25380                             cls : 'roo-headline',
25381                             html : this.headline
25382                         },
25383                         {
25384                             tag : 'p',
25385                             cls : 'roo-content',
25386                             html : this.content
25387                         }
25388                     ]
25389                 }
25390             ]
25391         };
25392         
25393         if(this.icon){
25394             cfg.cn.push({
25395                 tag : 'div',
25396                 cls : 'icon',
25397                 cn :[
25398                     {
25399                         tag : 'i',
25400                         cls : 'ion ' + this.icon
25401                     }
25402                 ]
25403             });
25404         }
25405         
25406         if(this.footer){
25407             var footer = {
25408                 tag : 'a',
25409                 cls : 'small-box-footer',
25410                 href : this.fhref || '#',
25411                 html : this.footer
25412             };
25413             
25414             cfg.cn.push(footer);
25415             
25416         }
25417         
25418         return  cfg;
25419     },
25420
25421     onRender : function(ct,position){
25422         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25423
25424
25425        
25426                 
25427     },
25428
25429     setHeadline: function (value)
25430     {
25431         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25432     },
25433     
25434     setFooter: function (value, href)
25435     {
25436         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25437         
25438         if(href){
25439             this.el.select('a.small-box-footer',true).first().attr('href', href);
25440         }
25441         
25442     },
25443
25444     setContent: function (value)
25445     {
25446         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25447     },
25448
25449     initEvents: function() 
25450     {   
25451         
25452     }
25453     
25454 });
25455
25456  
25457 /*
25458  * - LGPL
25459  *
25460  * TabBox
25461  * 
25462  */
25463 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25464
25465 /**
25466  * @class Roo.bootstrap.dash.TabBox
25467  * @extends Roo.bootstrap.Component
25468  * Bootstrap TabBox class
25469  * @cfg {String} title Title of the TabBox
25470  * @cfg {String} icon Icon of the TabBox
25471  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25472  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25473  * 
25474  * @constructor
25475  * Create a new TabBox
25476  * @param {Object} config The config object
25477  */
25478
25479
25480 Roo.bootstrap.dash.TabBox = function(config){
25481     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25482     this.addEvents({
25483         // raw events
25484         /**
25485          * @event addpane
25486          * When a pane is added
25487          * @param {Roo.bootstrap.dash.TabPane} pane
25488          */
25489         "addpane" : true,
25490         /**
25491          * @event activatepane
25492          * When a pane is activated
25493          * @param {Roo.bootstrap.dash.TabPane} pane
25494          */
25495         "activatepane" : true
25496         
25497          
25498     });
25499     
25500     this.panes = [];
25501 };
25502
25503 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25504
25505     title : '',
25506     icon : false,
25507     showtabs : true,
25508     tabScrollable : false,
25509     
25510     getChildContainer : function()
25511     {
25512         return this.el.select('.tab-content', true).first();
25513     },
25514     
25515     getAutoCreate : function(){
25516         
25517         var header = {
25518             tag: 'li',
25519             cls: 'pull-left header',
25520             html: this.title,
25521             cn : []
25522         };
25523         
25524         if(this.icon){
25525             header.cn.push({
25526                 tag: 'i',
25527                 cls: 'fa ' + this.icon
25528             });
25529         }
25530         
25531         var h = {
25532             tag: 'ul',
25533             cls: 'nav nav-tabs pull-right',
25534             cn: [
25535                 header
25536             ]
25537         };
25538         
25539         if(this.tabScrollable){
25540             h = {
25541                 tag: 'div',
25542                 cls: 'tab-header',
25543                 cn: [
25544                     {
25545                         tag: 'ul',
25546                         cls: 'nav nav-tabs pull-right',
25547                         cn: [
25548                             header
25549                         ]
25550                     }
25551                 ]
25552             };
25553         }
25554         
25555         var cfg = {
25556             tag: 'div',
25557             cls: 'nav-tabs-custom',
25558             cn: [
25559                 h,
25560                 {
25561                     tag: 'div',
25562                     cls: 'tab-content no-padding',
25563                     cn: []
25564                 }
25565             ]
25566         };
25567
25568         return  cfg;
25569     },
25570     initEvents : function()
25571     {
25572         //Roo.log('add add pane handler');
25573         this.on('addpane', this.onAddPane, this);
25574     },
25575      /**
25576      * Updates the box title
25577      * @param {String} html to set the title to.
25578      */
25579     setTitle : function(value)
25580     {
25581         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25582     },
25583     onAddPane : function(pane)
25584     {
25585         this.panes.push(pane);
25586         //Roo.log('addpane');
25587         //Roo.log(pane);
25588         // tabs are rendere left to right..
25589         if(!this.showtabs){
25590             return;
25591         }
25592         
25593         var ctr = this.el.select('.nav-tabs', true).first();
25594          
25595          
25596         var existing = ctr.select('.nav-tab',true);
25597         var qty = existing.getCount();;
25598         
25599         
25600         var tab = ctr.createChild({
25601             tag : 'li',
25602             cls : 'nav-tab' + (qty ? '' : ' active'),
25603             cn : [
25604                 {
25605                     tag : 'a',
25606                     href:'#',
25607                     html : pane.title
25608                 }
25609             ]
25610         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25611         pane.tab = tab;
25612         
25613         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25614         if (!qty) {
25615             pane.el.addClass('active');
25616         }
25617         
25618                 
25619     },
25620     onTabClick : function(ev,un,ob,pane)
25621     {
25622         //Roo.log('tab - prev default');
25623         ev.preventDefault();
25624         
25625         
25626         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25627         pane.tab.addClass('active');
25628         //Roo.log(pane.title);
25629         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25630         // technically we should have a deactivate event.. but maybe add later.
25631         // and it should not de-activate the selected tab...
25632         this.fireEvent('activatepane', pane);
25633         pane.el.addClass('active');
25634         pane.fireEvent('activate');
25635         
25636         
25637     },
25638     
25639     getActivePane : function()
25640     {
25641         var r = false;
25642         Roo.each(this.panes, function(p) {
25643             if(p.el.hasClass('active')){
25644                 r = p;
25645                 return false;
25646             }
25647             
25648             return;
25649         });
25650         
25651         return r;
25652     }
25653     
25654     
25655 });
25656
25657  
25658 /*
25659  * - LGPL
25660  *
25661  * Tab pane
25662  * 
25663  */
25664 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25665 /**
25666  * @class Roo.bootstrap.TabPane
25667  * @extends Roo.bootstrap.Component
25668  * Bootstrap TabPane class
25669  * @cfg {Boolean} active (false | true) Default false
25670  * @cfg {String} title title of panel
25671
25672  * 
25673  * @constructor
25674  * Create a new TabPane
25675  * @param {Object} config The config object
25676  */
25677
25678 Roo.bootstrap.dash.TabPane = function(config){
25679     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25680     
25681     this.addEvents({
25682         // raw events
25683         /**
25684          * @event activate
25685          * When a pane is activated
25686          * @param {Roo.bootstrap.dash.TabPane} pane
25687          */
25688         "activate" : true
25689          
25690     });
25691 };
25692
25693 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25694     
25695     active : false,
25696     title : '',
25697     
25698     // the tabBox that this is attached to.
25699     tab : false,
25700      
25701     getAutoCreate : function() 
25702     {
25703         var cfg = {
25704             tag: 'div',
25705             cls: 'tab-pane'
25706         };
25707         
25708         if(this.active){
25709             cfg.cls += ' active';
25710         }
25711         
25712         return cfg;
25713     },
25714     initEvents  : function()
25715     {
25716         //Roo.log('trigger add pane handler');
25717         this.parent().fireEvent('addpane', this)
25718     },
25719     
25720      /**
25721      * Updates the tab title 
25722      * @param {String} html to set the title to.
25723      */
25724     setTitle: function(str)
25725     {
25726         if (!this.tab) {
25727             return;
25728         }
25729         this.title = str;
25730         this.tab.select('a', true).first().dom.innerHTML = str;
25731         
25732     }
25733     
25734     
25735     
25736 });
25737
25738  
25739
25740
25741  /*
25742  * - LGPL
25743  *
25744  * menu
25745  * 
25746  */
25747 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25748
25749 /**
25750  * @class Roo.bootstrap.menu.Menu
25751  * @extends Roo.bootstrap.Component
25752  * Bootstrap Menu class - container for Menu
25753  * @cfg {String} html Text of the menu
25754  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25755  * @cfg {String} icon Font awesome icon
25756  * @cfg {String} pos Menu align to (top | bottom) default bottom
25757  * 
25758  * 
25759  * @constructor
25760  * Create a new Menu
25761  * @param {Object} config The config object
25762  */
25763
25764
25765 Roo.bootstrap.menu.Menu = function(config){
25766     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25767     
25768     this.addEvents({
25769         /**
25770          * @event beforeshow
25771          * Fires before this menu is displayed
25772          * @param {Roo.bootstrap.menu.Menu} this
25773          */
25774         beforeshow : true,
25775         /**
25776          * @event beforehide
25777          * Fires before this menu is hidden
25778          * @param {Roo.bootstrap.menu.Menu} this
25779          */
25780         beforehide : true,
25781         /**
25782          * @event show
25783          * Fires after this menu is displayed
25784          * @param {Roo.bootstrap.menu.Menu} this
25785          */
25786         show : true,
25787         /**
25788          * @event hide
25789          * Fires after this menu is hidden
25790          * @param {Roo.bootstrap.menu.Menu} this
25791          */
25792         hide : true,
25793         /**
25794          * @event click
25795          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25796          * @param {Roo.bootstrap.menu.Menu} this
25797          * @param {Roo.EventObject} e
25798          */
25799         click : true
25800     });
25801     
25802 };
25803
25804 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25805     
25806     submenu : false,
25807     html : '',
25808     weight : 'default',
25809     icon : false,
25810     pos : 'bottom',
25811     
25812     
25813     getChildContainer : function() {
25814         if(this.isSubMenu){
25815             return this.el;
25816         }
25817         
25818         return this.el.select('ul.dropdown-menu', true).first();  
25819     },
25820     
25821     getAutoCreate : function()
25822     {
25823         var text = [
25824             {
25825                 tag : 'span',
25826                 cls : 'roo-menu-text',
25827                 html : this.html
25828             }
25829         ];
25830         
25831         if(this.icon){
25832             text.unshift({
25833                 tag : 'i',
25834                 cls : 'fa ' + this.icon
25835             })
25836         }
25837         
25838         
25839         var cfg = {
25840             tag : 'div',
25841             cls : 'btn-group',
25842             cn : [
25843                 {
25844                     tag : 'button',
25845                     cls : 'dropdown-button btn btn-' + this.weight,
25846                     cn : text
25847                 },
25848                 {
25849                     tag : 'button',
25850                     cls : 'dropdown-toggle btn btn-' + this.weight,
25851                     cn : [
25852                         {
25853                             tag : 'span',
25854                             cls : 'caret'
25855                         }
25856                     ]
25857                 },
25858                 {
25859                     tag : 'ul',
25860                     cls : 'dropdown-menu'
25861                 }
25862             ]
25863             
25864         };
25865         
25866         if(this.pos == 'top'){
25867             cfg.cls += ' dropup';
25868         }
25869         
25870         if(this.isSubMenu){
25871             cfg = {
25872                 tag : 'ul',
25873                 cls : 'dropdown-menu'
25874             }
25875         }
25876         
25877         return cfg;
25878     },
25879     
25880     onRender : function(ct, position)
25881     {
25882         this.isSubMenu = ct.hasClass('dropdown-submenu');
25883         
25884         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25885     },
25886     
25887     initEvents : function() 
25888     {
25889         if(this.isSubMenu){
25890             return;
25891         }
25892         
25893         this.hidden = true;
25894         
25895         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25896         this.triggerEl.on('click', this.onTriggerPress, this);
25897         
25898         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25899         this.buttonEl.on('click', this.onClick, this);
25900         
25901     },
25902     
25903     list : function()
25904     {
25905         if(this.isSubMenu){
25906             return this.el;
25907         }
25908         
25909         return this.el.select('ul.dropdown-menu', true).first();
25910     },
25911     
25912     onClick : function(e)
25913     {
25914         this.fireEvent("click", this, e);
25915     },
25916     
25917     onTriggerPress  : function(e)
25918     {   
25919         if (this.isVisible()) {
25920             this.hide();
25921         } else {
25922             this.show();
25923         }
25924     },
25925     
25926     isVisible : function(){
25927         return !this.hidden;
25928     },
25929     
25930     show : function()
25931     {
25932         this.fireEvent("beforeshow", this);
25933         
25934         this.hidden = false;
25935         this.el.addClass('open');
25936         
25937         Roo.get(document).on("mouseup", this.onMouseUp, this);
25938         
25939         this.fireEvent("show", this);
25940         
25941         
25942     },
25943     
25944     hide : function()
25945     {
25946         this.fireEvent("beforehide", this);
25947         
25948         this.hidden = true;
25949         this.el.removeClass('open');
25950         
25951         Roo.get(document).un("mouseup", this.onMouseUp);
25952         
25953         this.fireEvent("hide", this);
25954     },
25955     
25956     onMouseUp : function()
25957     {
25958         this.hide();
25959     }
25960     
25961 });
25962
25963  
25964  /*
25965  * - LGPL
25966  *
25967  * menu item
25968  * 
25969  */
25970 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25971
25972 /**
25973  * @class Roo.bootstrap.menu.Item
25974  * @extends Roo.bootstrap.Component
25975  * Bootstrap MenuItem class
25976  * @cfg {Boolean} submenu (true | false) default false
25977  * @cfg {String} html text of the item
25978  * @cfg {String} href the link
25979  * @cfg {Boolean} disable (true | false) default false
25980  * @cfg {Boolean} preventDefault (true | false) default true
25981  * @cfg {String} icon Font awesome icon
25982  * @cfg {String} pos Submenu align to (left | right) default right 
25983  * 
25984  * 
25985  * @constructor
25986  * Create a new Item
25987  * @param {Object} config The config object
25988  */
25989
25990
25991 Roo.bootstrap.menu.Item = function(config){
25992     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25993     this.addEvents({
25994         /**
25995          * @event mouseover
25996          * Fires when the mouse is hovering over this menu
25997          * @param {Roo.bootstrap.menu.Item} this
25998          * @param {Roo.EventObject} e
25999          */
26000         mouseover : true,
26001         /**
26002          * @event mouseout
26003          * Fires when the mouse exits this menu
26004          * @param {Roo.bootstrap.menu.Item} this
26005          * @param {Roo.EventObject} e
26006          */
26007         mouseout : true,
26008         // raw events
26009         /**
26010          * @event click
26011          * The raw click event for the entire grid.
26012          * @param {Roo.EventObject} e
26013          */
26014         click : true
26015     });
26016 };
26017
26018 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26019     
26020     submenu : false,
26021     href : '',
26022     html : '',
26023     preventDefault: true,
26024     disable : false,
26025     icon : false,
26026     pos : 'right',
26027     
26028     getAutoCreate : function()
26029     {
26030         var text = [
26031             {
26032                 tag : 'span',
26033                 cls : 'roo-menu-item-text',
26034                 html : this.html
26035             }
26036         ];
26037         
26038         if(this.icon){
26039             text.unshift({
26040                 tag : 'i',
26041                 cls : 'fa ' + this.icon
26042             })
26043         }
26044         
26045         var cfg = {
26046             tag : 'li',
26047             cn : [
26048                 {
26049                     tag : 'a',
26050                     href : this.href || '#',
26051                     cn : text
26052                 }
26053             ]
26054         };
26055         
26056         if(this.disable){
26057             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26058         }
26059         
26060         if(this.submenu){
26061             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26062             
26063             if(this.pos == 'left'){
26064                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26065             }
26066         }
26067         
26068         return cfg;
26069     },
26070     
26071     initEvents : function() 
26072     {
26073         this.el.on('mouseover', this.onMouseOver, this);
26074         this.el.on('mouseout', this.onMouseOut, this);
26075         
26076         this.el.select('a', true).first().on('click', this.onClick, this);
26077         
26078     },
26079     
26080     onClick : function(e)
26081     {
26082         if(this.preventDefault){
26083             e.preventDefault();
26084         }
26085         
26086         this.fireEvent("click", this, e);
26087     },
26088     
26089     onMouseOver : function(e)
26090     {
26091         if(this.submenu && this.pos == 'left'){
26092             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26093         }
26094         
26095         this.fireEvent("mouseover", this, e);
26096     },
26097     
26098     onMouseOut : function(e)
26099     {
26100         this.fireEvent("mouseout", this, e);
26101     }
26102 });
26103
26104  
26105
26106  /*
26107  * - LGPL
26108  *
26109  * menu separator
26110  * 
26111  */
26112 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26113
26114 /**
26115  * @class Roo.bootstrap.menu.Separator
26116  * @extends Roo.bootstrap.Component
26117  * Bootstrap Separator class
26118  * 
26119  * @constructor
26120  * Create a new Separator
26121  * @param {Object} config The config object
26122  */
26123
26124
26125 Roo.bootstrap.menu.Separator = function(config){
26126     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26127 };
26128
26129 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26130     
26131     getAutoCreate : function(){
26132         var cfg = {
26133             tag : 'li',
26134             cls: 'divider'
26135         };
26136         
26137         return cfg;
26138     }
26139    
26140 });
26141
26142  
26143
26144  /*
26145  * - LGPL
26146  *
26147  * Tooltip
26148  * 
26149  */
26150
26151 /**
26152  * @class Roo.bootstrap.Tooltip
26153  * Bootstrap Tooltip class
26154  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26155  * to determine which dom element triggers the tooltip.
26156  * 
26157  * It needs to add support for additional attributes like tooltip-position
26158  * 
26159  * @constructor
26160  * Create a new Toolti
26161  * @param {Object} config The config object
26162  */
26163
26164 Roo.bootstrap.Tooltip = function(config){
26165     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26166     
26167     this.alignment = Roo.bootstrap.Tooltip.alignment;
26168     
26169     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26170         this.alignment = config.alignment;
26171     }
26172     
26173 };
26174
26175 Roo.apply(Roo.bootstrap.Tooltip, {
26176     /**
26177      * @function init initialize tooltip monitoring.
26178      * @static
26179      */
26180     currentEl : false,
26181     currentTip : false,
26182     currentRegion : false,
26183     
26184     //  init : delay?
26185     
26186     init : function()
26187     {
26188         Roo.get(document).on('mouseover', this.enter ,this);
26189         Roo.get(document).on('mouseout', this.leave, this);
26190          
26191         
26192         this.currentTip = new Roo.bootstrap.Tooltip();
26193     },
26194     
26195     enter : function(ev)
26196     {
26197         var dom = ev.getTarget();
26198         
26199         //Roo.log(['enter',dom]);
26200         var el = Roo.fly(dom);
26201         if (this.currentEl) {
26202             //Roo.log(dom);
26203             //Roo.log(this.currentEl);
26204             //Roo.log(this.currentEl.contains(dom));
26205             if (this.currentEl == el) {
26206                 return;
26207             }
26208             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26209                 return;
26210             }
26211
26212         }
26213         
26214         if (this.currentTip.el) {
26215             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26216         }    
26217         //Roo.log(ev);
26218         
26219         if(!el || el.dom == document){
26220             return;
26221         }
26222         
26223         var bindEl = el;
26224         
26225         // you can not look for children, as if el is the body.. then everythign is the child..
26226         if (!el.attr('tooltip')) { //
26227             if (!el.select("[tooltip]").elements.length) {
26228                 return;
26229             }
26230             // is the mouse over this child...?
26231             bindEl = el.select("[tooltip]").first();
26232             var xy = ev.getXY();
26233             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26234                 //Roo.log("not in region.");
26235                 return;
26236             }
26237             //Roo.log("child element over..");
26238             
26239         }
26240         this.currentEl = bindEl;
26241         this.currentTip.bind(bindEl);
26242         this.currentRegion = Roo.lib.Region.getRegion(dom);
26243         this.currentTip.enter();
26244         
26245     },
26246     leave : function(ev)
26247     {
26248         var dom = ev.getTarget();
26249         //Roo.log(['leave',dom]);
26250         if (!this.currentEl) {
26251             return;
26252         }
26253         
26254         
26255         if (dom != this.currentEl.dom) {
26256             return;
26257         }
26258         var xy = ev.getXY();
26259         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26260             return;
26261         }
26262         // only activate leave if mouse cursor is outside... bounding box..
26263         
26264         
26265         
26266         
26267         if (this.currentTip) {
26268             this.currentTip.leave();
26269         }
26270         //Roo.log('clear currentEl');
26271         this.currentEl = false;
26272         
26273         
26274     },
26275     alignment : {
26276         'left' : ['r-l', [-2,0], 'right'],
26277         'right' : ['l-r', [2,0], 'left'],
26278         'bottom' : ['t-b', [0,2], 'top'],
26279         'top' : [ 'b-t', [0,-2], 'bottom']
26280     }
26281     
26282 });
26283
26284
26285 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26286     
26287     
26288     bindEl : false,
26289     
26290     delay : null, // can be { show : 300 , hide: 500}
26291     
26292     timeout : null,
26293     
26294     hoverState : null, //???
26295     
26296     placement : 'bottom', 
26297     
26298     alignment : false,
26299     
26300     getAutoCreate : function(){
26301     
26302         var cfg = {
26303            cls : 'tooltip',
26304            role : 'tooltip',
26305            cn : [
26306                 {
26307                     cls : 'tooltip-arrow'
26308                 },
26309                 {
26310                     cls : 'tooltip-inner'
26311                 }
26312            ]
26313         };
26314         
26315         return cfg;
26316     },
26317     bind : function(el)
26318     {
26319         this.bindEl = el;
26320     },
26321       
26322     
26323     enter : function () {
26324        
26325         if (this.timeout != null) {
26326             clearTimeout(this.timeout);
26327         }
26328         
26329         this.hoverState = 'in';
26330          //Roo.log("enter - show");
26331         if (!this.delay || !this.delay.show) {
26332             this.show();
26333             return;
26334         }
26335         var _t = this;
26336         this.timeout = setTimeout(function () {
26337             if (_t.hoverState == 'in') {
26338                 _t.show();
26339             }
26340         }, this.delay.show);
26341     },
26342     leave : function()
26343     {
26344         clearTimeout(this.timeout);
26345     
26346         this.hoverState = 'out';
26347          if (!this.delay || !this.delay.hide) {
26348             this.hide();
26349             return;
26350         }
26351        
26352         var _t = this;
26353         this.timeout = setTimeout(function () {
26354             //Roo.log("leave - timeout");
26355             
26356             if (_t.hoverState == 'out') {
26357                 _t.hide();
26358                 Roo.bootstrap.Tooltip.currentEl = false;
26359             }
26360         }, delay);
26361     },
26362     
26363     show : function (msg)
26364     {
26365         if (!this.el) {
26366             this.render(document.body);
26367         }
26368         // set content.
26369         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26370         
26371         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26372         
26373         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26374         
26375         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26376         
26377         var placement = typeof this.placement == 'function' ?
26378             this.placement.call(this, this.el, on_el) :
26379             this.placement;
26380             
26381         var autoToken = /\s?auto?\s?/i;
26382         var autoPlace = autoToken.test(placement);
26383         if (autoPlace) {
26384             placement = placement.replace(autoToken, '') || 'top';
26385         }
26386         
26387         //this.el.detach()
26388         //this.el.setXY([0,0]);
26389         this.el.show();
26390         //this.el.dom.style.display='block';
26391         
26392         //this.el.appendTo(on_el);
26393         
26394         var p = this.getPosition();
26395         var box = this.el.getBox();
26396         
26397         if (autoPlace) {
26398             // fixme..
26399         }
26400         
26401         var align = this.alignment[placement];
26402         
26403         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26404         
26405         if(placement == 'top' || placement == 'bottom'){
26406             if(xy[0] < 0){
26407                 placement = 'right';
26408             }
26409             
26410             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26411                 placement = 'left';
26412             }
26413             
26414             var scroll = Roo.select('body', true).first().getScroll();
26415             
26416             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26417                 placement = 'top';
26418             }
26419             
26420             align = this.alignment[placement];
26421         }
26422         
26423         this.el.alignTo(this.bindEl, align[0],align[1]);
26424         //var arrow = this.el.select('.arrow',true).first();
26425         //arrow.set(align[2], 
26426         
26427         this.el.addClass(placement);
26428         
26429         this.el.addClass('in fade');
26430         
26431         this.hoverState = null;
26432         
26433         if (this.el.hasClass('fade')) {
26434             // fade it?
26435         }
26436         
26437     },
26438     hide : function()
26439     {
26440          
26441         if (!this.el) {
26442             return;
26443         }
26444         //this.el.setXY([0,0]);
26445         this.el.removeClass('in');
26446         //this.el.hide();
26447         
26448     }
26449     
26450 });
26451  
26452
26453  /*
26454  * - LGPL
26455  *
26456  * Location Picker
26457  * 
26458  */
26459
26460 /**
26461  * @class Roo.bootstrap.LocationPicker
26462  * @extends Roo.bootstrap.Component
26463  * Bootstrap LocationPicker class
26464  * @cfg {Number} latitude Position when init default 0
26465  * @cfg {Number} longitude Position when init default 0
26466  * @cfg {Number} zoom default 15
26467  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26468  * @cfg {Boolean} mapTypeControl default false
26469  * @cfg {Boolean} disableDoubleClickZoom default false
26470  * @cfg {Boolean} scrollwheel default true
26471  * @cfg {Boolean} streetViewControl default false
26472  * @cfg {Number} radius default 0
26473  * @cfg {String} locationName
26474  * @cfg {Boolean} draggable default true
26475  * @cfg {Boolean} enableAutocomplete default false
26476  * @cfg {Boolean} enableReverseGeocode default true
26477  * @cfg {String} markerTitle
26478  * 
26479  * @constructor
26480  * Create a new LocationPicker
26481  * @param {Object} config The config object
26482  */
26483
26484
26485 Roo.bootstrap.LocationPicker = function(config){
26486     
26487     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26488     
26489     this.addEvents({
26490         /**
26491          * @event initial
26492          * Fires when the picker initialized.
26493          * @param {Roo.bootstrap.LocationPicker} this
26494          * @param {Google Location} location
26495          */
26496         initial : true,
26497         /**
26498          * @event positionchanged
26499          * Fires when the picker position changed.
26500          * @param {Roo.bootstrap.LocationPicker} this
26501          * @param {Google Location} location
26502          */
26503         positionchanged : true,
26504         /**
26505          * @event resize
26506          * Fires when the map resize.
26507          * @param {Roo.bootstrap.LocationPicker} this
26508          */
26509         resize : true,
26510         /**
26511          * @event show
26512          * Fires when the map show.
26513          * @param {Roo.bootstrap.LocationPicker} this
26514          */
26515         show : true,
26516         /**
26517          * @event hide
26518          * Fires when the map hide.
26519          * @param {Roo.bootstrap.LocationPicker} this
26520          */
26521         hide : true,
26522         /**
26523          * @event mapClick
26524          * Fires when click the map.
26525          * @param {Roo.bootstrap.LocationPicker} this
26526          * @param {Map event} e
26527          */
26528         mapClick : true,
26529         /**
26530          * @event mapRightClick
26531          * Fires when right click the map.
26532          * @param {Roo.bootstrap.LocationPicker} this
26533          * @param {Map event} e
26534          */
26535         mapRightClick : true,
26536         /**
26537          * @event markerClick
26538          * Fires when click the marker.
26539          * @param {Roo.bootstrap.LocationPicker} this
26540          * @param {Map event} e
26541          */
26542         markerClick : true,
26543         /**
26544          * @event markerRightClick
26545          * Fires when right click the marker.
26546          * @param {Roo.bootstrap.LocationPicker} this
26547          * @param {Map event} e
26548          */
26549         markerRightClick : true,
26550         /**
26551          * @event OverlayViewDraw
26552          * Fires when OverlayView Draw
26553          * @param {Roo.bootstrap.LocationPicker} this
26554          */
26555         OverlayViewDraw : true,
26556         /**
26557          * @event OverlayViewOnAdd
26558          * Fires when OverlayView Draw
26559          * @param {Roo.bootstrap.LocationPicker} this
26560          */
26561         OverlayViewOnAdd : true,
26562         /**
26563          * @event OverlayViewOnRemove
26564          * Fires when OverlayView Draw
26565          * @param {Roo.bootstrap.LocationPicker} this
26566          */
26567         OverlayViewOnRemove : true,
26568         /**
26569          * @event OverlayViewShow
26570          * Fires when OverlayView Draw
26571          * @param {Roo.bootstrap.LocationPicker} this
26572          * @param {Pixel} cpx
26573          */
26574         OverlayViewShow : true,
26575         /**
26576          * @event OverlayViewHide
26577          * Fires when OverlayView Draw
26578          * @param {Roo.bootstrap.LocationPicker} this
26579          */
26580         OverlayViewHide : true,
26581         /**
26582          * @event loadexception
26583          * Fires when load google lib failed.
26584          * @param {Roo.bootstrap.LocationPicker} this
26585          */
26586         loadexception : true
26587     });
26588         
26589 };
26590
26591 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26592     
26593     gMapContext: false,
26594     
26595     latitude: 0,
26596     longitude: 0,
26597     zoom: 15,
26598     mapTypeId: false,
26599     mapTypeControl: false,
26600     disableDoubleClickZoom: false,
26601     scrollwheel: true,
26602     streetViewControl: false,
26603     radius: 0,
26604     locationName: '',
26605     draggable: true,
26606     enableAutocomplete: false,
26607     enableReverseGeocode: true,
26608     markerTitle: '',
26609     
26610     getAutoCreate: function()
26611     {
26612
26613         var cfg = {
26614             tag: 'div',
26615             cls: 'roo-location-picker'
26616         };
26617         
26618         return cfg
26619     },
26620     
26621     initEvents: function(ct, position)
26622     {       
26623         if(!this.el.getWidth() || this.isApplied()){
26624             return;
26625         }
26626         
26627         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26628         
26629         this.initial();
26630     },
26631     
26632     initial: function()
26633     {
26634         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26635             this.fireEvent('loadexception', this);
26636             return;
26637         }
26638         
26639         if(!this.mapTypeId){
26640             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26641         }
26642         
26643         this.gMapContext = this.GMapContext();
26644         
26645         this.initOverlayView();
26646         
26647         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26648         
26649         var _this = this;
26650                 
26651         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26652             _this.setPosition(_this.gMapContext.marker.position);
26653         });
26654         
26655         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26656             _this.fireEvent('mapClick', this, event);
26657             
26658         });
26659
26660         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26661             _this.fireEvent('mapRightClick', this, event);
26662             
26663         });
26664         
26665         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26666             _this.fireEvent('markerClick', this, event);
26667             
26668         });
26669
26670         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26671             _this.fireEvent('markerRightClick', this, event);
26672             
26673         });
26674         
26675         this.setPosition(this.gMapContext.location);
26676         
26677         this.fireEvent('initial', this, this.gMapContext.location);
26678     },
26679     
26680     initOverlayView: function()
26681     {
26682         var _this = this;
26683         
26684         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26685             
26686             draw: function()
26687             {
26688                 _this.fireEvent('OverlayViewDraw', _this);
26689             },
26690             
26691             onAdd: function()
26692             {
26693                 _this.fireEvent('OverlayViewOnAdd', _this);
26694             },
26695             
26696             onRemove: function()
26697             {
26698                 _this.fireEvent('OverlayViewOnRemove', _this);
26699             },
26700             
26701             show: function(cpx)
26702             {
26703                 _this.fireEvent('OverlayViewShow', _this, cpx);
26704             },
26705             
26706             hide: function()
26707             {
26708                 _this.fireEvent('OverlayViewHide', _this);
26709             }
26710             
26711         });
26712     },
26713     
26714     fromLatLngToContainerPixel: function(event)
26715     {
26716         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26717     },
26718     
26719     isApplied: function() 
26720     {
26721         return this.getGmapContext() == false ? false : true;
26722     },
26723     
26724     getGmapContext: function() 
26725     {
26726         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26727     },
26728     
26729     GMapContext: function() 
26730     {
26731         var position = new google.maps.LatLng(this.latitude, this.longitude);
26732         
26733         var _map = new google.maps.Map(this.el.dom, {
26734             center: position,
26735             zoom: this.zoom,
26736             mapTypeId: this.mapTypeId,
26737             mapTypeControl: this.mapTypeControl,
26738             disableDoubleClickZoom: this.disableDoubleClickZoom,
26739             scrollwheel: this.scrollwheel,
26740             streetViewControl: this.streetViewControl,
26741             locationName: this.locationName,
26742             draggable: this.draggable,
26743             enableAutocomplete: this.enableAutocomplete,
26744             enableReverseGeocode: this.enableReverseGeocode
26745         });
26746         
26747         var _marker = new google.maps.Marker({
26748             position: position,
26749             map: _map,
26750             title: this.markerTitle,
26751             draggable: this.draggable
26752         });
26753         
26754         return {
26755             map: _map,
26756             marker: _marker,
26757             circle: null,
26758             location: position,
26759             radius: this.radius,
26760             locationName: this.locationName,
26761             addressComponents: {
26762                 formatted_address: null,
26763                 addressLine1: null,
26764                 addressLine2: null,
26765                 streetName: null,
26766                 streetNumber: null,
26767                 city: null,
26768                 district: null,
26769                 state: null,
26770                 stateOrProvince: null
26771             },
26772             settings: this,
26773             domContainer: this.el.dom,
26774             geodecoder: new google.maps.Geocoder()
26775         };
26776     },
26777     
26778     drawCircle: function(center, radius, options) 
26779     {
26780         if (this.gMapContext.circle != null) {
26781             this.gMapContext.circle.setMap(null);
26782         }
26783         if (radius > 0) {
26784             radius *= 1;
26785             options = Roo.apply({}, options, {
26786                 strokeColor: "#0000FF",
26787                 strokeOpacity: .35,
26788                 strokeWeight: 2,
26789                 fillColor: "#0000FF",
26790                 fillOpacity: .2
26791             });
26792             
26793             options.map = this.gMapContext.map;
26794             options.radius = radius;
26795             options.center = center;
26796             this.gMapContext.circle = new google.maps.Circle(options);
26797             return this.gMapContext.circle;
26798         }
26799         
26800         return null;
26801     },
26802     
26803     setPosition: function(location) 
26804     {
26805         this.gMapContext.location = location;
26806         this.gMapContext.marker.setPosition(location);
26807         this.gMapContext.map.panTo(location);
26808         this.drawCircle(location, this.gMapContext.radius, {});
26809         
26810         var _this = this;
26811         
26812         if (this.gMapContext.settings.enableReverseGeocode) {
26813             this.gMapContext.geodecoder.geocode({
26814                 latLng: this.gMapContext.location
26815             }, function(results, status) {
26816                 
26817                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26818                     _this.gMapContext.locationName = results[0].formatted_address;
26819                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26820                     
26821                     _this.fireEvent('positionchanged', this, location);
26822                 }
26823             });
26824             
26825             return;
26826         }
26827         
26828         this.fireEvent('positionchanged', this, location);
26829     },
26830     
26831     resize: function()
26832     {
26833         google.maps.event.trigger(this.gMapContext.map, "resize");
26834         
26835         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26836         
26837         this.fireEvent('resize', this);
26838     },
26839     
26840     setPositionByLatLng: function(latitude, longitude)
26841     {
26842         this.setPosition(new google.maps.LatLng(latitude, longitude));
26843     },
26844     
26845     getCurrentPosition: function() 
26846     {
26847         return {
26848             latitude: this.gMapContext.location.lat(),
26849             longitude: this.gMapContext.location.lng()
26850         };
26851     },
26852     
26853     getAddressName: function() 
26854     {
26855         return this.gMapContext.locationName;
26856     },
26857     
26858     getAddressComponents: function() 
26859     {
26860         return this.gMapContext.addressComponents;
26861     },
26862     
26863     address_component_from_google_geocode: function(address_components) 
26864     {
26865         var result = {};
26866         
26867         for (var i = 0; i < address_components.length; i++) {
26868             var component = address_components[i];
26869             if (component.types.indexOf("postal_code") >= 0) {
26870                 result.postalCode = component.short_name;
26871             } else if (component.types.indexOf("street_number") >= 0) {
26872                 result.streetNumber = component.short_name;
26873             } else if (component.types.indexOf("route") >= 0) {
26874                 result.streetName = component.short_name;
26875             } else if (component.types.indexOf("neighborhood") >= 0) {
26876                 result.city = component.short_name;
26877             } else if (component.types.indexOf("locality") >= 0) {
26878                 result.city = component.short_name;
26879             } else if (component.types.indexOf("sublocality") >= 0) {
26880                 result.district = component.short_name;
26881             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26882                 result.stateOrProvince = component.short_name;
26883             } else if (component.types.indexOf("country") >= 0) {
26884                 result.country = component.short_name;
26885             }
26886         }
26887         
26888         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26889         result.addressLine2 = "";
26890         return result;
26891     },
26892     
26893     setZoomLevel: function(zoom)
26894     {
26895         this.gMapContext.map.setZoom(zoom);
26896     },
26897     
26898     show: function()
26899     {
26900         if(!this.el){
26901             return;
26902         }
26903         
26904         this.el.show();
26905         
26906         this.resize();
26907         
26908         this.fireEvent('show', this);
26909     },
26910     
26911     hide: function()
26912     {
26913         if(!this.el){
26914             return;
26915         }
26916         
26917         this.el.hide();
26918         
26919         this.fireEvent('hide', this);
26920     }
26921     
26922 });
26923
26924 Roo.apply(Roo.bootstrap.LocationPicker, {
26925     
26926     OverlayView : function(map, options)
26927     {
26928         options = options || {};
26929         
26930         this.setMap(map);
26931     }
26932     
26933     
26934 });/*
26935  * - LGPL
26936  *
26937  * Alert
26938  * 
26939  */
26940
26941 /**
26942  * @class Roo.bootstrap.Alert
26943  * @extends Roo.bootstrap.Component
26944  * Bootstrap Alert class
26945  * @cfg {String} title The title of alert
26946  * @cfg {String} html The content of alert
26947  * @cfg {String} weight (  success | info | warning | danger )
26948  * @cfg {String} faicon font-awesomeicon
26949  * 
26950  * @constructor
26951  * Create a new alert
26952  * @param {Object} config The config object
26953  */
26954
26955
26956 Roo.bootstrap.Alert = function(config){
26957     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26958     
26959 };
26960
26961 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26962     
26963     title: '',
26964     html: '',
26965     weight: false,
26966     faicon: false,
26967     
26968     getAutoCreate : function()
26969     {
26970         
26971         var cfg = {
26972             tag : 'div',
26973             cls : 'alert',
26974             cn : [
26975                 {
26976                     tag : 'i',
26977                     cls : 'roo-alert-icon'
26978                     
26979                 },
26980                 {
26981                     tag : 'b',
26982                     cls : 'roo-alert-title',
26983                     html : this.title
26984                 },
26985                 {
26986                     tag : 'span',
26987                     cls : 'roo-alert-text',
26988                     html : this.html
26989                 }
26990             ]
26991         };
26992         
26993         if(this.faicon){
26994             cfg.cn[0].cls += ' fa ' + this.faicon;
26995         }
26996         
26997         if(this.weight){
26998             cfg.cls += ' alert-' + this.weight;
26999         }
27000         
27001         return cfg;
27002     },
27003     
27004     initEvents: function() 
27005     {
27006         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27007     },
27008     
27009     setTitle : function(str)
27010     {
27011         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27012     },
27013     
27014     setText : function(str)
27015     {
27016         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27017     },
27018     
27019     setWeight : function(weight)
27020     {
27021         if(this.weight){
27022             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27023         }
27024         
27025         this.weight = weight;
27026         
27027         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27028     },
27029     
27030     setIcon : function(icon)
27031     {
27032         if(this.faicon){
27033             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27034         }
27035         
27036         this.faicon = icon;
27037         
27038         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27039     },
27040     
27041     hide: function() 
27042     {
27043         this.el.hide();   
27044     },
27045     
27046     show: function() 
27047     {  
27048         this.el.show();   
27049     }
27050     
27051 });
27052
27053  
27054 /*
27055 * Licence: LGPL
27056 */
27057
27058 /**
27059  * @class Roo.bootstrap.UploadCropbox
27060  * @extends Roo.bootstrap.Component
27061  * Bootstrap UploadCropbox class
27062  * @cfg {String} emptyText show when image has been loaded
27063  * @cfg {String} rotateNotify show when image too small to rotate
27064  * @cfg {Number} errorTimeout default 3000
27065  * @cfg {Number} minWidth default 300
27066  * @cfg {Number} minHeight default 300
27067  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27068  * @cfg {Boolean} isDocument (true|false) default false
27069  * @cfg {String} url action url
27070  * @cfg {String} paramName default 'imageUpload'
27071  * @cfg {String} method default POST
27072  * @cfg {Boolean} loadMask (true|false) default true
27073  * @cfg {Boolean} loadingText default 'Loading...'
27074  * 
27075  * @constructor
27076  * Create a new UploadCropbox
27077  * @param {Object} config The config object
27078  */
27079
27080 Roo.bootstrap.UploadCropbox = function(config){
27081     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27082     
27083     this.addEvents({
27084         /**
27085          * @event beforeselectfile
27086          * Fire before select file
27087          * @param {Roo.bootstrap.UploadCropbox} this
27088          */
27089         "beforeselectfile" : true,
27090         /**
27091          * @event initial
27092          * Fire after initEvent
27093          * @param {Roo.bootstrap.UploadCropbox} this
27094          */
27095         "initial" : true,
27096         /**
27097          * @event crop
27098          * Fire after initEvent
27099          * @param {Roo.bootstrap.UploadCropbox} this
27100          * @param {String} data
27101          */
27102         "crop" : true,
27103         /**
27104          * @event prepare
27105          * Fire when preparing the file data
27106          * @param {Roo.bootstrap.UploadCropbox} this
27107          * @param {Object} file
27108          */
27109         "prepare" : true,
27110         /**
27111          * @event exception
27112          * Fire when get exception
27113          * @param {Roo.bootstrap.UploadCropbox} this
27114          * @param {XMLHttpRequest} xhr
27115          */
27116         "exception" : true,
27117         /**
27118          * @event beforeloadcanvas
27119          * Fire before load the canvas
27120          * @param {Roo.bootstrap.UploadCropbox} this
27121          * @param {String} src
27122          */
27123         "beforeloadcanvas" : true,
27124         /**
27125          * @event trash
27126          * Fire when trash image
27127          * @param {Roo.bootstrap.UploadCropbox} this
27128          */
27129         "trash" : true,
27130         /**
27131          * @event download
27132          * Fire when download the image
27133          * @param {Roo.bootstrap.UploadCropbox} this
27134          */
27135         "download" : true,
27136         /**
27137          * @event footerbuttonclick
27138          * Fire when footerbuttonclick
27139          * @param {Roo.bootstrap.UploadCropbox} this
27140          * @param {String} type
27141          */
27142         "footerbuttonclick" : true,
27143         /**
27144          * @event resize
27145          * Fire when resize
27146          * @param {Roo.bootstrap.UploadCropbox} this
27147          */
27148         "resize" : true,
27149         /**
27150          * @event rotate
27151          * Fire when rotate the image
27152          * @param {Roo.bootstrap.UploadCropbox} this
27153          * @param {String} pos
27154          */
27155         "rotate" : true,
27156         /**
27157          * @event inspect
27158          * Fire when inspect the file
27159          * @param {Roo.bootstrap.UploadCropbox} this
27160          * @param {Object} file
27161          */
27162         "inspect" : true,
27163         /**
27164          * @event upload
27165          * Fire when xhr upload the file
27166          * @param {Roo.bootstrap.UploadCropbox} this
27167          * @param {Object} data
27168          */
27169         "upload" : true,
27170         /**
27171          * @event arrange
27172          * Fire when arrange the file data
27173          * @param {Roo.bootstrap.UploadCropbox} this
27174          * @param {Object} formData
27175          */
27176         "arrange" : true
27177     });
27178     
27179     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27180 };
27181
27182 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27183     
27184     emptyText : 'Click to upload image',
27185     rotateNotify : 'Image is too small to rotate',
27186     errorTimeout : 3000,
27187     scale : 0,
27188     baseScale : 1,
27189     rotate : 0,
27190     dragable : false,
27191     pinching : false,
27192     mouseX : 0,
27193     mouseY : 0,
27194     cropData : false,
27195     minWidth : 300,
27196     minHeight : 300,
27197     file : false,
27198     exif : {},
27199     baseRotate : 1,
27200     cropType : 'image/jpeg',
27201     buttons : false,
27202     canvasLoaded : false,
27203     isDocument : false,
27204     method : 'POST',
27205     paramName : 'imageUpload',
27206     loadMask : true,
27207     loadingText : 'Loading...',
27208     maskEl : false,
27209     
27210     getAutoCreate : function()
27211     {
27212         var cfg = {
27213             tag : 'div',
27214             cls : 'roo-upload-cropbox',
27215             cn : [
27216                 {
27217                     tag : 'input',
27218                     cls : 'roo-upload-cropbox-selector',
27219                     type : 'file'
27220                 },
27221                 {
27222                     tag : 'div',
27223                     cls : 'roo-upload-cropbox-body',
27224                     style : 'cursor:pointer',
27225                     cn : [
27226                         {
27227                             tag : 'div',
27228                             cls : 'roo-upload-cropbox-preview'
27229                         },
27230                         {
27231                             tag : 'div',
27232                             cls : 'roo-upload-cropbox-thumb'
27233                         },
27234                         {
27235                             tag : 'div',
27236                             cls : 'roo-upload-cropbox-empty-notify',
27237                             html : this.emptyText
27238                         },
27239                         {
27240                             tag : 'div',
27241                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27242                             html : this.rotateNotify
27243                         }
27244                     ]
27245                 },
27246                 {
27247                     tag : 'div',
27248                     cls : 'roo-upload-cropbox-footer',
27249                     cn : {
27250                         tag : 'div',
27251                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27252                         cn : []
27253                     }
27254                 }
27255             ]
27256         };
27257         
27258         return cfg;
27259     },
27260     
27261     onRender : function(ct, position)
27262     {
27263         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27264         
27265         if (this.buttons.length) {
27266             
27267             Roo.each(this.buttons, function(bb) {
27268                 
27269                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27270                 
27271                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27272                 
27273             }, this);
27274         }
27275         
27276         if(this.loadMask){
27277             this.maskEl = this.el;
27278         }
27279     },
27280     
27281     initEvents : function()
27282     {
27283         this.urlAPI = (window.createObjectURL && window) || 
27284                                 (window.URL && URL.revokeObjectURL && URL) || 
27285                                 (window.webkitURL && webkitURL);
27286                         
27287         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27288         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27289         
27290         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27291         this.selectorEl.hide();
27292         
27293         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27294         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27295         
27296         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27297         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27298         this.thumbEl.hide();
27299         
27300         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27301         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27302         
27303         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27304         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27305         this.errorEl.hide();
27306         
27307         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27308         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27309         this.footerEl.hide();
27310         
27311         this.setThumbBoxSize();
27312         
27313         this.bind();
27314         
27315         this.resize();
27316         
27317         this.fireEvent('initial', this);
27318     },
27319
27320     bind : function()
27321     {
27322         var _this = this;
27323         
27324         window.addEventListener("resize", function() { _this.resize(); } );
27325         
27326         this.bodyEl.on('click', this.beforeSelectFile, this);
27327         
27328         if(Roo.isTouch){
27329             this.bodyEl.on('touchstart', this.onTouchStart, this);
27330             this.bodyEl.on('touchmove', this.onTouchMove, this);
27331             this.bodyEl.on('touchend', this.onTouchEnd, this);
27332         }
27333         
27334         if(!Roo.isTouch){
27335             this.bodyEl.on('mousedown', this.onMouseDown, this);
27336             this.bodyEl.on('mousemove', this.onMouseMove, this);
27337             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27338             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27339             Roo.get(document).on('mouseup', this.onMouseUp, this);
27340         }
27341         
27342         this.selectorEl.on('change', this.onFileSelected, this);
27343     },
27344     
27345     reset : function()
27346     {    
27347         this.scale = 0;
27348         this.baseScale = 1;
27349         this.rotate = 0;
27350         this.baseRotate = 1;
27351         this.dragable = false;
27352         this.pinching = false;
27353         this.mouseX = 0;
27354         this.mouseY = 0;
27355         this.cropData = false;
27356         this.notifyEl.dom.innerHTML = this.emptyText;
27357         
27358         this.selectorEl.dom.value = '';
27359         
27360     },
27361     
27362     resize : function()
27363     {
27364         if(this.fireEvent('resize', this) != false){
27365             this.setThumbBoxPosition();
27366             this.setCanvasPosition();
27367         }
27368     },
27369     
27370     onFooterButtonClick : function(e, el, o, type)
27371     {
27372         switch (type) {
27373             case 'rotate-left' :
27374                 this.onRotateLeft(e);
27375                 break;
27376             case 'rotate-right' :
27377                 this.onRotateRight(e);
27378                 break;
27379             case 'picture' :
27380                 this.beforeSelectFile(e);
27381                 break;
27382             case 'trash' :
27383                 this.trash(e);
27384                 break;
27385             case 'crop' :
27386                 this.crop(e);
27387                 break;
27388             case 'download' :
27389                 this.download(e);
27390                 break;
27391             default :
27392                 break;
27393         }
27394         
27395         this.fireEvent('footerbuttonclick', this, type);
27396     },
27397     
27398     beforeSelectFile : function(e)
27399     {
27400         e.preventDefault();
27401         
27402         if(this.fireEvent('beforeselectfile', this) != false){
27403             this.selectorEl.dom.click();
27404         }
27405     },
27406     
27407     onFileSelected : function(e)
27408     {
27409         e.preventDefault();
27410         
27411         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27412             return;
27413         }
27414         
27415         var file = this.selectorEl.dom.files[0];
27416         
27417         if(this.fireEvent('inspect', this, file) != false){
27418             this.prepare(file);
27419         }
27420         
27421     },
27422     
27423     trash : function(e)
27424     {
27425         this.fireEvent('trash', this);
27426     },
27427     
27428     download : function(e)
27429     {
27430         this.fireEvent('download', this);
27431     },
27432     
27433     loadCanvas : function(src)
27434     {   
27435         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27436             
27437             this.reset();
27438             
27439             this.imageEl = document.createElement('img');
27440             
27441             var _this = this;
27442             
27443             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27444             
27445             this.imageEl.src = src;
27446         }
27447     },
27448     
27449     onLoadCanvas : function()
27450     {   
27451         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27452         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27453         
27454         this.bodyEl.un('click', this.beforeSelectFile, this);
27455         
27456         this.notifyEl.hide();
27457         this.thumbEl.show();
27458         this.footerEl.show();
27459         
27460         this.baseRotateLevel();
27461         
27462         if(this.isDocument){
27463             this.setThumbBoxSize();
27464         }
27465         
27466         this.setThumbBoxPosition();
27467         
27468         this.baseScaleLevel();
27469         
27470         this.draw();
27471         
27472         this.resize();
27473         
27474         this.canvasLoaded = true;
27475         
27476         if(this.loadMask){
27477             this.maskEl.unmask();
27478         }
27479         
27480     },
27481     
27482     setCanvasPosition : function()
27483     {   
27484         if(!this.canvasEl){
27485             return;
27486         }
27487         
27488         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27489         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27490         
27491         this.previewEl.setLeft(pw);
27492         this.previewEl.setTop(ph);
27493         
27494     },
27495     
27496     onMouseDown : function(e)
27497     {   
27498         e.stopEvent();
27499         
27500         this.dragable = true;
27501         this.pinching = false;
27502         
27503         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27504             this.dragable = false;
27505             return;
27506         }
27507         
27508         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27509         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27510         
27511     },
27512     
27513     onMouseMove : function(e)
27514     {   
27515         e.stopEvent();
27516         
27517         if(!this.canvasLoaded){
27518             return;
27519         }
27520         
27521         if (!this.dragable){
27522             return;
27523         }
27524         
27525         var minX = Math.ceil(this.thumbEl.getLeft(true));
27526         var minY = Math.ceil(this.thumbEl.getTop(true));
27527         
27528         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27529         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27530         
27531         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27532         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27533         
27534         x = x - this.mouseX;
27535         y = y - this.mouseY;
27536         
27537         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27538         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27539         
27540         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27541         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27542         
27543         this.previewEl.setLeft(bgX);
27544         this.previewEl.setTop(bgY);
27545         
27546         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27547         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27548     },
27549     
27550     onMouseUp : function(e)
27551     {   
27552         e.stopEvent();
27553         
27554         this.dragable = false;
27555     },
27556     
27557     onMouseWheel : function(e)
27558     {   
27559         e.stopEvent();
27560         
27561         this.startScale = this.scale;
27562         
27563         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27564         
27565         if(!this.zoomable()){
27566             this.scale = this.startScale;
27567             return;
27568         }
27569         
27570         this.draw();
27571         
27572         return;
27573     },
27574     
27575     zoomable : function()
27576     {
27577         var minScale = this.thumbEl.getWidth() / this.minWidth;
27578         
27579         if(this.minWidth < this.minHeight){
27580             minScale = this.thumbEl.getHeight() / this.minHeight;
27581         }
27582         
27583         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27584         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27585         
27586         if(
27587                 this.isDocument &&
27588                 (this.rotate == 0 || this.rotate == 180) && 
27589                 (
27590                     width > this.imageEl.OriginWidth || 
27591                     height > this.imageEl.OriginHeight ||
27592                     (width < this.minWidth && height < this.minHeight)
27593                 )
27594         ){
27595             return false;
27596         }
27597         
27598         if(
27599                 this.isDocument &&
27600                 (this.rotate == 90 || this.rotate == 270) && 
27601                 (
27602                     width > this.imageEl.OriginWidth || 
27603                     height > this.imageEl.OriginHeight ||
27604                     (width < this.minHeight && height < this.minWidth)
27605                 )
27606         ){
27607             return false;
27608         }
27609         
27610         if(
27611                 !this.isDocument &&
27612                 (this.rotate == 0 || this.rotate == 180) && 
27613                 (
27614                     width < this.minWidth || 
27615                     width > this.imageEl.OriginWidth || 
27616                     height < this.minHeight || 
27617                     height > this.imageEl.OriginHeight
27618                 )
27619         ){
27620             return false;
27621         }
27622         
27623         if(
27624                 !this.isDocument &&
27625                 (this.rotate == 90 || this.rotate == 270) && 
27626                 (
27627                     width < this.minHeight || 
27628                     width > this.imageEl.OriginWidth || 
27629                     height < this.minWidth || 
27630                     height > this.imageEl.OriginHeight
27631                 )
27632         ){
27633             return false;
27634         }
27635         
27636         return true;
27637         
27638     },
27639     
27640     onRotateLeft : function(e)
27641     {   
27642         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27643             
27644             var minScale = this.thumbEl.getWidth() / this.minWidth;
27645             
27646             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27647             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27648             
27649             this.startScale = this.scale;
27650             
27651             while (this.getScaleLevel() < minScale){
27652             
27653                 this.scale = this.scale + 1;
27654                 
27655                 if(!this.zoomable()){
27656                     break;
27657                 }
27658                 
27659                 if(
27660                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27661                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27662                 ){
27663                     continue;
27664                 }
27665                 
27666                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27667
27668                 this.draw();
27669                 
27670                 return;
27671             }
27672             
27673             this.scale = this.startScale;
27674             
27675             this.onRotateFail();
27676             
27677             return false;
27678         }
27679         
27680         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27681
27682         if(this.isDocument){
27683             this.setThumbBoxSize();
27684             this.setThumbBoxPosition();
27685             this.setCanvasPosition();
27686         }
27687         
27688         this.draw();
27689         
27690         this.fireEvent('rotate', this, 'left');
27691         
27692     },
27693     
27694     onRotateRight : function(e)
27695     {
27696         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27697             
27698             var minScale = this.thumbEl.getWidth() / this.minWidth;
27699         
27700             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27701             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27702             
27703             this.startScale = this.scale;
27704             
27705             while (this.getScaleLevel() < minScale){
27706             
27707                 this.scale = this.scale + 1;
27708                 
27709                 if(!this.zoomable()){
27710                     break;
27711                 }
27712                 
27713                 if(
27714                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27715                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27716                 ){
27717                     continue;
27718                 }
27719                 
27720                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27721
27722                 this.draw();
27723                 
27724                 return;
27725             }
27726             
27727             this.scale = this.startScale;
27728             
27729             this.onRotateFail();
27730             
27731             return false;
27732         }
27733         
27734         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27735
27736         if(this.isDocument){
27737             this.setThumbBoxSize();
27738             this.setThumbBoxPosition();
27739             this.setCanvasPosition();
27740         }
27741         
27742         this.draw();
27743         
27744         this.fireEvent('rotate', this, 'right');
27745     },
27746     
27747     onRotateFail : function()
27748     {
27749         this.errorEl.show(true);
27750         
27751         var _this = this;
27752         
27753         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27754     },
27755     
27756     draw : function()
27757     {
27758         this.previewEl.dom.innerHTML = '';
27759         
27760         var canvasEl = document.createElement("canvas");
27761         
27762         var contextEl = canvasEl.getContext("2d");
27763         
27764         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27765         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27766         var center = this.imageEl.OriginWidth / 2;
27767         
27768         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27769             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27770             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27771             center = this.imageEl.OriginHeight / 2;
27772         }
27773         
27774         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27775         
27776         contextEl.translate(center, center);
27777         contextEl.rotate(this.rotate * Math.PI / 180);
27778
27779         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27780         
27781         this.canvasEl = document.createElement("canvas");
27782         
27783         this.contextEl = this.canvasEl.getContext("2d");
27784         
27785         switch (this.rotate) {
27786             case 0 :
27787                 
27788                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27789                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27790                 
27791                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27792                 
27793                 break;
27794             case 90 : 
27795                 
27796                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27797                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27798                 
27799                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27800                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27801                     break;
27802                 }
27803                 
27804                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27805                 
27806                 break;
27807             case 180 :
27808                 
27809                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27810                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27811                 
27812                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27813                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27814                     break;
27815                 }
27816                 
27817                 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);
27818                 
27819                 break;
27820             case 270 :
27821                 
27822                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27823                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27824         
27825                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27826                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27827                     break;
27828                 }
27829                 
27830                 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);
27831                 
27832                 break;
27833             default : 
27834                 break;
27835         }
27836         
27837         this.previewEl.appendChild(this.canvasEl);
27838         
27839         this.setCanvasPosition();
27840     },
27841     
27842     crop : function()
27843     {
27844         if(!this.canvasLoaded){
27845             return;
27846         }
27847         
27848         var imageCanvas = document.createElement("canvas");
27849         
27850         var imageContext = imageCanvas.getContext("2d");
27851         
27852         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27853         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27854         
27855         var center = imageCanvas.width / 2;
27856         
27857         imageContext.translate(center, center);
27858         
27859         imageContext.rotate(this.rotate * Math.PI / 180);
27860         
27861         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27862         
27863         var canvas = document.createElement("canvas");
27864         
27865         var context = canvas.getContext("2d");
27866                 
27867         canvas.width = this.minWidth;
27868         canvas.height = this.minHeight;
27869
27870         switch (this.rotate) {
27871             case 0 :
27872                 
27873                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27874                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27875                 
27876                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27877                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27878                 
27879                 var targetWidth = this.minWidth - 2 * x;
27880                 var targetHeight = this.minHeight - 2 * y;
27881                 
27882                 var scale = 1;
27883                 
27884                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27885                     scale = targetWidth / width;
27886                 }
27887                 
27888                 if(x > 0 && y == 0){
27889                     scale = targetHeight / height;
27890                 }
27891                 
27892                 if(x > 0 && y > 0){
27893                     scale = targetWidth / width;
27894                     
27895                     if(width < height){
27896                         scale = targetHeight / height;
27897                     }
27898                 }
27899                 
27900                 context.scale(scale, scale);
27901                 
27902                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27903                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27904
27905                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27906                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27907
27908                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27909                 
27910                 break;
27911             case 90 : 
27912                 
27913                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27914                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27915                 
27916                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27917                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27918                 
27919                 var targetWidth = this.minWidth - 2 * x;
27920                 var targetHeight = this.minHeight - 2 * y;
27921                 
27922                 var scale = 1;
27923                 
27924                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27925                     scale = targetWidth / width;
27926                 }
27927                 
27928                 if(x > 0 && y == 0){
27929                     scale = targetHeight / height;
27930                 }
27931                 
27932                 if(x > 0 && y > 0){
27933                     scale = targetWidth / width;
27934                     
27935                     if(width < height){
27936                         scale = targetHeight / height;
27937                     }
27938                 }
27939                 
27940                 context.scale(scale, scale);
27941                 
27942                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27943                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27944
27945                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27946                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27947                 
27948                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27949                 
27950                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27951                 
27952                 break;
27953             case 180 :
27954                 
27955                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27956                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27957                 
27958                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27959                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27960                 
27961                 var targetWidth = this.minWidth - 2 * x;
27962                 var targetHeight = this.minHeight - 2 * y;
27963                 
27964                 var scale = 1;
27965                 
27966                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27967                     scale = targetWidth / width;
27968                 }
27969                 
27970                 if(x > 0 && y == 0){
27971                     scale = targetHeight / height;
27972                 }
27973                 
27974                 if(x > 0 && y > 0){
27975                     scale = targetWidth / width;
27976                     
27977                     if(width < height){
27978                         scale = targetHeight / height;
27979                     }
27980                 }
27981                 
27982                 context.scale(scale, scale);
27983                 
27984                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27985                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27986
27987                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27988                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27989
27990                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27991                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27992                 
27993                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27994                 
27995                 break;
27996             case 270 :
27997                 
27998                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27999                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28000                 
28001                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28002                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28003                 
28004                 var targetWidth = this.minWidth - 2 * x;
28005                 var targetHeight = this.minHeight - 2 * y;
28006                 
28007                 var scale = 1;
28008                 
28009                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28010                     scale = targetWidth / width;
28011                 }
28012                 
28013                 if(x > 0 && y == 0){
28014                     scale = targetHeight / height;
28015                 }
28016                 
28017                 if(x > 0 && y > 0){
28018                     scale = targetWidth / width;
28019                     
28020                     if(width < height){
28021                         scale = targetHeight / height;
28022                     }
28023                 }
28024                 
28025                 context.scale(scale, scale);
28026                 
28027                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28028                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28029
28030                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28031                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28032                 
28033                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28034                 
28035                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28036                 
28037                 break;
28038             default : 
28039                 break;
28040         }
28041         
28042         this.cropData = canvas.toDataURL(this.cropType);
28043         
28044         if(this.fireEvent('crop', this, this.cropData) !== false){
28045             this.process(this.file, this.cropData);
28046         }
28047         
28048         return;
28049         
28050     },
28051     
28052     setThumbBoxSize : function()
28053     {
28054         var width, height;
28055         
28056         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28057             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28058             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28059             
28060             this.minWidth = width;
28061             this.minHeight = height;
28062             
28063             if(this.rotate == 90 || this.rotate == 270){
28064                 this.minWidth = height;
28065                 this.minHeight = width;
28066             }
28067         }
28068         
28069         height = 300;
28070         width = Math.ceil(this.minWidth * height / this.minHeight);
28071         
28072         if(this.minWidth > this.minHeight){
28073             width = 300;
28074             height = Math.ceil(this.minHeight * width / this.minWidth);
28075         }
28076         
28077         this.thumbEl.setStyle({
28078             width : width + 'px',
28079             height : height + 'px'
28080         });
28081
28082         return;
28083             
28084     },
28085     
28086     setThumbBoxPosition : function()
28087     {
28088         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28089         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28090         
28091         this.thumbEl.setLeft(x);
28092         this.thumbEl.setTop(y);
28093         
28094     },
28095     
28096     baseRotateLevel : function()
28097     {
28098         this.baseRotate = 1;
28099         
28100         if(
28101                 typeof(this.exif) != 'undefined' &&
28102                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28103                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28104         ){
28105             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28106         }
28107         
28108         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28109         
28110     },
28111     
28112     baseScaleLevel : function()
28113     {
28114         var width, height;
28115         
28116         if(this.isDocument){
28117             
28118             if(this.baseRotate == 6 || this.baseRotate == 8){
28119             
28120                 height = this.thumbEl.getHeight();
28121                 this.baseScale = height / this.imageEl.OriginWidth;
28122
28123                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28124                     width = this.thumbEl.getWidth();
28125                     this.baseScale = width / this.imageEl.OriginHeight;
28126                 }
28127
28128                 return;
28129             }
28130
28131             height = this.thumbEl.getHeight();
28132             this.baseScale = height / this.imageEl.OriginHeight;
28133
28134             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28135                 width = this.thumbEl.getWidth();
28136                 this.baseScale = width / this.imageEl.OriginWidth;
28137             }
28138
28139             return;
28140         }
28141         
28142         if(this.baseRotate == 6 || this.baseRotate == 8){
28143             
28144             width = this.thumbEl.getHeight();
28145             this.baseScale = width / this.imageEl.OriginHeight;
28146             
28147             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28148                 height = this.thumbEl.getWidth();
28149                 this.baseScale = height / this.imageEl.OriginHeight;
28150             }
28151             
28152             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28153                 height = this.thumbEl.getWidth();
28154                 this.baseScale = height / this.imageEl.OriginHeight;
28155                 
28156                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28157                     width = this.thumbEl.getHeight();
28158                     this.baseScale = width / this.imageEl.OriginWidth;
28159                 }
28160             }
28161             
28162             return;
28163         }
28164         
28165         width = this.thumbEl.getWidth();
28166         this.baseScale = width / this.imageEl.OriginWidth;
28167         
28168         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28169             height = this.thumbEl.getHeight();
28170             this.baseScale = height / this.imageEl.OriginHeight;
28171         }
28172         
28173         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28174             
28175             height = this.thumbEl.getHeight();
28176             this.baseScale = height / this.imageEl.OriginHeight;
28177             
28178             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28179                 width = this.thumbEl.getWidth();
28180                 this.baseScale = width / this.imageEl.OriginWidth;
28181             }
28182             
28183         }
28184         
28185         return;
28186     },
28187     
28188     getScaleLevel : function()
28189     {
28190         return this.baseScale * Math.pow(1.1, this.scale);
28191     },
28192     
28193     onTouchStart : function(e)
28194     {
28195         if(!this.canvasLoaded){
28196             this.beforeSelectFile(e);
28197             return;
28198         }
28199         
28200         var touches = e.browserEvent.touches;
28201         
28202         if(!touches){
28203             return;
28204         }
28205         
28206         if(touches.length == 1){
28207             this.onMouseDown(e);
28208             return;
28209         }
28210         
28211         if(touches.length != 2){
28212             return;
28213         }
28214         
28215         var coords = [];
28216         
28217         for(var i = 0, finger; finger = touches[i]; i++){
28218             coords.push(finger.pageX, finger.pageY);
28219         }
28220         
28221         var x = Math.pow(coords[0] - coords[2], 2);
28222         var y = Math.pow(coords[1] - coords[3], 2);
28223         
28224         this.startDistance = Math.sqrt(x + y);
28225         
28226         this.startScale = this.scale;
28227         
28228         this.pinching = true;
28229         this.dragable = false;
28230         
28231     },
28232     
28233     onTouchMove : function(e)
28234     {
28235         if(!this.pinching && !this.dragable){
28236             return;
28237         }
28238         
28239         var touches = e.browserEvent.touches;
28240         
28241         if(!touches){
28242             return;
28243         }
28244         
28245         if(this.dragable){
28246             this.onMouseMove(e);
28247             return;
28248         }
28249         
28250         var coords = [];
28251         
28252         for(var i = 0, finger; finger = touches[i]; i++){
28253             coords.push(finger.pageX, finger.pageY);
28254         }
28255         
28256         var x = Math.pow(coords[0] - coords[2], 2);
28257         var y = Math.pow(coords[1] - coords[3], 2);
28258         
28259         this.endDistance = Math.sqrt(x + y);
28260         
28261         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28262         
28263         if(!this.zoomable()){
28264             this.scale = this.startScale;
28265             return;
28266         }
28267         
28268         this.draw();
28269         
28270     },
28271     
28272     onTouchEnd : function(e)
28273     {
28274         this.pinching = false;
28275         this.dragable = false;
28276         
28277     },
28278     
28279     process : function(file, crop)
28280     {
28281         if(this.loadMask){
28282             this.maskEl.mask(this.loadingText);
28283         }
28284         
28285         this.xhr = new XMLHttpRequest();
28286         
28287         file.xhr = this.xhr;
28288
28289         this.xhr.open(this.method, this.url, true);
28290         
28291         var headers = {
28292             "Accept": "application/json",
28293             "Cache-Control": "no-cache",
28294             "X-Requested-With": "XMLHttpRequest"
28295         };
28296         
28297         for (var headerName in headers) {
28298             var headerValue = headers[headerName];
28299             if (headerValue) {
28300                 this.xhr.setRequestHeader(headerName, headerValue);
28301             }
28302         }
28303         
28304         var _this = this;
28305         
28306         this.xhr.onload = function()
28307         {
28308             _this.xhrOnLoad(_this.xhr);
28309         }
28310         
28311         this.xhr.onerror = function()
28312         {
28313             _this.xhrOnError(_this.xhr);
28314         }
28315         
28316         var formData = new FormData();
28317
28318         formData.append('returnHTML', 'NO');
28319         
28320         if(crop){
28321             formData.append('crop', crop);
28322         }
28323         
28324         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28325             formData.append(this.paramName, file, file.name);
28326         }
28327         
28328         if(typeof(file.filename) != 'undefined'){
28329             formData.append('filename', file.filename);
28330         }
28331         
28332         if(typeof(file.mimetype) != 'undefined'){
28333             formData.append('mimetype', file.mimetype);
28334         }
28335         
28336         if(this.fireEvent('arrange', this, formData) != false){
28337             this.xhr.send(formData);
28338         };
28339     },
28340     
28341     xhrOnLoad : function(xhr)
28342     {
28343         if(this.loadMask){
28344             this.maskEl.unmask();
28345         }
28346         
28347         if (xhr.readyState !== 4) {
28348             this.fireEvent('exception', this, xhr);
28349             return;
28350         }
28351
28352         var response = Roo.decode(xhr.responseText);
28353         
28354         if(!response.success){
28355             this.fireEvent('exception', this, xhr);
28356             return;
28357         }
28358         
28359         var response = Roo.decode(xhr.responseText);
28360         
28361         this.fireEvent('upload', this, response);
28362         
28363     },
28364     
28365     xhrOnError : function()
28366     {
28367         if(this.loadMask){
28368             this.maskEl.unmask();
28369         }
28370         
28371         Roo.log('xhr on error');
28372         
28373         var response = Roo.decode(xhr.responseText);
28374           
28375         Roo.log(response);
28376         
28377     },
28378     
28379     prepare : function(file)
28380     {   
28381         if(this.loadMask){
28382             this.maskEl.mask(this.loadingText);
28383         }
28384         
28385         this.file = false;
28386         this.exif = {};
28387         
28388         if(typeof(file) === 'string'){
28389             this.loadCanvas(file);
28390             return;
28391         }
28392         
28393         if(!file || !this.urlAPI){
28394             return;
28395         }
28396         
28397         this.file = file;
28398         this.cropType = file.type;
28399         
28400         var _this = this;
28401         
28402         if(this.fireEvent('prepare', this, this.file) != false){
28403             
28404             var reader = new FileReader();
28405             
28406             reader.onload = function (e) {
28407                 if (e.target.error) {
28408                     Roo.log(e.target.error);
28409                     return;
28410                 }
28411                 
28412                 var buffer = e.target.result,
28413                     dataView = new DataView(buffer),
28414                     offset = 2,
28415                     maxOffset = dataView.byteLength - 4,
28416                     markerBytes,
28417                     markerLength;
28418                 
28419                 if (dataView.getUint16(0) === 0xffd8) {
28420                     while (offset < maxOffset) {
28421                         markerBytes = dataView.getUint16(offset);
28422                         
28423                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28424                             markerLength = dataView.getUint16(offset + 2) + 2;
28425                             if (offset + markerLength > dataView.byteLength) {
28426                                 Roo.log('Invalid meta data: Invalid segment size.');
28427                                 break;
28428                             }
28429                             
28430                             if(markerBytes == 0xffe1){
28431                                 _this.parseExifData(
28432                                     dataView,
28433                                     offset,
28434                                     markerLength
28435                                 );
28436                             }
28437                             
28438                             offset += markerLength;
28439                             
28440                             continue;
28441                         }
28442                         
28443                         break;
28444                     }
28445                     
28446                 }
28447                 
28448                 var url = _this.urlAPI.createObjectURL(_this.file);
28449                 
28450                 _this.loadCanvas(url);
28451                 
28452                 return;
28453             }
28454             
28455             reader.readAsArrayBuffer(this.file);
28456             
28457         }
28458         
28459     },
28460     
28461     parseExifData : function(dataView, offset, length)
28462     {
28463         var tiffOffset = offset + 10,
28464             littleEndian,
28465             dirOffset;
28466     
28467         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28468             // No Exif data, might be XMP data instead
28469             return;
28470         }
28471         
28472         // Check for the ASCII code for "Exif" (0x45786966):
28473         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28474             // No Exif data, might be XMP data instead
28475             return;
28476         }
28477         if (tiffOffset + 8 > dataView.byteLength) {
28478             Roo.log('Invalid Exif data: Invalid segment size.');
28479             return;
28480         }
28481         // Check for the two null bytes:
28482         if (dataView.getUint16(offset + 8) !== 0x0000) {
28483             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28484             return;
28485         }
28486         // Check the byte alignment:
28487         switch (dataView.getUint16(tiffOffset)) {
28488         case 0x4949:
28489             littleEndian = true;
28490             break;
28491         case 0x4D4D:
28492             littleEndian = false;
28493             break;
28494         default:
28495             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28496             return;
28497         }
28498         // Check for the TIFF tag marker (0x002A):
28499         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28500             Roo.log('Invalid Exif data: Missing TIFF marker.');
28501             return;
28502         }
28503         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28504         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28505         
28506         this.parseExifTags(
28507             dataView,
28508             tiffOffset,
28509             tiffOffset + dirOffset,
28510             littleEndian
28511         );
28512     },
28513     
28514     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28515     {
28516         var tagsNumber,
28517             dirEndOffset,
28518             i;
28519         if (dirOffset + 6 > dataView.byteLength) {
28520             Roo.log('Invalid Exif data: Invalid directory offset.');
28521             return;
28522         }
28523         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28524         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28525         if (dirEndOffset + 4 > dataView.byteLength) {
28526             Roo.log('Invalid Exif data: Invalid directory size.');
28527             return;
28528         }
28529         for (i = 0; i < tagsNumber; i += 1) {
28530             this.parseExifTag(
28531                 dataView,
28532                 tiffOffset,
28533                 dirOffset + 2 + 12 * i, // tag offset
28534                 littleEndian
28535             );
28536         }
28537         // Return the offset to the next directory:
28538         return dataView.getUint32(dirEndOffset, littleEndian);
28539     },
28540     
28541     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28542     {
28543         var tag = dataView.getUint16(offset, littleEndian);
28544         
28545         this.exif[tag] = this.getExifValue(
28546             dataView,
28547             tiffOffset,
28548             offset,
28549             dataView.getUint16(offset + 2, littleEndian), // tag type
28550             dataView.getUint32(offset + 4, littleEndian), // tag length
28551             littleEndian
28552         );
28553     },
28554     
28555     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28556     {
28557         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28558             tagSize,
28559             dataOffset,
28560             values,
28561             i,
28562             str,
28563             c;
28564     
28565         if (!tagType) {
28566             Roo.log('Invalid Exif data: Invalid tag type.');
28567             return;
28568         }
28569         
28570         tagSize = tagType.size * length;
28571         // Determine if the value is contained in the dataOffset bytes,
28572         // or if the value at the dataOffset is a pointer to the actual data:
28573         dataOffset = tagSize > 4 ?
28574                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28575         if (dataOffset + tagSize > dataView.byteLength) {
28576             Roo.log('Invalid Exif data: Invalid data offset.');
28577             return;
28578         }
28579         if (length === 1) {
28580             return tagType.getValue(dataView, dataOffset, littleEndian);
28581         }
28582         values = [];
28583         for (i = 0; i < length; i += 1) {
28584             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28585         }
28586         
28587         if (tagType.ascii) {
28588             str = '';
28589             // Concatenate the chars:
28590             for (i = 0; i < values.length; i += 1) {
28591                 c = values[i];
28592                 // Ignore the terminating NULL byte(s):
28593                 if (c === '\u0000') {
28594                     break;
28595                 }
28596                 str += c;
28597             }
28598             return str;
28599         }
28600         return values;
28601     }
28602     
28603 });
28604
28605 Roo.apply(Roo.bootstrap.UploadCropbox, {
28606     tags : {
28607         'Orientation': 0x0112
28608     },
28609     
28610     Orientation: {
28611             1: 0, //'top-left',
28612 //            2: 'top-right',
28613             3: 180, //'bottom-right',
28614 //            4: 'bottom-left',
28615 //            5: 'left-top',
28616             6: 90, //'right-top',
28617 //            7: 'right-bottom',
28618             8: 270 //'left-bottom'
28619     },
28620     
28621     exifTagTypes : {
28622         // byte, 8-bit unsigned int:
28623         1: {
28624             getValue: function (dataView, dataOffset) {
28625                 return dataView.getUint8(dataOffset);
28626             },
28627             size: 1
28628         },
28629         // ascii, 8-bit byte:
28630         2: {
28631             getValue: function (dataView, dataOffset) {
28632                 return String.fromCharCode(dataView.getUint8(dataOffset));
28633             },
28634             size: 1,
28635             ascii: true
28636         },
28637         // short, 16 bit int:
28638         3: {
28639             getValue: function (dataView, dataOffset, littleEndian) {
28640                 return dataView.getUint16(dataOffset, littleEndian);
28641             },
28642             size: 2
28643         },
28644         // long, 32 bit int:
28645         4: {
28646             getValue: function (dataView, dataOffset, littleEndian) {
28647                 return dataView.getUint32(dataOffset, littleEndian);
28648             },
28649             size: 4
28650         },
28651         // rational = two long values, first is numerator, second is denominator:
28652         5: {
28653             getValue: function (dataView, dataOffset, littleEndian) {
28654                 return dataView.getUint32(dataOffset, littleEndian) /
28655                     dataView.getUint32(dataOffset + 4, littleEndian);
28656             },
28657             size: 8
28658         },
28659         // slong, 32 bit signed int:
28660         9: {
28661             getValue: function (dataView, dataOffset, littleEndian) {
28662                 return dataView.getInt32(dataOffset, littleEndian);
28663             },
28664             size: 4
28665         },
28666         // srational, two slongs, first is numerator, second is denominator:
28667         10: {
28668             getValue: function (dataView, dataOffset, littleEndian) {
28669                 return dataView.getInt32(dataOffset, littleEndian) /
28670                     dataView.getInt32(dataOffset + 4, littleEndian);
28671             },
28672             size: 8
28673         }
28674     },
28675     
28676     footer : {
28677         STANDARD : [
28678             {
28679                 tag : 'div',
28680                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28681                 action : 'rotate-left',
28682                 cn : [
28683                     {
28684                         tag : 'button',
28685                         cls : 'btn btn-default',
28686                         html : '<i class="fa fa-undo"></i>'
28687                     }
28688                 ]
28689             },
28690             {
28691                 tag : 'div',
28692                 cls : 'btn-group roo-upload-cropbox-picture',
28693                 action : 'picture',
28694                 cn : [
28695                     {
28696                         tag : 'button',
28697                         cls : 'btn btn-default',
28698                         html : '<i class="fa fa-picture-o"></i>'
28699                     }
28700                 ]
28701             },
28702             {
28703                 tag : 'div',
28704                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28705                 action : 'rotate-right',
28706                 cn : [
28707                     {
28708                         tag : 'button',
28709                         cls : 'btn btn-default',
28710                         html : '<i class="fa fa-repeat"></i>'
28711                     }
28712                 ]
28713             }
28714         ],
28715         DOCUMENT : [
28716             {
28717                 tag : 'div',
28718                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28719                 action : 'rotate-left',
28720                 cn : [
28721                     {
28722                         tag : 'button',
28723                         cls : 'btn btn-default',
28724                         html : '<i class="fa fa-undo"></i>'
28725                     }
28726                 ]
28727             },
28728             {
28729                 tag : 'div',
28730                 cls : 'btn-group roo-upload-cropbox-download',
28731                 action : 'download',
28732                 cn : [
28733                     {
28734                         tag : 'button',
28735                         cls : 'btn btn-default',
28736                         html : '<i class="fa fa-download"></i>'
28737                     }
28738                 ]
28739             },
28740             {
28741                 tag : 'div',
28742                 cls : 'btn-group roo-upload-cropbox-crop',
28743                 action : 'crop',
28744                 cn : [
28745                     {
28746                         tag : 'button',
28747                         cls : 'btn btn-default',
28748                         html : '<i class="fa fa-crop"></i>'
28749                     }
28750                 ]
28751             },
28752             {
28753                 tag : 'div',
28754                 cls : 'btn-group roo-upload-cropbox-trash',
28755                 action : 'trash',
28756                 cn : [
28757                     {
28758                         tag : 'button',
28759                         cls : 'btn btn-default',
28760                         html : '<i class="fa fa-trash"></i>'
28761                     }
28762                 ]
28763             },
28764             {
28765                 tag : 'div',
28766                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28767                 action : 'rotate-right',
28768                 cn : [
28769                     {
28770                         tag : 'button',
28771                         cls : 'btn btn-default',
28772                         html : '<i class="fa fa-repeat"></i>'
28773                     }
28774                 ]
28775             }
28776         ],
28777         ROTATOR : [
28778             {
28779                 tag : 'div',
28780                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28781                 action : 'rotate-left',
28782                 cn : [
28783                     {
28784                         tag : 'button',
28785                         cls : 'btn btn-default',
28786                         html : '<i class="fa fa-undo"></i>'
28787                     }
28788                 ]
28789             },
28790             {
28791                 tag : 'div',
28792                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28793                 action : 'rotate-right',
28794                 cn : [
28795                     {
28796                         tag : 'button',
28797                         cls : 'btn btn-default',
28798                         html : '<i class="fa fa-repeat"></i>'
28799                     }
28800                 ]
28801             }
28802         ]
28803     }
28804 });
28805
28806 /*
28807 * Licence: LGPL
28808 */
28809
28810 /**
28811  * @class Roo.bootstrap.DocumentManager
28812  * @extends Roo.bootstrap.Component
28813  * Bootstrap DocumentManager class
28814  * @cfg {String} paramName default 'imageUpload'
28815  * @cfg {String} toolTipName default 'filename'
28816  * @cfg {String} method default POST
28817  * @cfg {String} url action url
28818  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28819  * @cfg {Boolean} multiple multiple upload default true
28820  * @cfg {Number} thumbSize default 300
28821  * @cfg {String} fieldLabel
28822  * @cfg {Number} labelWidth default 4
28823  * @cfg {String} labelAlign (left|top) default left
28824  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28825 * @cfg {Number} labellg set the width of label (1-12)
28826  * @cfg {Number} labelmd set the width of label (1-12)
28827  * @cfg {Number} labelsm set the width of label (1-12)
28828  * @cfg {Number} labelxs set the width of label (1-12)
28829  * 
28830  * @constructor
28831  * Create a new DocumentManager
28832  * @param {Object} config The config object
28833  */
28834
28835 Roo.bootstrap.DocumentManager = function(config){
28836     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28837     
28838     this.files = [];
28839     this.delegates = [];
28840     
28841     this.addEvents({
28842         /**
28843          * @event initial
28844          * Fire when initial the DocumentManager
28845          * @param {Roo.bootstrap.DocumentManager} this
28846          */
28847         "initial" : true,
28848         /**
28849          * @event inspect
28850          * inspect selected file
28851          * @param {Roo.bootstrap.DocumentManager} this
28852          * @param {File} file
28853          */
28854         "inspect" : true,
28855         /**
28856          * @event exception
28857          * Fire when xhr load exception
28858          * @param {Roo.bootstrap.DocumentManager} this
28859          * @param {XMLHttpRequest} xhr
28860          */
28861         "exception" : true,
28862         /**
28863          * @event afterupload
28864          * Fire when xhr load exception
28865          * @param {Roo.bootstrap.DocumentManager} this
28866          * @param {XMLHttpRequest} xhr
28867          */
28868         "afterupload" : true,
28869         /**
28870          * @event prepare
28871          * prepare the form data
28872          * @param {Roo.bootstrap.DocumentManager} this
28873          * @param {Object} formData
28874          */
28875         "prepare" : true,
28876         /**
28877          * @event remove
28878          * Fire when remove the file
28879          * @param {Roo.bootstrap.DocumentManager} this
28880          * @param {Object} file
28881          */
28882         "remove" : true,
28883         /**
28884          * @event refresh
28885          * Fire after refresh the file
28886          * @param {Roo.bootstrap.DocumentManager} this
28887          */
28888         "refresh" : true,
28889         /**
28890          * @event click
28891          * Fire after click the image
28892          * @param {Roo.bootstrap.DocumentManager} this
28893          * @param {Object} file
28894          */
28895         "click" : true,
28896         /**
28897          * @event edit
28898          * Fire when upload a image and editable set to true
28899          * @param {Roo.bootstrap.DocumentManager} this
28900          * @param {Object} file
28901          */
28902         "edit" : true,
28903         /**
28904          * @event beforeselectfile
28905          * Fire before select file
28906          * @param {Roo.bootstrap.DocumentManager} this
28907          */
28908         "beforeselectfile" : true,
28909         /**
28910          * @event process
28911          * Fire before process file
28912          * @param {Roo.bootstrap.DocumentManager} this
28913          * @param {Object} file
28914          */
28915         "process" : true,
28916         /**
28917          * @event previewrendered
28918          * Fire when preview rendered
28919          * @param {Roo.bootstrap.DocumentManager} this
28920          * @param {Object} file
28921          */
28922         "previewrendered" : true,
28923         /**
28924          */
28925         "previewResize" : true
28926         
28927     });
28928 };
28929
28930 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28931     
28932     boxes : 0,
28933     inputName : '',
28934     thumbSize : 300,
28935     multiple : true,
28936     files : false,
28937     method : 'POST',
28938     url : '',
28939     paramName : 'imageUpload',
28940     toolTipName : 'filename',
28941     fieldLabel : '',
28942     labelWidth : 4,
28943     labelAlign : 'left',
28944     editable : true,
28945     delegates : false,
28946     xhr : false, 
28947     
28948     labellg : 0,
28949     labelmd : 0,
28950     labelsm : 0,
28951     labelxs : 0,
28952     
28953     getAutoCreate : function()
28954     {   
28955         var managerWidget = {
28956             tag : 'div',
28957             cls : 'roo-document-manager',
28958             cn : [
28959                 {
28960                     tag : 'input',
28961                     cls : 'roo-document-manager-selector',
28962                     type : 'file'
28963                 },
28964                 {
28965                     tag : 'div',
28966                     cls : 'roo-document-manager-uploader',
28967                     cn : [
28968                         {
28969                             tag : 'div',
28970                             cls : 'roo-document-manager-upload-btn',
28971                             html : '<i class="fa fa-plus"></i>'
28972                         }
28973                     ]
28974                     
28975                 }
28976             ]
28977         };
28978         
28979         var content = [
28980             {
28981                 tag : 'div',
28982                 cls : 'column col-md-12',
28983                 cn : managerWidget
28984             }
28985         ];
28986         
28987         if(this.fieldLabel.length){
28988             
28989             content = [
28990                 {
28991                     tag : 'div',
28992                     cls : 'column col-md-12',
28993                     html : this.fieldLabel
28994                 },
28995                 {
28996                     tag : 'div',
28997                     cls : 'column col-md-12',
28998                     cn : managerWidget
28999                 }
29000             ];
29001
29002             if(this.labelAlign == 'left'){
29003                 content = [
29004                     {
29005                         tag : 'div',
29006                         cls : 'column',
29007                         html : this.fieldLabel
29008                     },
29009                     {
29010                         tag : 'div',
29011                         cls : 'column',
29012                         cn : managerWidget
29013                     }
29014                 ];
29015                 
29016                 if(this.labelWidth > 12){
29017                     content[0].style = "width: " + this.labelWidth + 'px';
29018                 }
29019
29020                 if(this.labelWidth < 13 && this.labelmd == 0){
29021                     this.labelmd = this.labelWidth;
29022                 }
29023
29024                 if(this.labellg > 0){
29025                     content[0].cls += ' col-lg-' + this.labellg;
29026                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29027                 }
29028
29029                 if(this.labelmd > 0){
29030                     content[0].cls += ' col-md-' + this.labelmd;
29031                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29032                 }
29033
29034                 if(this.labelsm > 0){
29035                     content[0].cls += ' col-sm-' + this.labelsm;
29036                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29037                 }
29038
29039                 if(this.labelxs > 0){
29040                     content[0].cls += ' col-xs-' + this.labelxs;
29041                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29042                 }
29043                 
29044             }
29045         }
29046         
29047         var cfg = {
29048             tag : 'div',
29049             cls : 'row clearfix',
29050             cn : content
29051         };
29052         
29053         return cfg;
29054         
29055     },
29056     
29057     initEvents : function()
29058     {
29059         this.managerEl = this.el.select('.roo-document-manager', true).first();
29060         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29061         
29062         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29063         this.selectorEl.hide();
29064         
29065         if(this.multiple){
29066             this.selectorEl.attr('multiple', 'multiple');
29067         }
29068         
29069         this.selectorEl.on('change', this.onFileSelected, this);
29070         
29071         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29072         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29073         
29074         this.uploader.on('click', this.onUploaderClick, this);
29075         
29076         this.renderProgressDialog();
29077         
29078         var _this = this;
29079         
29080         window.addEventListener("resize", function() { _this.refresh(); } );
29081         
29082         this.fireEvent('initial', this);
29083     },
29084     
29085     renderProgressDialog : function()
29086     {
29087         var _this = this;
29088         
29089         this.progressDialog = new Roo.bootstrap.Modal({
29090             cls : 'roo-document-manager-progress-dialog',
29091             allow_close : false,
29092             title : '',
29093             buttons : [
29094                 {
29095                     name  :'cancel',
29096                     weight : 'danger',
29097                     html : 'Cancel'
29098                 }
29099             ], 
29100             listeners : { 
29101                 btnclick : function() {
29102                     _this.uploadCancel();
29103                     this.hide();
29104                 }
29105             }
29106         });
29107          
29108         this.progressDialog.render(Roo.get(document.body));
29109          
29110         this.progress = new Roo.bootstrap.Progress({
29111             cls : 'roo-document-manager-progress',
29112             active : true,
29113             striped : true
29114         });
29115         
29116         this.progress.render(this.progressDialog.getChildContainer());
29117         
29118         this.progressBar = new Roo.bootstrap.ProgressBar({
29119             cls : 'roo-document-manager-progress-bar',
29120             aria_valuenow : 0,
29121             aria_valuemin : 0,
29122             aria_valuemax : 12,
29123             panel : 'success'
29124         });
29125         
29126         this.progressBar.render(this.progress.getChildContainer());
29127     },
29128     
29129     onUploaderClick : function(e)
29130     {
29131         e.preventDefault();
29132      
29133         if(this.fireEvent('beforeselectfile', this) != false){
29134             this.selectorEl.dom.click();
29135         }
29136         
29137     },
29138     
29139     onFileSelected : function(e)
29140     {
29141         e.preventDefault();
29142         
29143         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29144             return;
29145         }
29146         
29147         Roo.each(this.selectorEl.dom.files, function(file){
29148             if(this.fireEvent('inspect', this, file) != false){
29149                 this.files.push(file);
29150             }
29151         }, this);
29152         
29153         this.queue();
29154         
29155     },
29156     
29157     queue : function()
29158     {
29159         this.selectorEl.dom.value = '';
29160         
29161         if(!this.files || !this.files.length){
29162             return;
29163         }
29164         
29165         if(this.boxes > 0 && this.files.length > this.boxes){
29166             this.files = this.files.slice(0, this.boxes);
29167         }
29168         
29169         this.uploader.show();
29170         
29171         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29172             this.uploader.hide();
29173         }
29174         
29175         var _this = this;
29176         
29177         var files = [];
29178         
29179         var docs = [];
29180         
29181         Roo.each(this.files, function(file){
29182             
29183             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29184                 var f = this.renderPreview(file);
29185                 files.push(f);
29186                 return;
29187             }
29188             
29189             if(file.type.indexOf('image') != -1){
29190                 this.delegates.push(
29191                     (function(){
29192                         _this.process(file);
29193                     }).createDelegate(this)
29194                 );
29195         
29196                 return;
29197             }
29198             
29199             docs.push(
29200                 (function(){
29201                     _this.process(file);
29202                 }).createDelegate(this)
29203             );
29204             
29205         }, this);
29206         
29207         this.files = files;
29208         
29209         this.delegates = this.delegates.concat(docs);
29210         
29211         if(!this.delegates.length){
29212             this.refresh();
29213             return;
29214         }
29215         
29216         this.progressBar.aria_valuemax = this.delegates.length;
29217         
29218         this.arrange();
29219         
29220         return;
29221     },
29222     
29223     arrange : function()
29224     {
29225         if(!this.delegates.length){
29226             this.progressDialog.hide();
29227             this.refresh();
29228             return;
29229         }
29230         
29231         var delegate = this.delegates.shift();
29232         
29233         this.progressDialog.show();
29234         
29235         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29236         
29237         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29238         
29239         delegate();
29240     },
29241     
29242     refresh : function()
29243     {
29244         this.uploader.show();
29245         
29246         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29247             this.uploader.hide();
29248         }
29249         
29250         Roo.isTouch ? this.closable(false) : this.closable(true);
29251         
29252         this.fireEvent('refresh', this);
29253     },
29254     
29255     onRemove : function(e, el, o)
29256     {
29257         e.preventDefault();
29258         
29259         this.fireEvent('remove', this, o);
29260         
29261     },
29262     
29263     remove : function(o)
29264     {
29265         var files = [];
29266         
29267         Roo.each(this.files, function(file){
29268             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29269                 files.push(file);
29270                 return;
29271             }
29272
29273             o.target.remove();
29274
29275         }, this);
29276         
29277         this.files = files;
29278         
29279         this.refresh();
29280     },
29281     
29282     clear : function()
29283     {
29284         Roo.each(this.files, function(file){
29285             if(!file.target){
29286                 return;
29287             }
29288             
29289             file.target.remove();
29290
29291         }, this);
29292         
29293         this.files = [];
29294         
29295         this.refresh();
29296     },
29297     
29298     onClick : function(e, el, o)
29299     {
29300         e.preventDefault();
29301         
29302         this.fireEvent('click', this, o);
29303         
29304     },
29305     
29306     closable : function(closable)
29307     {
29308         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29309             
29310             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29311             
29312             if(closable){
29313                 el.show();
29314                 return;
29315             }
29316             
29317             el.hide();
29318             
29319         }, this);
29320     },
29321     
29322     xhrOnLoad : function(xhr)
29323     {
29324         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29325             el.remove();
29326         }, this);
29327         
29328         if (xhr.readyState !== 4) {
29329             this.arrange();
29330             this.fireEvent('exception', this, xhr);
29331             return;
29332         }
29333
29334         var response = Roo.decode(xhr.responseText);
29335         
29336         if(!response.success){
29337             this.arrange();
29338             this.fireEvent('exception', this, xhr);
29339             return;
29340         }
29341         
29342         var file = this.renderPreview(response.data);
29343         
29344         this.files.push(file);
29345         
29346         this.arrange();
29347         
29348         this.fireEvent('afterupload', this, xhr);
29349         
29350     },
29351     
29352     xhrOnError : function(xhr)
29353     {
29354         Roo.log('xhr on error');
29355         
29356         var response = Roo.decode(xhr.responseText);
29357           
29358         Roo.log(response);
29359         
29360         this.arrange();
29361     },
29362     
29363     process : function(file)
29364     {
29365         if(this.fireEvent('process', this, file) !== false){
29366             if(this.editable && file.type.indexOf('image') != -1){
29367                 this.fireEvent('edit', this, file);
29368                 return;
29369             }
29370
29371             this.uploadStart(file, false);
29372
29373             return;
29374         }
29375         
29376     },
29377     
29378     uploadStart : function(file, crop)
29379     {
29380         this.xhr = new XMLHttpRequest();
29381         
29382         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29383             this.arrange();
29384             return;
29385         }
29386         
29387         file.xhr = this.xhr;
29388             
29389         this.managerEl.createChild({
29390             tag : 'div',
29391             cls : 'roo-document-manager-loading',
29392             cn : [
29393                 {
29394                     tag : 'div',
29395                     tooltip : file.name,
29396                     cls : 'roo-document-manager-thumb',
29397                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29398                 }
29399             ]
29400
29401         });
29402
29403         this.xhr.open(this.method, this.url, true);
29404         
29405         var headers = {
29406             "Accept": "application/json",
29407             "Cache-Control": "no-cache",
29408             "X-Requested-With": "XMLHttpRequest"
29409         };
29410         
29411         for (var headerName in headers) {
29412             var headerValue = headers[headerName];
29413             if (headerValue) {
29414                 this.xhr.setRequestHeader(headerName, headerValue);
29415             }
29416         }
29417         
29418         var _this = this;
29419         
29420         this.xhr.onload = function()
29421         {
29422             _this.xhrOnLoad(_this.xhr);
29423         }
29424         
29425         this.xhr.onerror = function()
29426         {
29427             _this.xhrOnError(_this.xhr);
29428         }
29429         
29430         var formData = new FormData();
29431
29432         formData.append('returnHTML', 'NO');
29433         
29434         if(crop){
29435             formData.append('crop', crop);
29436         }
29437         
29438         formData.append(this.paramName, file, file.name);
29439         
29440         var options = {
29441             file : file, 
29442             manually : false
29443         };
29444         
29445         if(this.fireEvent('prepare', this, formData, options) != false){
29446             
29447             if(options.manually){
29448                 return;
29449             }
29450             
29451             this.xhr.send(formData);
29452             return;
29453         };
29454         
29455         this.uploadCancel();
29456     },
29457     
29458     uploadCancel : function()
29459     {
29460         if (this.xhr) {
29461             this.xhr.abort();
29462         }
29463         
29464         this.delegates = [];
29465         
29466         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29467             el.remove();
29468         }, this);
29469         
29470         this.arrange();
29471     },
29472     
29473     renderPreview : function(file)
29474     {
29475         if(typeof(file.target) != 'undefined' && file.target){
29476             return file;
29477         }
29478         
29479         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29480         
29481         var previewEl = this.managerEl.createChild({
29482             tag : 'div',
29483             cls : 'roo-document-manager-preview',
29484             cn : [
29485                 {
29486                     tag : 'div',
29487                     tooltip : file[this.toolTipName],
29488                     cls : 'roo-document-manager-thumb',
29489                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29490                 },
29491                 {
29492                     tag : 'button',
29493                     cls : 'close',
29494                     html : '<i class="fa fa-times-circle"></i>'
29495                 }
29496             ]
29497         });
29498
29499         var close = previewEl.select('button.close', true).first();
29500
29501         close.on('click', this.onRemove, this, file);
29502
29503         file.target = previewEl;
29504
29505         var image = previewEl.select('img', true).first();
29506         
29507         var _this = this;
29508         
29509         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29510         
29511         image.on('click', this.onClick, this, file);
29512         
29513         this.fireEvent('previewrendered', this, file);
29514         
29515         return file;
29516         
29517     },
29518     
29519     onPreviewLoad : function(file, image)
29520     {
29521         if(typeof(file.target) == 'undefined' || !file.target){
29522             return;
29523         }
29524         
29525         var width = image.dom.naturalWidth || image.dom.width;
29526         var height = image.dom.naturalHeight || image.dom.height;
29527         
29528         if(!this.previewResize) {
29529             return;
29530         }
29531         
29532         if(width > height){
29533             file.target.addClass('wide');
29534             return;
29535         }
29536         
29537         file.target.addClass('tall');
29538         return;
29539         
29540     },
29541     
29542     uploadFromSource : function(file, crop)
29543     {
29544         this.xhr = new XMLHttpRequest();
29545         
29546         this.managerEl.createChild({
29547             tag : 'div',
29548             cls : 'roo-document-manager-loading',
29549             cn : [
29550                 {
29551                     tag : 'div',
29552                     tooltip : file.name,
29553                     cls : 'roo-document-manager-thumb',
29554                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29555                 }
29556             ]
29557
29558         });
29559
29560         this.xhr.open(this.method, this.url, true);
29561         
29562         var headers = {
29563             "Accept": "application/json",
29564             "Cache-Control": "no-cache",
29565             "X-Requested-With": "XMLHttpRequest"
29566         };
29567         
29568         for (var headerName in headers) {
29569             var headerValue = headers[headerName];
29570             if (headerValue) {
29571                 this.xhr.setRequestHeader(headerName, headerValue);
29572             }
29573         }
29574         
29575         var _this = this;
29576         
29577         this.xhr.onload = function()
29578         {
29579             _this.xhrOnLoad(_this.xhr);
29580         }
29581         
29582         this.xhr.onerror = function()
29583         {
29584             _this.xhrOnError(_this.xhr);
29585         }
29586         
29587         var formData = new FormData();
29588
29589         formData.append('returnHTML', 'NO');
29590         
29591         formData.append('crop', crop);
29592         
29593         if(typeof(file.filename) != 'undefined'){
29594             formData.append('filename', file.filename);
29595         }
29596         
29597         if(typeof(file.mimetype) != 'undefined'){
29598             formData.append('mimetype', file.mimetype);
29599         }
29600         
29601         Roo.log(formData);
29602         
29603         if(this.fireEvent('prepare', this, formData) != false){
29604             this.xhr.send(formData);
29605         };
29606     }
29607 });
29608
29609 /*
29610 * Licence: LGPL
29611 */
29612
29613 /**
29614  * @class Roo.bootstrap.DocumentViewer
29615  * @extends Roo.bootstrap.Component
29616  * Bootstrap DocumentViewer class
29617  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29618  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29619  * 
29620  * @constructor
29621  * Create a new DocumentViewer
29622  * @param {Object} config The config object
29623  */
29624
29625 Roo.bootstrap.DocumentViewer = function(config){
29626     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29627     
29628     this.addEvents({
29629         /**
29630          * @event initial
29631          * Fire after initEvent
29632          * @param {Roo.bootstrap.DocumentViewer} this
29633          */
29634         "initial" : true,
29635         /**
29636          * @event click
29637          * Fire after click
29638          * @param {Roo.bootstrap.DocumentViewer} this
29639          */
29640         "click" : true,
29641         /**
29642          * @event download
29643          * Fire after download button
29644          * @param {Roo.bootstrap.DocumentViewer} this
29645          */
29646         "download" : true,
29647         /**
29648          * @event trash
29649          * Fire after trash button
29650          * @param {Roo.bootstrap.DocumentViewer} this
29651          */
29652         "trash" : true
29653         
29654     });
29655 };
29656
29657 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29658     
29659     showDownload : true,
29660     
29661     showTrash : true,
29662     
29663     getAutoCreate : function()
29664     {
29665         var cfg = {
29666             tag : 'div',
29667             cls : 'roo-document-viewer',
29668             cn : [
29669                 {
29670                     tag : 'div',
29671                     cls : 'roo-document-viewer-body',
29672                     cn : [
29673                         {
29674                             tag : 'div',
29675                             cls : 'roo-document-viewer-thumb',
29676                             cn : [
29677                                 {
29678                                     tag : 'img',
29679                                     cls : 'roo-document-viewer-image'
29680                                 }
29681                             ]
29682                         }
29683                     ]
29684                 },
29685                 {
29686                     tag : 'div',
29687                     cls : 'roo-document-viewer-footer',
29688                     cn : {
29689                         tag : 'div',
29690                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29691                         cn : [
29692                             {
29693                                 tag : 'div',
29694                                 cls : 'btn-group roo-document-viewer-download',
29695                                 cn : [
29696                                     {
29697                                         tag : 'button',
29698                                         cls : 'btn btn-default',
29699                                         html : '<i class="fa fa-download"></i>'
29700                                     }
29701                                 ]
29702                             },
29703                             {
29704                                 tag : 'div',
29705                                 cls : 'btn-group roo-document-viewer-trash',
29706                                 cn : [
29707                                     {
29708                                         tag : 'button',
29709                                         cls : 'btn btn-default',
29710                                         html : '<i class="fa fa-trash"></i>'
29711                                     }
29712                                 ]
29713                             }
29714                         ]
29715                     }
29716                 }
29717             ]
29718         };
29719         
29720         return cfg;
29721     },
29722     
29723     initEvents : function()
29724     {
29725         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29726         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29727         
29728         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29729         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29730         
29731         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29732         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29733         
29734         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29735         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29736         
29737         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29738         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29739         
29740         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29741         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29742         
29743         this.bodyEl.on('click', this.onClick, this);
29744         this.downloadBtn.on('click', this.onDownload, this);
29745         this.trashBtn.on('click', this.onTrash, this);
29746         
29747         this.downloadBtn.hide();
29748         this.trashBtn.hide();
29749         
29750         if(this.showDownload){
29751             this.downloadBtn.show();
29752         }
29753         
29754         if(this.showTrash){
29755             this.trashBtn.show();
29756         }
29757         
29758         if(!this.showDownload && !this.showTrash) {
29759             this.footerEl.hide();
29760         }
29761         
29762     },
29763     
29764     initial : function()
29765     {
29766         this.fireEvent('initial', this);
29767         
29768     },
29769     
29770     onClick : function(e)
29771     {
29772         e.preventDefault();
29773         
29774         this.fireEvent('click', this);
29775     },
29776     
29777     onDownload : function(e)
29778     {
29779         e.preventDefault();
29780         
29781         this.fireEvent('download', this);
29782     },
29783     
29784     onTrash : function(e)
29785     {
29786         e.preventDefault();
29787         
29788         this.fireEvent('trash', this);
29789     }
29790     
29791 });
29792 /*
29793  * - LGPL
29794  *
29795  * nav progress bar
29796  * 
29797  */
29798
29799 /**
29800  * @class Roo.bootstrap.NavProgressBar
29801  * @extends Roo.bootstrap.Component
29802  * Bootstrap NavProgressBar class
29803  * 
29804  * @constructor
29805  * Create a new nav progress bar
29806  * @param {Object} config The config object
29807  */
29808
29809 Roo.bootstrap.NavProgressBar = function(config){
29810     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29811
29812     this.bullets = this.bullets || [];
29813    
29814 //    Roo.bootstrap.NavProgressBar.register(this);
29815      this.addEvents({
29816         /**
29817              * @event changed
29818              * Fires when the active item changes
29819              * @param {Roo.bootstrap.NavProgressBar} this
29820              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29821              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29822          */
29823         'changed': true
29824      });
29825     
29826 };
29827
29828 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29829     
29830     bullets : [],
29831     barItems : [],
29832     
29833     getAutoCreate : function()
29834     {
29835         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29836         
29837         cfg = {
29838             tag : 'div',
29839             cls : 'roo-navigation-bar-group',
29840             cn : [
29841                 {
29842                     tag : 'div',
29843                     cls : 'roo-navigation-top-bar'
29844                 },
29845                 {
29846                     tag : 'div',
29847                     cls : 'roo-navigation-bullets-bar',
29848                     cn : [
29849                         {
29850                             tag : 'ul',
29851                             cls : 'roo-navigation-bar'
29852                         }
29853                     ]
29854                 },
29855                 
29856                 {
29857                     tag : 'div',
29858                     cls : 'roo-navigation-bottom-bar'
29859                 }
29860             ]
29861             
29862         };
29863         
29864         return cfg;
29865         
29866     },
29867     
29868     initEvents: function() 
29869     {
29870         
29871     },
29872     
29873     onRender : function(ct, position) 
29874     {
29875         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29876         
29877         if(this.bullets.length){
29878             Roo.each(this.bullets, function(b){
29879                this.addItem(b);
29880             }, this);
29881         }
29882         
29883         this.format();
29884         
29885     },
29886     
29887     addItem : function(cfg)
29888     {
29889         var item = new Roo.bootstrap.NavProgressItem(cfg);
29890         
29891         item.parentId = this.id;
29892         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29893         
29894         if(cfg.html){
29895             var top = new Roo.bootstrap.Element({
29896                 tag : 'div',
29897                 cls : 'roo-navigation-bar-text'
29898             });
29899             
29900             var bottom = new Roo.bootstrap.Element({
29901                 tag : 'div',
29902                 cls : 'roo-navigation-bar-text'
29903             });
29904             
29905             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29906             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29907             
29908             var topText = new Roo.bootstrap.Element({
29909                 tag : 'span',
29910                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29911             });
29912             
29913             var bottomText = new Roo.bootstrap.Element({
29914                 tag : 'span',
29915                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29916             });
29917             
29918             topText.onRender(top.el, null);
29919             bottomText.onRender(bottom.el, null);
29920             
29921             item.topEl = top;
29922             item.bottomEl = bottom;
29923         }
29924         
29925         this.barItems.push(item);
29926         
29927         return item;
29928     },
29929     
29930     getActive : function()
29931     {
29932         var active = false;
29933         
29934         Roo.each(this.barItems, function(v){
29935             
29936             if (!v.isActive()) {
29937                 return;
29938             }
29939             
29940             active = v;
29941             return false;
29942             
29943         });
29944         
29945         return active;
29946     },
29947     
29948     setActiveItem : function(item)
29949     {
29950         var prev = false;
29951         
29952         Roo.each(this.barItems, function(v){
29953             if (v.rid == item.rid) {
29954                 return ;
29955             }
29956             
29957             if (v.isActive()) {
29958                 v.setActive(false);
29959                 prev = v;
29960             }
29961         });
29962
29963         item.setActive(true);
29964         
29965         this.fireEvent('changed', this, item, prev);
29966     },
29967     
29968     getBarItem: function(rid)
29969     {
29970         var ret = false;
29971         
29972         Roo.each(this.barItems, function(e) {
29973             if (e.rid != rid) {
29974                 return;
29975             }
29976             
29977             ret =  e;
29978             return false;
29979         });
29980         
29981         return ret;
29982     },
29983     
29984     indexOfItem : function(item)
29985     {
29986         var index = false;
29987         
29988         Roo.each(this.barItems, function(v, i){
29989             
29990             if (v.rid != item.rid) {
29991                 return;
29992             }
29993             
29994             index = i;
29995             return false
29996         });
29997         
29998         return index;
29999     },
30000     
30001     setActiveNext : function()
30002     {
30003         var i = this.indexOfItem(this.getActive());
30004         
30005         if (i > this.barItems.length) {
30006             return;
30007         }
30008         
30009         this.setActiveItem(this.barItems[i+1]);
30010     },
30011     
30012     setActivePrev : function()
30013     {
30014         var i = this.indexOfItem(this.getActive());
30015         
30016         if (i  < 1) {
30017             return;
30018         }
30019         
30020         this.setActiveItem(this.barItems[i-1]);
30021     },
30022     
30023     format : function()
30024     {
30025         if(!this.barItems.length){
30026             return;
30027         }
30028      
30029         var width = 100 / this.barItems.length;
30030         
30031         Roo.each(this.barItems, function(i){
30032             i.el.setStyle('width', width + '%');
30033             i.topEl.el.setStyle('width', width + '%');
30034             i.bottomEl.el.setStyle('width', width + '%');
30035         }, this);
30036         
30037     }
30038     
30039 });
30040 /*
30041  * - LGPL
30042  *
30043  * Nav Progress Item
30044  * 
30045  */
30046
30047 /**
30048  * @class Roo.bootstrap.NavProgressItem
30049  * @extends Roo.bootstrap.Component
30050  * Bootstrap NavProgressItem class
30051  * @cfg {String} rid the reference id
30052  * @cfg {Boolean} active (true|false) Is item active default false
30053  * @cfg {Boolean} disabled (true|false) Is item active default false
30054  * @cfg {String} html
30055  * @cfg {String} position (top|bottom) text position default bottom
30056  * @cfg {String} icon show icon instead of number
30057  * 
30058  * @constructor
30059  * Create a new NavProgressItem
30060  * @param {Object} config The config object
30061  */
30062 Roo.bootstrap.NavProgressItem = function(config){
30063     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30064     this.addEvents({
30065         // raw events
30066         /**
30067          * @event click
30068          * The raw click event for the entire grid.
30069          * @param {Roo.bootstrap.NavProgressItem} this
30070          * @param {Roo.EventObject} e
30071          */
30072         "click" : true
30073     });
30074    
30075 };
30076
30077 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30078     
30079     rid : '',
30080     active : false,
30081     disabled : false,
30082     html : '',
30083     position : 'bottom',
30084     icon : false,
30085     
30086     getAutoCreate : function()
30087     {
30088         var iconCls = 'roo-navigation-bar-item-icon';
30089         
30090         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30091         
30092         var cfg = {
30093             tag: 'li',
30094             cls: 'roo-navigation-bar-item',
30095             cn : [
30096                 {
30097                     tag : 'i',
30098                     cls : iconCls
30099                 }
30100             ]
30101         };
30102         
30103         if(this.active){
30104             cfg.cls += ' active';
30105         }
30106         if(this.disabled){
30107             cfg.cls += ' disabled';
30108         }
30109         
30110         return cfg;
30111     },
30112     
30113     disable : function()
30114     {
30115         this.setDisabled(true);
30116     },
30117     
30118     enable : function()
30119     {
30120         this.setDisabled(false);
30121     },
30122     
30123     initEvents: function() 
30124     {
30125         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30126         
30127         this.iconEl.on('click', this.onClick, this);
30128     },
30129     
30130     onClick : function(e)
30131     {
30132         e.preventDefault();
30133         
30134         if(this.disabled){
30135             return;
30136         }
30137         
30138         if(this.fireEvent('click', this, e) === false){
30139             return;
30140         };
30141         
30142         this.parent().setActiveItem(this);
30143     },
30144     
30145     isActive: function () 
30146     {
30147         return this.active;
30148     },
30149     
30150     setActive : function(state)
30151     {
30152         if(this.active == state){
30153             return;
30154         }
30155         
30156         this.active = state;
30157         
30158         if (state) {
30159             this.el.addClass('active');
30160             return;
30161         }
30162         
30163         this.el.removeClass('active');
30164         
30165         return;
30166     },
30167     
30168     setDisabled : function(state)
30169     {
30170         if(this.disabled == state){
30171             return;
30172         }
30173         
30174         this.disabled = state;
30175         
30176         if (state) {
30177             this.el.addClass('disabled');
30178             return;
30179         }
30180         
30181         this.el.removeClass('disabled');
30182     },
30183     
30184     tooltipEl : function()
30185     {
30186         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30187     }
30188 });
30189  
30190
30191  /*
30192  * - LGPL
30193  *
30194  * FieldLabel
30195  * 
30196  */
30197
30198 /**
30199  * @class Roo.bootstrap.FieldLabel
30200  * @extends Roo.bootstrap.Component
30201  * Bootstrap FieldLabel class
30202  * @cfg {String} html contents of the element
30203  * @cfg {String} tag tag of the element default label
30204  * @cfg {String} cls class of the element
30205  * @cfg {String} target label target 
30206  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30207  * @cfg {String} invalidClass default "text-warning"
30208  * @cfg {String} validClass default "text-success"
30209  * @cfg {String} iconTooltip default "This field is required"
30210  * @cfg {String} indicatorpos (left|right) default left
30211  * 
30212  * @constructor
30213  * Create a new FieldLabel
30214  * @param {Object} config The config object
30215  */
30216
30217 Roo.bootstrap.FieldLabel = function(config){
30218     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30219     
30220     this.addEvents({
30221             /**
30222              * @event invalid
30223              * Fires after the field has been marked as invalid.
30224              * @param {Roo.form.FieldLabel} this
30225              * @param {String} msg The validation message
30226              */
30227             invalid : true,
30228             /**
30229              * @event valid
30230              * Fires after the field has been validated with no errors.
30231              * @param {Roo.form.FieldLabel} this
30232              */
30233             valid : true
30234         });
30235 };
30236
30237 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30238     
30239     tag: 'label',
30240     cls: '',
30241     html: '',
30242     target: '',
30243     allowBlank : true,
30244     invalidClass : 'has-warning',
30245     validClass : 'has-success',
30246     iconTooltip : 'This field is required',
30247     indicatorpos : 'left',
30248     
30249     getAutoCreate : function(){
30250         
30251         var cls = "";
30252         if (!this.allowBlank) {
30253             cls  = "visible";
30254         }
30255         
30256         var cfg = {
30257             tag : this.tag,
30258             cls : 'roo-bootstrap-field-label ' + this.cls,
30259             for : this.target,
30260             cn : [
30261                 {
30262                     tag : 'i',
30263                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30264                     tooltip : this.iconTooltip
30265                 },
30266                 {
30267                     tag : 'span',
30268                     html : this.html
30269                 }
30270             ] 
30271         };
30272         
30273         if(this.indicatorpos == 'right'){
30274             var cfg = {
30275                 tag : this.tag,
30276                 cls : 'roo-bootstrap-field-label ' + this.cls,
30277                 for : this.target,
30278                 cn : [
30279                     {
30280                         tag : 'span',
30281                         html : this.html
30282                     },
30283                     {
30284                         tag : 'i',
30285                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30286                         tooltip : this.iconTooltip
30287                     }
30288                 ] 
30289             };
30290         }
30291         
30292         return cfg;
30293     },
30294     
30295     initEvents: function() 
30296     {
30297         Roo.bootstrap.Element.superclass.initEvents.call(this);
30298         
30299         this.indicator = this.indicatorEl();
30300         
30301         if(this.indicator){
30302             this.indicator.removeClass('visible');
30303             this.indicator.addClass('invisible');
30304         }
30305         
30306         Roo.bootstrap.FieldLabel.register(this);
30307     },
30308     
30309     indicatorEl : function()
30310     {
30311         var indicator = this.el.select('i.roo-required-indicator',true).first();
30312         
30313         if(!indicator){
30314             return false;
30315         }
30316         
30317         return indicator;
30318         
30319     },
30320     
30321     /**
30322      * Mark this field as valid
30323      */
30324     markValid : function()
30325     {
30326         if(this.indicator){
30327             this.indicator.removeClass('visible');
30328             this.indicator.addClass('invisible');
30329         }
30330         
30331         this.el.removeClass(this.invalidClass);
30332         
30333         this.el.addClass(this.validClass);
30334         
30335         this.fireEvent('valid', this);
30336     },
30337     
30338     /**
30339      * Mark this field as invalid
30340      * @param {String} msg The validation message
30341      */
30342     markInvalid : function(msg)
30343     {
30344         if(this.indicator){
30345             this.indicator.removeClass('invisible');
30346             this.indicator.addClass('visible');
30347         }
30348         
30349         this.el.removeClass(this.validClass);
30350         
30351         this.el.addClass(this.invalidClass);
30352         
30353         this.fireEvent('invalid', this, msg);
30354     }
30355     
30356    
30357 });
30358
30359 Roo.apply(Roo.bootstrap.FieldLabel, {
30360     
30361     groups: {},
30362     
30363      /**
30364     * register a FieldLabel Group
30365     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30366     */
30367     register : function(label)
30368     {
30369         if(this.groups.hasOwnProperty(label.target)){
30370             return;
30371         }
30372      
30373         this.groups[label.target] = label;
30374         
30375     },
30376     /**
30377     * fetch a FieldLabel Group based on the target
30378     * @param {string} target
30379     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30380     */
30381     get: function(target) {
30382         if (typeof(this.groups[target]) == 'undefined') {
30383             return false;
30384         }
30385         
30386         return this.groups[target] ;
30387     }
30388 });
30389
30390  
30391
30392  /*
30393  * - LGPL
30394  *
30395  * page DateSplitField.
30396  * 
30397  */
30398
30399
30400 /**
30401  * @class Roo.bootstrap.DateSplitField
30402  * @extends Roo.bootstrap.Component
30403  * Bootstrap DateSplitField class
30404  * @cfg {string} fieldLabel - the label associated
30405  * @cfg {Number} labelWidth set the width of label (0-12)
30406  * @cfg {String} labelAlign (top|left)
30407  * @cfg {Boolean} dayAllowBlank (true|false) default false
30408  * @cfg {Boolean} monthAllowBlank (true|false) default false
30409  * @cfg {Boolean} yearAllowBlank (true|false) default false
30410  * @cfg {string} dayPlaceholder 
30411  * @cfg {string} monthPlaceholder
30412  * @cfg {string} yearPlaceholder
30413  * @cfg {string} dayFormat default 'd'
30414  * @cfg {string} monthFormat default 'm'
30415  * @cfg {string} yearFormat default 'Y'
30416  * @cfg {Number} labellg set the width of label (1-12)
30417  * @cfg {Number} labelmd set the width of label (1-12)
30418  * @cfg {Number} labelsm set the width of label (1-12)
30419  * @cfg {Number} labelxs set the width of label (1-12)
30420
30421  *     
30422  * @constructor
30423  * Create a new DateSplitField
30424  * @param {Object} config The config object
30425  */
30426
30427 Roo.bootstrap.DateSplitField = function(config){
30428     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30429     
30430     this.addEvents({
30431         // raw events
30432          /**
30433          * @event years
30434          * getting the data of years
30435          * @param {Roo.bootstrap.DateSplitField} this
30436          * @param {Object} years
30437          */
30438         "years" : true,
30439         /**
30440          * @event days
30441          * getting the data of days
30442          * @param {Roo.bootstrap.DateSplitField} this
30443          * @param {Object} days
30444          */
30445         "days" : true,
30446         /**
30447          * @event invalid
30448          * Fires after the field has been marked as invalid.
30449          * @param {Roo.form.Field} this
30450          * @param {String} msg The validation message
30451          */
30452         invalid : true,
30453        /**
30454          * @event valid
30455          * Fires after the field has been validated with no errors.
30456          * @param {Roo.form.Field} this
30457          */
30458         valid : true
30459     });
30460 };
30461
30462 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30463     
30464     fieldLabel : '',
30465     labelAlign : 'top',
30466     labelWidth : 3,
30467     dayAllowBlank : false,
30468     monthAllowBlank : false,
30469     yearAllowBlank : false,
30470     dayPlaceholder : '',
30471     monthPlaceholder : '',
30472     yearPlaceholder : '',
30473     dayFormat : 'd',
30474     monthFormat : 'm',
30475     yearFormat : 'Y',
30476     isFormField : true,
30477     labellg : 0,
30478     labelmd : 0,
30479     labelsm : 0,
30480     labelxs : 0,
30481     
30482     getAutoCreate : function()
30483     {
30484         var cfg = {
30485             tag : 'div',
30486             cls : 'row roo-date-split-field-group',
30487             cn : [
30488                 {
30489                     tag : 'input',
30490                     type : 'hidden',
30491                     cls : 'form-hidden-field roo-date-split-field-group-value',
30492                     name : this.name
30493                 }
30494             ]
30495         };
30496         
30497         var labelCls = 'col-md-12';
30498         var contentCls = 'col-md-4';
30499         
30500         if(this.fieldLabel){
30501             
30502             var label = {
30503                 tag : 'div',
30504                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30505                 cn : [
30506                     {
30507                         tag : 'label',
30508                         html : this.fieldLabel
30509                     }
30510                 ]
30511             };
30512             
30513             if(this.labelAlign == 'left'){
30514             
30515                 if(this.labelWidth > 12){
30516                     label.style = "width: " + this.labelWidth + 'px';
30517                 }
30518
30519                 if(this.labelWidth < 13 && this.labelmd == 0){
30520                     this.labelmd = this.labelWidth;
30521                 }
30522
30523                 if(this.labellg > 0){
30524                     labelCls = ' col-lg-' + this.labellg;
30525                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30526                 }
30527
30528                 if(this.labelmd > 0){
30529                     labelCls = ' col-md-' + this.labelmd;
30530                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30531                 }
30532
30533                 if(this.labelsm > 0){
30534                     labelCls = ' col-sm-' + this.labelsm;
30535                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30536                 }
30537
30538                 if(this.labelxs > 0){
30539                     labelCls = ' col-xs-' + this.labelxs;
30540                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30541                 }
30542             }
30543             
30544             label.cls += ' ' + labelCls;
30545             
30546             cfg.cn.push(label);
30547         }
30548         
30549         Roo.each(['day', 'month', 'year'], function(t){
30550             cfg.cn.push({
30551                 tag : 'div',
30552                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30553             });
30554         }, this);
30555         
30556         return cfg;
30557     },
30558     
30559     inputEl: function ()
30560     {
30561         return this.el.select('.roo-date-split-field-group-value', true).first();
30562     },
30563     
30564     onRender : function(ct, position) 
30565     {
30566         var _this = this;
30567         
30568         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30569         
30570         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30571         
30572         this.dayField = new Roo.bootstrap.ComboBox({
30573             allowBlank : this.dayAllowBlank,
30574             alwaysQuery : true,
30575             displayField : 'value',
30576             editable : false,
30577             fieldLabel : '',
30578             forceSelection : true,
30579             mode : 'local',
30580             placeholder : this.dayPlaceholder,
30581             selectOnFocus : true,
30582             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30583             triggerAction : 'all',
30584             typeAhead : true,
30585             valueField : 'value',
30586             store : new Roo.data.SimpleStore({
30587                 data : (function() {    
30588                     var days = [];
30589                     _this.fireEvent('days', _this, days);
30590                     return days;
30591                 })(),
30592                 fields : [ 'value' ]
30593             }),
30594             listeners : {
30595                 select : function (_self, record, index)
30596                 {
30597                     _this.setValue(_this.getValue());
30598                 }
30599             }
30600         });
30601
30602         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30603         
30604         this.monthField = new Roo.bootstrap.MonthField({
30605             after : '<i class=\"fa fa-calendar\"></i>',
30606             allowBlank : this.monthAllowBlank,
30607             placeholder : this.monthPlaceholder,
30608             readOnly : true,
30609             listeners : {
30610                 render : function (_self)
30611                 {
30612                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30613                         e.preventDefault();
30614                         _self.focus();
30615                     });
30616                 },
30617                 select : function (_self, oldvalue, newvalue)
30618                 {
30619                     _this.setValue(_this.getValue());
30620                 }
30621             }
30622         });
30623         
30624         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30625         
30626         this.yearField = new Roo.bootstrap.ComboBox({
30627             allowBlank : this.yearAllowBlank,
30628             alwaysQuery : true,
30629             displayField : 'value',
30630             editable : false,
30631             fieldLabel : '',
30632             forceSelection : true,
30633             mode : 'local',
30634             placeholder : this.yearPlaceholder,
30635             selectOnFocus : true,
30636             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30637             triggerAction : 'all',
30638             typeAhead : true,
30639             valueField : 'value',
30640             store : new Roo.data.SimpleStore({
30641                 data : (function() {
30642                     var years = [];
30643                     _this.fireEvent('years', _this, years);
30644                     return years;
30645                 })(),
30646                 fields : [ 'value' ]
30647             }),
30648             listeners : {
30649                 select : function (_self, record, index)
30650                 {
30651                     _this.setValue(_this.getValue());
30652                 }
30653             }
30654         });
30655
30656         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30657     },
30658     
30659     setValue : function(v, format)
30660     {
30661         this.inputEl.dom.value = v;
30662         
30663         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30664         
30665         var d = Date.parseDate(v, f);
30666         
30667         if(!d){
30668             this.validate();
30669             return;
30670         }
30671         
30672         this.setDay(d.format(this.dayFormat));
30673         this.setMonth(d.format(this.monthFormat));
30674         this.setYear(d.format(this.yearFormat));
30675         
30676         this.validate();
30677         
30678         return;
30679     },
30680     
30681     setDay : function(v)
30682     {
30683         this.dayField.setValue(v);
30684         this.inputEl.dom.value = this.getValue();
30685         this.validate();
30686         return;
30687     },
30688     
30689     setMonth : function(v)
30690     {
30691         this.monthField.setValue(v, true);
30692         this.inputEl.dom.value = this.getValue();
30693         this.validate();
30694         return;
30695     },
30696     
30697     setYear : function(v)
30698     {
30699         this.yearField.setValue(v);
30700         this.inputEl.dom.value = this.getValue();
30701         this.validate();
30702         return;
30703     },
30704     
30705     getDay : function()
30706     {
30707         return this.dayField.getValue();
30708     },
30709     
30710     getMonth : function()
30711     {
30712         return this.monthField.getValue();
30713     },
30714     
30715     getYear : function()
30716     {
30717         return this.yearField.getValue();
30718     },
30719     
30720     getValue : function()
30721     {
30722         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30723         
30724         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30725         
30726         return date;
30727     },
30728     
30729     reset : function()
30730     {
30731         this.setDay('');
30732         this.setMonth('');
30733         this.setYear('');
30734         this.inputEl.dom.value = '';
30735         this.validate();
30736         return;
30737     },
30738     
30739     validate : function()
30740     {
30741         var d = this.dayField.validate();
30742         var m = this.monthField.validate();
30743         var y = this.yearField.validate();
30744         
30745         var valid = true;
30746         
30747         if(
30748                 (!this.dayAllowBlank && !d) ||
30749                 (!this.monthAllowBlank && !m) ||
30750                 (!this.yearAllowBlank && !y)
30751         ){
30752             valid = false;
30753         }
30754         
30755         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30756             return valid;
30757         }
30758         
30759         if(valid){
30760             this.markValid();
30761             return valid;
30762         }
30763         
30764         this.markInvalid();
30765         
30766         return valid;
30767     },
30768     
30769     markValid : function()
30770     {
30771         
30772         var label = this.el.select('label', true).first();
30773         var icon = this.el.select('i.fa-star', true).first();
30774
30775         if(label && icon){
30776             icon.remove();
30777         }
30778         
30779         this.fireEvent('valid', this);
30780     },
30781     
30782      /**
30783      * Mark this field as invalid
30784      * @param {String} msg The validation message
30785      */
30786     markInvalid : function(msg)
30787     {
30788         
30789         var label = this.el.select('label', true).first();
30790         var icon = this.el.select('i.fa-star', true).first();
30791
30792         if(label && !icon){
30793             this.el.select('.roo-date-split-field-label', true).createChild({
30794                 tag : 'i',
30795                 cls : 'text-danger fa fa-lg fa-star',
30796                 tooltip : 'This field is required',
30797                 style : 'margin-right:5px;'
30798             }, label, true);
30799         }
30800         
30801         this.fireEvent('invalid', this, msg);
30802     },
30803     
30804     clearInvalid : function()
30805     {
30806         var label = this.el.select('label', true).first();
30807         var icon = this.el.select('i.fa-star', true).first();
30808
30809         if(label && icon){
30810             icon.remove();
30811         }
30812         
30813         this.fireEvent('valid', this);
30814     },
30815     
30816     getName: function()
30817     {
30818         return this.name;
30819     }
30820     
30821 });
30822
30823  /**
30824  *
30825  * This is based on 
30826  * http://masonry.desandro.com
30827  *
30828  * The idea is to render all the bricks based on vertical width...
30829  *
30830  * The original code extends 'outlayer' - we might need to use that....
30831  * 
30832  */
30833
30834
30835 /**
30836  * @class Roo.bootstrap.LayoutMasonry
30837  * @extends Roo.bootstrap.Component
30838  * Bootstrap Layout Masonry class
30839  * 
30840  * @constructor
30841  * Create a new Element
30842  * @param {Object} config The config object
30843  */
30844
30845 Roo.bootstrap.LayoutMasonry = function(config){
30846     
30847     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30848     
30849     this.bricks = [];
30850     
30851     Roo.bootstrap.LayoutMasonry.register(this);
30852     
30853     this.addEvents({
30854         // raw events
30855         /**
30856          * @event layout
30857          * Fire after layout the items
30858          * @param {Roo.bootstrap.LayoutMasonry} this
30859          * @param {Roo.EventObject} e
30860          */
30861         "layout" : true
30862     });
30863     
30864 };
30865
30866 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30867     
30868     /**
30869      * @cfg {Boolean} isLayoutInstant = no animation?
30870      */   
30871     isLayoutInstant : false, // needed?
30872    
30873     /**
30874      * @cfg {Number} boxWidth  width of the columns
30875      */   
30876     boxWidth : 450,
30877     
30878       /**
30879      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30880      */   
30881     boxHeight : 0,
30882     
30883     /**
30884      * @cfg {Number} padWidth padding below box..
30885      */   
30886     padWidth : 10, 
30887     
30888     /**
30889      * @cfg {Number} gutter gutter width..
30890      */   
30891     gutter : 10,
30892     
30893      /**
30894      * @cfg {Number} maxCols maximum number of columns
30895      */   
30896     
30897     maxCols: 0,
30898     
30899     /**
30900      * @cfg {Boolean} isAutoInitial defalut true
30901      */   
30902     isAutoInitial : true, 
30903     
30904     containerWidth: 0,
30905     
30906     /**
30907      * @cfg {Boolean} isHorizontal defalut false
30908      */   
30909     isHorizontal : false, 
30910
30911     currentSize : null,
30912     
30913     tag: 'div',
30914     
30915     cls: '',
30916     
30917     bricks: null, //CompositeElement
30918     
30919     cols : 1,
30920     
30921     _isLayoutInited : false,
30922     
30923 //    isAlternative : false, // only use for vertical layout...
30924     
30925     /**
30926      * @cfg {Number} alternativePadWidth padding below box..
30927      */   
30928     alternativePadWidth : 50,
30929     
30930     selectedBrick : [],
30931     
30932     getAutoCreate : function(){
30933         
30934         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30935         
30936         var cfg = {
30937             tag: this.tag,
30938             cls: 'blog-masonary-wrapper ' + this.cls,
30939             cn : {
30940                 cls : 'mas-boxes masonary'
30941             }
30942         };
30943         
30944         return cfg;
30945     },
30946     
30947     getChildContainer: function( )
30948     {
30949         if (this.boxesEl) {
30950             return this.boxesEl;
30951         }
30952         
30953         this.boxesEl = this.el.select('.mas-boxes').first();
30954         
30955         return this.boxesEl;
30956     },
30957     
30958     
30959     initEvents : function()
30960     {
30961         var _this = this;
30962         
30963         if(this.isAutoInitial){
30964             Roo.log('hook children rendered');
30965             this.on('childrenrendered', function() {
30966                 Roo.log('children rendered');
30967                 _this.initial();
30968             } ,this);
30969         }
30970     },
30971     
30972     initial : function()
30973     {
30974         this.selectedBrick = [];
30975         
30976         this.currentSize = this.el.getBox(true);
30977         
30978         Roo.EventManager.onWindowResize(this.resize, this); 
30979
30980         if(!this.isAutoInitial){
30981             this.layout();
30982             return;
30983         }
30984         
30985         this.layout();
30986         
30987         return;
30988         //this.layout.defer(500,this);
30989         
30990     },
30991     
30992     resize : function()
30993     {
30994         var cs = this.el.getBox(true);
30995         
30996         if (
30997                 this.currentSize.width == cs.width && 
30998                 this.currentSize.x == cs.x && 
30999                 this.currentSize.height == cs.height && 
31000                 this.currentSize.y == cs.y 
31001         ) {
31002             Roo.log("no change in with or X or Y");
31003             return;
31004         }
31005         
31006         this.currentSize = cs;
31007         
31008         this.layout();
31009         
31010     },
31011     
31012     layout : function()
31013     {   
31014         this._resetLayout();
31015         
31016         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31017         
31018         this.layoutItems( isInstant );
31019       
31020         this._isLayoutInited = true;
31021         
31022         this.fireEvent('layout', this);
31023         
31024     },
31025     
31026     _resetLayout : function()
31027     {
31028         if(this.isHorizontal){
31029             this.horizontalMeasureColumns();
31030             return;
31031         }
31032         
31033         this.verticalMeasureColumns();
31034         
31035     },
31036     
31037     verticalMeasureColumns : function()
31038     {
31039         this.getContainerWidth();
31040         
31041 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31042 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31043 //            return;
31044 //        }
31045         
31046         var boxWidth = this.boxWidth + this.padWidth;
31047         
31048         if(this.containerWidth < this.boxWidth){
31049             boxWidth = this.containerWidth
31050         }
31051         
31052         var containerWidth = this.containerWidth;
31053         
31054         var cols = Math.floor(containerWidth / boxWidth);
31055         
31056         this.cols = Math.max( cols, 1 );
31057         
31058         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31059         
31060         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31061         
31062         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31063         
31064         this.colWidth = boxWidth + avail - this.padWidth;
31065         
31066         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31067         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31068     },
31069     
31070     horizontalMeasureColumns : function()
31071     {
31072         this.getContainerWidth();
31073         
31074         var boxWidth = this.boxWidth;
31075         
31076         if(this.containerWidth < boxWidth){
31077             boxWidth = this.containerWidth;
31078         }
31079         
31080         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31081         
31082         this.el.setHeight(boxWidth);
31083         
31084     },
31085     
31086     getContainerWidth : function()
31087     {
31088         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31089     },
31090     
31091     layoutItems : function( isInstant )
31092     {
31093         Roo.log(this.bricks);
31094         
31095         var items = Roo.apply([], this.bricks);
31096         
31097         if(this.isHorizontal){
31098             this._horizontalLayoutItems( items , isInstant );
31099             return;
31100         }
31101         
31102 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31103 //            this._verticalAlternativeLayoutItems( items , isInstant );
31104 //            return;
31105 //        }
31106         
31107         this._verticalLayoutItems( items , isInstant );
31108         
31109     },
31110     
31111     _verticalLayoutItems : function ( items , isInstant)
31112     {
31113         if ( !items || !items.length ) {
31114             return;
31115         }
31116         
31117         var standard = [
31118             ['xs', 'xs', 'xs', 'tall'],
31119             ['xs', 'xs', 'tall'],
31120             ['xs', 'xs', 'sm'],
31121             ['xs', 'xs', 'xs'],
31122             ['xs', 'tall'],
31123             ['xs', 'sm'],
31124             ['xs', 'xs'],
31125             ['xs'],
31126             
31127             ['sm', 'xs', 'xs'],
31128             ['sm', 'xs'],
31129             ['sm'],
31130             
31131             ['tall', 'xs', 'xs', 'xs'],
31132             ['tall', 'xs', 'xs'],
31133             ['tall', 'xs'],
31134             ['tall']
31135             
31136         ];
31137         
31138         var queue = [];
31139         
31140         var boxes = [];
31141         
31142         var box = [];
31143         
31144         Roo.each(items, function(item, k){
31145             
31146             switch (item.size) {
31147                 // these layouts take up a full box,
31148                 case 'md' :
31149                 case 'md-left' :
31150                 case 'md-right' :
31151                 case 'wide' :
31152                     
31153                     if(box.length){
31154                         boxes.push(box);
31155                         box = [];
31156                     }
31157                     
31158                     boxes.push([item]);
31159                     
31160                     break;
31161                     
31162                 case 'xs' :
31163                 case 'sm' :
31164                 case 'tall' :
31165                     
31166                     box.push(item);
31167                     
31168                     break;
31169                 default :
31170                     break;
31171                     
31172             }
31173             
31174         }, this);
31175         
31176         if(box.length){
31177             boxes.push(box);
31178             box = [];
31179         }
31180         
31181         var filterPattern = function(box, length)
31182         {
31183             if(!box.length){
31184                 return;
31185             }
31186             
31187             var match = false;
31188             
31189             var pattern = box.slice(0, length);
31190             
31191             var format = [];
31192             
31193             Roo.each(pattern, function(i){
31194                 format.push(i.size);
31195             }, this);
31196             
31197             Roo.each(standard, function(s){
31198                 
31199                 if(String(s) != String(format)){
31200                     return;
31201                 }
31202                 
31203                 match = true;
31204                 return false;
31205                 
31206             }, this);
31207             
31208             if(!match && length == 1){
31209                 return;
31210             }
31211             
31212             if(!match){
31213                 filterPattern(box, length - 1);
31214                 return;
31215             }
31216                 
31217             queue.push(pattern);
31218
31219             box = box.slice(length, box.length);
31220
31221             filterPattern(box, 4);
31222
31223             return;
31224             
31225         }
31226         
31227         Roo.each(boxes, function(box, k){
31228             
31229             if(!box.length){
31230                 return;
31231             }
31232             
31233             if(box.length == 1){
31234                 queue.push(box);
31235                 return;
31236             }
31237             
31238             filterPattern(box, 4);
31239             
31240         }, this);
31241         
31242         this._processVerticalLayoutQueue( queue, isInstant );
31243         
31244     },
31245     
31246 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31247 //    {
31248 //        if ( !items || !items.length ) {
31249 //            return;
31250 //        }
31251 //
31252 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31253 //        
31254 //    },
31255     
31256     _horizontalLayoutItems : function ( items , isInstant)
31257     {
31258         if ( !items || !items.length || items.length < 3) {
31259             return;
31260         }
31261         
31262         items.reverse();
31263         
31264         var eItems = items.slice(0, 3);
31265         
31266         items = items.slice(3, items.length);
31267         
31268         var standard = [
31269             ['xs', 'xs', 'xs', 'wide'],
31270             ['xs', 'xs', 'wide'],
31271             ['xs', 'xs', 'sm'],
31272             ['xs', 'xs', 'xs'],
31273             ['xs', 'wide'],
31274             ['xs', 'sm'],
31275             ['xs', 'xs'],
31276             ['xs'],
31277             
31278             ['sm', 'xs', 'xs'],
31279             ['sm', 'xs'],
31280             ['sm'],
31281             
31282             ['wide', 'xs', 'xs', 'xs'],
31283             ['wide', 'xs', 'xs'],
31284             ['wide', 'xs'],
31285             ['wide'],
31286             
31287             ['wide-thin']
31288         ];
31289         
31290         var queue = [];
31291         
31292         var boxes = [];
31293         
31294         var box = [];
31295         
31296         Roo.each(items, function(item, k){
31297             
31298             switch (item.size) {
31299                 case 'md' :
31300                 case 'md-left' :
31301                 case 'md-right' :
31302                 case 'tall' :
31303                     
31304                     if(box.length){
31305                         boxes.push(box);
31306                         box = [];
31307                     }
31308                     
31309                     boxes.push([item]);
31310                     
31311                     break;
31312                     
31313                 case 'xs' :
31314                 case 'sm' :
31315                 case 'wide' :
31316                 case 'wide-thin' :
31317                     
31318                     box.push(item);
31319                     
31320                     break;
31321                 default :
31322                     break;
31323                     
31324             }
31325             
31326         }, this);
31327         
31328         if(box.length){
31329             boxes.push(box);
31330             box = [];
31331         }
31332         
31333         var filterPattern = function(box, length)
31334         {
31335             if(!box.length){
31336                 return;
31337             }
31338             
31339             var match = false;
31340             
31341             var pattern = box.slice(0, length);
31342             
31343             var format = [];
31344             
31345             Roo.each(pattern, function(i){
31346                 format.push(i.size);
31347             }, this);
31348             
31349             Roo.each(standard, function(s){
31350                 
31351                 if(String(s) != String(format)){
31352                     return;
31353                 }
31354                 
31355                 match = true;
31356                 return false;
31357                 
31358             }, this);
31359             
31360             if(!match && length == 1){
31361                 return;
31362             }
31363             
31364             if(!match){
31365                 filterPattern(box, length - 1);
31366                 return;
31367             }
31368                 
31369             queue.push(pattern);
31370
31371             box = box.slice(length, box.length);
31372
31373             filterPattern(box, 4);
31374
31375             return;
31376             
31377         }
31378         
31379         Roo.each(boxes, function(box, k){
31380             
31381             if(!box.length){
31382                 return;
31383             }
31384             
31385             if(box.length == 1){
31386                 queue.push(box);
31387                 return;
31388             }
31389             
31390             filterPattern(box, 4);
31391             
31392         }, this);
31393         
31394         
31395         var prune = [];
31396         
31397         var pos = this.el.getBox(true);
31398         
31399         var minX = pos.x;
31400         
31401         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31402         
31403         var hit_end = false;
31404         
31405         Roo.each(queue, function(box){
31406             
31407             if(hit_end){
31408                 
31409                 Roo.each(box, function(b){
31410                 
31411                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31412                     b.el.hide();
31413
31414                 }, this);
31415
31416                 return;
31417             }
31418             
31419             var mx = 0;
31420             
31421             Roo.each(box, function(b){
31422                 
31423                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31424                 b.el.show();
31425
31426                 mx = Math.max(mx, b.x);
31427                 
31428             }, this);
31429             
31430             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31431             
31432             if(maxX < minX){
31433                 
31434                 Roo.each(box, function(b){
31435                 
31436                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31437                     b.el.hide();
31438                     
31439                 }, this);
31440                 
31441                 hit_end = true;
31442                 
31443                 return;
31444             }
31445             
31446             prune.push(box);
31447             
31448         }, this);
31449         
31450         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31451     },
31452     
31453     /** Sets position of item in DOM
31454     * @param {Element} item
31455     * @param {Number} x - horizontal position
31456     * @param {Number} y - vertical position
31457     * @param {Boolean} isInstant - disables transitions
31458     */
31459     _processVerticalLayoutQueue : function( queue, isInstant )
31460     {
31461         var pos = this.el.getBox(true);
31462         var x = pos.x;
31463         var y = pos.y;
31464         var maxY = [];
31465         
31466         for (var i = 0; i < this.cols; i++){
31467             maxY[i] = pos.y;
31468         }
31469         
31470         Roo.each(queue, function(box, k){
31471             
31472             var col = k % this.cols;
31473             
31474             Roo.each(box, function(b,kk){
31475                 
31476                 b.el.position('absolute');
31477                 
31478                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31479                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31480                 
31481                 if(b.size == 'md-left' || b.size == 'md-right'){
31482                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31483                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31484                 }
31485                 
31486                 b.el.setWidth(width);
31487                 b.el.setHeight(height);
31488                 // iframe?
31489                 b.el.select('iframe',true).setSize(width,height);
31490                 
31491             }, this);
31492             
31493             for (var i = 0; i < this.cols; i++){
31494                 
31495                 if(maxY[i] < maxY[col]){
31496                     col = i;
31497                     continue;
31498                 }
31499                 
31500                 col = Math.min(col, i);
31501                 
31502             }
31503             
31504             x = pos.x + col * (this.colWidth + this.padWidth);
31505             
31506             y = maxY[col];
31507             
31508             var positions = [];
31509             
31510             switch (box.length){
31511                 case 1 :
31512                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31513                     break;
31514                 case 2 :
31515                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31516                     break;
31517                 case 3 :
31518                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31519                     break;
31520                 case 4 :
31521                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31522                     break;
31523                 default :
31524                     break;
31525             }
31526             
31527             Roo.each(box, function(b,kk){
31528                 
31529                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31530                 
31531                 var sz = b.el.getSize();
31532                 
31533                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31534                 
31535             }, this);
31536             
31537         }, this);
31538         
31539         var mY = 0;
31540         
31541         for (var i = 0; i < this.cols; i++){
31542             mY = Math.max(mY, maxY[i]);
31543         }
31544         
31545         this.el.setHeight(mY - pos.y);
31546         
31547     },
31548     
31549 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31550 //    {
31551 //        var pos = this.el.getBox(true);
31552 //        var x = pos.x;
31553 //        var y = pos.y;
31554 //        var maxX = pos.right;
31555 //        
31556 //        var maxHeight = 0;
31557 //        
31558 //        Roo.each(items, function(item, k){
31559 //            
31560 //            var c = k % 2;
31561 //            
31562 //            item.el.position('absolute');
31563 //                
31564 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31565 //
31566 //            item.el.setWidth(width);
31567 //
31568 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31569 //
31570 //            item.el.setHeight(height);
31571 //            
31572 //            if(c == 0){
31573 //                item.el.setXY([x, y], isInstant ? false : true);
31574 //            } else {
31575 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31576 //            }
31577 //            
31578 //            y = y + height + this.alternativePadWidth;
31579 //            
31580 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31581 //            
31582 //        }, this);
31583 //        
31584 //        this.el.setHeight(maxHeight);
31585 //        
31586 //    },
31587     
31588     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31589     {
31590         var pos = this.el.getBox(true);
31591         
31592         var minX = pos.x;
31593         var minY = pos.y;
31594         
31595         var maxX = pos.right;
31596         
31597         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31598         
31599         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31600         
31601         Roo.each(queue, function(box, k){
31602             
31603             Roo.each(box, function(b, kk){
31604                 
31605                 b.el.position('absolute');
31606                 
31607                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31608                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31609                 
31610                 if(b.size == 'md-left' || b.size == 'md-right'){
31611                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31612                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31613                 }
31614                 
31615                 b.el.setWidth(width);
31616                 b.el.setHeight(height);
31617                 
31618             }, this);
31619             
31620             if(!box.length){
31621                 return;
31622             }
31623             
31624             var positions = [];
31625             
31626             switch (box.length){
31627                 case 1 :
31628                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31629                     break;
31630                 case 2 :
31631                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31632                     break;
31633                 case 3 :
31634                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31635                     break;
31636                 case 4 :
31637                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31638                     break;
31639                 default :
31640                     break;
31641             }
31642             
31643             Roo.each(box, function(b,kk){
31644                 
31645                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31646                 
31647                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31648                 
31649             }, this);
31650             
31651         }, this);
31652         
31653     },
31654     
31655     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31656     {
31657         Roo.each(eItems, function(b,k){
31658             
31659             b.size = (k == 0) ? 'sm' : 'xs';
31660             b.x = (k == 0) ? 2 : 1;
31661             b.y = (k == 0) ? 2 : 1;
31662             
31663             b.el.position('absolute');
31664             
31665             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31666                 
31667             b.el.setWidth(width);
31668             
31669             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31670             
31671             b.el.setHeight(height);
31672             
31673         }, this);
31674
31675         var positions = [];
31676         
31677         positions.push({
31678             x : maxX - this.unitWidth * 2 - this.gutter,
31679             y : minY
31680         });
31681         
31682         positions.push({
31683             x : maxX - this.unitWidth,
31684             y : minY + (this.unitWidth + this.gutter) * 2
31685         });
31686         
31687         positions.push({
31688             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31689             y : minY
31690         });
31691         
31692         Roo.each(eItems, function(b,k){
31693             
31694             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31695
31696         }, this);
31697         
31698     },
31699     
31700     getVerticalOneBoxColPositions : function(x, y, box)
31701     {
31702         var pos = [];
31703         
31704         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31705         
31706         if(box[0].size == 'md-left'){
31707             rand = 0;
31708         }
31709         
31710         if(box[0].size == 'md-right'){
31711             rand = 1;
31712         }
31713         
31714         pos.push({
31715             x : x + (this.unitWidth + this.gutter) * rand,
31716             y : y
31717         });
31718         
31719         return pos;
31720     },
31721     
31722     getVerticalTwoBoxColPositions : function(x, y, box)
31723     {
31724         var pos = [];
31725         
31726         if(box[0].size == 'xs'){
31727             
31728             pos.push({
31729                 x : x,
31730                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31731             });
31732
31733             pos.push({
31734                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31735                 y : y
31736             });
31737             
31738             return pos;
31739             
31740         }
31741         
31742         pos.push({
31743             x : x,
31744             y : y
31745         });
31746
31747         pos.push({
31748             x : x + (this.unitWidth + this.gutter) * 2,
31749             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31750         });
31751         
31752         return pos;
31753         
31754     },
31755     
31756     getVerticalThreeBoxColPositions : function(x, y, box)
31757     {
31758         var pos = [];
31759         
31760         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31761             
31762             pos.push({
31763                 x : x,
31764                 y : y
31765             });
31766
31767             pos.push({
31768                 x : x + (this.unitWidth + this.gutter) * 1,
31769                 y : y
31770             });
31771             
31772             pos.push({
31773                 x : x + (this.unitWidth + this.gutter) * 2,
31774                 y : y
31775             });
31776             
31777             return pos;
31778             
31779         }
31780         
31781         if(box[0].size == 'xs' && box[1].size == 'xs'){
31782             
31783             pos.push({
31784                 x : x,
31785                 y : y
31786             });
31787
31788             pos.push({
31789                 x : x,
31790                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31791             });
31792             
31793             pos.push({
31794                 x : x + (this.unitWidth + this.gutter) * 1,
31795                 y : y
31796             });
31797             
31798             return pos;
31799             
31800         }
31801         
31802         pos.push({
31803             x : x,
31804             y : y
31805         });
31806
31807         pos.push({
31808             x : x + (this.unitWidth + this.gutter) * 2,
31809             y : y
31810         });
31811
31812         pos.push({
31813             x : x + (this.unitWidth + this.gutter) * 2,
31814             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31815         });
31816             
31817         return pos;
31818         
31819     },
31820     
31821     getVerticalFourBoxColPositions : function(x, y, box)
31822     {
31823         var pos = [];
31824         
31825         if(box[0].size == 'xs'){
31826             
31827             pos.push({
31828                 x : x,
31829                 y : y
31830             });
31831
31832             pos.push({
31833                 x : x,
31834                 y : y + (this.unitHeight + this.gutter) * 1
31835             });
31836             
31837             pos.push({
31838                 x : x,
31839                 y : y + (this.unitHeight + this.gutter) * 2
31840             });
31841             
31842             pos.push({
31843                 x : x + (this.unitWidth + this.gutter) * 1,
31844                 y : y
31845             });
31846             
31847             return pos;
31848             
31849         }
31850         
31851         pos.push({
31852             x : x,
31853             y : y
31854         });
31855
31856         pos.push({
31857             x : x + (this.unitWidth + this.gutter) * 2,
31858             y : y
31859         });
31860
31861         pos.push({
31862             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31863             y : y + (this.unitHeight + this.gutter) * 1
31864         });
31865
31866         pos.push({
31867             x : x + (this.unitWidth + this.gutter) * 2,
31868             y : y + (this.unitWidth + this.gutter) * 2
31869         });
31870
31871         return pos;
31872         
31873     },
31874     
31875     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31876     {
31877         var pos = [];
31878         
31879         if(box[0].size == 'md-left'){
31880             pos.push({
31881                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31882                 y : minY
31883             });
31884             
31885             return pos;
31886         }
31887         
31888         if(box[0].size == 'md-right'){
31889             pos.push({
31890                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31891                 y : minY + (this.unitWidth + this.gutter) * 1
31892             });
31893             
31894             return pos;
31895         }
31896         
31897         var rand = Math.floor(Math.random() * (4 - box[0].y));
31898         
31899         pos.push({
31900             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31901             y : minY + (this.unitWidth + this.gutter) * rand
31902         });
31903         
31904         return pos;
31905         
31906     },
31907     
31908     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31909     {
31910         var pos = [];
31911         
31912         if(box[0].size == 'xs'){
31913             
31914             pos.push({
31915                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31916                 y : minY
31917             });
31918
31919             pos.push({
31920                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31921                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31922             });
31923             
31924             return pos;
31925             
31926         }
31927         
31928         pos.push({
31929             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31930             y : minY
31931         });
31932
31933         pos.push({
31934             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31935             y : minY + (this.unitWidth + this.gutter) * 2
31936         });
31937         
31938         return pos;
31939         
31940     },
31941     
31942     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31943     {
31944         var pos = [];
31945         
31946         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31947             
31948             pos.push({
31949                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31950                 y : minY
31951             });
31952
31953             pos.push({
31954                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31955                 y : minY + (this.unitWidth + this.gutter) * 1
31956             });
31957             
31958             pos.push({
31959                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31960                 y : minY + (this.unitWidth + this.gutter) * 2
31961             });
31962             
31963             return pos;
31964             
31965         }
31966         
31967         if(box[0].size == 'xs' && box[1].size == 'xs'){
31968             
31969             pos.push({
31970                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31971                 y : minY
31972             });
31973
31974             pos.push({
31975                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31976                 y : minY
31977             });
31978             
31979             pos.push({
31980                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31981                 y : minY + (this.unitWidth + this.gutter) * 1
31982             });
31983             
31984             return pos;
31985             
31986         }
31987         
31988         pos.push({
31989             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31990             y : minY
31991         });
31992
31993         pos.push({
31994             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31995             y : minY + (this.unitWidth + this.gutter) * 2
31996         });
31997
31998         pos.push({
31999             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32000             y : minY + (this.unitWidth + this.gutter) * 2
32001         });
32002             
32003         return pos;
32004         
32005     },
32006     
32007     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32008     {
32009         var pos = [];
32010         
32011         if(box[0].size == 'xs'){
32012             
32013             pos.push({
32014                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32015                 y : minY
32016             });
32017
32018             pos.push({
32019                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32020                 y : minY
32021             });
32022             
32023             pos.push({
32024                 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),
32025                 y : minY
32026             });
32027             
32028             pos.push({
32029                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32030                 y : minY + (this.unitWidth + this.gutter) * 1
32031             });
32032             
32033             return pos;
32034             
32035         }
32036         
32037         pos.push({
32038             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32039             y : minY
32040         });
32041         
32042         pos.push({
32043             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32044             y : minY + (this.unitWidth + this.gutter) * 2
32045         });
32046         
32047         pos.push({
32048             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32049             y : minY + (this.unitWidth + this.gutter) * 2
32050         });
32051         
32052         pos.push({
32053             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),
32054             y : minY + (this.unitWidth + this.gutter) * 2
32055         });
32056
32057         return pos;
32058         
32059     },
32060     
32061     /**
32062     * remove a Masonry Brick
32063     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32064     */
32065     removeBrick : function(brick_id)
32066     {
32067         if (!brick_id) {
32068             return;
32069         }
32070         
32071         for (var i = 0; i<this.bricks.length; i++) {
32072             if (this.bricks[i].id == brick_id) {
32073                 this.bricks.splice(i,1);
32074                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32075                 this.initial();
32076             }
32077         }
32078     },
32079     
32080     /**
32081     * adds a Masonry Brick
32082     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32083     */
32084     addBrick : function(cfg)
32085     {
32086         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32087         //this.register(cn);
32088         cn.parentId = this.id;
32089         cn.render(this.el);
32090         return cn;
32091     },
32092     
32093     /**
32094     * register a Masonry Brick
32095     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32096     */
32097     
32098     register : function(brick)
32099     {
32100         this.bricks.push(brick);
32101         brick.masonryId = this.id;
32102     },
32103     
32104     /**
32105     * clear all the Masonry Brick
32106     */
32107     clearAll : function()
32108     {
32109         this.bricks = [];
32110         //this.getChildContainer().dom.innerHTML = "";
32111         this.el.dom.innerHTML = '';
32112     },
32113     
32114     getSelected : function()
32115     {
32116         if (!this.selectedBrick) {
32117             return false;
32118         }
32119         
32120         return this.selectedBrick;
32121     }
32122 });
32123
32124 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32125     
32126     groups: {},
32127      /**
32128     * register a Masonry Layout
32129     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32130     */
32131     
32132     register : function(layout)
32133     {
32134         this.groups[layout.id] = layout;
32135     },
32136     /**
32137     * fetch a  Masonry Layout based on the masonry layout ID
32138     * @param {string} the masonry layout to add
32139     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32140     */
32141     
32142     get: function(layout_id) {
32143         if (typeof(this.groups[layout_id]) == 'undefined') {
32144             return false;
32145         }
32146         return this.groups[layout_id] ;
32147     }
32148     
32149     
32150     
32151 });
32152
32153  
32154
32155  /**
32156  *
32157  * This is based on 
32158  * http://masonry.desandro.com
32159  *
32160  * The idea is to render all the bricks based on vertical width...
32161  *
32162  * The original code extends 'outlayer' - we might need to use that....
32163  * 
32164  */
32165
32166
32167 /**
32168  * @class Roo.bootstrap.LayoutMasonryAuto
32169  * @extends Roo.bootstrap.Component
32170  * Bootstrap Layout Masonry class
32171  * 
32172  * @constructor
32173  * Create a new Element
32174  * @param {Object} config The config object
32175  */
32176
32177 Roo.bootstrap.LayoutMasonryAuto = function(config){
32178     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32179 };
32180
32181 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32182     
32183       /**
32184      * @cfg {Boolean} isFitWidth  - resize the width..
32185      */   
32186     isFitWidth : false,  // options..
32187     /**
32188      * @cfg {Boolean} isOriginLeft = left align?
32189      */   
32190     isOriginLeft : true,
32191     /**
32192      * @cfg {Boolean} isOriginTop = top align?
32193      */   
32194     isOriginTop : false,
32195     /**
32196      * @cfg {Boolean} isLayoutInstant = no animation?
32197      */   
32198     isLayoutInstant : false, // needed?
32199     /**
32200      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32201      */   
32202     isResizingContainer : true,
32203     /**
32204      * @cfg {Number} columnWidth  width of the columns 
32205      */   
32206     
32207     columnWidth : 0,
32208     
32209     /**
32210      * @cfg {Number} maxCols maximum number of columns
32211      */   
32212     
32213     maxCols: 0,
32214     /**
32215      * @cfg {Number} padHeight padding below box..
32216      */   
32217     
32218     padHeight : 10, 
32219     
32220     /**
32221      * @cfg {Boolean} isAutoInitial defalut true
32222      */   
32223     
32224     isAutoInitial : true, 
32225     
32226     // private?
32227     gutter : 0,
32228     
32229     containerWidth: 0,
32230     initialColumnWidth : 0,
32231     currentSize : null,
32232     
32233     colYs : null, // array.
32234     maxY : 0,
32235     padWidth: 10,
32236     
32237     
32238     tag: 'div',
32239     cls: '',
32240     bricks: null, //CompositeElement
32241     cols : 0, // array?
32242     // element : null, // wrapped now this.el
32243     _isLayoutInited : null, 
32244     
32245     
32246     getAutoCreate : function(){
32247         
32248         var cfg = {
32249             tag: this.tag,
32250             cls: 'blog-masonary-wrapper ' + this.cls,
32251             cn : {
32252                 cls : 'mas-boxes masonary'
32253             }
32254         };
32255         
32256         return cfg;
32257     },
32258     
32259     getChildContainer: function( )
32260     {
32261         if (this.boxesEl) {
32262             return this.boxesEl;
32263         }
32264         
32265         this.boxesEl = this.el.select('.mas-boxes').first();
32266         
32267         return this.boxesEl;
32268     },
32269     
32270     
32271     initEvents : function()
32272     {
32273         var _this = this;
32274         
32275         if(this.isAutoInitial){
32276             Roo.log('hook children rendered');
32277             this.on('childrenrendered', function() {
32278                 Roo.log('children rendered');
32279                 _this.initial();
32280             } ,this);
32281         }
32282         
32283     },
32284     
32285     initial : function()
32286     {
32287         this.reloadItems();
32288
32289         this.currentSize = this.el.getBox(true);
32290
32291         /// was window resize... - let's see if this works..
32292         Roo.EventManager.onWindowResize(this.resize, this); 
32293
32294         if(!this.isAutoInitial){
32295             this.layout();
32296             return;
32297         }
32298         
32299         this.layout.defer(500,this);
32300     },
32301     
32302     reloadItems: function()
32303     {
32304         this.bricks = this.el.select('.masonry-brick', true);
32305         
32306         this.bricks.each(function(b) {
32307             //Roo.log(b.getSize());
32308             if (!b.attr('originalwidth')) {
32309                 b.attr('originalwidth',  b.getSize().width);
32310             }
32311             
32312         });
32313         
32314         Roo.log(this.bricks.elements.length);
32315     },
32316     
32317     resize : function()
32318     {
32319         Roo.log('resize');
32320         var cs = this.el.getBox(true);
32321         
32322         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32323             Roo.log("no change in with or X");
32324             return;
32325         }
32326         this.currentSize = cs;
32327         this.layout();
32328     },
32329     
32330     layout : function()
32331     {
32332          Roo.log('layout');
32333         this._resetLayout();
32334         //this._manageStamps();
32335       
32336         // don't animate first layout
32337         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32338         this.layoutItems( isInstant );
32339       
32340         // flag for initalized
32341         this._isLayoutInited = true;
32342     },
32343     
32344     layoutItems : function( isInstant )
32345     {
32346         //var items = this._getItemsForLayout( this.items );
32347         // original code supports filtering layout items.. we just ignore it..
32348         
32349         this._layoutItems( this.bricks , isInstant );
32350       
32351         this._postLayout();
32352     },
32353     _layoutItems : function ( items , isInstant)
32354     {
32355        //this.fireEvent( 'layout', this, items );
32356     
32357
32358         if ( !items || !items.elements.length ) {
32359           // no items, emit event with empty array
32360             return;
32361         }
32362
32363         var queue = [];
32364         items.each(function(item) {
32365             Roo.log("layout item");
32366             Roo.log(item);
32367             // get x/y object from method
32368             var position = this._getItemLayoutPosition( item );
32369             // enqueue
32370             position.item = item;
32371             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32372             queue.push( position );
32373         }, this);
32374       
32375         this._processLayoutQueue( queue );
32376     },
32377     /** Sets position of item in DOM
32378     * @param {Element} item
32379     * @param {Number} x - horizontal position
32380     * @param {Number} y - vertical position
32381     * @param {Boolean} isInstant - disables transitions
32382     */
32383     _processLayoutQueue : function( queue )
32384     {
32385         for ( var i=0, len = queue.length; i < len; i++ ) {
32386             var obj = queue[i];
32387             obj.item.position('absolute');
32388             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32389         }
32390     },
32391       
32392     
32393     /**
32394     * Any logic you want to do after each layout,
32395     * i.e. size the container
32396     */
32397     _postLayout : function()
32398     {
32399         this.resizeContainer();
32400     },
32401     
32402     resizeContainer : function()
32403     {
32404         if ( !this.isResizingContainer ) {
32405             return;
32406         }
32407         var size = this._getContainerSize();
32408         if ( size ) {
32409             this.el.setSize(size.width,size.height);
32410             this.boxesEl.setSize(size.width,size.height);
32411         }
32412     },
32413     
32414     
32415     
32416     _resetLayout : function()
32417     {
32418         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32419         this.colWidth = this.el.getWidth();
32420         //this.gutter = this.el.getWidth(); 
32421         
32422         this.measureColumns();
32423
32424         // reset column Y
32425         var i = this.cols;
32426         this.colYs = [];
32427         while (i--) {
32428             this.colYs.push( 0 );
32429         }
32430     
32431         this.maxY = 0;
32432     },
32433
32434     measureColumns : function()
32435     {
32436         this.getContainerWidth();
32437       // if columnWidth is 0, default to outerWidth of first item
32438         if ( !this.columnWidth ) {
32439             var firstItem = this.bricks.first();
32440             Roo.log(firstItem);
32441             this.columnWidth  = this.containerWidth;
32442             if (firstItem && firstItem.attr('originalwidth') ) {
32443                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32444             }
32445             // columnWidth fall back to item of first element
32446             Roo.log("set column width?");
32447                         this.initialColumnWidth = this.columnWidth  ;
32448
32449             // if first elem has no width, default to size of container
32450             
32451         }
32452         
32453         
32454         if (this.initialColumnWidth) {
32455             this.columnWidth = this.initialColumnWidth;
32456         }
32457         
32458         
32459             
32460         // column width is fixed at the top - however if container width get's smaller we should
32461         // reduce it...
32462         
32463         // this bit calcs how man columns..
32464             
32465         var columnWidth = this.columnWidth += this.gutter;
32466       
32467         // calculate columns
32468         var containerWidth = this.containerWidth + this.gutter;
32469         
32470         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32471         // fix rounding errors, typically with gutters
32472         var excess = columnWidth - containerWidth % columnWidth;
32473         
32474         
32475         // if overshoot is less than a pixel, round up, otherwise floor it
32476         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32477         cols = Math[ mathMethod ]( cols );
32478         this.cols = Math.max( cols, 1 );
32479         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32480         
32481          // padding positioning..
32482         var totalColWidth = this.cols * this.columnWidth;
32483         var padavail = this.containerWidth - totalColWidth;
32484         // so for 2 columns - we need 3 'pads'
32485         
32486         var padNeeded = (1+this.cols) * this.padWidth;
32487         
32488         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32489         
32490         this.columnWidth += padExtra
32491         //this.padWidth = Math.floor(padavail /  ( this.cols));
32492         
32493         // adjust colum width so that padding is fixed??
32494         
32495         // we have 3 columns ... total = width * 3
32496         // we have X left over... that should be used by 
32497         
32498         //if (this.expandC) {
32499             
32500         //}
32501         
32502         
32503         
32504     },
32505     
32506     getContainerWidth : function()
32507     {
32508        /* // container is parent if fit width
32509         var container = this.isFitWidth ? this.element.parentNode : this.element;
32510         // check that this.size and size are there
32511         // IE8 triggers resize on body size change, so they might not be
32512         
32513         var size = getSize( container );  //FIXME
32514         this.containerWidth = size && size.innerWidth; //FIXME
32515         */
32516          
32517         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32518         
32519     },
32520     
32521     _getItemLayoutPosition : function( item )  // what is item?
32522     {
32523         // we resize the item to our columnWidth..
32524       
32525         item.setWidth(this.columnWidth);
32526         item.autoBoxAdjust  = false;
32527         
32528         var sz = item.getSize();
32529  
32530         // how many columns does this brick span
32531         var remainder = this.containerWidth % this.columnWidth;
32532         
32533         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32534         // round if off by 1 pixel, otherwise use ceil
32535         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32536         colSpan = Math.min( colSpan, this.cols );
32537         
32538         // normally this should be '1' as we dont' currently allow multi width columns..
32539         
32540         var colGroup = this._getColGroup( colSpan );
32541         // get the minimum Y value from the columns
32542         var minimumY = Math.min.apply( Math, colGroup );
32543         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32544         
32545         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32546          
32547         // position the brick
32548         var position = {
32549             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32550             y: this.currentSize.y + minimumY + this.padHeight
32551         };
32552         
32553         Roo.log(position);
32554         // apply setHeight to necessary columns
32555         var setHeight = minimumY + sz.height + this.padHeight;
32556         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32557         
32558         var setSpan = this.cols + 1 - colGroup.length;
32559         for ( var i = 0; i < setSpan; i++ ) {
32560           this.colYs[ shortColIndex + i ] = setHeight ;
32561         }
32562       
32563         return position;
32564     },
32565     
32566     /**
32567      * @param {Number} colSpan - number of columns the element spans
32568      * @returns {Array} colGroup
32569      */
32570     _getColGroup : function( colSpan )
32571     {
32572         if ( colSpan < 2 ) {
32573           // if brick spans only one column, use all the column Ys
32574           return this.colYs;
32575         }
32576       
32577         var colGroup = [];
32578         // how many different places could this brick fit horizontally
32579         var groupCount = this.cols + 1 - colSpan;
32580         // for each group potential horizontal position
32581         for ( var i = 0; i < groupCount; i++ ) {
32582           // make an array of colY values for that one group
32583           var groupColYs = this.colYs.slice( i, i + colSpan );
32584           // and get the max value of the array
32585           colGroup[i] = Math.max.apply( Math, groupColYs );
32586         }
32587         return colGroup;
32588     },
32589     /*
32590     _manageStamp : function( stamp )
32591     {
32592         var stampSize =  stamp.getSize();
32593         var offset = stamp.getBox();
32594         // get the columns that this stamp affects
32595         var firstX = this.isOriginLeft ? offset.x : offset.right;
32596         var lastX = firstX + stampSize.width;
32597         var firstCol = Math.floor( firstX / this.columnWidth );
32598         firstCol = Math.max( 0, firstCol );
32599         
32600         var lastCol = Math.floor( lastX / this.columnWidth );
32601         // lastCol should not go over if multiple of columnWidth #425
32602         lastCol -= lastX % this.columnWidth ? 0 : 1;
32603         lastCol = Math.min( this.cols - 1, lastCol );
32604         
32605         // set colYs to bottom of the stamp
32606         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32607             stampSize.height;
32608             
32609         for ( var i = firstCol; i <= lastCol; i++ ) {
32610           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32611         }
32612     },
32613     */
32614     
32615     _getContainerSize : function()
32616     {
32617         this.maxY = Math.max.apply( Math, this.colYs );
32618         var size = {
32619             height: this.maxY
32620         };
32621       
32622         if ( this.isFitWidth ) {
32623             size.width = this._getContainerFitWidth();
32624         }
32625       
32626         return size;
32627     },
32628     
32629     _getContainerFitWidth : function()
32630     {
32631         var unusedCols = 0;
32632         // count unused columns
32633         var i = this.cols;
32634         while ( --i ) {
32635           if ( this.colYs[i] !== 0 ) {
32636             break;
32637           }
32638           unusedCols++;
32639         }
32640         // fit container to columns that have been used
32641         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32642     },
32643     
32644     needsResizeLayout : function()
32645     {
32646         var previousWidth = this.containerWidth;
32647         this.getContainerWidth();
32648         return previousWidth !== this.containerWidth;
32649     }
32650  
32651 });
32652
32653  
32654
32655  /*
32656  * - LGPL
32657  *
32658  * element
32659  * 
32660  */
32661
32662 /**
32663  * @class Roo.bootstrap.MasonryBrick
32664  * @extends Roo.bootstrap.Component
32665  * Bootstrap MasonryBrick class
32666  * 
32667  * @constructor
32668  * Create a new MasonryBrick
32669  * @param {Object} config The config object
32670  */
32671
32672 Roo.bootstrap.MasonryBrick = function(config){
32673     
32674     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32675     
32676     Roo.bootstrap.MasonryBrick.register(this);
32677     
32678     this.addEvents({
32679         // raw events
32680         /**
32681          * @event click
32682          * When a MasonryBrick is clcik
32683          * @param {Roo.bootstrap.MasonryBrick} this
32684          * @param {Roo.EventObject} e
32685          */
32686         "click" : true
32687     });
32688 };
32689
32690 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32691     
32692     /**
32693      * @cfg {String} title
32694      */   
32695     title : '',
32696     /**
32697      * @cfg {String} html
32698      */   
32699     html : '',
32700     /**
32701      * @cfg {String} bgimage
32702      */   
32703     bgimage : '',
32704     /**
32705      * @cfg {String} videourl
32706      */   
32707     videourl : '',
32708     /**
32709      * @cfg {String} cls
32710      */   
32711     cls : '',
32712     /**
32713      * @cfg {String} href
32714      */   
32715     href : '',
32716     /**
32717      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32718      */   
32719     size : 'xs',
32720     
32721     /**
32722      * @cfg {String} placetitle (center|bottom)
32723      */   
32724     placetitle : '',
32725     
32726     /**
32727      * @cfg {Boolean} isFitContainer defalut true
32728      */   
32729     isFitContainer : true, 
32730     
32731     /**
32732      * @cfg {Boolean} preventDefault defalut false
32733      */   
32734     preventDefault : false, 
32735     
32736     /**
32737      * @cfg {Boolean} inverse defalut false
32738      */   
32739     maskInverse : false, 
32740     
32741     getAutoCreate : function()
32742     {
32743         if(!this.isFitContainer){
32744             return this.getSplitAutoCreate();
32745         }
32746         
32747         var cls = 'masonry-brick masonry-brick-full';
32748         
32749         if(this.href.length){
32750             cls += ' masonry-brick-link';
32751         }
32752         
32753         if(this.bgimage.length){
32754             cls += ' masonry-brick-image';
32755         }
32756         
32757         if(this.maskInverse){
32758             cls += ' mask-inverse';
32759         }
32760         
32761         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32762             cls += ' enable-mask';
32763         }
32764         
32765         if(this.size){
32766             cls += ' masonry-' + this.size + '-brick';
32767         }
32768         
32769         if(this.placetitle.length){
32770             
32771             switch (this.placetitle) {
32772                 case 'center' :
32773                     cls += ' masonry-center-title';
32774                     break;
32775                 case 'bottom' :
32776                     cls += ' masonry-bottom-title';
32777                     break;
32778                 default:
32779                     break;
32780             }
32781             
32782         } else {
32783             if(!this.html.length && !this.bgimage.length){
32784                 cls += ' masonry-center-title';
32785             }
32786
32787             if(!this.html.length && this.bgimage.length){
32788                 cls += ' masonry-bottom-title';
32789             }
32790         }
32791         
32792         if(this.cls){
32793             cls += ' ' + this.cls;
32794         }
32795         
32796         var cfg = {
32797             tag: (this.href.length) ? 'a' : 'div',
32798             cls: cls,
32799             cn: [
32800                 {
32801                     tag: 'div',
32802                     cls: 'masonry-brick-mask'
32803                 },
32804                 {
32805                     tag: 'div',
32806                     cls: 'masonry-brick-paragraph',
32807                     cn: []
32808                 }
32809             ]
32810         };
32811         
32812         if(this.href.length){
32813             cfg.href = this.href;
32814         }
32815         
32816         var cn = cfg.cn[1].cn;
32817         
32818         if(this.title.length){
32819             cn.push({
32820                 tag: 'h4',
32821                 cls: 'masonry-brick-title',
32822                 html: this.title
32823             });
32824         }
32825         
32826         if(this.html.length){
32827             cn.push({
32828                 tag: 'p',
32829                 cls: 'masonry-brick-text',
32830                 html: this.html
32831             });
32832         }
32833         
32834         if (!this.title.length && !this.html.length) {
32835             cfg.cn[1].cls += ' hide';
32836         }
32837         
32838         if(this.bgimage.length){
32839             cfg.cn.push({
32840                 tag: 'img',
32841                 cls: 'masonry-brick-image-view',
32842                 src: this.bgimage
32843             });
32844         }
32845         
32846         if(this.videourl.length){
32847             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32848             // youtube support only?
32849             cfg.cn.push({
32850                 tag: 'iframe',
32851                 cls: 'masonry-brick-image-view',
32852                 src: vurl,
32853                 frameborder : 0,
32854                 allowfullscreen : true
32855             });
32856         }
32857         
32858         return cfg;
32859         
32860     },
32861     
32862     getSplitAutoCreate : function()
32863     {
32864         var cls = 'masonry-brick masonry-brick-split';
32865         
32866         if(this.href.length){
32867             cls += ' masonry-brick-link';
32868         }
32869         
32870         if(this.bgimage.length){
32871             cls += ' masonry-brick-image';
32872         }
32873         
32874         if(this.size){
32875             cls += ' masonry-' + this.size + '-brick';
32876         }
32877         
32878         switch (this.placetitle) {
32879             case 'center' :
32880                 cls += ' masonry-center-title';
32881                 break;
32882             case 'bottom' :
32883                 cls += ' masonry-bottom-title';
32884                 break;
32885             default:
32886                 if(!this.bgimage.length){
32887                     cls += ' masonry-center-title';
32888                 }
32889
32890                 if(this.bgimage.length){
32891                     cls += ' masonry-bottom-title';
32892                 }
32893                 break;
32894         }
32895         
32896         if(this.cls){
32897             cls += ' ' + this.cls;
32898         }
32899         
32900         var cfg = {
32901             tag: (this.href.length) ? 'a' : 'div',
32902             cls: cls,
32903             cn: [
32904                 {
32905                     tag: 'div',
32906                     cls: 'masonry-brick-split-head',
32907                     cn: [
32908                         {
32909                             tag: 'div',
32910                             cls: 'masonry-brick-paragraph',
32911                             cn: []
32912                         }
32913                     ]
32914                 },
32915                 {
32916                     tag: 'div',
32917                     cls: 'masonry-brick-split-body',
32918                     cn: []
32919                 }
32920             ]
32921         };
32922         
32923         if(this.href.length){
32924             cfg.href = this.href;
32925         }
32926         
32927         if(this.title.length){
32928             cfg.cn[0].cn[0].cn.push({
32929                 tag: 'h4',
32930                 cls: 'masonry-brick-title',
32931                 html: this.title
32932             });
32933         }
32934         
32935         if(this.html.length){
32936             cfg.cn[1].cn.push({
32937                 tag: 'p',
32938                 cls: 'masonry-brick-text',
32939                 html: this.html
32940             });
32941         }
32942
32943         if(this.bgimage.length){
32944             cfg.cn[0].cn.push({
32945                 tag: 'img',
32946                 cls: 'masonry-brick-image-view',
32947                 src: this.bgimage
32948             });
32949         }
32950         
32951         if(this.videourl.length){
32952             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32953             // youtube support only?
32954             cfg.cn[0].cn.cn.push({
32955                 tag: 'iframe',
32956                 cls: 'masonry-brick-image-view',
32957                 src: vurl,
32958                 frameborder : 0,
32959                 allowfullscreen : true
32960             });
32961         }
32962         
32963         return cfg;
32964     },
32965     
32966     initEvents: function() 
32967     {
32968         switch (this.size) {
32969             case 'xs' :
32970                 this.x = 1;
32971                 this.y = 1;
32972                 break;
32973             case 'sm' :
32974                 this.x = 2;
32975                 this.y = 2;
32976                 break;
32977             case 'md' :
32978             case 'md-left' :
32979             case 'md-right' :
32980                 this.x = 3;
32981                 this.y = 3;
32982                 break;
32983             case 'tall' :
32984                 this.x = 2;
32985                 this.y = 3;
32986                 break;
32987             case 'wide' :
32988                 this.x = 3;
32989                 this.y = 2;
32990                 break;
32991             case 'wide-thin' :
32992                 this.x = 3;
32993                 this.y = 1;
32994                 break;
32995                         
32996             default :
32997                 break;
32998         }
32999         
33000         if(Roo.isTouch){
33001             this.el.on('touchstart', this.onTouchStart, this);
33002             this.el.on('touchmove', this.onTouchMove, this);
33003             this.el.on('touchend', this.onTouchEnd, this);
33004             this.el.on('contextmenu', this.onContextMenu, this);
33005         } else {
33006             this.el.on('mouseenter'  ,this.enter, this);
33007             this.el.on('mouseleave', this.leave, this);
33008             this.el.on('click', this.onClick, this);
33009         }
33010         
33011         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33012             this.parent().bricks.push(this);   
33013         }
33014         
33015     },
33016     
33017     onClick: function(e, el)
33018     {
33019         var time = this.endTimer - this.startTimer;
33020         // Roo.log(e.preventDefault());
33021         if(Roo.isTouch){
33022             if(time > 1000){
33023                 e.preventDefault();
33024                 return;
33025             }
33026         }
33027         
33028         if(!this.preventDefault){
33029             return;
33030         }
33031         
33032         e.preventDefault();
33033         
33034         if (this.activeClass != '') {
33035             this.selectBrick();
33036         }
33037         
33038         this.fireEvent('click', this, e);
33039     },
33040     
33041     enter: function(e, el)
33042     {
33043         e.preventDefault();
33044         
33045         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33046             return;
33047         }
33048         
33049         if(this.bgimage.length && this.html.length){
33050             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33051         }
33052     },
33053     
33054     leave: function(e, el)
33055     {
33056         e.preventDefault();
33057         
33058         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33059             return;
33060         }
33061         
33062         if(this.bgimage.length && this.html.length){
33063             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33064         }
33065     },
33066     
33067     onTouchStart: function(e, el)
33068     {
33069 //        e.preventDefault();
33070         
33071         this.touchmoved = false;
33072         
33073         if(!this.isFitContainer){
33074             return;
33075         }
33076         
33077         if(!this.bgimage.length || !this.html.length){
33078             return;
33079         }
33080         
33081         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33082         
33083         this.timer = new Date().getTime();
33084         
33085     },
33086     
33087     onTouchMove: function(e, el)
33088     {
33089         this.touchmoved = true;
33090     },
33091     
33092     onContextMenu : function(e,el)
33093     {
33094         e.preventDefault();
33095         e.stopPropagation();
33096         return false;
33097     },
33098     
33099     onTouchEnd: function(e, el)
33100     {
33101 //        e.preventDefault();
33102         
33103         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33104         
33105             this.leave(e,el);
33106             
33107             return;
33108         }
33109         
33110         if(!this.bgimage.length || !this.html.length){
33111             
33112             if(this.href.length){
33113                 window.location.href = this.href;
33114             }
33115             
33116             return;
33117         }
33118         
33119         if(!this.isFitContainer){
33120             return;
33121         }
33122         
33123         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33124         
33125         window.location.href = this.href;
33126     },
33127     
33128     //selection on single brick only
33129     selectBrick : function() {
33130         
33131         if (!this.parentId) {
33132             return;
33133         }
33134         
33135         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33136         var index = m.selectedBrick.indexOf(this.id);
33137         
33138         if ( index > -1) {
33139             m.selectedBrick.splice(index,1);
33140             this.el.removeClass(this.activeClass);
33141             return;
33142         }
33143         
33144         for(var i = 0; i < m.selectedBrick.length; i++) {
33145             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33146             b.el.removeClass(b.activeClass);
33147         }
33148         
33149         m.selectedBrick = [];
33150         
33151         m.selectedBrick.push(this.id);
33152         this.el.addClass(this.activeClass);
33153         return;
33154     },
33155     
33156     isSelected : function(){
33157         return this.el.hasClass(this.activeClass);
33158         
33159     }
33160 });
33161
33162 Roo.apply(Roo.bootstrap.MasonryBrick, {
33163     
33164     //groups: {},
33165     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33166      /**
33167     * register a Masonry Brick
33168     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33169     */
33170     
33171     register : function(brick)
33172     {
33173         //this.groups[brick.id] = brick;
33174         this.groups.add(brick.id, brick);
33175     },
33176     /**
33177     * fetch a  masonry brick based on the masonry brick ID
33178     * @param {string} the masonry brick to add
33179     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33180     */
33181     
33182     get: function(brick_id) 
33183     {
33184         // if (typeof(this.groups[brick_id]) == 'undefined') {
33185         //     return false;
33186         // }
33187         // return this.groups[brick_id] ;
33188         
33189         if(this.groups.key(brick_id)) {
33190             return this.groups.key(brick_id);
33191         }
33192         
33193         return false;
33194     }
33195     
33196     
33197     
33198 });
33199
33200  /*
33201  * - LGPL
33202  *
33203  * element
33204  * 
33205  */
33206
33207 /**
33208  * @class Roo.bootstrap.Brick
33209  * @extends Roo.bootstrap.Component
33210  * Bootstrap Brick class
33211  * 
33212  * @constructor
33213  * Create a new Brick
33214  * @param {Object} config The config object
33215  */
33216
33217 Roo.bootstrap.Brick = function(config){
33218     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33219     
33220     this.addEvents({
33221         // raw events
33222         /**
33223          * @event click
33224          * When a Brick is click
33225          * @param {Roo.bootstrap.Brick} this
33226          * @param {Roo.EventObject} e
33227          */
33228         "click" : true
33229     });
33230 };
33231
33232 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33233     
33234     /**
33235      * @cfg {String} title
33236      */   
33237     title : '',
33238     /**
33239      * @cfg {String} html
33240      */   
33241     html : '',
33242     /**
33243      * @cfg {String} bgimage
33244      */   
33245     bgimage : '',
33246     /**
33247      * @cfg {String} cls
33248      */   
33249     cls : '',
33250     /**
33251      * @cfg {String} href
33252      */   
33253     href : '',
33254     /**
33255      * @cfg {String} video
33256      */   
33257     video : '',
33258     /**
33259      * @cfg {Boolean} square
33260      */   
33261     square : true,
33262     
33263     getAutoCreate : function()
33264     {
33265         var cls = 'roo-brick';
33266         
33267         if(this.href.length){
33268             cls += ' roo-brick-link';
33269         }
33270         
33271         if(this.bgimage.length){
33272             cls += ' roo-brick-image';
33273         }
33274         
33275         if(!this.html.length && !this.bgimage.length){
33276             cls += ' roo-brick-center-title';
33277         }
33278         
33279         if(!this.html.length && this.bgimage.length){
33280             cls += ' roo-brick-bottom-title';
33281         }
33282         
33283         if(this.cls){
33284             cls += ' ' + this.cls;
33285         }
33286         
33287         var cfg = {
33288             tag: (this.href.length) ? 'a' : 'div',
33289             cls: cls,
33290             cn: [
33291                 {
33292                     tag: 'div',
33293                     cls: 'roo-brick-paragraph',
33294                     cn: []
33295                 }
33296             ]
33297         };
33298         
33299         if(this.href.length){
33300             cfg.href = this.href;
33301         }
33302         
33303         var cn = cfg.cn[0].cn;
33304         
33305         if(this.title.length){
33306             cn.push({
33307                 tag: 'h4',
33308                 cls: 'roo-brick-title',
33309                 html: this.title
33310             });
33311         }
33312         
33313         if(this.html.length){
33314             cn.push({
33315                 tag: 'p',
33316                 cls: 'roo-brick-text',
33317                 html: this.html
33318             });
33319         } else {
33320             cn.cls += ' hide';
33321         }
33322         
33323         if(this.bgimage.length){
33324             cfg.cn.push({
33325                 tag: 'img',
33326                 cls: 'roo-brick-image-view',
33327                 src: this.bgimage
33328             });
33329         }
33330         
33331         return cfg;
33332     },
33333     
33334     initEvents: function() 
33335     {
33336         if(this.title.length || this.html.length){
33337             this.el.on('mouseenter'  ,this.enter, this);
33338             this.el.on('mouseleave', this.leave, this);
33339         }
33340         
33341         Roo.EventManager.onWindowResize(this.resize, this); 
33342         
33343         if(this.bgimage.length){
33344             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33345             this.imageEl.on('load', this.onImageLoad, this);
33346             return;
33347         }
33348         
33349         this.resize();
33350     },
33351     
33352     onImageLoad : function()
33353     {
33354         this.resize();
33355     },
33356     
33357     resize : function()
33358     {
33359         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33360         
33361         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33362         
33363         if(this.bgimage.length){
33364             var image = this.el.select('.roo-brick-image-view', true).first();
33365             
33366             image.setWidth(paragraph.getWidth());
33367             
33368             if(this.square){
33369                 image.setHeight(paragraph.getWidth());
33370             }
33371             
33372             this.el.setHeight(image.getHeight());
33373             paragraph.setHeight(image.getHeight());
33374             
33375         }
33376         
33377     },
33378     
33379     enter: function(e, el)
33380     {
33381         e.preventDefault();
33382         
33383         if(this.bgimage.length){
33384             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33385             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33386         }
33387     },
33388     
33389     leave: function(e, el)
33390     {
33391         e.preventDefault();
33392         
33393         if(this.bgimage.length){
33394             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33395             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33396         }
33397     }
33398     
33399 });
33400
33401  
33402
33403  /*
33404  * - LGPL
33405  *
33406  * Number field 
33407  */
33408
33409 /**
33410  * @class Roo.bootstrap.NumberField
33411  * @extends Roo.bootstrap.Input
33412  * Bootstrap NumberField class
33413  * 
33414  * 
33415  * 
33416  * 
33417  * @constructor
33418  * Create a new NumberField
33419  * @param {Object} config The config object
33420  */
33421
33422 Roo.bootstrap.NumberField = function(config){
33423     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33424 };
33425
33426 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33427     
33428     /**
33429      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33430      */
33431     allowDecimals : true,
33432     /**
33433      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33434      */
33435     decimalSeparator : ".",
33436     /**
33437      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33438      */
33439     decimalPrecision : 2,
33440     /**
33441      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33442      */
33443     allowNegative : true,
33444     
33445     /**
33446      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33447      */
33448     allowZero: true,
33449     /**
33450      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33451      */
33452     minValue : Number.NEGATIVE_INFINITY,
33453     /**
33454      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33455      */
33456     maxValue : Number.MAX_VALUE,
33457     /**
33458      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33459      */
33460     minText : "The minimum value for this field is {0}",
33461     /**
33462      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33463      */
33464     maxText : "The maximum value for this field is {0}",
33465     /**
33466      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33467      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33468      */
33469     nanText : "{0} is not a valid number",
33470     /**
33471      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33472      */
33473     thousandsDelimiter : false,
33474     /**
33475      * @cfg {String} valueAlign alignment of value
33476      */
33477     valueAlign : "left",
33478
33479     getAutoCreate : function()
33480     {
33481         var hiddenInput = {
33482             tag: 'input',
33483             type: 'hidden',
33484             id: Roo.id(),
33485             cls: 'hidden-number-input'
33486         };
33487         
33488         if (this.name) {
33489             hiddenInput.name = this.name;
33490         }
33491         
33492         this.name = '';
33493         
33494         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33495         
33496         this.name = hiddenInput.name;
33497         
33498         if(cfg.cn.length > 0) {
33499             cfg.cn.push(hiddenInput);
33500         }
33501         
33502         return cfg;
33503     },
33504
33505     // private
33506     initEvents : function()
33507     {   
33508         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33509         
33510         var allowed = "0123456789";
33511         
33512         if(this.allowDecimals){
33513             allowed += this.decimalSeparator;
33514         }
33515         
33516         if(this.allowNegative){
33517             allowed += "-";
33518         }
33519         
33520         if(this.thousandsDelimiter) {
33521             allowed += ",";
33522         }
33523         
33524         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33525         
33526         var keyPress = function(e){
33527             
33528             var k = e.getKey();
33529             
33530             var c = e.getCharCode();
33531             
33532             if(
33533                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33534                     allowed.indexOf(String.fromCharCode(c)) === -1
33535             ){
33536                 e.stopEvent();
33537                 return;
33538             }
33539             
33540             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33541                 return;
33542             }
33543             
33544             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33545                 e.stopEvent();
33546             }
33547         };
33548         
33549         this.el.on("keypress", keyPress, this);
33550     },
33551     
33552     validateValue : function(value)
33553     {
33554         
33555         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33556             return false;
33557         }
33558         
33559         var num = this.parseValue(value);
33560         
33561         if(isNaN(num)){
33562             this.markInvalid(String.format(this.nanText, value));
33563             return false;
33564         }
33565         
33566         if(num < this.minValue){
33567             this.markInvalid(String.format(this.minText, this.minValue));
33568             return false;
33569         }
33570         
33571         if(num > this.maxValue){
33572             this.markInvalid(String.format(this.maxText, this.maxValue));
33573             return false;
33574         }
33575         
33576         return true;
33577     },
33578
33579     getValue : function()
33580     {
33581         var v = this.hiddenEl().getValue();
33582         
33583         return this.fixPrecision(this.parseValue(v));
33584     },
33585
33586     parseValue : function(value)
33587     {
33588         if(this.thousandsDelimiter) {
33589             value += "";
33590             r = new RegExp(",", "g");
33591             value = value.replace(r, "");
33592         }
33593         
33594         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33595         return isNaN(value) ? '' : value;
33596     },
33597
33598     fixPrecision : function(value)
33599     {
33600         if(this.thousandsDelimiter) {
33601             value += "";
33602             r = new RegExp(",", "g");
33603             value = value.replace(r, "");
33604         }
33605         
33606         var nan = isNaN(value);
33607         
33608         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33609             return nan ? '' : value;
33610         }
33611         return parseFloat(value).toFixed(this.decimalPrecision);
33612     },
33613
33614     setValue : function(v)
33615     {
33616         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33617         
33618         this.value = v;
33619         
33620         if(this.rendered){
33621             
33622             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33623             
33624             this.inputEl().dom.value = (v == '') ? '' :
33625                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33626             
33627             if(!this.allowZero && v === '0') {
33628                 this.hiddenEl().dom.value = '';
33629                 this.inputEl().dom.value = '';
33630             }
33631             
33632             this.validate();
33633         }
33634     },
33635
33636     decimalPrecisionFcn : function(v)
33637     {
33638         return Math.floor(v);
33639     },
33640
33641     beforeBlur : function()
33642     {
33643         var v = this.parseValue(this.getRawValue());
33644         
33645         if(v || v === 0 || v === ''){
33646             this.setValue(v);
33647         }
33648     },
33649     
33650     hiddenEl : function()
33651     {
33652         return this.el.select('input.hidden-number-input',true).first();
33653     }
33654     
33655 });
33656
33657  
33658
33659 /*
33660 * Licence: LGPL
33661 */
33662
33663 /**
33664  * @class Roo.bootstrap.DocumentSlider
33665  * @extends Roo.bootstrap.Component
33666  * Bootstrap DocumentSlider class
33667  * 
33668  * @constructor
33669  * Create a new DocumentViewer
33670  * @param {Object} config The config object
33671  */
33672
33673 Roo.bootstrap.DocumentSlider = function(config){
33674     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33675     
33676     this.files = [];
33677     
33678     this.addEvents({
33679         /**
33680          * @event initial
33681          * Fire after initEvent
33682          * @param {Roo.bootstrap.DocumentSlider} this
33683          */
33684         "initial" : true,
33685         /**
33686          * @event update
33687          * Fire after update
33688          * @param {Roo.bootstrap.DocumentSlider} this
33689          */
33690         "update" : true,
33691         /**
33692          * @event click
33693          * Fire after click
33694          * @param {Roo.bootstrap.DocumentSlider} this
33695          */
33696         "click" : true
33697     });
33698 };
33699
33700 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33701     
33702     files : false,
33703     
33704     indicator : 0,
33705     
33706     getAutoCreate : function()
33707     {
33708         var cfg = {
33709             tag : 'div',
33710             cls : 'roo-document-slider',
33711             cn : [
33712                 {
33713                     tag : 'div',
33714                     cls : 'roo-document-slider-header',
33715                     cn : [
33716                         {
33717                             tag : 'div',
33718                             cls : 'roo-document-slider-header-title'
33719                         }
33720                     ]
33721                 },
33722                 {
33723                     tag : 'div',
33724                     cls : 'roo-document-slider-body',
33725                     cn : [
33726                         {
33727                             tag : 'div',
33728                             cls : 'roo-document-slider-prev',
33729                             cn : [
33730                                 {
33731                                     tag : 'i',
33732                                     cls : 'fa fa-chevron-left'
33733                                 }
33734                             ]
33735                         },
33736                         {
33737                             tag : 'div',
33738                             cls : 'roo-document-slider-thumb',
33739                             cn : [
33740                                 {
33741                                     tag : 'img',
33742                                     cls : 'roo-document-slider-image'
33743                                 }
33744                             ]
33745                         },
33746                         {
33747                             tag : 'div',
33748                             cls : 'roo-document-slider-next',
33749                             cn : [
33750                                 {
33751                                     tag : 'i',
33752                                     cls : 'fa fa-chevron-right'
33753                                 }
33754                             ]
33755                         }
33756                     ]
33757                 }
33758             ]
33759         };
33760         
33761         return cfg;
33762     },
33763     
33764     initEvents : function()
33765     {
33766         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33767         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33768         
33769         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33770         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33771         
33772         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33773         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33774         
33775         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33776         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33777         
33778         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33779         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33780         
33781         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33782         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33783         
33784         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33785         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33786         
33787         this.thumbEl.on('click', this.onClick, this);
33788         
33789         this.prevIndicator.on('click', this.prev, this);
33790         
33791         this.nextIndicator.on('click', this.next, this);
33792         
33793     },
33794     
33795     initial : function()
33796     {
33797         if(this.files.length){
33798             this.indicator = 1;
33799             this.update()
33800         }
33801         
33802         this.fireEvent('initial', this);
33803     },
33804     
33805     update : function()
33806     {
33807         this.imageEl.attr('src', this.files[this.indicator - 1]);
33808         
33809         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33810         
33811         this.prevIndicator.show();
33812         
33813         if(this.indicator == 1){
33814             this.prevIndicator.hide();
33815         }
33816         
33817         this.nextIndicator.show();
33818         
33819         if(this.indicator == this.files.length){
33820             this.nextIndicator.hide();
33821         }
33822         
33823         this.thumbEl.scrollTo('top');
33824         
33825         this.fireEvent('update', this);
33826     },
33827     
33828     onClick : function(e)
33829     {
33830         e.preventDefault();
33831         
33832         this.fireEvent('click', this);
33833     },
33834     
33835     prev : function(e)
33836     {
33837         e.preventDefault();
33838         
33839         this.indicator = Math.max(1, this.indicator - 1);
33840         
33841         this.update();
33842     },
33843     
33844     next : function(e)
33845     {
33846         e.preventDefault();
33847         
33848         this.indicator = Math.min(this.files.length, this.indicator + 1);
33849         
33850         this.update();
33851     }
33852 });
33853 /*
33854  * - LGPL
33855  *
33856  * RadioSet
33857  *
33858  *
33859  */
33860
33861 /**
33862  * @class Roo.bootstrap.RadioSet
33863  * @extends Roo.bootstrap.Input
33864  * Bootstrap RadioSet class
33865  * @cfg {String} indicatorpos (left|right) default left
33866  * @cfg {Boolean} inline (true|false) inline the element (default true)
33867  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33868  * @constructor
33869  * Create a new RadioSet
33870  * @param {Object} config The config object
33871  */
33872
33873 Roo.bootstrap.RadioSet = function(config){
33874     
33875     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33876     
33877     this.radioes = [];
33878     
33879     Roo.bootstrap.RadioSet.register(this);
33880     
33881     this.addEvents({
33882         /**
33883         * @event check
33884         * Fires when the element is checked or unchecked.
33885         * @param {Roo.bootstrap.RadioSet} this This radio
33886         * @param {Roo.bootstrap.Radio} item The checked item
33887         */
33888        check : true,
33889        /**
33890         * @event click
33891         * Fires when the element is click.
33892         * @param {Roo.bootstrap.RadioSet} this This radio set
33893         * @param {Roo.bootstrap.Radio} item The checked item
33894         * @param {Roo.EventObject} e The event object
33895         */
33896        click : true
33897     });
33898     
33899 };
33900
33901 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33902
33903     radioes : false,
33904     
33905     inline : true,
33906     
33907     weight : '',
33908     
33909     indicatorpos : 'left',
33910     
33911     getAutoCreate : function()
33912     {
33913         var label = {
33914             tag : 'label',
33915             cls : 'roo-radio-set-label',
33916             cn : [
33917                 {
33918                     tag : 'span',
33919                     html : this.fieldLabel
33920                 }
33921             ]
33922         };
33923         
33924         if(this.indicatorpos == 'left'){
33925             label.cn.unshift({
33926                 tag : 'i',
33927                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33928                 tooltip : 'This field is required'
33929             });
33930         } else {
33931             label.cn.push({
33932                 tag : 'i',
33933                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33934                 tooltip : 'This field is required'
33935             });
33936         }
33937         
33938         var items = {
33939             tag : 'div',
33940             cls : 'roo-radio-set-items'
33941         };
33942         
33943         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33944         
33945         if (align === 'left' && this.fieldLabel.length) {
33946             
33947             items = {
33948                 cls : "roo-radio-set-right", 
33949                 cn: [
33950                     items
33951                 ]
33952             };
33953             
33954             if(this.labelWidth > 12){
33955                 label.style = "width: " + this.labelWidth + 'px';
33956             }
33957             
33958             if(this.labelWidth < 13 && this.labelmd == 0){
33959                 this.labelmd = this.labelWidth;
33960             }
33961             
33962             if(this.labellg > 0){
33963                 label.cls += ' col-lg-' + this.labellg;
33964                 items.cls += ' col-lg-' + (12 - this.labellg);
33965             }
33966             
33967             if(this.labelmd > 0){
33968                 label.cls += ' col-md-' + this.labelmd;
33969                 items.cls += ' col-md-' + (12 - this.labelmd);
33970             }
33971             
33972             if(this.labelsm > 0){
33973                 label.cls += ' col-sm-' + this.labelsm;
33974                 items.cls += ' col-sm-' + (12 - this.labelsm);
33975             }
33976             
33977             if(this.labelxs > 0){
33978                 label.cls += ' col-xs-' + this.labelxs;
33979                 items.cls += ' col-xs-' + (12 - this.labelxs);
33980             }
33981         }
33982         
33983         var cfg = {
33984             tag : 'div',
33985             cls : 'roo-radio-set',
33986             cn : [
33987                 {
33988                     tag : 'input',
33989                     cls : 'roo-radio-set-input',
33990                     type : 'hidden',
33991                     name : this.name,
33992                     value : this.value ? this.value :  ''
33993                 },
33994                 label,
33995                 items
33996             ]
33997         };
33998         
33999         if(this.weight.length){
34000             cfg.cls += ' roo-radio-' + this.weight;
34001         }
34002         
34003         if(this.inline) {
34004             cfg.cls += ' roo-radio-set-inline';
34005         }
34006         
34007         var settings=this;
34008         ['xs','sm','md','lg'].map(function(size){
34009             if (settings[size]) {
34010                 cfg.cls += ' col-' + size + '-' + settings[size];
34011             }
34012         });
34013         
34014         return cfg;
34015         
34016     },
34017
34018     initEvents : function()
34019     {
34020         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34021         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34022         
34023         if(!this.fieldLabel.length){
34024             this.labelEl.hide();
34025         }
34026         
34027         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34028         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34029         
34030         this.indicator = this.indicatorEl();
34031         
34032         if(this.indicator){
34033             this.indicator.addClass('invisible');
34034         }
34035         
34036         this.originalValue = this.getValue();
34037         
34038     },
34039     
34040     inputEl: function ()
34041     {
34042         return this.el.select('.roo-radio-set-input', true).first();
34043     },
34044     
34045     getChildContainer : function()
34046     {
34047         return this.itemsEl;
34048     },
34049     
34050     register : function(item)
34051     {
34052         this.radioes.push(item);
34053         
34054     },
34055     
34056     validate : function()
34057     {   
34058         if(this.getVisibilityEl().hasClass('hidden')){
34059             return true;
34060         }
34061         
34062         var valid = false;
34063         
34064         Roo.each(this.radioes, function(i){
34065             if(!i.checked){
34066                 return;
34067             }
34068             
34069             valid = true;
34070             return false;
34071         });
34072         
34073         if(this.allowBlank) {
34074             return true;
34075         }
34076         
34077         if(this.disabled || valid){
34078             this.markValid();
34079             return true;
34080         }
34081         
34082         this.markInvalid();
34083         return false;
34084         
34085     },
34086     
34087     markValid : function()
34088     {
34089         if(this.labelEl.isVisible(true)){
34090             this.indicatorEl().removeClass('visible');
34091             this.indicatorEl().addClass('invisible');
34092         }
34093         
34094         this.el.removeClass([this.invalidClass, this.validClass]);
34095         this.el.addClass(this.validClass);
34096         
34097         this.fireEvent('valid', this);
34098     },
34099     
34100     markInvalid : function(msg)
34101     {
34102         if(this.allowBlank || this.disabled){
34103             return;
34104         }
34105         
34106         if(this.labelEl.isVisible(true)){
34107             this.indicatorEl().removeClass('invisible');
34108             this.indicatorEl().addClass('visible');
34109         }
34110         
34111         this.el.removeClass([this.invalidClass, this.validClass]);
34112         this.el.addClass(this.invalidClass);
34113         
34114         this.fireEvent('invalid', this, msg);
34115         
34116     },
34117     
34118     setValue : function(v, suppressEvent)
34119     {   
34120         if(this.value === v){
34121             return;
34122         }
34123         
34124         this.value = v;
34125         
34126         if(this.rendered){
34127             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34128         }
34129         
34130         Roo.each(this.radioes, function(i){
34131             i.checked = false;
34132             i.el.removeClass('checked');
34133         });
34134         
34135         Roo.each(this.radioes, function(i){
34136             
34137             if(i.value === v || i.value.toString() === v.toString()){
34138                 i.checked = true;
34139                 i.el.addClass('checked');
34140                 
34141                 if(suppressEvent !== true){
34142                     this.fireEvent('check', this, i);
34143                 }
34144                 
34145                 return false;
34146             }
34147             
34148         }, this);
34149         
34150         this.validate();
34151     },
34152     
34153     clearInvalid : function(){
34154         
34155         if(!this.el || this.preventMark){
34156             return;
34157         }
34158         
34159         this.el.removeClass([this.invalidClass]);
34160         
34161         this.fireEvent('valid', this);
34162     }
34163     
34164 });
34165
34166 Roo.apply(Roo.bootstrap.RadioSet, {
34167     
34168     groups: {},
34169     
34170     register : function(set)
34171     {
34172         this.groups[set.name] = set;
34173     },
34174     
34175     get: function(name) 
34176     {
34177         if (typeof(this.groups[name]) == 'undefined') {
34178             return false;
34179         }
34180         
34181         return this.groups[name] ;
34182     }
34183     
34184 });
34185 /*
34186  * Based on:
34187  * Ext JS Library 1.1.1
34188  * Copyright(c) 2006-2007, Ext JS, LLC.
34189  *
34190  * Originally Released Under LGPL - original licence link has changed is not relivant.
34191  *
34192  * Fork - LGPL
34193  * <script type="text/javascript">
34194  */
34195
34196
34197 /**
34198  * @class Roo.bootstrap.SplitBar
34199  * @extends Roo.util.Observable
34200  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34201  * <br><br>
34202  * Usage:
34203  * <pre><code>
34204 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34205                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34206 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34207 split.minSize = 100;
34208 split.maxSize = 600;
34209 split.animate = true;
34210 split.on('moved', splitterMoved);
34211 </code></pre>
34212  * @constructor
34213  * Create a new SplitBar
34214  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34215  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34216  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34217  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34218                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34219                         position of the SplitBar).
34220  */
34221 Roo.bootstrap.SplitBar = function(cfg){
34222     
34223     /** @private */
34224     
34225     //{
34226     //  dragElement : elm
34227     //  resizingElement: el,
34228         // optional..
34229     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34230     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34231         // existingProxy ???
34232     //}
34233     
34234     this.el = Roo.get(cfg.dragElement, true);
34235     this.el.dom.unselectable = "on";
34236     /** @private */
34237     this.resizingEl = Roo.get(cfg.resizingElement, true);
34238
34239     /**
34240      * @private
34241      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34242      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34243      * @type Number
34244      */
34245     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34246     
34247     /**
34248      * The minimum size of the resizing element. (Defaults to 0)
34249      * @type Number
34250      */
34251     this.minSize = 0;
34252     
34253     /**
34254      * The maximum size of the resizing element. (Defaults to 2000)
34255      * @type Number
34256      */
34257     this.maxSize = 2000;
34258     
34259     /**
34260      * Whether to animate the transition to the new size
34261      * @type Boolean
34262      */
34263     this.animate = false;
34264     
34265     /**
34266      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34267      * @type Boolean
34268      */
34269     this.useShim = false;
34270     
34271     /** @private */
34272     this.shim = null;
34273     
34274     if(!cfg.existingProxy){
34275         /** @private */
34276         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34277     }else{
34278         this.proxy = Roo.get(cfg.existingProxy).dom;
34279     }
34280     /** @private */
34281     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34282     
34283     /** @private */
34284     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34285     
34286     /** @private */
34287     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34288     
34289     /** @private */
34290     this.dragSpecs = {};
34291     
34292     /**
34293      * @private The adapter to use to positon and resize elements
34294      */
34295     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34296     this.adapter.init(this);
34297     
34298     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34299         /** @private */
34300         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34301         this.el.addClass("roo-splitbar-h");
34302     }else{
34303         /** @private */
34304         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34305         this.el.addClass("roo-splitbar-v");
34306     }
34307     
34308     this.addEvents({
34309         /**
34310          * @event resize
34311          * Fires when the splitter is moved (alias for {@link #event-moved})
34312          * @param {Roo.bootstrap.SplitBar} this
34313          * @param {Number} newSize the new width or height
34314          */
34315         "resize" : true,
34316         /**
34317          * @event moved
34318          * Fires when the splitter is moved
34319          * @param {Roo.bootstrap.SplitBar} this
34320          * @param {Number} newSize the new width or height
34321          */
34322         "moved" : true,
34323         /**
34324          * @event beforeresize
34325          * Fires before the splitter is dragged
34326          * @param {Roo.bootstrap.SplitBar} this
34327          */
34328         "beforeresize" : true,
34329
34330         "beforeapply" : true
34331     });
34332
34333     Roo.util.Observable.call(this);
34334 };
34335
34336 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34337     onStartProxyDrag : function(x, y){
34338         this.fireEvent("beforeresize", this);
34339         if(!this.overlay){
34340             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34341             o.unselectable();
34342             o.enableDisplayMode("block");
34343             // all splitbars share the same overlay
34344             Roo.bootstrap.SplitBar.prototype.overlay = o;
34345         }
34346         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34347         this.overlay.show();
34348         Roo.get(this.proxy).setDisplayed("block");
34349         var size = this.adapter.getElementSize(this);
34350         this.activeMinSize = this.getMinimumSize();;
34351         this.activeMaxSize = this.getMaximumSize();;
34352         var c1 = size - this.activeMinSize;
34353         var c2 = Math.max(this.activeMaxSize - size, 0);
34354         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34355             this.dd.resetConstraints();
34356             this.dd.setXConstraint(
34357                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34358                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34359             );
34360             this.dd.setYConstraint(0, 0);
34361         }else{
34362             this.dd.resetConstraints();
34363             this.dd.setXConstraint(0, 0);
34364             this.dd.setYConstraint(
34365                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34366                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34367             );
34368          }
34369         this.dragSpecs.startSize = size;
34370         this.dragSpecs.startPoint = [x, y];
34371         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34372     },
34373     
34374     /** 
34375      * @private Called after the drag operation by the DDProxy
34376      */
34377     onEndProxyDrag : function(e){
34378         Roo.get(this.proxy).setDisplayed(false);
34379         var endPoint = Roo.lib.Event.getXY(e);
34380         if(this.overlay){
34381             this.overlay.hide();
34382         }
34383         var newSize;
34384         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34385             newSize = this.dragSpecs.startSize + 
34386                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34387                     endPoint[0] - this.dragSpecs.startPoint[0] :
34388                     this.dragSpecs.startPoint[0] - endPoint[0]
34389                 );
34390         }else{
34391             newSize = this.dragSpecs.startSize + 
34392                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34393                     endPoint[1] - this.dragSpecs.startPoint[1] :
34394                     this.dragSpecs.startPoint[1] - endPoint[1]
34395                 );
34396         }
34397         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34398         if(newSize != this.dragSpecs.startSize){
34399             if(this.fireEvent('beforeapply', this, newSize) !== false){
34400                 this.adapter.setElementSize(this, newSize);
34401                 this.fireEvent("moved", this, newSize);
34402                 this.fireEvent("resize", this, newSize);
34403             }
34404         }
34405     },
34406     
34407     /**
34408      * Get the adapter this SplitBar uses
34409      * @return The adapter object
34410      */
34411     getAdapter : function(){
34412         return this.adapter;
34413     },
34414     
34415     /**
34416      * Set the adapter this SplitBar uses
34417      * @param {Object} adapter A SplitBar adapter object
34418      */
34419     setAdapter : function(adapter){
34420         this.adapter = adapter;
34421         this.adapter.init(this);
34422     },
34423     
34424     /**
34425      * Gets the minimum size for the resizing element
34426      * @return {Number} The minimum size
34427      */
34428     getMinimumSize : function(){
34429         return this.minSize;
34430     },
34431     
34432     /**
34433      * Sets the minimum size for the resizing element
34434      * @param {Number} minSize The minimum size
34435      */
34436     setMinimumSize : function(minSize){
34437         this.minSize = minSize;
34438     },
34439     
34440     /**
34441      * Gets the maximum size for the resizing element
34442      * @return {Number} The maximum size
34443      */
34444     getMaximumSize : function(){
34445         return this.maxSize;
34446     },
34447     
34448     /**
34449      * Sets the maximum size for the resizing element
34450      * @param {Number} maxSize The maximum size
34451      */
34452     setMaximumSize : function(maxSize){
34453         this.maxSize = maxSize;
34454     },
34455     
34456     /**
34457      * Sets the initialize size for the resizing element
34458      * @param {Number} size The initial size
34459      */
34460     setCurrentSize : function(size){
34461         var oldAnimate = this.animate;
34462         this.animate = false;
34463         this.adapter.setElementSize(this, size);
34464         this.animate = oldAnimate;
34465     },
34466     
34467     /**
34468      * Destroy this splitbar. 
34469      * @param {Boolean} removeEl True to remove the element
34470      */
34471     destroy : function(removeEl){
34472         if(this.shim){
34473             this.shim.remove();
34474         }
34475         this.dd.unreg();
34476         this.proxy.parentNode.removeChild(this.proxy);
34477         if(removeEl){
34478             this.el.remove();
34479         }
34480     }
34481 });
34482
34483 /**
34484  * @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.
34485  */
34486 Roo.bootstrap.SplitBar.createProxy = function(dir){
34487     var proxy = new Roo.Element(document.createElement("div"));
34488     proxy.unselectable();
34489     var cls = 'roo-splitbar-proxy';
34490     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34491     document.body.appendChild(proxy.dom);
34492     return proxy.dom;
34493 };
34494
34495 /** 
34496  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34497  * Default Adapter. It assumes the splitter and resizing element are not positioned
34498  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34499  */
34500 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34501 };
34502
34503 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34504     // do nothing for now
34505     init : function(s){
34506     
34507     },
34508     /**
34509      * Called before drag operations to get the current size of the resizing element. 
34510      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34511      */
34512      getElementSize : function(s){
34513         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34514             return s.resizingEl.getWidth();
34515         }else{
34516             return s.resizingEl.getHeight();
34517         }
34518     },
34519     
34520     /**
34521      * Called after drag operations to set the size of the resizing element.
34522      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34523      * @param {Number} newSize The new size to set
34524      * @param {Function} onComplete A function to be invoked when resizing is complete
34525      */
34526     setElementSize : function(s, newSize, onComplete){
34527         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34528             if(!s.animate){
34529                 s.resizingEl.setWidth(newSize);
34530                 if(onComplete){
34531                     onComplete(s, newSize);
34532                 }
34533             }else{
34534                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34535             }
34536         }else{
34537             
34538             if(!s.animate){
34539                 s.resizingEl.setHeight(newSize);
34540                 if(onComplete){
34541                     onComplete(s, newSize);
34542                 }
34543             }else{
34544                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34545             }
34546         }
34547     }
34548 };
34549
34550 /** 
34551  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34552  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34553  * Adapter that  moves the splitter element to align with the resized sizing element. 
34554  * Used with an absolute positioned SplitBar.
34555  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34556  * document.body, make sure you assign an id to the body element.
34557  */
34558 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34559     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34560     this.container = Roo.get(container);
34561 };
34562
34563 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34564     init : function(s){
34565         this.basic.init(s);
34566     },
34567     
34568     getElementSize : function(s){
34569         return this.basic.getElementSize(s);
34570     },
34571     
34572     setElementSize : function(s, newSize, onComplete){
34573         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34574     },
34575     
34576     moveSplitter : function(s){
34577         var yes = Roo.bootstrap.SplitBar;
34578         switch(s.placement){
34579             case yes.LEFT:
34580                 s.el.setX(s.resizingEl.getRight());
34581                 break;
34582             case yes.RIGHT:
34583                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34584                 break;
34585             case yes.TOP:
34586                 s.el.setY(s.resizingEl.getBottom());
34587                 break;
34588             case yes.BOTTOM:
34589                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34590                 break;
34591         }
34592     }
34593 };
34594
34595 /**
34596  * Orientation constant - Create a vertical SplitBar
34597  * @static
34598  * @type Number
34599  */
34600 Roo.bootstrap.SplitBar.VERTICAL = 1;
34601
34602 /**
34603  * Orientation constant - Create a horizontal SplitBar
34604  * @static
34605  * @type Number
34606  */
34607 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34608
34609 /**
34610  * Placement constant - The resizing element is to the left of the splitter element
34611  * @static
34612  * @type Number
34613  */
34614 Roo.bootstrap.SplitBar.LEFT = 1;
34615
34616 /**
34617  * Placement constant - The resizing element is to the right of the splitter element
34618  * @static
34619  * @type Number
34620  */
34621 Roo.bootstrap.SplitBar.RIGHT = 2;
34622
34623 /**
34624  * Placement constant - The resizing element is positioned above the splitter element
34625  * @static
34626  * @type Number
34627  */
34628 Roo.bootstrap.SplitBar.TOP = 3;
34629
34630 /**
34631  * Placement constant - The resizing element is positioned under splitter element
34632  * @static
34633  * @type Number
34634  */
34635 Roo.bootstrap.SplitBar.BOTTOM = 4;
34636 Roo.namespace("Roo.bootstrap.layout");/*
34637  * Based on:
34638  * Ext JS Library 1.1.1
34639  * Copyright(c) 2006-2007, Ext JS, LLC.
34640  *
34641  * Originally Released Under LGPL - original licence link has changed is not relivant.
34642  *
34643  * Fork - LGPL
34644  * <script type="text/javascript">
34645  */
34646
34647 /**
34648  * @class Roo.bootstrap.layout.Manager
34649  * @extends Roo.bootstrap.Component
34650  * Base class for layout managers.
34651  */
34652 Roo.bootstrap.layout.Manager = function(config)
34653 {
34654     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34655
34656
34657
34658
34659
34660     /** false to disable window resize monitoring @type Boolean */
34661     this.monitorWindowResize = true;
34662     this.regions = {};
34663     this.addEvents({
34664         /**
34665          * @event layout
34666          * Fires when a layout is performed.
34667          * @param {Roo.LayoutManager} this
34668          */
34669         "layout" : true,
34670         /**
34671          * @event regionresized
34672          * Fires when the user resizes a region.
34673          * @param {Roo.LayoutRegion} region The resized region
34674          * @param {Number} newSize The new size (width for east/west, height for north/south)
34675          */
34676         "regionresized" : true,
34677         /**
34678          * @event regioncollapsed
34679          * Fires when a region is collapsed.
34680          * @param {Roo.LayoutRegion} region The collapsed region
34681          */
34682         "regioncollapsed" : true,
34683         /**
34684          * @event regionexpanded
34685          * Fires when a region is expanded.
34686          * @param {Roo.LayoutRegion} region The expanded region
34687          */
34688         "regionexpanded" : true
34689     });
34690     this.updating = false;
34691
34692     if (config.el) {
34693         this.el = Roo.get(config.el);
34694         this.initEvents();
34695     }
34696
34697 };
34698
34699 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34700
34701
34702     regions : null,
34703
34704     monitorWindowResize : true,
34705
34706
34707     updating : false,
34708
34709
34710     onRender : function(ct, position)
34711     {
34712         if(!this.el){
34713             this.el = Roo.get(ct);
34714             this.initEvents();
34715         }
34716         //this.fireEvent('render',this);
34717     },
34718
34719
34720     initEvents: function()
34721     {
34722
34723
34724         // ie scrollbar fix
34725         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34726             document.body.scroll = "no";
34727         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34728             this.el.position('relative');
34729         }
34730         this.id = this.el.id;
34731         this.el.addClass("roo-layout-container");
34732         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34733         if(this.el.dom != document.body ) {
34734             this.el.on('resize', this.layout,this);
34735             this.el.on('show', this.layout,this);
34736         }
34737
34738     },
34739
34740     /**
34741      * Returns true if this layout is currently being updated
34742      * @return {Boolean}
34743      */
34744     isUpdating : function(){
34745         return this.updating;
34746     },
34747
34748     /**
34749      * Suspend the LayoutManager from doing auto-layouts while
34750      * making multiple add or remove calls
34751      */
34752     beginUpdate : function(){
34753         this.updating = true;
34754     },
34755
34756     /**
34757      * Restore auto-layouts and optionally disable the manager from performing a layout
34758      * @param {Boolean} noLayout true to disable a layout update
34759      */
34760     endUpdate : function(noLayout){
34761         this.updating = false;
34762         if(!noLayout){
34763             this.layout();
34764         }
34765     },
34766
34767     layout: function(){
34768         // abstract...
34769     },
34770
34771     onRegionResized : function(region, newSize){
34772         this.fireEvent("regionresized", region, newSize);
34773         this.layout();
34774     },
34775
34776     onRegionCollapsed : function(region){
34777         this.fireEvent("regioncollapsed", region);
34778     },
34779
34780     onRegionExpanded : function(region){
34781         this.fireEvent("regionexpanded", region);
34782     },
34783
34784     /**
34785      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34786      * performs box-model adjustments.
34787      * @return {Object} The size as an object {width: (the width), height: (the height)}
34788      */
34789     getViewSize : function()
34790     {
34791         var size;
34792         if(this.el.dom != document.body){
34793             size = this.el.getSize();
34794         }else{
34795             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34796         }
34797         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34798         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34799         return size;
34800     },
34801
34802     /**
34803      * Returns the Element this layout is bound to.
34804      * @return {Roo.Element}
34805      */
34806     getEl : function(){
34807         return this.el;
34808     },
34809
34810     /**
34811      * Returns the specified region.
34812      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34813      * @return {Roo.LayoutRegion}
34814      */
34815     getRegion : function(target){
34816         return this.regions[target.toLowerCase()];
34817     },
34818
34819     onWindowResize : function(){
34820         if(this.monitorWindowResize){
34821             this.layout();
34822         }
34823     }
34824 });
34825 /*
34826  * Based on:
34827  * Ext JS Library 1.1.1
34828  * Copyright(c) 2006-2007, Ext JS, LLC.
34829  *
34830  * Originally Released Under LGPL - original licence link has changed is not relivant.
34831  *
34832  * Fork - LGPL
34833  * <script type="text/javascript">
34834  */
34835 /**
34836  * @class Roo.bootstrap.layout.Border
34837  * @extends Roo.bootstrap.layout.Manager
34838  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34839  * please see: examples/bootstrap/nested.html<br><br>
34840  
34841 <b>The container the layout is rendered into can be either the body element or any other element.
34842 If it is not the body element, the container needs to either be an absolute positioned element,
34843 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34844 the container size if it is not the body element.</b>
34845
34846 * @constructor
34847 * Create a new Border
34848 * @param {Object} config Configuration options
34849  */
34850 Roo.bootstrap.layout.Border = function(config){
34851     config = config || {};
34852     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34853     
34854     
34855     
34856     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34857         if(config[region]){
34858             config[region].region = region;
34859             this.addRegion(config[region]);
34860         }
34861     },this);
34862     
34863 };
34864
34865 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34866
34867 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34868     /**
34869      * Creates and adds a new region if it doesn't already exist.
34870      * @param {String} target The target region key (north, south, east, west or center).
34871      * @param {Object} config The regions config object
34872      * @return {BorderLayoutRegion} The new region
34873      */
34874     addRegion : function(config)
34875     {
34876         if(!this.regions[config.region]){
34877             var r = this.factory(config);
34878             this.bindRegion(r);
34879         }
34880         return this.regions[config.region];
34881     },
34882
34883     // private (kinda)
34884     bindRegion : function(r){
34885         this.regions[r.config.region] = r;
34886         
34887         r.on("visibilitychange",    this.layout, this);
34888         r.on("paneladded",          this.layout, this);
34889         r.on("panelremoved",        this.layout, this);
34890         r.on("invalidated",         this.layout, this);
34891         r.on("resized",             this.onRegionResized, this);
34892         r.on("collapsed",           this.onRegionCollapsed, this);
34893         r.on("expanded",            this.onRegionExpanded, this);
34894     },
34895
34896     /**
34897      * Performs a layout update.
34898      */
34899     layout : function()
34900     {
34901         if(this.updating) {
34902             return;
34903         }
34904         
34905         // render all the rebions if they have not been done alreayd?
34906         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34907             if(this.regions[region] && !this.regions[region].bodyEl){
34908                 this.regions[region].onRender(this.el)
34909             }
34910         },this);
34911         
34912         var size = this.getViewSize();
34913         var w = size.width;
34914         var h = size.height;
34915         var centerW = w;
34916         var centerH = h;
34917         var centerY = 0;
34918         var centerX = 0;
34919         //var x = 0, y = 0;
34920
34921         var rs = this.regions;
34922         var north = rs["north"];
34923         var south = rs["south"]; 
34924         var west = rs["west"];
34925         var east = rs["east"];
34926         var center = rs["center"];
34927         //if(this.hideOnLayout){ // not supported anymore
34928             //c.el.setStyle("display", "none");
34929         //}
34930         if(north && north.isVisible()){
34931             var b = north.getBox();
34932             var m = north.getMargins();
34933             b.width = w - (m.left+m.right);
34934             b.x = m.left;
34935             b.y = m.top;
34936             centerY = b.height + b.y + m.bottom;
34937             centerH -= centerY;
34938             north.updateBox(this.safeBox(b));
34939         }
34940         if(south && south.isVisible()){
34941             var b = south.getBox();
34942             var m = south.getMargins();
34943             b.width = w - (m.left+m.right);
34944             b.x = m.left;
34945             var totalHeight = (b.height + m.top + m.bottom);
34946             b.y = h - totalHeight + m.top;
34947             centerH -= totalHeight;
34948             south.updateBox(this.safeBox(b));
34949         }
34950         if(west && west.isVisible()){
34951             var b = west.getBox();
34952             var m = west.getMargins();
34953             b.height = centerH - (m.top+m.bottom);
34954             b.x = m.left;
34955             b.y = centerY + m.top;
34956             var totalWidth = (b.width + m.left + m.right);
34957             centerX += totalWidth;
34958             centerW -= totalWidth;
34959             west.updateBox(this.safeBox(b));
34960         }
34961         if(east && east.isVisible()){
34962             var b = east.getBox();
34963             var m = east.getMargins();
34964             b.height = centerH - (m.top+m.bottom);
34965             var totalWidth = (b.width + m.left + m.right);
34966             b.x = w - totalWidth + m.left;
34967             b.y = centerY + m.top;
34968             centerW -= totalWidth;
34969             east.updateBox(this.safeBox(b));
34970         }
34971         if(center){
34972             var m = center.getMargins();
34973             var centerBox = {
34974                 x: centerX + m.left,
34975                 y: centerY + m.top,
34976                 width: centerW - (m.left+m.right),
34977                 height: centerH - (m.top+m.bottom)
34978             };
34979             //if(this.hideOnLayout){
34980                 //center.el.setStyle("display", "block");
34981             //}
34982             center.updateBox(this.safeBox(centerBox));
34983         }
34984         this.el.repaint();
34985         this.fireEvent("layout", this);
34986     },
34987
34988     // private
34989     safeBox : function(box){
34990         box.width = Math.max(0, box.width);
34991         box.height = Math.max(0, box.height);
34992         return box;
34993     },
34994
34995     /**
34996      * Adds a ContentPanel (or subclass) to this layout.
34997      * @param {String} target The target region key (north, south, east, west or center).
34998      * @param {Roo.ContentPanel} panel The panel to add
34999      * @return {Roo.ContentPanel} The added panel
35000      */
35001     add : function(target, panel){
35002          
35003         target = target.toLowerCase();
35004         return this.regions[target].add(panel);
35005     },
35006
35007     /**
35008      * Remove a ContentPanel (or subclass) to this layout.
35009      * @param {String} target The target region key (north, south, east, west or center).
35010      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35011      * @return {Roo.ContentPanel} The removed panel
35012      */
35013     remove : function(target, panel){
35014         target = target.toLowerCase();
35015         return this.regions[target].remove(panel);
35016     },
35017
35018     /**
35019      * Searches all regions for a panel with the specified id
35020      * @param {String} panelId
35021      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35022      */
35023     findPanel : function(panelId){
35024         var rs = this.regions;
35025         for(var target in rs){
35026             if(typeof rs[target] != "function"){
35027                 var p = rs[target].getPanel(panelId);
35028                 if(p){
35029                     return p;
35030                 }
35031             }
35032         }
35033         return null;
35034     },
35035
35036     /**
35037      * Searches all regions for a panel with the specified id and activates (shows) it.
35038      * @param {String/ContentPanel} panelId The panels id or the panel itself
35039      * @return {Roo.ContentPanel} The shown panel or null
35040      */
35041     showPanel : function(panelId) {
35042       var rs = this.regions;
35043       for(var target in rs){
35044          var r = rs[target];
35045          if(typeof r != "function"){
35046             if(r.hasPanel(panelId)){
35047                return r.showPanel(panelId);
35048             }
35049          }
35050       }
35051       return null;
35052    },
35053
35054    /**
35055      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35056      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35057      */
35058    /*
35059     restoreState : function(provider){
35060         if(!provider){
35061             provider = Roo.state.Manager;
35062         }
35063         var sm = new Roo.LayoutStateManager();
35064         sm.init(this, provider);
35065     },
35066 */
35067  
35068  
35069     /**
35070      * Adds a xtype elements to the layout.
35071      * <pre><code>
35072
35073 layout.addxtype({
35074        xtype : 'ContentPanel',
35075        region: 'west',
35076        items: [ .... ]
35077    }
35078 );
35079
35080 layout.addxtype({
35081         xtype : 'NestedLayoutPanel',
35082         region: 'west',
35083         layout: {
35084            center: { },
35085            west: { }   
35086         },
35087         items : [ ... list of content panels or nested layout panels.. ]
35088    }
35089 );
35090 </code></pre>
35091      * @param {Object} cfg Xtype definition of item to add.
35092      */
35093     addxtype : function(cfg)
35094     {
35095         // basically accepts a pannel...
35096         // can accept a layout region..!?!?
35097         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35098         
35099         
35100         // theory?  children can only be panels??
35101         
35102         //if (!cfg.xtype.match(/Panel$/)) {
35103         //    return false;
35104         //}
35105         var ret = false;
35106         
35107         if (typeof(cfg.region) == 'undefined') {
35108             Roo.log("Failed to add Panel, region was not set");
35109             Roo.log(cfg);
35110             return false;
35111         }
35112         var region = cfg.region;
35113         delete cfg.region;
35114         
35115           
35116         var xitems = [];
35117         if (cfg.items) {
35118             xitems = cfg.items;
35119             delete cfg.items;
35120         }
35121         var nb = false;
35122         
35123         switch(cfg.xtype) 
35124         {
35125             case 'Content':  // ContentPanel (el, cfg)
35126             case 'Scroll':  // ContentPanel (el, cfg)
35127             case 'View': 
35128                 cfg.autoCreate = true;
35129                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35130                 //} else {
35131                 //    var el = this.el.createChild();
35132                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35133                 //}
35134                 
35135                 this.add(region, ret);
35136                 break;
35137             
35138             /*
35139             case 'TreePanel': // our new panel!
35140                 cfg.el = this.el.createChild();
35141                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35142                 this.add(region, ret);
35143                 break;
35144             */
35145             
35146             case 'Nest': 
35147                 // create a new Layout (which is  a Border Layout...
35148                 
35149                 var clayout = cfg.layout;
35150                 clayout.el  = this.el.createChild();
35151                 clayout.items   = clayout.items  || [];
35152                 
35153                 delete cfg.layout;
35154                 
35155                 // replace this exitems with the clayout ones..
35156                 xitems = clayout.items;
35157                  
35158                 // force background off if it's in center...
35159                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35160                     cfg.background = false;
35161                 }
35162                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35163                 
35164                 
35165                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35166                 //console.log('adding nested layout panel '  + cfg.toSource());
35167                 this.add(region, ret);
35168                 nb = {}; /// find first...
35169                 break;
35170             
35171             case 'Grid':
35172                 
35173                 // needs grid and region
35174                 
35175                 //var el = this.getRegion(region).el.createChild();
35176                 /*
35177                  *var el = this.el.createChild();
35178                 // create the grid first...
35179                 cfg.grid.container = el;
35180                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35181                 */
35182                 
35183                 if (region == 'center' && this.active ) {
35184                     cfg.background = false;
35185                 }
35186                 
35187                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35188                 
35189                 this.add(region, ret);
35190                 /*
35191                 if (cfg.background) {
35192                     // render grid on panel activation (if panel background)
35193                     ret.on('activate', function(gp) {
35194                         if (!gp.grid.rendered) {
35195                     //        gp.grid.render(el);
35196                         }
35197                     });
35198                 } else {
35199                   //  cfg.grid.render(el);
35200                 }
35201                 */
35202                 break;
35203            
35204            
35205             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35206                 // it was the old xcomponent building that caused this before.
35207                 // espeically if border is the top element in the tree.
35208                 ret = this;
35209                 break; 
35210                 
35211                     
35212                 
35213                 
35214                 
35215             default:
35216                 /*
35217                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35218                     
35219                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35220                     this.add(region, ret);
35221                 } else {
35222                 */
35223                     Roo.log(cfg);
35224                     throw "Can not add '" + cfg.xtype + "' to Border";
35225                     return null;
35226              
35227                                 
35228              
35229         }
35230         this.beginUpdate();
35231         // add children..
35232         var region = '';
35233         var abn = {};
35234         Roo.each(xitems, function(i)  {
35235             region = nb && i.region ? i.region : false;
35236             
35237             var add = ret.addxtype(i);
35238            
35239             if (region) {
35240                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35241                 if (!i.background) {
35242                     abn[region] = nb[region] ;
35243                 }
35244             }
35245             
35246         });
35247         this.endUpdate();
35248
35249         // make the last non-background panel active..
35250         //if (nb) { Roo.log(abn); }
35251         if (nb) {
35252             
35253             for(var r in abn) {
35254                 region = this.getRegion(r);
35255                 if (region) {
35256                     // tried using nb[r], but it does not work..
35257                      
35258                     region.showPanel(abn[r]);
35259                    
35260                 }
35261             }
35262         }
35263         return ret;
35264         
35265     },
35266     
35267     
35268 // private
35269     factory : function(cfg)
35270     {
35271         
35272         var validRegions = Roo.bootstrap.layout.Border.regions;
35273
35274         var target = cfg.region;
35275         cfg.mgr = this;
35276         
35277         var r = Roo.bootstrap.layout;
35278         Roo.log(target);
35279         switch(target){
35280             case "north":
35281                 return new r.North(cfg);
35282             case "south":
35283                 return new r.South(cfg);
35284             case "east":
35285                 return new r.East(cfg);
35286             case "west":
35287                 return new r.West(cfg);
35288             case "center":
35289                 return new r.Center(cfg);
35290         }
35291         throw 'Layout region "'+target+'" not supported.';
35292     }
35293     
35294     
35295 });
35296  /*
35297  * Based on:
35298  * Ext JS Library 1.1.1
35299  * Copyright(c) 2006-2007, Ext JS, LLC.
35300  *
35301  * Originally Released Under LGPL - original licence link has changed is not relivant.
35302  *
35303  * Fork - LGPL
35304  * <script type="text/javascript">
35305  */
35306  
35307 /**
35308  * @class Roo.bootstrap.layout.Basic
35309  * @extends Roo.util.Observable
35310  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35311  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35312  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35313  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35314  * @cfg {string}   region  the region that it inhabits..
35315  * @cfg {bool}   skipConfig skip config?
35316  * 
35317
35318  */
35319 Roo.bootstrap.layout.Basic = function(config){
35320     
35321     this.mgr = config.mgr;
35322     
35323     this.position = config.region;
35324     
35325     var skipConfig = config.skipConfig;
35326     
35327     this.events = {
35328         /**
35329          * @scope Roo.BasicLayoutRegion
35330          */
35331         
35332         /**
35333          * @event beforeremove
35334          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35335          * @param {Roo.LayoutRegion} this
35336          * @param {Roo.ContentPanel} panel The panel
35337          * @param {Object} e The cancel event object
35338          */
35339         "beforeremove" : true,
35340         /**
35341          * @event invalidated
35342          * Fires when the layout for this region is changed.
35343          * @param {Roo.LayoutRegion} this
35344          */
35345         "invalidated" : true,
35346         /**
35347          * @event visibilitychange
35348          * Fires when this region is shown or hidden 
35349          * @param {Roo.LayoutRegion} this
35350          * @param {Boolean} visibility true or false
35351          */
35352         "visibilitychange" : true,
35353         /**
35354          * @event paneladded
35355          * Fires when a panel is added. 
35356          * @param {Roo.LayoutRegion} this
35357          * @param {Roo.ContentPanel} panel The panel
35358          */
35359         "paneladded" : true,
35360         /**
35361          * @event panelremoved
35362          * Fires when a panel is removed. 
35363          * @param {Roo.LayoutRegion} this
35364          * @param {Roo.ContentPanel} panel The panel
35365          */
35366         "panelremoved" : true,
35367         /**
35368          * @event beforecollapse
35369          * Fires when this region before collapse.
35370          * @param {Roo.LayoutRegion} this
35371          */
35372         "beforecollapse" : true,
35373         /**
35374          * @event collapsed
35375          * Fires when this region is collapsed.
35376          * @param {Roo.LayoutRegion} this
35377          */
35378         "collapsed" : true,
35379         /**
35380          * @event expanded
35381          * Fires when this region is expanded.
35382          * @param {Roo.LayoutRegion} this
35383          */
35384         "expanded" : true,
35385         /**
35386          * @event slideshow
35387          * Fires when this region is slid into view.
35388          * @param {Roo.LayoutRegion} this
35389          */
35390         "slideshow" : true,
35391         /**
35392          * @event slidehide
35393          * Fires when this region slides out of view. 
35394          * @param {Roo.LayoutRegion} this
35395          */
35396         "slidehide" : true,
35397         /**
35398          * @event panelactivated
35399          * Fires when a panel is activated. 
35400          * @param {Roo.LayoutRegion} this
35401          * @param {Roo.ContentPanel} panel The activated panel
35402          */
35403         "panelactivated" : true,
35404         /**
35405          * @event resized
35406          * Fires when the user resizes this region. 
35407          * @param {Roo.LayoutRegion} this
35408          * @param {Number} newSize The new size (width for east/west, height for north/south)
35409          */
35410         "resized" : true
35411     };
35412     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35413     this.panels = new Roo.util.MixedCollection();
35414     this.panels.getKey = this.getPanelId.createDelegate(this);
35415     this.box = null;
35416     this.activePanel = null;
35417     // ensure listeners are added...
35418     
35419     if (config.listeners || config.events) {
35420         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35421             listeners : config.listeners || {},
35422             events : config.events || {}
35423         });
35424     }
35425     
35426     if(skipConfig !== true){
35427         this.applyConfig(config);
35428     }
35429 };
35430
35431 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35432 {
35433     getPanelId : function(p){
35434         return p.getId();
35435     },
35436     
35437     applyConfig : function(config){
35438         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35439         this.config = config;
35440         
35441     },
35442     
35443     /**
35444      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35445      * the width, for horizontal (north, south) the height.
35446      * @param {Number} newSize The new width or height
35447      */
35448     resizeTo : function(newSize){
35449         var el = this.el ? this.el :
35450                  (this.activePanel ? this.activePanel.getEl() : null);
35451         if(el){
35452             switch(this.position){
35453                 case "east":
35454                 case "west":
35455                     el.setWidth(newSize);
35456                     this.fireEvent("resized", this, newSize);
35457                 break;
35458                 case "north":
35459                 case "south":
35460                     el.setHeight(newSize);
35461                     this.fireEvent("resized", this, newSize);
35462                 break;                
35463             }
35464         }
35465     },
35466     
35467     getBox : function(){
35468         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35469     },
35470     
35471     getMargins : function(){
35472         return this.margins;
35473     },
35474     
35475     updateBox : function(box){
35476         this.box = box;
35477         var el = this.activePanel.getEl();
35478         el.dom.style.left = box.x + "px";
35479         el.dom.style.top = box.y + "px";
35480         this.activePanel.setSize(box.width, box.height);
35481     },
35482     
35483     /**
35484      * Returns the container element for this region.
35485      * @return {Roo.Element}
35486      */
35487     getEl : function(){
35488         return this.activePanel;
35489     },
35490     
35491     /**
35492      * Returns true if this region is currently visible.
35493      * @return {Boolean}
35494      */
35495     isVisible : function(){
35496         return this.activePanel ? true : false;
35497     },
35498     
35499     setActivePanel : function(panel){
35500         panel = this.getPanel(panel);
35501         if(this.activePanel && this.activePanel != panel){
35502             this.activePanel.setActiveState(false);
35503             this.activePanel.getEl().setLeftTop(-10000,-10000);
35504         }
35505         this.activePanel = panel;
35506         panel.setActiveState(true);
35507         if(this.box){
35508             panel.setSize(this.box.width, this.box.height);
35509         }
35510         this.fireEvent("panelactivated", this, panel);
35511         this.fireEvent("invalidated");
35512     },
35513     
35514     /**
35515      * Show the specified panel.
35516      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35517      * @return {Roo.ContentPanel} The shown panel or null
35518      */
35519     showPanel : function(panel){
35520         panel = this.getPanel(panel);
35521         if(panel){
35522             this.setActivePanel(panel);
35523         }
35524         return panel;
35525     },
35526     
35527     /**
35528      * Get the active panel for this region.
35529      * @return {Roo.ContentPanel} The active panel or null
35530      */
35531     getActivePanel : function(){
35532         return this.activePanel;
35533     },
35534     
35535     /**
35536      * Add the passed ContentPanel(s)
35537      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35538      * @return {Roo.ContentPanel} The panel added (if only one was added)
35539      */
35540     add : function(panel){
35541         if(arguments.length > 1){
35542             for(var i = 0, len = arguments.length; i < len; i++) {
35543                 this.add(arguments[i]);
35544             }
35545             return null;
35546         }
35547         if(this.hasPanel(panel)){
35548             this.showPanel(panel);
35549             return panel;
35550         }
35551         var el = panel.getEl();
35552         if(el.dom.parentNode != this.mgr.el.dom){
35553             this.mgr.el.dom.appendChild(el.dom);
35554         }
35555         if(panel.setRegion){
35556             panel.setRegion(this);
35557         }
35558         this.panels.add(panel);
35559         el.setStyle("position", "absolute");
35560         if(!panel.background){
35561             this.setActivePanel(panel);
35562             if(this.config.initialSize && this.panels.getCount()==1){
35563                 this.resizeTo(this.config.initialSize);
35564             }
35565         }
35566         this.fireEvent("paneladded", this, panel);
35567         return panel;
35568     },
35569     
35570     /**
35571      * Returns true if the panel is in this region.
35572      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35573      * @return {Boolean}
35574      */
35575     hasPanel : function(panel){
35576         if(typeof panel == "object"){ // must be panel obj
35577             panel = panel.getId();
35578         }
35579         return this.getPanel(panel) ? true : false;
35580     },
35581     
35582     /**
35583      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35584      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35585      * @param {Boolean} preservePanel Overrides the config preservePanel option
35586      * @return {Roo.ContentPanel} The panel that was removed
35587      */
35588     remove : function(panel, preservePanel){
35589         panel = this.getPanel(panel);
35590         if(!panel){
35591             return null;
35592         }
35593         var e = {};
35594         this.fireEvent("beforeremove", this, panel, e);
35595         if(e.cancel === true){
35596             return null;
35597         }
35598         var panelId = panel.getId();
35599         this.panels.removeKey(panelId);
35600         return panel;
35601     },
35602     
35603     /**
35604      * Returns the panel specified or null if it's not in this region.
35605      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35606      * @return {Roo.ContentPanel}
35607      */
35608     getPanel : function(id){
35609         if(typeof id == "object"){ // must be panel obj
35610             return id;
35611         }
35612         return this.panels.get(id);
35613     },
35614     
35615     /**
35616      * Returns this regions position (north/south/east/west/center).
35617      * @return {String} 
35618      */
35619     getPosition: function(){
35620         return this.position;    
35621     }
35622 });/*
35623  * Based on:
35624  * Ext JS Library 1.1.1
35625  * Copyright(c) 2006-2007, Ext JS, LLC.
35626  *
35627  * Originally Released Under LGPL - original licence link has changed is not relivant.
35628  *
35629  * Fork - LGPL
35630  * <script type="text/javascript">
35631  */
35632  
35633 /**
35634  * @class Roo.bootstrap.layout.Region
35635  * @extends Roo.bootstrap.layout.Basic
35636  * This class represents a region in a layout manager.
35637  
35638  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35639  * @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})
35640  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35641  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35642  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35643  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35644  * @cfg {String}    title           The title for the region (overrides panel titles)
35645  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35646  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35647  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35648  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35649  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35650  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35651  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35652  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35653  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35654  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35655
35656  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35657  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35658  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35659  * @cfg {Number}    width           For East/West panels
35660  * @cfg {Number}    height          For North/South panels
35661  * @cfg {Boolean}   split           To show the splitter
35662  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35663  * 
35664  * @cfg {string}   cls             Extra CSS classes to add to region
35665  * 
35666  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35667  * @cfg {string}   region  the region that it inhabits..
35668  *
35669
35670  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35671  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35672
35673  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35674  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35675  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35676  */
35677 Roo.bootstrap.layout.Region = function(config)
35678 {
35679     this.applyConfig(config);
35680
35681     var mgr = config.mgr;
35682     var pos = config.region;
35683     config.skipConfig = true;
35684     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35685     
35686     if (mgr.el) {
35687         this.onRender(mgr.el);   
35688     }
35689      
35690     this.visible = true;
35691     this.collapsed = false;
35692     this.unrendered_panels = [];
35693 };
35694
35695 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35696
35697     position: '', // set by wrapper (eg. north/south etc..)
35698     unrendered_panels : null,  // unrendered panels.
35699     createBody : function(){
35700         /** This region's body element 
35701         * @type Roo.Element */
35702         this.bodyEl = this.el.createChild({
35703                 tag: "div",
35704                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35705         });
35706     },
35707
35708     onRender: function(ctr, pos)
35709     {
35710         var dh = Roo.DomHelper;
35711         /** This region's container element 
35712         * @type Roo.Element */
35713         this.el = dh.append(ctr.dom, {
35714                 tag: "div",
35715                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35716             }, true);
35717         /** This region's title element 
35718         * @type Roo.Element */
35719     
35720         this.titleEl = dh.append(this.el.dom,
35721             {
35722                     tag: "div",
35723                     unselectable: "on",
35724                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35725                     children:[
35726                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35727                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35728                     ]}, true);
35729         
35730         this.titleEl.enableDisplayMode();
35731         /** This region's title text element 
35732         * @type HTMLElement */
35733         this.titleTextEl = this.titleEl.dom.firstChild;
35734         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35735         /*
35736         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35737         this.closeBtn.enableDisplayMode();
35738         this.closeBtn.on("click", this.closeClicked, this);
35739         this.closeBtn.hide();
35740     */
35741         this.createBody(this.config);
35742         if(this.config.hideWhenEmpty){
35743             this.hide();
35744             this.on("paneladded", this.validateVisibility, this);
35745             this.on("panelremoved", this.validateVisibility, this);
35746         }
35747         if(this.autoScroll){
35748             this.bodyEl.setStyle("overflow", "auto");
35749         }else{
35750             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35751         }
35752         //if(c.titlebar !== false){
35753             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35754                 this.titleEl.hide();
35755             }else{
35756                 this.titleEl.show();
35757                 if(this.config.title){
35758                     this.titleTextEl.innerHTML = this.config.title;
35759                 }
35760             }
35761         //}
35762         if(this.config.collapsed){
35763             this.collapse(true);
35764         }
35765         if(this.config.hidden){
35766             this.hide();
35767         }
35768         
35769         if (this.unrendered_panels && this.unrendered_panels.length) {
35770             for (var i =0;i< this.unrendered_panels.length; i++) {
35771                 this.add(this.unrendered_panels[i]);
35772             }
35773             this.unrendered_panels = null;
35774             
35775         }
35776         
35777     },
35778     
35779     applyConfig : function(c)
35780     {
35781         /*
35782          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35783             var dh = Roo.DomHelper;
35784             if(c.titlebar !== false){
35785                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35786                 this.collapseBtn.on("click", this.collapse, this);
35787                 this.collapseBtn.enableDisplayMode();
35788                 /*
35789                 if(c.showPin === true || this.showPin){
35790                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35791                     this.stickBtn.enableDisplayMode();
35792                     this.stickBtn.on("click", this.expand, this);
35793                     this.stickBtn.hide();
35794                 }
35795                 
35796             }
35797             */
35798             /** This region's collapsed element
35799             * @type Roo.Element */
35800             /*
35801              *
35802             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35803                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35804             ]}, true);
35805             
35806             if(c.floatable !== false){
35807                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35808                this.collapsedEl.on("click", this.collapseClick, this);
35809             }
35810
35811             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35812                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35813                    id: "message", unselectable: "on", style:{"float":"left"}});
35814                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35815              }
35816             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35817             this.expandBtn.on("click", this.expand, this);
35818             
35819         }
35820         
35821         if(this.collapseBtn){
35822             this.collapseBtn.setVisible(c.collapsible == true);
35823         }
35824         
35825         this.cmargins = c.cmargins || this.cmargins ||
35826                          (this.position == "west" || this.position == "east" ?
35827                              {top: 0, left: 2, right:2, bottom: 0} :
35828                              {top: 2, left: 0, right:0, bottom: 2});
35829         */
35830         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35831         
35832         
35833         this.bottomTabs = c.tabPosition != "top";
35834         
35835         this.autoScroll = c.autoScroll || false;
35836         
35837         
35838        
35839         
35840         this.duration = c.duration || .30;
35841         this.slideDuration = c.slideDuration || .45;
35842         this.config = c;
35843        
35844     },
35845     /**
35846      * Returns true if this region is currently visible.
35847      * @return {Boolean}
35848      */
35849     isVisible : function(){
35850         return this.visible;
35851     },
35852
35853     /**
35854      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35855      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35856      */
35857     //setCollapsedTitle : function(title){
35858     //    title = title || "&#160;";
35859      //   if(this.collapsedTitleTextEl){
35860       //      this.collapsedTitleTextEl.innerHTML = title;
35861        // }
35862     //},
35863
35864     getBox : function(){
35865         var b;
35866       //  if(!this.collapsed){
35867             b = this.el.getBox(false, true);
35868        // }else{
35869           //  b = this.collapsedEl.getBox(false, true);
35870         //}
35871         return b;
35872     },
35873
35874     getMargins : function(){
35875         return this.margins;
35876         //return this.collapsed ? this.cmargins : this.margins;
35877     },
35878 /*
35879     highlight : function(){
35880         this.el.addClass("x-layout-panel-dragover");
35881     },
35882
35883     unhighlight : function(){
35884         this.el.removeClass("x-layout-panel-dragover");
35885     },
35886 */
35887     updateBox : function(box)
35888     {
35889         if (!this.bodyEl) {
35890             return; // not rendered yet..
35891         }
35892         
35893         this.box = box;
35894         if(!this.collapsed){
35895             this.el.dom.style.left = box.x + "px";
35896             this.el.dom.style.top = box.y + "px";
35897             this.updateBody(box.width, box.height);
35898         }else{
35899             this.collapsedEl.dom.style.left = box.x + "px";
35900             this.collapsedEl.dom.style.top = box.y + "px";
35901             this.collapsedEl.setSize(box.width, box.height);
35902         }
35903         if(this.tabs){
35904             this.tabs.autoSizeTabs();
35905         }
35906     },
35907
35908     updateBody : function(w, h)
35909     {
35910         if(w !== null){
35911             this.el.setWidth(w);
35912             w -= this.el.getBorderWidth("rl");
35913             if(this.config.adjustments){
35914                 w += this.config.adjustments[0];
35915             }
35916         }
35917         if(h !== null && h > 0){
35918             this.el.setHeight(h);
35919             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35920             h -= this.el.getBorderWidth("tb");
35921             if(this.config.adjustments){
35922                 h += this.config.adjustments[1];
35923             }
35924             this.bodyEl.setHeight(h);
35925             if(this.tabs){
35926                 h = this.tabs.syncHeight(h);
35927             }
35928         }
35929         if(this.panelSize){
35930             w = w !== null ? w : this.panelSize.width;
35931             h = h !== null ? h : this.panelSize.height;
35932         }
35933         if(this.activePanel){
35934             var el = this.activePanel.getEl();
35935             w = w !== null ? w : el.getWidth();
35936             h = h !== null ? h : el.getHeight();
35937             this.panelSize = {width: w, height: h};
35938             this.activePanel.setSize(w, h);
35939         }
35940         if(Roo.isIE && this.tabs){
35941             this.tabs.el.repaint();
35942         }
35943     },
35944
35945     /**
35946      * Returns the container element for this region.
35947      * @return {Roo.Element}
35948      */
35949     getEl : function(){
35950         return this.el;
35951     },
35952
35953     /**
35954      * Hides this region.
35955      */
35956     hide : function(){
35957         //if(!this.collapsed){
35958             this.el.dom.style.left = "-2000px";
35959             this.el.hide();
35960         //}else{
35961          //   this.collapsedEl.dom.style.left = "-2000px";
35962          //   this.collapsedEl.hide();
35963        // }
35964         this.visible = false;
35965         this.fireEvent("visibilitychange", this, false);
35966     },
35967
35968     /**
35969      * Shows this region if it was previously hidden.
35970      */
35971     show : function(){
35972         //if(!this.collapsed){
35973             this.el.show();
35974         //}else{
35975         //    this.collapsedEl.show();
35976        // }
35977         this.visible = true;
35978         this.fireEvent("visibilitychange", this, true);
35979     },
35980 /*
35981     closeClicked : function(){
35982         if(this.activePanel){
35983             this.remove(this.activePanel);
35984         }
35985     },
35986
35987     collapseClick : function(e){
35988         if(this.isSlid){
35989            e.stopPropagation();
35990            this.slideIn();
35991         }else{
35992            e.stopPropagation();
35993            this.slideOut();
35994         }
35995     },
35996 */
35997     /**
35998      * Collapses this region.
35999      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36000      */
36001     /*
36002     collapse : function(skipAnim, skipCheck = false){
36003         if(this.collapsed) {
36004             return;
36005         }
36006         
36007         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36008             
36009             this.collapsed = true;
36010             if(this.split){
36011                 this.split.el.hide();
36012             }
36013             if(this.config.animate && skipAnim !== true){
36014                 this.fireEvent("invalidated", this);
36015                 this.animateCollapse();
36016             }else{
36017                 this.el.setLocation(-20000,-20000);
36018                 this.el.hide();
36019                 this.collapsedEl.show();
36020                 this.fireEvent("collapsed", this);
36021                 this.fireEvent("invalidated", this);
36022             }
36023         }
36024         
36025     },
36026 */
36027     animateCollapse : function(){
36028         // overridden
36029     },
36030
36031     /**
36032      * Expands this region if it was previously collapsed.
36033      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36034      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36035      */
36036     /*
36037     expand : function(e, skipAnim){
36038         if(e) {
36039             e.stopPropagation();
36040         }
36041         if(!this.collapsed || this.el.hasActiveFx()) {
36042             return;
36043         }
36044         if(this.isSlid){
36045             this.afterSlideIn();
36046             skipAnim = true;
36047         }
36048         this.collapsed = false;
36049         if(this.config.animate && skipAnim !== true){
36050             this.animateExpand();
36051         }else{
36052             this.el.show();
36053             if(this.split){
36054                 this.split.el.show();
36055             }
36056             this.collapsedEl.setLocation(-2000,-2000);
36057             this.collapsedEl.hide();
36058             this.fireEvent("invalidated", this);
36059             this.fireEvent("expanded", this);
36060         }
36061     },
36062 */
36063     animateExpand : function(){
36064         // overridden
36065     },
36066
36067     initTabs : function()
36068     {
36069         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36070         
36071         var ts = new Roo.bootstrap.panel.Tabs({
36072                 el: this.bodyEl.dom,
36073                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36074                 disableTooltips: this.config.disableTabTips,
36075                 toolbar : this.config.toolbar
36076             });
36077         
36078         if(this.config.hideTabs){
36079             ts.stripWrap.setDisplayed(false);
36080         }
36081         this.tabs = ts;
36082         ts.resizeTabs = this.config.resizeTabs === true;
36083         ts.minTabWidth = this.config.minTabWidth || 40;
36084         ts.maxTabWidth = this.config.maxTabWidth || 250;
36085         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36086         ts.monitorResize = false;
36087         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36088         ts.bodyEl.addClass('roo-layout-tabs-body');
36089         this.panels.each(this.initPanelAsTab, this);
36090     },
36091
36092     initPanelAsTab : function(panel){
36093         var ti = this.tabs.addTab(
36094             panel.getEl().id,
36095             panel.getTitle(),
36096             null,
36097             this.config.closeOnTab && panel.isClosable(),
36098             panel.tpl
36099         );
36100         if(panel.tabTip !== undefined){
36101             ti.setTooltip(panel.tabTip);
36102         }
36103         ti.on("activate", function(){
36104               this.setActivePanel(panel);
36105         }, this);
36106         
36107         if(this.config.closeOnTab){
36108             ti.on("beforeclose", function(t, e){
36109                 e.cancel = true;
36110                 this.remove(panel);
36111             }, this);
36112         }
36113         
36114         panel.tabItem = ti;
36115         
36116         return ti;
36117     },
36118
36119     updatePanelTitle : function(panel, title)
36120     {
36121         if(this.activePanel == panel){
36122             this.updateTitle(title);
36123         }
36124         if(this.tabs){
36125             var ti = this.tabs.getTab(panel.getEl().id);
36126             ti.setText(title);
36127             if(panel.tabTip !== undefined){
36128                 ti.setTooltip(panel.tabTip);
36129             }
36130         }
36131     },
36132
36133     updateTitle : function(title){
36134         if(this.titleTextEl && !this.config.title){
36135             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36136         }
36137     },
36138
36139     setActivePanel : function(panel)
36140     {
36141         panel = this.getPanel(panel);
36142         if(this.activePanel && this.activePanel != panel){
36143             if(this.activePanel.setActiveState(false) === false){
36144                 return;
36145             }
36146         }
36147         this.activePanel = panel;
36148         panel.setActiveState(true);
36149         if(this.panelSize){
36150             panel.setSize(this.panelSize.width, this.panelSize.height);
36151         }
36152         if(this.closeBtn){
36153             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36154         }
36155         this.updateTitle(panel.getTitle());
36156         if(this.tabs){
36157             this.fireEvent("invalidated", this);
36158         }
36159         this.fireEvent("panelactivated", this, panel);
36160     },
36161
36162     /**
36163      * Shows the specified panel.
36164      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36165      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36166      */
36167     showPanel : function(panel)
36168     {
36169         panel = this.getPanel(panel);
36170         if(panel){
36171             if(this.tabs){
36172                 var tab = this.tabs.getTab(panel.getEl().id);
36173                 if(tab.isHidden()){
36174                     this.tabs.unhideTab(tab.id);
36175                 }
36176                 tab.activate();
36177             }else{
36178                 this.setActivePanel(panel);
36179             }
36180         }
36181         return panel;
36182     },
36183
36184     /**
36185      * Get the active panel for this region.
36186      * @return {Roo.ContentPanel} The active panel or null
36187      */
36188     getActivePanel : function(){
36189         return this.activePanel;
36190     },
36191
36192     validateVisibility : function(){
36193         if(this.panels.getCount() < 1){
36194             this.updateTitle("&#160;");
36195             this.closeBtn.hide();
36196             this.hide();
36197         }else{
36198             if(!this.isVisible()){
36199                 this.show();
36200             }
36201         }
36202     },
36203
36204     /**
36205      * Adds the passed ContentPanel(s) to this region.
36206      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36207      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36208      */
36209     add : function(panel)
36210     {
36211         if(arguments.length > 1){
36212             for(var i = 0, len = arguments.length; i < len; i++) {
36213                 this.add(arguments[i]);
36214             }
36215             return null;
36216         }
36217         
36218         // if we have not been rendered yet, then we can not really do much of this..
36219         if (!this.bodyEl) {
36220             this.unrendered_panels.push(panel);
36221             return panel;
36222         }
36223         
36224         
36225         
36226         
36227         if(this.hasPanel(panel)){
36228             this.showPanel(panel);
36229             return panel;
36230         }
36231         panel.setRegion(this);
36232         this.panels.add(panel);
36233        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36234             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36235             // and hide them... ???
36236             this.bodyEl.dom.appendChild(panel.getEl().dom);
36237             if(panel.background !== true){
36238                 this.setActivePanel(panel);
36239             }
36240             this.fireEvent("paneladded", this, panel);
36241             return panel;
36242         }
36243         */
36244         if(!this.tabs){
36245             this.initTabs();
36246         }else{
36247             this.initPanelAsTab(panel);
36248         }
36249         
36250         
36251         if(panel.background !== true){
36252             this.tabs.activate(panel.getEl().id);
36253         }
36254         this.fireEvent("paneladded", this, panel);
36255         return panel;
36256     },
36257
36258     /**
36259      * Hides the tab for the specified panel.
36260      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36261      */
36262     hidePanel : function(panel){
36263         if(this.tabs && (panel = this.getPanel(panel))){
36264             this.tabs.hideTab(panel.getEl().id);
36265         }
36266     },
36267
36268     /**
36269      * Unhides the tab for a previously hidden panel.
36270      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36271      */
36272     unhidePanel : function(panel){
36273         if(this.tabs && (panel = this.getPanel(panel))){
36274             this.tabs.unhideTab(panel.getEl().id);
36275         }
36276     },
36277
36278     clearPanels : function(){
36279         while(this.panels.getCount() > 0){
36280              this.remove(this.panels.first());
36281         }
36282     },
36283
36284     /**
36285      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36286      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36287      * @param {Boolean} preservePanel Overrides the config preservePanel option
36288      * @return {Roo.ContentPanel} The panel that was removed
36289      */
36290     remove : function(panel, preservePanel)
36291     {
36292         panel = this.getPanel(panel);
36293         if(!panel){
36294             return null;
36295         }
36296         var e = {};
36297         this.fireEvent("beforeremove", this, panel, e);
36298         if(e.cancel === true){
36299             return null;
36300         }
36301         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36302         var panelId = panel.getId();
36303         this.panels.removeKey(panelId);
36304         if(preservePanel){
36305             document.body.appendChild(panel.getEl().dom);
36306         }
36307         if(this.tabs){
36308             this.tabs.removeTab(panel.getEl().id);
36309         }else if (!preservePanel){
36310             this.bodyEl.dom.removeChild(panel.getEl().dom);
36311         }
36312         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36313             var p = this.panels.first();
36314             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36315             tempEl.appendChild(p.getEl().dom);
36316             this.bodyEl.update("");
36317             this.bodyEl.dom.appendChild(p.getEl().dom);
36318             tempEl = null;
36319             this.updateTitle(p.getTitle());
36320             this.tabs = null;
36321             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36322             this.setActivePanel(p);
36323         }
36324         panel.setRegion(null);
36325         if(this.activePanel == panel){
36326             this.activePanel = null;
36327         }
36328         if(this.config.autoDestroy !== false && preservePanel !== true){
36329             try{panel.destroy();}catch(e){}
36330         }
36331         this.fireEvent("panelremoved", this, panel);
36332         return panel;
36333     },
36334
36335     /**
36336      * Returns the TabPanel component used by this region
36337      * @return {Roo.TabPanel}
36338      */
36339     getTabs : function(){
36340         return this.tabs;
36341     },
36342
36343     createTool : function(parentEl, className){
36344         var btn = Roo.DomHelper.append(parentEl, {
36345             tag: "div",
36346             cls: "x-layout-tools-button",
36347             children: [ {
36348                 tag: "div",
36349                 cls: "roo-layout-tools-button-inner " + className,
36350                 html: "&#160;"
36351             }]
36352         }, true);
36353         btn.addClassOnOver("roo-layout-tools-button-over");
36354         return btn;
36355     }
36356 });/*
36357  * Based on:
36358  * Ext JS Library 1.1.1
36359  * Copyright(c) 2006-2007, Ext JS, LLC.
36360  *
36361  * Originally Released Under LGPL - original licence link has changed is not relivant.
36362  *
36363  * Fork - LGPL
36364  * <script type="text/javascript">
36365  */
36366  
36367
36368
36369 /**
36370  * @class Roo.SplitLayoutRegion
36371  * @extends Roo.LayoutRegion
36372  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36373  */
36374 Roo.bootstrap.layout.Split = function(config){
36375     this.cursor = config.cursor;
36376     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36377 };
36378
36379 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36380 {
36381     splitTip : "Drag to resize.",
36382     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36383     useSplitTips : false,
36384
36385     applyConfig : function(config){
36386         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36387     },
36388     
36389     onRender : function(ctr,pos) {
36390         
36391         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36392         if(!this.config.split){
36393             return;
36394         }
36395         if(!this.split){
36396             
36397             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36398                             tag: "div",
36399                             id: this.el.id + "-split",
36400                             cls: "roo-layout-split roo-layout-split-"+this.position,
36401                             html: "&#160;"
36402             });
36403             /** The SplitBar for this region 
36404             * @type Roo.SplitBar */
36405             // does not exist yet...
36406             Roo.log([this.position, this.orientation]);
36407             
36408             this.split = new Roo.bootstrap.SplitBar({
36409                 dragElement : splitEl,
36410                 resizingElement: this.el,
36411                 orientation : this.orientation
36412             });
36413             
36414             this.split.on("moved", this.onSplitMove, this);
36415             this.split.useShim = this.config.useShim === true;
36416             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36417             if(this.useSplitTips){
36418                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36419             }
36420             //if(config.collapsible){
36421             //    this.split.el.on("dblclick", this.collapse,  this);
36422             //}
36423         }
36424         if(typeof this.config.minSize != "undefined"){
36425             this.split.minSize = this.config.minSize;
36426         }
36427         if(typeof this.config.maxSize != "undefined"){
36428             this.split.maxSize = this.config.maxSize;
36429         }
36430         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36431             this.hideSplitter();
36432         }
36433         
36434     },
36435
36436     getHMaxSize : function(){
36437          var cmax = this.config.maxSize || 10000;
36438          var center = this.mgr.getRegion("center");
36439          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36440     },
36441
36442     getVMaxSize : function(){
36443          var cmax = this.config.maxSize || 10000;
36444          var center = this.mgr.getRegion("center");
36445          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36446     },
36447
36448     onSplitMove : function(split, newSize){
36449         this.fireEvent("resized", this, newSize);
36450     },
36451     
36452     /** 
36453      * Returns the {@link Roo.SplitBar} for this region.
36454      * @return {Roo.SplitBar}
36455      */
36456     getSplitBar : function(){
36457         return this.split;
36458     },
36459     
36460     hide : function(){
36461         this.hideSplitter();
36462         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36463     },
36464
36465     hideSplitter : function(){
36466         if(this.split){
36467             this.split.el.setLocation(-2000,-2000);
36468             this.split.el.hide();
36469         }
36470     },
36471
36472     show : function(){
36473         if(this.split){
36474             this.split.el.show();
36475         }
36476         Roo.bootstrap.layout.Split.superclass.show.call(this);
36477     },
36478     
36479     beforeSlide: function(){
36480         if(Roo.isGecko){// firefox overflow auto bug workaround
36481             this.bodyEl.clip();
36482             if(this.tabs) {
36483                 this.tabs.bodyEl.clip();
36484             }
36485             if(this.activePanel){
36486                 this.activePanel.getEl().clip();
36487                 
36488                 if(this.activePanel.beforeSlide){
36489                     this.activePanel.beforeSlide();
36490                 }
36491             }
36492         }
36493     },
36494     
36495     afterSlide : function(){
36496         if(Roo.isGecko){// firefox overflow auto bug workaround
36497             this.bodyEl.unclip();
36498             if(this.tabs) {
36499                 this.tabs.bodyEl.unclip();
36500             }
36501             if(this.activePanel){
36502                 this.activePanel.getEl().unclip();
36503                 if(this.activePanel.afterSlide){
36504                     this.activePanel.afterSlide();
36505                 }
36506             }
36507         }
36508     },
36509
36510     initAutoHide : function(){
36511         if(this.autoHide !== false){
36512             if(!this.autoHideHd){
36513                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36514                 this.autoHideHd = {
36515                     "mouseout": function(e){
36516                         if(!e.within(this.el, true)){
36517                             st.delay(500);
36518                         }
36519                     },
36520                     "mouseover" : function(e){
36521                         st.cancel();
36522                     },
36523                     scope : this
36524                 };
36525             }
36526             this.el.on(this.autoHideHd);
36527         }
36528     },
36529
36530     clearAutoHide : function(){
36531         if(this.autoHide !== false){
36532             this.el.un("mouseout", this.autoHideHd.mouseout);
36533             this.el.un("mouseover", this.autoHideHd.mouseover);
36534         }
36535     },
36536
36537     clearMonitor : function(){
36538         Roo.get(document).un("click", this.slideInIf, this);
36539     },
36540
36541     // these names are backwards but not changed for compat
36542     slideOut : function(){
36543         if(this.isSlid || this.el.hasActiveFx()){
36544             return;
36545         }
36546         this.isSlid = true;
36547         if(this.collapseBtn){
36548             this.collapseBtn.hide();
36549         }
36550         this.closeBtnState = this.closeBtn.getStyle('display');
36551         this.closeBtn.hide();
36552         if(this.stickBtn){
36553             this.stickBtn.show();
36554         }
36555         this.el.show();
36556         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36557         this.beforeSlide();
36558         this.el.setStyle("z-index", 10001);
36559         this.el.slideIn(this.getSlideAnchor(), {
36560             callback: function(){
36561                 this.afterSlide();
36562                 this.initAutoHide();
36563                 Roo.get(document).on("click", this.slideInIf, this);
36564                 this.fireEvent("slideshow", this);
36565             },
36566             scope: this,
36567             block: true
36568         });
36569     },
36570
36571     afterSlideIn : function(){
36572         this.clearAutoHide();
36573         this.isSlid = false;
36574         this.clearMonitor();
36575         this.el.setStyle("z-index", "");
36576         if(this.collapseBtn){
36577             this.collapseBtn.show();
36578         }
36579         this.closeBtn.setStyle('display', this.closeBtnState);
36580         if(this.stickBtn){
36581             this.stickBtn.hide();
36582         }
36583         this.fireEvent("slidehide", this);
36584     },
36585
36586     slideIn : function(cb){
36587         if(!this.isSlid || this.el.hasActiveFx()){
36588             Roo.callback(cb);
36589             return;
36590         }
36591         this.isSlid = false;
36592         this.beforeSlide();
36593         this.el.slideOut(this.getSlideAnchor(), {
36594             callback: function(){
36595                 this.el.setLeftTop(-10000, -10000);
36596                 this.afterSlide();
36597                 this.afterSlideIn();
36598                 Roo.callback(cb);
36599             },
36600             scope: this,
36601             block: true
36602         });
36603     },
36604     
36605     slideInIf : function(e){
36606         if(!e.within(this.el)){
36607             this.slideIn();
36608         }
36609     },
36610
36611     animateCollapse : function(){
36612         this.beforeSlide();
36613         this.el.setStyle("z-index", 20000);
36614         var anchor = this.getSlideAnchor();
36615         this.el.slideOut(anchor, {
36616             callback : function(){
36617                 this.el.setStyle("z-index", "");
36618                 this.collapsedEl.slideIn(anchor, {duration:.3});
36619                 this.afterSlide();
36620                 this.el.setLocation(-10000,-10000);
36621                 this.el.hide();
36622                 this.fireEvent("collapsed", this);
36623             },
36624             scope: this,
36625             block: true
36626         });
36627     },
36628
36629     animateExpand : function(){
36630         this.beforeSlide();
36631         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36632         this.el.setStyle("z-index", 20000);
36633         this.collapsedEl.hide({
36634             duration:.1
36635         });
36636         this.el.slideIn(this.getSlideAnchor(), {
36637             callback : function(){
36638                 this.el.setStyle("z-index", "");
36639                 this.afterSlide();
36640                 if(this.split){
36641                     this.split.el.show();
36642                 }
36643                 this.fireEvent("invalidated", this);
36644                 this.fireEvent("expanded", this);
36645             },
36646             scope: this,
36647             block: true
36648         });
36649     },
36650
36651     anchors : {
36652         "west" : "left",
36653         "east" : "right",
36654         "north" : "top",
36655         "south" : "bottom"
36656     },
36657
36658     sanchors : {
36659         "west" : "l",
36660         "east" : "r",
36661         "north" : "t",
36662         "south" : "b"
36663     },
36664
36665     canchors : {
36666         "west" : "tl-tr",
36667         "east" : "tr-tl",
36668         "north" : "tl-bl",
36669         "south" : "bl-tl"
36670     },
36671
36672     getAnchor : function(){
36673         return this.anchors[this.position];
36674     },
36675
36676     getCollapseAnchor : function(){
36677         return this.canchors[this.position];
36678     },
36679
36680     getSlideAnchor : function(){
36681         return this.sanchors[this.position];
36682     },
36683
36684     getAlignAdj : function(){
36685         var cm = this.cmargins;
36686         switch(this.position){
36687             case "west":
36688                 return [0, 0];
36689             break;
36690             case "east":
36691                 return [0, 0];
36692             break;
36693             case "north":
36694                 return [0, 0];
36695             break;
36696             case "south":
36697                 return [0, 0];
36698             break;
36699         }
36700     },
36701
36702     getExpandAdj : function(){
36703         var c = this.collapsedEl, cm = this.cmargins;
36704         switch(this.position){
36705             case "west":
36706                 return [-(cm.right+c.getWidth()+cm.left), 0];
36707             break;
36708             case "east":
36709                 return [cm.right+c.getWidth()+cm.left, 0];
36710             break;
36711             case "north":
36712                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36713             break;
36714             case "south":
36715                 return [0, cm.top+cm.bottom+c.getHeight()];
36716             break;
36717         }
36718     }
36719 });/*
36720  * Based on:
36721  * Ext JS Library 1.1.1
36722  * Copyright(c) 2006-2007, Ext JS, LLC.
36723  *
36724  * Originally Released Under LGPL - original licence link has changed is not relivant.
36725  *
36726  * Fork - LGPL
36727  * <script type="text/javascript">
36728  */
36729 /*
36730  * These classes are private internal classes
36731  */
36732 Roo.bootstrap.layout.Center = function(config){
36733     config.region = "center";
36734     Roo.bootstrap.layout.Region.call(this, config);
36735     this.visible = true;
36736     this.minWidth = config.minWidth || 20;
36737     this.minHeight = config.minHeight || 20;
36738 };
36739
36740 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36741     hide : function(){
36742         // center panel can't be hidden
36743     },
36744     
36745     show : function(){
36746         // center panel can't be hidden
36747     },
36748     
36749     getMinWidth: function(){
36750         return this.minWidth;
36751     },
36752     
36753     getMinHeight: function(){
36754         return this.minHeight;
36755     }
36756 });
36757
36758
36759
36760
36761  
36762
36763
36764
36765
36766
36767 Roo.bootstrap.layout.North = function(config)
36768 {
36769     config.region = 'north';
36770     config.cursor = 'n-resize';
36771     
36772     Roo.bootstrap.layout.Split.call(this, config);
36773     
36774     
36775     if(this.split){
36776         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36777         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36778         this.split.el.addClass("roo-layout-split-v");
36779     }
36780     var size = config.initialSize || config.height;
36781     if(typeof size != "undefined"){
36782         this.el.setHeight(size);
36783     }
36784 };
36785 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36786 {
36787     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36788     
36789     
36790     
36791     getBox : function(){
36792         if(this.collapsed){
36793             return this.collapsedEl.getBox();
36794         }
36795         var box = this.el.getBox();
36796         if(this.split){
36797             box.height += this.split.el.getHeight();
36798         }
36799         return box;
36800     },
36801     
36802     updateBox : function(box){
36803         if(this.split && !this.collapsed){
36804             box.height -= this.split.el.getHeight();
36805             this.split.el.setLeft(box.x);
36806             this.split.el.setTop(box.y+box.height);
36807             this.split.el.setWidth(box.width);
36808         }
36809         if(this.collapsed){
36810             this.updateBody(box.width, null);
36811         }
36812         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36813     }
36814 });
36815
36816
36817
36818
36819
36820 Roo.bootstrap.layout.South = function(config){
36821     config.region = 'south';
36822     config.cursor = 's-resize';
36823     Roo.bootstrap.layout.Split.call(this, config);
36824     if(this.split){
36825         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36826         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36827         this.split.el.addClass("roo-layout-split-v");
36828     }
36829     var size = config.initialSize || config.height;
36830     if(typeof size != "undefined"){
36831         this.el.setHeight(size);
36832     }
36833 };
36834
36835 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36836     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36837     getBox : function(){
36838         if(this.collapsed){
36839             return this.collapsedEl.getBox();
36840         }
36841         var box = this.el.getBox();
36842         if(this.split){
36843             var sh = this.split.el.getHeight();
36844             box.height += sh;
36845             box.y -= sh;
36846         }
36847         return box;
36848     },
36849     
36850     updateBox : function(box){
36851         if(this.split && !this.collapsed){
36852             var sh = this.split.el.getHeight();
36853             box.height -= sh;
36854             box.y += sh;
36855             this.split.el.setLeft(box.x);
36856             this.split.el.setTop(box.y-sh);
36857             this.split.el.setWidth(box.width);
36858         }
36859         if(this.collapsed){
36860             this.updateBody(box.width, null);
36861         }
36862         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36863     }
36864 });
36865
36866 Roo.bootstrap.layout.East = function(config){
36867     config.region = "east";
36868     config.cursor = "e-resize";
36869     Roo.bootstrap.layout.Split.call(this, config);
36870     if(this.split){
36871         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36872         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36873         this.split.el.addClass("roo-layout-split-h");
36874     }
36875     var size = config.initialSize || config.width;
36876     if(typeof size != "undefined"){
36877         this.el.setWidth(size);
36878     }
36879 };
36880 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36881     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36882     getBox : function(){
36883         if(this.collapsed){
36884             return this.collapsedEl.getBox();
36885         }
36886         var box = this.el.getBox();
36887         if(this.split){
36888             var sw = this.split.el.getWidth();
36889             box.width += sw;
36890             box.x -= sw;
36891         }
36892         return box;
36893     },
36894
36895     updateBox : function(box){
36896         if(this.split && !this.collapsed){
36897             var sw = this.split.el.getWidth();
36898             box.width -= sw;
36899             this.split.el.setLeft(box.x);
36900             this.split.el.setTop(box.y);
36901             this.split.el.setHeight(box.height);
36902             box.x += sw;
36903         }
36904         if(this.collapsed){
36905             this.updateBody(null, box.height);
36906         }
36907         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36908     }
36909 });
36910
36911 Roo.bootstrap.layout.West = function(config){
36912     config.region = "west";
36913     config.cursor = "w-resize";
36914     
36915     Roo.bootstrap.layout.Split.call(this, config);
36916     if(this.split){
36917         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36918         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36919         this.split.el.addClass("roo-layout-split-h");
36920     }
36921     
36922 };
36923 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36924     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36925     
36926     onRender: function(ctr, pos)
36927     {
36928         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36929         var size = this.config.initialSize || this.config.width;
36930         if(typeof size != "undefined"){
36931             this.el.setWidth(size);
36932         }
36933     },
36934     
36935     getBox : function(){
36936         if(this.collapsed){
36937             return this.collapsedEl.getBox();
36938         }
36939         var box = this.el.getBox();
36940         if(this.split){
36941             box.width += this.split.el.getWidth();
36942         }
36943         return box;
36944     },
36945     
36946     updateBox : function(box){
36947         if(this.split && !this.collapsed){
36948             var sw = this.split.el.getWidth();
36949             box.width -= sw;
36950             this.split.el.setLeft(box.x+box.width);
36951             this.split.el.setTop(box.y);
36952             this.split.el.setHeight(box.height);
36953         }
36954         if(this.collapsed){
36955             this.updateBody(null, box.height);
36956         }
36957         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36958     }
36959 });
36960 Roo.namespace("Roo.bootstrap.panel");/*
36961  * Based on:
36962  * Ext JS Library 1.1.1
36963  * Copyright(c) 2006-2007, Ext JS, LLC.
36964  *
36965  * Originally Released Under LGPL - original licence link has changed is not relivant.
36966  *
36967  * Fork - LGPL
36968  * <script type="text/javascript">
36969  */
36970 /**
36971  * @class Roo.ContentPanel
36972  * @extends Roo.util.Observable
36973  * A basic ContentPanel element.
36974  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36975  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36976  * @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
36977  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36978  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36979  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36980  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36981  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36982  * @cfg {String} title          The title for this panel
36983  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36984  * @cfg {String} url            Calls {@link #setUrl} with this value
36985  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36986  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36987  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36988  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36989  * @cfg {Boolean} badges render the badges
36990
36991  * @constructor
36992  * Create a new ContentPanel.
36993  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36994  * @param {String/Object} config A string to set only the title or a config object
36995  * @param {String} content (optional) Set the HTML content for this panel
36996  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36997  */
36998 Roo.bootstrap.panel.Content = function( config){
36999     
37000     this.tpl = config.tpl || false;
37001     
37002     var el = config.el;
37003     var content = config.content;
37004
37005     if(config.autoCreate){ // xtype is available if this is called from factory
37006         el = Roo.id();
37007     }
37008     this.el = Roo.get(el);
37009     if(!this.el && config && config.autoCreate){
37010         if(typeof config.autoCreate == "object"){
37011             if(!config.autoCreate.id){
37012                 config.autoCreate.id = config.id||el;
37013             }
37014             this.el = Roo.DomHelper.append(document.body,
37015                         config.autoCreate, true);
37016         }else{
37017             var elcfg =  {   tag: "div",
37018                             cls: "roo-layout-inactive-content",
37019                             id: config.id||el
37020                             };
37021             if (config.html) {
37022                 elcfg.html = config.html;
37023                 
37024             }
37025                         
37026             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37027         }
37028     } 
37029     this.closable = false;
37030     this.loaded = false;
37031     this.active = false;
37032    
37033       
37034     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37035         
37036         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37037         
37038         this.wrapEl = this.el; //this.el.wrap();
37039         var ti = [];
37040         if (config.toolbar.items) {
37041             ti = config.toolbar.items ;
37042             delete config.toolbar.items ;
37043         }
37044         
37045         var nitems = [];
37046         this.toolbar.render(this.wrapEl, 'before');
37047         for(var i =0;i < ti.length;i++) {
37048           //  Roo.log(['add child', items[i]]);
37049             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37050         }
37051         this.toolbar.items = nitems;
37052         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37053         delete config.toolbar;
37054         
37055     }
37056     /*
37057     // xtype created footer. - not sure if will work as we normally have to render first..
37058     if (this.footer && !this.footer.el && this.footer.xtype) {
37059         if (!this.wrapEl) {
37060             this.wrapEl = this.el.wrap();
37061         }
37062     
37063         this.footer.container = this.wrapEl.createChild();
37064          
37065         this.footer = Roo.factory(this.footer, Roo);
37066         
37067     }
37068     */
37069     
37070      if(typeof config == "string"){
37071         this.title = config;
37072     }else{
37073         Roo.apply(this, config);
37074     }
37075     
37076     if(this.resizeEl){
37077         this.resizeEl = Roo.get(this.resizeEl, true);
37078     }else{
37079         this.resizeEl = this.el;
37080     }
37081     // handle view.xtype
37082     
37083  
37084     
37085     
37086     this.addEvents({
37087         /**
37088          * @event activate
37089          * Fires when this panel is activated. 
37090          * @param {Roo.ContentPanel} this
37091          */
37092         "activate" : true,
37093         /**
37094          * @event deactivate
37095          * Fires when this panel is activated. 
37096          * @param {Roo.ContentPanel} this
37097          */
37098         "deactivate" : true,
37099
37100         /**
37101          * @event resize
37102          * Fires when this panel is resized if fitToFrame is true.
37103          * @param {Roo.ContentPanel} this
37104          * @param {Number} width The width after any component adjustments
37105          * @param {Number} height The height after any component adjustments
37106          */
37107         "resize" : true,
37108         
37109          /**
37110          * @event render
37111          * Fires when this tab is created
37112          * @param {Roo.ContentPanel} this
37113          */
37114         "render" : true
37115         
37116         
37117         
37118     });
37119     
37120
37121     
37122     
37123     if(this.autoScroll){
37124         this.resizeEl.setStyle("overflow", "auto");
37125     } else {
37126         // fix randome scrolling
37127         //this.el.on('scroll', function() {
37128         //    Roo.log('fix random scolling');
37129         //    this.scrollTo('top',0); 
37130         //});
37131     }
37132     content = content || this.content;
37133     if(content){
37134         this.setContent(content);
37135     }
37136     if(config && config.url){
37137         this.setUrl(this.url, this.params, this.loadOnce);
37138     }
37139     
37140     
37141     
37142     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37143     
37144     if (this.view && typeof(this.view.xtype) != 'undefined') {
37145         this.view.el = this.el.appendChild(document.createElement("div"));
37146         this.view = Roo.factory(this.view); 
37147         this.view.render  &&  this.view.render(false, '');  
37148     }
37149     
37150     
37151     this.fireEvent('render', this);
37152 };
37153
37154 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37155     
37156     tabTip : '',
37157     
37158     setRegion : function(region){
37159         this.region = region;
37160         this.setActiveClass(region && !this.background);
37161     },
37162     
37163     
37164     setActiveClass: function(state)
37165     {
37166         if(state){
37167            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37168            this.el.setStyle('position','relative');
37169         }else{
37170            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37171            this.el.setStyle('position', 'absolute');
37172         } 
37173     },
37174     
37175     /**
37176      * Returns the toolbar for this Panel if one was configured. 
37177      * @return {Roo.Toolbar} 
37178      */
37179     getToolbar : function(){
37180         return this.toolbar;
37181     },
37182     
37183     setActiveState : function(active)
37184     {
37185         this.active = active;
37186         this.setActiveClass(active);
37187         if(!active){
37188             if(this.fireEvent("deactivate", this) === false){
37189                 return false;
37190             }
37191             return true;
37192         }
37193         this.fireEvent("activate", this);
37194         return true;
37195     },
37196     /**
37197      * Updates this panel's element
37198      * @param {String} content The new content
37199      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37200     */
37201     setContent : function(content, loadScripts){
37202         this.el.update(content, loadScripts);
37203     },
37204
37205     ignoreResize : function(w, h){
37206         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37207             return true;
37208         }else{
37209             this.lastSize = {width: w, height: h};
37210             return false;
37211         }
37212     },
37213     /**
37214      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37215      * @return {Roo.UpdateManager} The UpdateManager
37216      */
37217     getUpdateManager : function(){
37218         return this.el.getUpdateManager();
37219     },
37220      /**
37221      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37222      * @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:
37223 <pre><code>
37224 panel.load({
37225     url: "your-url.php",
37226     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37227     callback: yourFunction,
37228     scope: yourObject, //(optional scope)
37229     discardUrl: false,
37230     nocache: false,
37231     text: "Loading...",
37232     timeout: 30,
37233     scripts: false
37234 });
37235 </code></pre>
37236      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37237      * 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.
37238      * @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}
37239      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37240      * @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.
37241      * @return {Roo.ContentPanel} this
37242      */
37243     load : function(){
37244         var um = this.el.getUpdateManager();
37245         um.update.apply(um, arguments);
37246         return this;
37247     },
37248
37249
37250     /**
37251      * 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.
37252      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37253      * @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)
37254      * @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)
37255      * @return {Roo.UpdateManager} The UpdateManager
37256      */
37257     setUrl : function(url, params, loadOnce){
37258         if(this.refreshDelegate){
37259             this.removeListener("activate", this.refreshDelegate);
37260         }
37261         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37262         this.on("activate", this.refreshDelegate);
37263         return this.el.getUpdateManager();
37264     },
37265     
37266     _handleRefresh : function(url, params, loadOnce){
37267         if(!loadOnce || !this.loaded){
37268             var updater = this.el.getUpdateManager();
37269             updater.update(url, params, this._setLoaded.createDelegate(this));
37270         }
37271     },
37272     
37273     _setLoaded : function(){
37274         this.loaded = true;
37275     }, 
37276     
37277     /**
37278      * Returns this panel's id
37279      * @return {String} 
37280      */
37281     getId : function(){
37282         return this.el.id;
37283     },
37284     
37285     /** 
37286      * Returns this panel's element - used by regiosn to add.
37287      * @return {Roo.Element} 
37288      */
37289     getEl : function(){
37290         return this.wrapEl || this.el;
37291     },
37292     
37293    
37294     
37295     adjustForComponents : function(width, height)
37296     {
37297         //Roo.log('adjustForComponents ');
37298         if(this.resizeEl != this.el){
37299             width -= this.el.getFrameWidth('lr');
37300             height -= this.el.getFrameWidth('tb');
37301         }
37302         if(this.toolbar){
37303             var te = this.toolbar.getEl();
37304             te.setWidth(width);
37305             height -= te.getHeight();
37306         }
37307         if(this.footer){
37308             var te = this.footer.getEl();
37309             te.setWidth(width);
37310             height -= te.getHeight();
37311         }
37312         
37313         
37314         if(this.adjustments){
37315             width += this.adjustments[0];
37316             height += this.adjustments[1];
37317         }
37318         return {"width": width, "height": height};
37319     },
37320     
37321     setSize : function(width, height){
37322         if(this.fitToFrame && !this.ignoreResize(width, height)){
37323             if(this.fitContainer && this.resizeEl != this.el){
37324                 this.el.setSize(width, height);
37325             }
37326             var size = this.adjustForComponents(width, height);
37327             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37328             this.fireEvent('resize', this, size.width, size.height);
37329         }
37330     },
37331     
37332     /**
37333      * Returns this panel's title
37334      * @return {String} 
37335      */
37336     getTitle : function(){
37337         
37338         if (typeof(this.title) != 'object') {
37339             return this.title;
37340         }
37341         
37342         var t = '';
37343         for (var k in this.title) {
37344             if (!this.title.hasOwnProperty(k)) {
37345                 continue;
37346             }
37347             
37348             if (k.indexOf('-') >= 0) {
37349                 var s = k.split('-');
37350                 for (var i = 0; i<s.length; i++) {
37351                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37352                 }
37353             } else {
37354                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37355             }
37356         }
37357         return t;
37358     },
37359     
37360     /**
37361      * Set this panel's title
37362      * @param {String} title
37363      */
37364     setTitle : function(title){
37365         this.title = title;
37366         if(this.region){
37367             this.region.updatePanelTitle(this, title);
37368         }
37369     },
37370     
37371     /**
37372      * Returns true is this panel was configured to be closable
37373      * @return {Boolean} 
37374      */
37375     isClosable : function(){
37376         return this.closable;
37377     },
37378     
37379     beforeSlide : function(){
37380         this.el.clip();
37381         this.resizeEl.clip();
37382     },
37383     
37384     afterSlide : function(){
37385         this.el.unclip();
37386         this.resizeEl.unclip();
37387     },
37388     
37389     /**
37390      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37391      *   Will fail silently if the {@link #setUrl} method has not been called.
37392      *   This does not activate the panel, just updates its content.
37393      */
37394     refresh : function(){
37395         if(this.refreshDelegate){
37396            this.loaded = false;
37397            this.refreshDelegate();
37398         }
37399     },
37400     
37401     /**
37402      * Destroys this panel
37403      */
37404     destroy : function(){
37405         this.el.removeAllListeners();
37406         var tempEl = document.createElement("span");
37407         tempEl.appendChild(this.el.dom);
37408         tempEl.innerHTML = "";
37409         this.el.remove();
37410         this.el = null;
37411     },
37412     
37413     /**
37414      * form - if the content panel contains a form - this is a reference to it.
37415      * @type {Roo.form.Form}
37416      */
37417     form : false,
37418     /**
37419      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37420      *    This contains a reference to it.
37421      * @type {Roo.View}
37422      */
37423     view : false,
37424     
37425       /**
37426      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37427      * <pre><code>
37428
37429 layout.addxtype({
37430        xtype : 'Form',
37431        items: [ .... ]
37432    }
37433 );
37434
37435 </code></pre>
37436      * @param {Object} cfg Xtype definition of item to add.
37437      */
37438     
37439     
37440     getChildContainer: function () {
37441         return this.getEl();
37442     }
37443     
37444     
37445     /*
37446         var  ret = new Roo.factory(cfg);
37447         return ret;
37448         
37449         
37450         // add form..
37451         if (cfg.xtype.match(/^Form$/)) {
37452             
37453             var el;
37454             //if (this.footer) {
37455             //    el = this.footer.container.insertSibling(false, 'before');
37456             //} else {
37457                 el = this.el.createChild();
37458             //}
37459
37460             this.form = new  Roo.form.Form(cfg);
37461             
37462             
37463             if ( this.form.allItems.length) {
37464                 this.form.render(el.dom);
37465             }
37466             return this.form;
37467         }
37468         // should only have one of theses..
37469         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37470             // views.. should not be just added - used named prop 'view''
37471             
37472             cfg.el = this.el.appendChild(document.createElement("div"));
37473             // factory?
37474             
37475             var ret = new Roo.factory(cfg);
37476              
37477              ret.render && ret.render(false, ''); // render blank..
37478             this.view = ret;
37479             return ret;
37480         }
37481         return false;
37482     }
37483     \*/
37484 });
37485  
37486 /**
37487  * @class Roo.bootstrap.panel.Grid
37488  * @extends Roo.bootstrap.panel.Content
37489  * @constructor
37490  * Create a new GridPanel.
37491  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37492  * @param {Object} config A the config object
37493   
37494  */
37495
37496
37497
37498 Roo.bootstrap.panel.Grid = function(config)
37499 {
37500     
37501       
37502     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37503         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37504
37505     config.el = this.wrapper;
37506     //this.el = this.wrapper;
37507     
37508       if (config.container) {
37509         // ctor'ed from a Border/panel.grid
37510         
37511         
37512         this.wrapper.setStyle("overflow", "hidden");
37513         this.wrapper.addClass('roo-grid-container');
37514
37515     }
37516     
37517     
37518     if(config.toolbar){
37519         var tool_el = this.wrapper.createChild();    
37520         this.toolbar = Roo.factory(config.toolbar);
37521         var ti = [];
37522         if (config.toolbar.items) {
37523             ti = config.toolbar.items ;
37524             delete config.toolbar.items ;
37525         }
37526         
37527         var nitems = [];
37528         this.toolbar.render(tool_el);
37529         for(var i =0;i < ti.length;i++) {
37530           //  Roo.log(['add child', items[i]]);
37531             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37532         }
37533         this.toolbar.items = nitems;
37534         
37535         delete config.toolbar;
37536     }
37537     
37538     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37539     config.grid.scrollBody = true;;
37540     config.grid.monitorWindowResize = false; // turn off autosizing
37541     config.grid.autoHeight = false;
37542     config.grid.autoWidth = false;
37543     
37544     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37545     
37546     if (config.background) {
37547         // render grid on panel activation (if panel background)
37548         this.on('activate', function(gp) {
37549             if (!gp.grid.rendered) {
37550                 gp.grid.render(this.wrapper);
37551                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37552             }
37553         });
37554             
37555     } else {
37556         this.grid.render(this.wrapper);
37557         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37558
37559     }
37560     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37561     // ??? needed ??? config.el = this.wrapper;
37562     
37563     
37564     
37565   
37566     // xtype created footer. - not sure if will work as we normally have to render first..
37567     if (this.footer && !this.footer.el && this.footer.xtype) {
37568         
37569         var ctr = this.grid.getView().getFooterPanel(true);
37570         this.footer.dataSource = this.grid.dataSource;
37571         this.footer = Roo.factory(this.footer, Roo);
37572         this.footer.render(ctr);
37573         
37574     }
37575     
37576     
37577     
37578     
37579      
37580 };
37581
37582 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37583     getId : function(){
37584         return this.grid.id;
37585     },
37586     
37587     /**
37588      * Returns the grid for this panel
37589      * @return {Roo.bootstrap.Table} 
37590      */
37591     getGrid : function(){
37592         return this.grid;    
37593     },
37594     
37595     setSize : function(width, height){
37596         if(!this.ignoreResize(width, height)){
37597             var grid = this.grid;
37598             var size = this.adjustForComponents(width, height);
37599             var gridel = grid.getGridEl();
37600             gridel.setSize(size.width, size.height);
37601             /*
37602             var thd = grid.getGridEl().select('thead',true).first();
37603             var tbd = grid.getGridEl().select('tbody', true).first();
37604             if (tbd) {
37605                 tbd.setSize(width, height - thd.getHeight());
37606             }
37607             */
37608             grid.autoSize();
37609         }
37610     },
37611      
37612     
37613     
37614     beforeSlide : function(){
37615         this.grid.getView().scroller.clip();
37616     },
37617     
37618     afterSlide : function(){
37619         this.grid.getView().scroller.unclip();
37620     },
37621     
37622     destroy : function(){
37623         this.grid.destroy();
37624         delete this.grid;
37625         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37626     }
37627 });
37628
37629 /**
37630  * @class Roo.bootstrap.panel.Nest
37631  * @extends Roo.bootstrap.panel.Content
37632  * @constructor
37633  * Create a new Panel, that can contain a layout.Border.
37634  * 
37635  * 
37636  * @param {Roo.BorderLayout} layout The layout for this panel
37637  * @param {String/Object} config A string to set only the title or a config object
37638  */
37639 Roo.bootstrap.panel.Nest = function(config)
37640 {
37641     // construct with only one argument..
37642     /* FIXME - implement nicer consturctors
37643     if (layout.layout) {
37644         config = layout;
37645         layout = config.layout;
37646         delete config.layout;
37647     }
37648     if (layout.xtype && !layout.getEl) {
37649         // then layout needs constructing..
37650         layout = Roo.factory(layout, Roo);
37651     }
37652     */
37653     
37654     config.el =  config.layout.getEl();
37655     
37656     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37657     
37658     config.layout.monitorWindowResize = false; // turn off autosizing
37659     this.layout = config.layout;
37660     this.layout.getEl().addClass("roo-layout-nested-layout");
37661     
37662     
37663     
37664     
37665 };
37666
37667 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37668
37669     setSize : function(width, height){
37670         if(!this.ignoreResize(width, height)){
37671             var size = this.adjustForComponents(width, height);
37672             var el = this.layout.getEl();
37673             if (size.height < 1) {
37674                 el.setWidth(size.width);   
37675             } else {
37676                 el.setSize(size.width, size.height);
37677             }
37678             var touch = el.dom.offsetWidth;
37679             this.layout.layout();
37680             // ie requires a double layout on the first pass
37681             if(Roo.isIE && !this.initialized){
37682                 this.initialized = true;
37683                 this.layout.layout();
37684             }
37685         }
37686     },
37687     
37688     // activate all subpanels if not currently active..
37689     
37690     setActiveState : function(active){
37691         this.active = active;
37692         this.setActiveClass(active);
37693         
37694         if(!active){
37695             this.fireEvent("deactivate", this);
37696             return;
37697         }
37698         
37699         this.fireEvent("activate", this);
37700         // not sure if this should happen before or after..
37701         if (!this.layout) {
37702             return; // should not happen..
37703         }
37704         var reg = false;
37705         for (var r in this.layout.regions) {
37706             reg = this.layout.getRegion(r);
37707             if (reg.getActivePanel()) {
37708                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37709                 reg.setActivePanel(reg.getActivePanel());
37710                 continue;
37711             }
37712             if (!reg.panels.length) {
37713                 continue;
37714             }
37715             reg.showPanel(reg.getPanel(0));
37716         }
37717         
37718         
37719         
37720         
37721     },
37722     
37723     /**
37724      * Returns the nested BorderLayout for this panel
37725      * @return {Roo.BorderLayout} 
37726      */
37727     getLayout : function(){
37728         return this.layout;
37729     },
37730     
37731      /**
37732      * Adds a xtype elements to the layout of the nested panel
37733      * <pre><code>
37734
37735 panel.addxtype({
37736        xtype : 'ContentPanel',
37737        region: 'west',
37738        items: [ .... ]
37739    }
37740 );
37741
37742 panel.addxtype({
37743         xtype : 'NestedLayoutPanel',
37744         region: 'west',
37745         layout: {
37746            center: { },
37747            west: { }   
37748         },
37749         items : [ ... list of content panels or nested layout panels.. ]
37750    }
37751 );
37752 </code></pre>
37753      * @param {Object} cfg Xtype definition of item to add.
37754      */
37755     addxtype : function(cfg) {
37756         return this.layout.addxtype(cfg);
37757     
37758     }
37759 });        /*
37760  * Based on:
37761  * Ext JS Library 1.1.1
37762  * Copyright(c) 2006-2007, Ext JS, LLC.
37763  *
37764  * Originally Released Under LGPL - original licence link has changed is not relivant.
37765  *
37766  * Fork - LGPL
37767  * <script type="text/javascript">
37768  */
37769 /**
37770  * @class Roo.TabPanel
37771  * @extends Roo.util.Observable
37772  * A lightweight tab container.
37773  * <br><br>
37774  * Usage:
37775  * <pre><code>
37776 // basic tabs 1, built from existing content
37777 var tabs = new Roo.TabPanel("tabs1");
37778 tabs.addTab("script", "View Script");
37779 tabs.addTab("markup", "View Markup");
37780 tabs.activate("script");
37781
37782 // more advanced tabs, built from javascript
37783 var jtabs = new Roo.TabPanel("jtabs");
37784 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37785
37786 // set up the UpdateManager
37787 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37788 var updater = tab2.getUpdateManager();
37789 updater.setDefaultUrl("ajax1.htm");
37790 tab2.on('activate', updater.refresh, updater, true);
37791
37792 // Use setUrl for Ajax loading
37793 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37794 tab3.setUrl("ajax2.htm", null, true);
37795
37796 // Disabled tab
37797 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37798 tab4.disable();
37799
37800 jtabs.activate("jtabs-1");
37801  * </code></pre>
37802  * @constructor
37803  * Create a new TabPanel.
37804  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37805  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37806  */
37807 Roo.bootstrap.panel.Tabs = function(config){
37808     /**
37809     * The container element for this TabPanel.
37810     * @type Roo.Element
37811     */
37812     this.el = Roo.get(config.el);
37813     delete config.el;
37814     if(config){
37815         if(typeof config == "boolean"){
37816             this.tabPosition = config ? "bottom" : "top";
37817         }else{
37818             Roo.apply(this, config);
37819         }
37820     }
37821     
37822     if(this.tabPosition == "bottom"){
37823         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37824         this.el.addClass("roo-tabs-bottom");
37825     }
37826     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37827     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37828     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37829     if(Roo.isIE){
37830         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37831     }
37832     if(this.tabPosition != "bottom"){
37833         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37834          * @type Roo.Element
37835          */
37836         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37837         this.el.addClass("roo-tabs-top");
37838     }
37839     this.items = [];
37840
37841     this.bodyEl.setStyle("position", "relative");
37842
37843     this.active = null;
37844     this.activateDelegate = this.activate.createDelegate(this);
37845
37846     this.addEvents({
37847         /**
37848          * @event tabchange
37849          * Fires when the active tab changes
37850          * @param {Roo.TabPanel} this
37851          * @param {Roo.TabPanelItem} activePanel The new active tab
37852          */
37853         "tabchange": true,
37854         /**
37855          * @event beforetabchange
37856          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37857          * @param {Roo.TabPanel} this
37858          * @param {Object} e Set cancel to true on this object to cancel the tab change
37859          * @param {Roo.TabPanelItem} tab The tab being changed to
37860          */
37861         "beforetabchange" : true
37862     });
37863
37864     Roo.EventManager.onWindowResize(this.onResize, this);
37865     this.cpad = this.el.getPadding("lr");
37866     this.hiddenCount = 0;
37867
37868
37869     // toolbar on the tabbar support...
37870     if (this.toolbar) {
37871         alert("no toolbar support yet");
37872         this.toolbar  = false;
37873         /*
37874         var tcfg = this.toolbar;
37875         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37876         this.toolbar = new Roo.Toolbar(tcfg);
37877         if (Roo.isSafari) {
37878             var tbl = tcfg.container.child('table', true);
37879             tbl.setAttribute('width', '100%');
37880         }
37881         */
37882         
37883     }
37884    
37885
37886
37887     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37888 };
37889
37890 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37891     /*
37892      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37893      */
37894     tabPosition : "top",
37895     /*
37896      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37897      */
37898     currentTabWidth : 0,
37899     /*
37900      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37901      */
37902     minTabWidth : 40,
37903     /*
37904      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37905      */
37906     maxTabWidth : 250,
37907     /*
37908      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37909      */
37910     preferredTabWidth : 175,
37911     /*
37912      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37913      */
37914     resizeTabs : false,
37915     /*
37916      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37917      */
37918     monitorResize : true,
37919     /*
37920      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37921      */
37922     toolbar : false,
37923
37924     /**
37925      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37926      * @param {String} id The id of the div to use <b>or create</b>
37927      * @param {String} text The text for the tab
37928      * @param {String} content (optional) Content to put in the TabPanelItem body
37929      * @param {Boolean} closable (optional) True to create a close icon on the tab
37930      * @return {Roo.TabPanelItem} The created TabPanelItem
37931      */
37932     addTab : function(id, text, content, closable, tpl)
37933     {
37934         var item = new Roo.bootstrap.panel.TabItem({
37935             panel: this,
37936             id : id,
37937             text : text,
37938             closable : closable,
37939             tpl : tpl
37940         });
37941         this.addTabItem(item);
37942         if(content){
37943             item.setContent(content);
37944         }
37945         return item;
37946     },
37947
37948     /**
37949      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37950      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37951      * @return {Roo.TabPanelItem}
37952      */
37953     getTab : function(id){
37954         return this.items[id];
37955     },
37956
37957     /**
37958      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37959      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37960      */
37961     hideTab : function(id){
37962         var t = this.items[id];
37963         if(!t.isHidden()){
37964            t.setHidden(true);
37965            this.hiddenCount++;
37966            this.autoSizeTabs();
37967         }
37968     },
37969
37970     /**
37971      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37972      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37973      */
37974     unhideTab : function(id){
37975         var t = this.items[id];
37976         if(t.isHidden()){
37977            t.setHidden(false);
37978            this.hiddenCount--;
37979            this.autoSizeTabs();
37980         }
37981     },
37982
37983     /**
37984      * Adds an existing {@link Roo.TabPanelItem}.
37985      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37986      */
37987     addTabItem : function(item){
37988         this.items[item.id] = item;
37989         this.items.push(item);
37990       //  if(this.resizeTabs){
37991     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37992   //         this.autoSizeTabs();
37993 //        }else{
37994 //            item.autoSize();
37995        // }
37996     },
37997
37998     /**
37999      * Removes a {@link Roo.TabPanelItem}.
38000      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38001      */
38002     removeTab : function(id){
38003         var items = this.items;
38004         var tab = items[id];
38005         if(!tab) { return; }
38006         var index = items.indexOf(tab);
38007         if(this.active == tab && items.length > 1){
38008             var newTab = this.getNextAvailable(index);
38009             if(newTab) {
38010                 newTab.activate();
38011             }
38012         }
38013         this.stripEl.dom.removeChild(tab.pnode.dom);
38014         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38015             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38016         }
38017         items.splice(index, 1);
38018         delete this.items[tab.id];
38019         tab.fireEvent("close", tab);
38020         tab.purgeListeners();
38021         this.autoSizeTabs();
38022     },
38023
38024     getNextAvailable : function(start){
38025         var items = this.items;
38026         var index = start;
38027         // look for a next tab that will slide over to
38028         // replace the one being removed
38029         while(index < items.length){
38030             var item = items[++index];
38031             if(item && !item.isHidden()){
38032                 return item;
38033             }
38034         }
38035         // if one isn't found select the previous tab (on the left)
38036         index = start;
38037         while(index >= 0){
38038             var item = items[--index];
38039             if(item && !item.isHidden()){
38040                 return item;
38041             }
38042         }
38043         return null;
38044     },
38045
38046     /**
38047      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38048      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38049      */
38050     disableTab : function(id){
38051         var tab = this.items[id];
38052         if(tab && this.active != tab){
38053             tab.disable();
38054         }
38055     },
38056
38057     /**
38058      * Enables a {@link Roo.TabPanelItem} that is disabled.
38059      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38060      */
38061     enableTab : function(id){
38062         var tab = this.items[id];
38063         tab.enable();
38064     },
38065
38066     /**
38067      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38068      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38069      * @return {Roo.TabPanelItem} The TabPanelItem.
38070      */
38071     activate : function(id){
38072         var tab = this.items[id];
38073         if(!tab){
38074             return null;
38075         }
38076         if(tab == this.active || tab.disabled){
38077             return tab;
38078         }
38079         var e = {};
38080         this.fireEvent("beforetabchange", this, e, tab);
38081         if(e.cancel !== true && !tab.disabled){
38082             if(this.active){
38083                 this.active.hide();
38084             }
38085             this.active = this.items[id];
38086             this.active.show();
38087             this.fireEvent("tabchange", this, this.active);
38088         }
38089         return tab;
38090     },
38091
38092     /**
38093      * Gets the active {@link Roo.TabPanelItem}.
38094      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38095      */
38096     getActiveTab : function(){
38097         return this.active;
38098     },
38099
38100     /**
38101      * Updates the tab body element to fit the height of the container element
38102      * for overflow scrolling
38103      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38104      */
38105     syncHeight : function(targetHeight){
38106         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38107         var bm = this.bodyEl.getMargins();
38108         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38109         this.bodyEl.setHeight(newHeight);
38110         return newHeight;
38111     },
38112
38113     onResize : function(){
38114         if(this.monitorResize){
38115             this.autoSizeTabs();
38116         }
38117     },
38118
38119     /**
38120      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38121      */
38122     beginUpdate : function(){
38123         this.updating = true;
38124     },
38125
38126     /**
38127      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38128      */
38129     endUpdate : function(){
38130         this.updating = false;
38131         this.autoSizeTabs();
38132     },
38133
38134     /**
38135      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38136      */
38137     autoSizeTabs : function(){
38138         var count = this.items.length;
38139         var vcount = count - this.hiddenCount;
38140         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38141             return;
38142         }
38143         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38144         var availWidth = Math.floor(w / vcount);
38145         var b = this.stripBody;
38146         if(b.getWidth() > w){
38147             var tabs = this.items;
38148             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38149             if(availWidth < this.minTabWidth){
38150                 /*if(!this.sleft){    // incomplete scrolling code
38151                     this.createScrollButtons();
38152                 }
38153                 this.showScroll();
38154                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38155             }
38156         }else{
38157             if(this.currentTabWidth < this.preferredTabWidth){
38158                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38159             }
38160         }
38161     },
38162
38163     /**
38164      * Returns the number of tabs in this TabPanel.
38165      * @return {Number}
38166      */
38167      getCount : function(){
38168          return this.items.length;
38169      },
38170
38171     /**
38172      * Resizes all the tabs to the passed width
38173      * @param {Number} The new width
38174      */
38175     setTabWidth : function(width){
38176         this.currentTabWidth = width;
38177         for(var i = 0, len = this.items.length; i < len; i++) {
38178                 if(!this.items[i].isHidden()) {
38179                 this.items[i].setWidth(width);
38180             }
38181         }
38182     },
38183
38184     /**
38185      * Destroys this TabPanel
38186      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38187      */
38188     destroy : function(removeEl){
38189         Roo.EventManager.removeResizeListener(this.onResize, this);
38190         for(var i = 0, len = this.items.length; i < len; i++){
38191             this.items[i].purgeListeners();
38192         }
38193         if(removeEl === true){
38194             this.el.update("");
38195             this.el.remove();
38196         }
38197     },
38198     
38199     createStrip : function(container)
38200     {
38201         var strip = document.createElement("nav");
38202         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38203         container.appendChild(strip);
38204         return strip;
38205     },
38206     
38207     createStripList : function(strip)
38208     {
38209         // div wrapper for retard IE
38210         // returns the "tr" element.
38211         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38212         //'<div class="x-tabs-strip-wrap">'+
38213           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38214           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38215         return strip.firstChild; //.firstChild.firstChild.firstChild;
38216     },
38217     createBody : function(container)
38218     {
38219         var body = document.createElement("div");
38220         Roo.id(body, "tab-body");
38221         //Roo.fly(body).addClass("x-tabs-body");
38222         Roo.fly(body).addClass("tab-content");
38223         container.appendChild(body);
38224         return body;
38225     },
38226     createItemBody :function(bodyEl, id){
38227         var body = Roo.getDom(id);
38228         if(!body){
38229             body = document.createElement("div");
38230             body.id = id;
38231         }
38232         //Roo.fly(body).addClass("x-tabs-item-body");
38233         Roo.fly(body).addClass("tab-pane");
38234          bodyEl.insertBefore(body, bodyEl.firstChild);
38235         return body;
38236     },
38237     /** @private */
38238     createStripElements :  function(stripEl, text, closable, tpl)
38239     {
38240         var td = document.createElement("li"); // was td..
38241         
38242         
38243         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38244         
38245         
38246         stripEl.appendChild(td);
38247         /*if(closable){
38248             td.className = "x-tabs-closable";
38249             if(!this.closeTpl){
38250                 this.closeTpl = new Roo.Template(
38251                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38252                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38253                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38254                 );
38255             }
38256             var el = this.closeTpl.overwrite(td, {"text": text});
38257             var close = el.getElementsByTagName("div")[0];
38258             var inner = el.getElementsByTagName("em")[0];
38259             return {"el": el, "close": close, "inner": inner};
38260         } else {
38261         */
38262         // not sure what this is..
38263 //            if(!this.tabTpl){
38264                 //this.tabTpl = new Roo.Template(
38265                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38266                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38267                 //);
38268 //                this.tabTpl = new Roo.Template(
38269 //                   '<a href="#">' +
38270 //                   '<span unselectable="on"' +
38271 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38272 //                            ' >{text}</span></a>'
38273 //                );
38274 //                
38275 //            }
38276
38277
38278             var template = tpl || this.tabTpl || false;
38279             
38280             if(!template){
38281                 
38282                 template = new Roo.Template(
38283                    '<a href="#">' +
38284                    '<span unselectable="on"' +
38285                             (this.disableTooltips ? '' : ' title="{text}"') +
38286                             ' >{text}</span></a>'
38287                 );
38288             }
38289             
38290             switch (typeof(template)) {
38291                 case 'object' :
38292                     break;
38293                 case 'string' :
38294                     template = new Roo.Template(template);
38295                     break;
38296                 default :
38297                     break;
38298             }
38299             
38300             var el = template.overwrite(td, {"text": text});
38301             
38302             var inner = el.getElementsByTagName("span")[0];
38303             
38304             return {"el": el, "inner": inner};
38305             
38306     }
38307         
38308     
38309 });
38310
38311 /**
38312  * @class Roo.TabPanelItem
38313  * @extends Roo.util.Observable
38314  * Represents an individual item (tab plus body) in a TabPanel.
38315  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38316  * @param {String} id The id of this TabPanelItem
38317  * @param {String} text The text for the tab of this TabPanelItem
38318  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38319  */
38320 Roo.bootstrap.panel.TabItem = function(config){
38321     /**
38322      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38323      * @type Roo.TabPanel
38324      */
38325     this.tabPanel = config.panel;
38326     /**
38327      * The id for this TabPanelItem
38328      * @type String
38329      */
38330     this.id = config.id;
38331     /** @private */
38332     this.disabled = false;
38333     /** @private */
38334     this.text = config.text;
38335     /** @private */
38336     this.loaded = false;
38337     this.closable = config.closable;
38338
38339     /**
38340      * The body element for this TabPanelItem.
38341      * @type Roo.Element
38342      */
38343     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38344     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38345     this.bodyEl.setStyle("display", "block");
38346     this.bodyEl.setStyle("zoom", "1");
38347     //this.hideAction();
38348
38349     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38350     /** @private */
38351     this.el = Roo.get(els.el);
38352     this.inner = Roo.get(els.inner, true);
38353     this.textEl = Roo.get(this.el.dom.firstChild, true);
38354     this.pnode = Roo.get(els.el.parentNode, true);
38355 //    this.el.on("mousedown", this.onTabMouseDown, this);
38356     this.el.on("click", this.onTabClick, this);
38357     /** @private */
38358     if(config.closable){
38359         var c = Roo.get(els.close, true);
38360         c.dom.title = this.closeText;
38361         c.addClassOnOver("close-over");
38362         c.on("click", this.closeClick, this);
38363      }
38364
38365     this.addEvents({
38366          /**
38367          * @event activate
38368          * Fires when this tab becomes the active tab.
38369          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38370          * @param {Roo.TabPanelItem} this
38371          */
38372         "activate": true,
38373         /**
38374          * @event beforeclose
38375          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38376          * @param {Roo.TabPanelItem} this
38377          * @param {Object} e Set cancel to true on this object to cancel the close.
38378          */
38379         "beforeclose": true,
38380         /**
38381          * @event close
38382          * Fires when this tab is closed.
38383          * @param {Roo.TabPanelItem} this
38384          */
38385          "close": true,
38386         /**
38387          * @event deactivate
38388          * Fires when this tab is no longer the active tab.
38389          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38390          * @param {Roo.TabPanelItem} this
38391          */
38392          "deactivate" : true
38393     });
38394     this.hidden = false;
38395
38396     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38397 };
38398
38399 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38400            {
38401     purgeListeners : function(){
38402        Roo.util.Observable.prototype.purgeListeners.call(this);
38403        this.el.removeAllListeners();
38404     },
38405     /**
38406      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38407      */
38408     show : function(){
38409         this.pnode.addClass("active");
38410         this.showAction();
38411         if(Roo.isOpera){
38412             this.tabPanel.stripWrap.repaint();
38413         }
38414         this.fireEvent("activate", this.tabPanel, this);
38415     },
38416
38417     /**
38418      * Returns true if this tab is the active tab.
38419      * @return {Boolean}
38420      */
38421     isActive : function(){
38422         return this.tabPanel.getActiveTab() == this;
38423     },
38424
38425     /**
38426      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38427      */
38428     hide : function(){
38429         this.pnode.removeClass("active");
38430         this.hideAction();
38431         this.fireEvent("deactivate", this.tabPanel, this);
38432     },
38433
38434     hideAction : function(){
38435         this.bodyEl.hide();
38436         this.bodyEl.setStyle("position", "absolute");
38437         this.bodyEl.setLeft("-20000px");
38438         this.bodyEl.setTop("-20000px");
38439     },
38440
38441     showAction : function(){
38442         this.bodyEl.setStyle("position", "relative");
38443         this.bodyEl.setTop("");
38444         this.bodyEl.setLeft("");
38445         this.bodyEl.show();
38446     },
38447
38448     /**
38449      * Set the tooltip for the tab.
38450      * @param {String} tooltip The tab's tooltip
38451      */
38452     setTooltip : function(text){
38453         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38454             this.textEl.dom.qtip = text;
38455             this.textEl.dom.removeAttribute('title');
38456         }else{
38457             this.textEl.dom.title = text;
38458         }
38459     },
38460
38461     onTabClick : function(e){
38462         e.preventDefault();
38463         this.tabPanel.activate(this.id);
38464     },
38465
38466     onTabMouseDown : function(e){
38467         e.preventDefault();
38468         this.tabPanel.activate(this.id);
38469     },
38470 /*
38471     getWidth : function(){
38472         return this.inner.getWidth();
38473     },
38474
38475     setWidth : function(width){
38476         var iwidth = width - this.pnode.getPadding("lr");
38477         this.inner.setWidth(iwidth);
38478         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38479         this.pnode.setWidth(width);
38480     },
38481 */
38482     /**
38483      * Show or hide the tab
38484      * @param {Boolean} hidden True to hide or false to show.
38485      */
38486     setHidden : function(hidden){
38487         this.hidden = hidden;
38488         this.pnode.setStyle("display", hidden ? "none" : "");
38489     },
38490
38491     /**
38492      * Returns true if this tab is "hidden"
38493      * @return {Boolean}
38494      */
38495     isHidden : function(){
38496         return this.hidden;
38497     },
38498
38499     /**
38500      * Returns the text for this tab
38501      * @return {String}
38502      */
38503     getText : function(){
38504         return this.text;
38505     },
38506     /*
38507     autoSize : function(){
38508         //this.el.beginMeasure();
38509         this.textEl.setWidth(1);
38510         /*
38511          *  #2804 [new] Tabs in Roojs
38512          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38513          */
38514         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38515         //this.el.endMeasure();
38516     //},
38517
38518     /**
38519      * Sets the text for the tab (Note: this also sets the tooltip text)
38520      * @param {String} text The tab's text and tooltip
38521      */
38522     setText : function(text){
38523         this.text = text;
38524         this.textEl.update(text);
38525         this.setTooltip(text);
38526         //if(!this.tabPanel.resizeTabs){
38527         //    this.autoSize();
38528         //}
38529     },
38530     /**
38531      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38532      */
38533     activate : function(){
38534         this.tabPanel.activate(this.id);
38535     },
38536
38537     /**
38538      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38539      */
38540     disable : function(){
38541         if(this.tabPanel.active != this){
38542             this.disabled = true;
38543             this.pnode.addClass("disabled");
38544         }
38545     },
38546
38547     /**
38548      * Enables this TabPanelItem if it was previously disabled.
38549      */
38550     enable : function(){
38551         this.disabled = false;
38552         this.pnode.removeClass("disabled");
38553     },
38554
38555     /**
38556      * Sets the content for this TabPanelItem.
38557      * @param {String} content The content
38558      * @param {Boolean} loadScripts true to look for and load scripts
38559      */
38560     setContent : function(content, loadScripts){
38561         this.bodyEl.update(content, loadScripts);
38562     },
38563
38564     /**
38565      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38566      * @return {Roo.UpdateManager} The UpdateManager
38567      */
38568     getUpdateManager : function(){
38569         return this.bodyEl.getUpdateManager();
38570     },
38571
38572     /**
38573      * Set a URL to be used to load the content for this TabPanelItem.
38574      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38575      * @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)
38576      * @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)
38577      * @return {Roo.UpdateManager} The UpdateManager
38578      */
38579     setUrl : function(url, params, loadOnce){
38580         if(this.refreshDelegate){
38581             this.un('activate', this.refreshDelegate);
38582         }
38583         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38584         this.on("activate", this.refreshDelegate);
38585         return this.bodyEl.getUpdateManager();
38586     },
38587
38588     /** @private */
38589     _handleRefresh : function(url, params, loadOnce){
38590         if(!loadOnce || !this.loaded){
38591             var updater = this.bodyEl.getUpdateManager();
38592             updater.update(url, params, this._setLoaded.createDelegate(this));
38593         }
38594     },
38595
38596     /**
38597      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38598      *   Will fail silently if the setUrl method has not been called.
38599      *   This does not activate the panel, just updates its content.
38600      */
38601     refresh : function(){
38602         if(this.refreshDelegate){
38603            this.loaded = false;
38604            this.refreshDelegate();
38605         }
38606     },
38607
38608     /** @private */
38609     _setLoaded : function(){
38610         this.loaded = true;
38611     },
38612
38613     /** @private */
38614     closeClick : function(e){
38615         var o = {};
38616         e.stopEvent();
38617         this.fireEvent("beforeclose", this, o);
38618         if(o.cancel !== true){
38619             this.tabPanel.removeTab(this.id);
38620         }
38621     },
38622     /**
38623      * The text displayed in the tooltip for the close icon.
38624      * @type String
38625      */
38626     closeText : "Close this tab"
38627 });
38628 /**
38629 *    This script refer to:
38630 *    Title: International Telephone Input
38631 *    Author: Jack O'Connor
38632 *    Code version:  v12.1.12
38633 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38634 **/
38635
38636 Roo.bootstrap.PhoneInputData = function() {
38637     var d = [
38638       [
38639         "Afghanistan (‫افغانستان‬‎)",
38640         "af",
38641         "93"
38642       ],
38643       [
38644         "Albania (Shqipëri)",
38645         "al",
38646         "355"
38647       ],
38648       [
38649         "Algeria (‫الجزائر‬‎)",
38650         "dz",
38651         "213"
38652       ],
38653       [
38654         "American Samoa",
38655         "as",
38656         "1684"
38657       ],
38658       [
38659         "Andorra",
38660         "ad",
38661         "376"
38662       ],
38663       [
38664         "Angola",
38665         "ao",
38666         "244"
38667       ],
38668       [
38669         "Anguilla",
38670         "ai",
38671         "1264"
38672       ],
38673       [
38674         "Antigua and Barbuda",
38675         "ag",
38676         "1268"
38677       ],
38678       [
38679         "Argentina",
38680         "ar",
38681         "54"
38682       ],
38683       [
38684         "Armenia (Հայաստան)",
38685         "am",
38686         "374"
38687       ],
38688       [
38689         "Aruba",
38690         "aw",
38691         "297"
38692       ],
38693       [
38694         "Australia",
38695         "au",
38696         "61",
38697         0
38698       ],
38699       [
38700         "Austria (Österreich)",
38701         "at",
38702         "43"
38703       ],
38704       [
38705         "Azerbaijan (Azərbaycan)",
38706         "az",
38707         "994"
38708       ],
38709       [
38710         "Bahamas",
38711         "bs",
38712         "1242"
38713       ],
38714       [
38715         "Bahrain (‫البحرين‬‎)",
38716         "bh",
38717         "973"
38718       ],
38719       [
38720         "Bangladesh (বাংলাদেশ)",
38721         "bd",
38722         "880"
38723       ],
38724       [
38725         "Barbados",
38726         "bb",
38727         "1246"
38728       ],
38729       [
38730         "Belarus (Беларусь)",
38731         "by",
38732         "375"
38733       ],
38734       [
38735         "Belgium (België)",
38736         "be",
38737         "32"
38738       ],
38739       [
38740         "Belize",
38741         "bz",
38742         "501"
38743       ],
38744       [
38745         "Benin (Bénin)",
38746         "bj",
38747         "229"
38748       ],
38749       [
38750         "Bermuda",
38751         "bm",
38752         "1441"
38753       ],
38754       [
38755         "Bhutan (འབྲུག)",
38756         "bt",
38757         "975"
38758       ],
38759       [
38760         "Bolivia",
38761         "bo",
38762         "591"
38763       ],
38764       [
38765         "Bosnia and Herzegovina (Босна и Херцеговина)",
38766         "ba",
38767         "387"
38768       ],
38769       [
38770         "Botswana",
38771         "bw",
38772         "267"
38773       ],
38774       [
38775         "Brazil (Brasil)",
38776         "br",
38777         "55"
38778       ],
38779       [
38780         "British Indian Ocean Territory",
38781         "io",
38782         "246"
38783       ],
38784       [
38785         "British Virgin Islands",
38786         "vg",
38787         "1284"
38788       ],
38789       [
38790         "Brunei",
38791         "bn",
38792         "673"
38793       ],
38794       [
38795         "Bulgaria (България)",
38796         "bg",
38797         "359"
38798       ],
38799       [
38800         "Burkina Faso",
38801         "bf",
38802         "226"
38803       ],
38804       [
38805         "Burundi (Uburundi)",
38806         "bi",
38807         "257"
38808       ],
38809       [
38810         "Cambodia (កម្ពុជា)",
38811         "kh",
38812         "855"
38813       ],
38814       [
38815         "Cameroon (Cameroun)",
38816         "cm",
38817         "237"
38818       ],
38819       [
38820         "Canada",
38821         "ca",
38822         "1",
38823         1,
38824         ["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"]
38825       ],
38826       [
38827         "Cape Verde (Kabu Verdi)",
38828         "cv",
38829         "238"
38830       ],
38831       [
38832         "Caribbean Netherlands",
38833         "bq",
38834         "599",
38835         1
38836       ],
38837       [
38838         "Cayman Islands",
38839         "ky",
38840         "1345"
38841       ],
38842       [
38843         "Central African Republic (République centrafricaine)",
38844         "cf",
38845         "236"
38846       ],
38847       [
38848         "Chad (Tchad)",
38849         "td",
38850         "235"
38851       ],
38852       [
38853         "Chile",
38854         "cl",
38855         "56"
38856       ],
38857       [
38858         "China (中国)",
38859         "cn",
38860         "86"
38861       ],
38862       [
38863         "Christmas Island",
38864         "cx",
38865         "61",
38866         2
38867       ],
38868       [
38869         "Cocos (Keeling) Islands",
38870         "cc",
38871         "61",
38872         1
38873       ],
38874       [
38875         "Colombia",
38876         "co",
38877         "57"
38878       ],
38879       [
38880         "Comoros (‫جزر القمر‬‎)",
38881         "km",
38882         "269"
38883       ],
38884       [
38885         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38886         "cd",
38887         "243"
38888       ],
38889       [
38890         "Congo (Republic) (Congo-Brazzaville)",
38891         "cg",
38892         "242"
38893       ],
38894       [
38895         "Cook Islands",
38896         "ck",
38897         "682"
38898       ],
38899       [
38900         "Costa Rica",
38901         "cr",
38902         "506"
38903       ],
38904       [
38905         "Côte d’Ivoire",
38906         "ci",
38907         "225"
38908       ],
38909       [
38910         "Croatia (Hrvatska)",
38911         "hr",
38912         "385"
38913       ],
38914       [
38915         "Cuba",
38916         "cu",
38917         "53"
38918       ],
38919       [
38920         "Curaçao",
38921         "cw",
38922         "599",
38923         0
38924       ],
38925       [
38926         "Cyprus (Κύπρος)",
38927         "cy",
38928         "357"
38929       ],
38930       [
38931         "Czech Republic (Česká republika)",
38932         "cz",
38933         "420"
38934       ],
38935       [
38936         "Denmark (Danmark)",
38937         "dk",
38938         "45"
38939       ],
38940       [
38941         "Djibouti",
38942         "dj",
38943         "253"
38944       ],
38945       [
38946         "Dominica",
38947         "dm",
38948         "1767"
38949       ],
38950       [
38951         "Dominican Republic (República Dominicana)",
38952         "do",
38953         "1",
38954         2,
38955         ["809", "829", "849"]
38956       ],
38957       [
38958         "Ecuador",
38959         "ec",
38960         "593"
38961       ],
38962       [
38963         "Egypt (‫مصر‬‎)",
38964         "eg",
38965         "20"
38966       ],
38967       [
38968         "El Salvador",
38969         "sv",
38970         "503"
38971       ],
38972       [
38973         "Equatorial Guinea (Guinea Ecuatorial)",
38974         "gq",
38975         "240"
38976       ],
38977       [
38978         "Eritrea",
38979         "er",
38980         "291"
38981       ],
38982       [
38983         "Estonia (Eesti)",
38984         "ee",
38985         "372"
38986       ],
38987       [
38988         "Ethiopia",
38989         "et",
38990         "251"
38991       ],
38992       [
38993         "Falkland Islands (Islas Malvinas)",
38994         "fk",
38995         "500"
38996       ],
38997       [
38998         "Faroe Islands (Føroyar)",
38999         "fo",
39000         "298"
39001       ],
39002       [
39003         "Fiji",
39004         "fj",
39005         "679"
39006       ],
39007       [
39008         "Finland (Suomi)",
39009         "fi",
39010         "358",
39011         0
39012       ],
39013       [
39014         "France",
39015         "fr",
39016         "33"
39017       ],
39018       [
39019         "French Guiana (Guyane française)",
39020         "gf",
39021         "594"
39022       ],
39023       [
39024         "French Polynesia (Polynésie française)",
39025         "pf",
39026         "689"
39027       ],
39028       [
39029         "Gabon",
39030         "ga",
39031         "241"
39032       ],
39033       [
39034         "Gambia",
39035         "gm",
39036         "220"
39037       ],
39038       [
39039         "Georgia (საქართველო)",
39040         "ge",
39041         "995"
39042       ],
39043       [
39044         "Germany (Deutschland)",
39045         "de",
39046         "49"
39047       ],
39048       [
39049         "Ghana (Gaana)",
39050         "gh",
39051         "233"
39052       ],
39053       [
39054         "Gibraltar",
39055         "gi",
39056         "350"
39057       ],
39058       [
39059         "Greece (Ελλάδα)",
39060         "gr",
39061         "30"
39062       ],
39063       [
39064         "Greenland (Kalaallit Nunaat)",
39065         "gl",
39066         "299"
39067       ],
39068       [
39069         "Grenada",
39070         "gd",
39071         "1473"
39072       ],
39073       [
39074         "Guadeloupe",
39075         "gp",
39076         "590",
39077         0
39078       ],
39079       [
39080         "Guam",
39081         "gu",
39082         "1671"
39083       ],
39084       [
39085         "Guatemala",
39086         "gt",
39087         "502"
39088       ],
39089       [
39090         "Guernsey",
39091         "gg",
39092         "44",
39093         1
39094       ],
39095       [
39096         "Guinea (Guinée)",
39097         "gn",
39098         "224"
39099       ],
39100       [
39101         "Guinea-Bissau (Guiné Bissau)",
39102         "gw",
39103         "245"
39104       ],
39105       [
39106         "Guyana",
39107         "gy",
39108         "592"
39109       ],
39110       [
39111         "Haiti",
39112         "ht",
39113         "509"
39114       ],
39115       [
39116         "Honduras",
39117         "hn",
39118         "504"
39119       ],
39120       [
39121         "Hong Kong (香港)",
39122         "hk",
39123         "852"
39124       ],
39125       [
39126         "Hungary (Magyarország)",
39127         "hu",
39128         "36"
39129       ],
39130       [
39131         "Iceland (Ísland)",
39132         "is",
39133         "354"
39134       ],
39135       [
39136         "India (भारत)",
39137         "in",
39138         "91"
39139       ],
39140       [
39141         "Indonesia",
39142         "id",
39143         "62"
39144       ],
39145       [
39146         "Iran (‫ایران‬‎)",
39147         "ir",
39148         "98"
39149       ],
39150       [
39151         "Iraq (‫العراق‬‎)",
39152         "iq",
39153         "964"
39154       ],
39155       [
39156         "Ireland",
39157         "ie",
39158         "353"
39159       ],
39160       [
39161         "Isle of Man",
39162         "im",
39163         "44",
39164         2
39165       ],
39166       [
39167         "Israel (‫ישראל‬‎)",
39168         "il",
39169         "972"
39170       ],
39171       [
39172         "Italy (Italia)",
39173         "it",
39174         "39",
39175         0
39176       ],
39177       [
39178         "Jamaica",
39179         "jm",
39180         "1876"
39181       ],
39182       [
39183         "Japan (日本)",
39184         "jp",
39185         "81"
39186       ],
39187       [
39188         "Jersey",
39189         "je",
39190         "44",
39191         3
39192       ],
39193       [
39194         "Jordan (‫الأردن‬‎)",
39195         "jo",
39196         "962"
39197       ],
39198       [
39199         "Kazakhstan (Казахстан)",
39200         "kz",
39201         "7",
39202         1
39203       ],
39204       [
39205         "Kenya",
39206         "ke",
39207         "254"
39208       ],
39209       [
39210         "Kiribati",
39211         "ki",
39212         "686"
39213       ],
39214       [
39215         "Kosovo",
39216         "xk",
39217         "383"
39218       ],
39219       [
39220         "Kuwait (‫الكويت‬‎)",
39221         "kw",
39222         "965"
39223       ],
39224       [
39225         "Kyrgyzstan (Кыргызстан)",
39226         "kg",
39227         "996"
39228       ],
39229       [
39230         "Laos (ລາວ)",
39231         "la",
39232         "856"
39233       ],
39234       [
39235         "Latvia (Latvija)",
39236         "lv",
39237         "371"
39238       ],
39239       [
39240         "Lebanon (‫لبنان‬‎)",
39241         "lb",
39242         "961"
39243       ],
39244       [
39245         "Lesotho",
39246         "ls",
39247         "266"
39248       ],
39249       [
39250         "Liberia",
39251         "lr",
39252         "231"
39253       ],
39254       [
39255         "Libya (‫ليبيا‬‎)",
39256         "ly",
39257         "218"
39258       ],
39259       [
39260         "Liechtenstein",
39261         "li",
39262         "423"
39263       ],
39264       [
39265         "Lithuania (Lietuva)",
39266         "lt",
39267         "370"
39268       ],
39269       [
39270         "Luxembourg",
39271         "lu",
39272         "352"
39273       ],
39274       [
39275         "Macau (澳門)",
39276         "mo",
39277         "853"
39278       ],
39279       [
39280         "Macedonia (FYROM) (Македонија)",
39281         "mk",
39282         "389"
39283       ],
39284       [
39285         "Madagascar (Madagasikara)",
39286         "mg",
39287         "261"
39288       ],
39289       [
39290         "Malawi",
39291         "mw",
39292         "265"
39293       ],
39294       [
39295         "Malaysia",
39296         "my",
39297         "60"
39298       ],
39299       [
39300         "Maldives",
39301         "mv",
39302         "960"
39303       ],
39304       [
39305         "Mali",
39306         "ml",
39307         "223"
39308       ],
39309       [
39310         "Malta",
39311         "mt",
39312         "356"
39313       ],
39314       [
39315         "Marshall Islands",
39316         "mh",
39317         "692"
39318       ],
39319       [
39320         "Martinique",
39321         "mq",
39322         "596"
39323       ],
39324       [
39325         "Mauritania (‫موريتانيا‬‎)",
39326         "mr",
39327         "222"
39328       ],
39329       [
39330         "Mauritius (Moris)",
39331         "mu",
39332         "230"
39333       ],
39334       [
39335         "Mayotte",
39336         "yt",
39337         "262",
39338         1
39339       ],
39340       [
39341         "Mexico (México)",
39342         "mx",
39343         "52"
39344       ],
39345       [
39346         "Micronesia",
39347         "fm",
39348         "691"
39349       ],
39350       [
39351         "Moldova (Republica Moldova)",
39352         "md",
39353         "373"
39354       ],
39355       [
39356         "Monaco",
39357         "mc",
39358         "377"
39359       ],
39360       [
39361         "Mongolia (Монгол)",
39362         "mn",
39363         "976"
39364       ],
39365       [
39366         "Montenegro (Crna Gora)",
39367         "me",
39368         "382"
39369       ],
39370       [
39371         "Montserrat",
39372         "ms",
39373         "1664"
39374       ],
39375       [
39376         "Morocco (‫المغرب‬‎)",
39377         "ma",
39378         "212",
39379         0
39380       ],
39381       [
39382         "Mozambique (Moçambique)",
39383         "mz",
39384         "258"
39385       ],
39386       [
39387         "Myanmar (Burma) (မြန်မာ)",
39388         "mm",
39389         "95"
39390       ],
39391       [
39392         "Namibia (Namibië)",
39393         "na",
39394         "264"
39395       ],
39396       [
39397         "Nauru",
39398         "nr",
39399         "674"
39400       ],
39401       [
39402         "Nepal (नेपाल)",
39403         "np",
39404         "977"
39405       ],
39406       [
39407         "Netherlands (Nederland)",
39408         "nl",
39409         "31"
39410       ],
39411       [
39412         "New Caledonia (Nouvelle-Calédonie)",
39413         "nc",
39414         "687"
39415       ],
39416       [
39417         "New Zealand",
39418         "nz",
39419         "64"
39420       ],
39421       [
39422         "Nicaragua",
39423         "ni",
39424         "505"
39425       ],
39426       [
39427         "Niger (Nijar)",
39428         "ne",
39429         "227"
39430       ],
39431       [
39432         "Nigeria",
39433         "ng",
39434         "234"
39435       ],
39436       [
39437         "Niue",
39438         "nu",
39439         "683"
39440       ],
39441       [
39442         "Norfolk Island",
39443         "nf",
39444         "672"
39445       ],
39446       [
39447         "North Korea (조선 민주주의 인민 공화국)",
39448         "kp",
39449         "850"
39450       ],
39451       [
39452         "Northern Mariana Islands",
39453         "mp",
39454         "1670"
39455       ],
39456       [
39457         "Norway (Norge)",
39458         "no",
39459         "47",
39460         0
39461       ],
39462       [
39463         "Oman (‫عُمان‬‎)",
39464         "om",
39465         "968"
39466       ],
39467       [
39468         "Pakistan (‫پاکستان‬‎)",
39469         "pk",
39470         "92"
39471       ],
39472       [
39473         "Palau",
39474         "pw",
39475         "680"
39476       ],
39477       [
39478         "Palestine (‫فلسطين‬‎)",
39479         "ps",
39480         "970"
39481       ],
39482       [
39483         "Panama (Panamá)",
39484         "pa",
39485         "507"
39486       ],
39487       [
39488         "Papua New Guinea",
39489         "pg",
39490         "675"
39491       ],
39492       [
39493         "Paraguay",
39494         "py",
39495         "595"
39496       ],
39497       [
39498         "Peru (Perú)",
39499         "pe",
39500         "51"
39501       ],
39502       [
39503         "Philippines",
39504         "ph",
39505         "63"
39506       ],
39507       [
39508         "Poland (Polska)",
39509         "pl",
39510         "48"
39511       ],
39512       [
39513         "Portugal",
39514         "pt",
39515         "351"
39516       ],
39517       [
39518         "Puerto Rico",
39519         "pr",
39520         "1",
39521         3,
39522         ["787", "939"]
39523       ],
39524       [
39525         "Qatar (‫قطر‬‎)",
39526         "qa",
39527         "974"
39528       ],
39529       [
39530         "Réunion (La Réunion)",
39531         "re",
39532         "262",
39533         0
39534       ],
39535       [
39536         "Romania (România)",
39537         "ro",
39538         "40"
39539       ],
39540       [
39541         "Russia (Россия)",
39542         "ru",
39543         "7",
39544         0
39545       ],
39546       [
39547         "Rwanda",
39548         "rw",
39549         "250"
39550       ],
39551       [
39552         "Saint Barthélemy",
39553         "bl",
39554         "590",
39555         1
39556       ],
39557       [
39558         "Saint Helena",
39559         "sh",
39560         "290"
39561       ],
39562       [
39563         "Saint Kitts and Nevis",
39564         "kn",
39565         "1869"
39566       ],
39567       [
39568         "Saint Lucia",
39569         "lc",
39570         "1758"
39571       ],
39572       [
39573         "Saint Martin (Saint-Martin (partie française))",
39574         "mf",
39575         "590",
39576         2
39577       ],
39578       [
39579         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39580         "pm",
39581         "508"
39582       ],
39583       [
39584         "Saint Vincent and the Grenadines",
39585         "vc",
39586         "1784"
39587       ],
39588       [
39589         "Samoa",
39590         "ws",
39591         "685"
39592       ],
39593       [
39594         "San Marino",
39595         "sm",
39596         "378"
39597       ],
39598       [
39599         "São Tomé and Príncipe (São Tomé e Príncipe)",
39600         "st",
39601         "239"
39602       ],
39603       [
39604         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39605         "sa",
39606         "966"
39607       ],
39608       [
39609         "Senegal (Sénégal)",
39610         "sn",
39611         "221"
39612       ],
39613       [
39614         "Serbia (Србија)",
39615         "rs",
39616         "381"
39617       ],
39618       [
39619         "Seychelles",
39620         "sc",
39621         "248"
39622       ],
39623       [
39624         "Sierra Leone",
39625         "sl",
39626         "232"
39627       ],
39628       [
39629         "Singapore",
39630         "sg",
39631         "65"
39632       ],
39633       [
39634         "Sint Maarten",
39635         "sx",
39636         "1721"
39637       ],
39638       [
39639         "Slovakia (Slovensko)",
39640         "sk",
39641         "421"
39642       ],
39643       [
39644         "Slovenia (Slovenija)",
39645         "si",
39646         "386"
39647       ],
39648       [
39649         "Solomon Islands",
39650         "sb",
39651         "677"
39652       ],
39653       [
39654         "Somalia (Soomaaliya)",
39655         "so",
39656         "252"
39657       ],
39658       [
39659         "South Africa",
39660         "za",
39661         "27"
39662       ],
39663       [
39664         "South Korea (대한민국)",
39665         "kr",
39666         "82"
39667       ],
39668       [
39669         "South Sudan (‫جنوب السودان‬‎)",
39670         "ss",
39671         "211"
39672       ],
39673       [
39674         "Spain (España)",
39675         "es",
39676         "34"
39677       ],
39678       [
39679         "Sri Lanka (ශ්‍රී ලංකාව)",
39680         "lk",
39681         "94"
39682       ],
39683       [
39684         "Sudan (‫السودان‬‎)",
39685         "sd",
39686         "249"
39687       ],
39688       [
39689         "Suriname",
39690         "sr",
39691         "597"
39692       ],
39693       [
39694         "Svalbard and Jan Mayen",
39695         "sj",
39696         "47",
39697         1
39698       ],
39699       [
39700         "Swaziland",
39701         "sz",
39702         "268"
39703       ],
39704       [
39705         "Sweden (Sverige)",
39706         "se",
39707         "46"
39708       ],
39709       [
39710         "Switzerland (Schweiz)",
39711         "ch",
39712         "41"
39713       ],
39714       [
39715         "Syria (‫سوريا‬‎)",
39716         "sy",
39717         "963"
39718       ],
39719       [
39720         "Taiwan (台灣)",
39721         "tw",
39722         "886"
39723       ],
39724       [
39725         "Tajikistan",
39726         "tj",
39727         "992"
39728       ],
39729       [
39730         "Tanzania",
39731         "tz",
39732         "255"
39733       ],
39734       [
39735         "Thailand (ไทย)",
39736         "th",
39737         "66"
39738       ],
39739       [
39740         "Timor-Leste",
39741         "tl",
39742         "670"
39743       ],
39744       [
39745         "Togo",
39746         "tg",
39747         "228"
39748       ],
39749       [
39750         "Tokelau",
39751         "tk",
39752         "690"
39753       ],
39754       [
39755         "Tonga",
39756         "to",
39757         "676"
39758       ],
39759       [
39760         "Trinidad and Tobago",
39761         "tt",
39762         "1868"
39763       ],
39764       [
39765         "Tunisia (‫تونس‬‎)",
39766         "tn",
39767         "216"
39768       ],
39769       [
39770         "Turkey (Türkiye)",
39771         "tr",
39772         "90"
39773       ],
39774       [
39775         "Turkmenistan",
39776         "tm",
39777         "993"
39778       ],
39779       [
39780         "Turks and Caicos Islands",
39781         "tc",
39782         "1649"
39783       ],
39784       [
39785         "Tuvalu",
39786         "tv",
39787         "688"
39788       ],
39789       [
39790         "U.S. Virgin Islands",
39791         "vi",
39792         "1340"
39793       ],
39794       [
39795         "Uganda",
39796         "ug",
39797         "256"
39798       ],
39799       [
39800         "Ukraine (Україна)",
39801         "ua",
39802         "380"
39803       ],
39804       [
39805         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39806         "ae",
39807         "971"
39808       ],
39809       [
39810         "United Kingdom",
39811         "gb",
39812         "44",
39813         0
39814       ],
39815       [
39816         "United States",
39817         "us",
39818         "1",
39819         0
39820       ],
39821       [
39822         "Uruguay",
39823         "uy",
39824         "598"
39825       ],
39826       [
39827         "Uzbekistan (Oʻzbekiston)",
39828         "uz",
39829         "998"
39830       ],
39831       [
39832         "Vanuatu",
39833         "vu",
39834         "678"
39835       ],
39836       [
39837         "Vatican City (Città del Vaticano)",
39838         "va",
39839         "39",
39840         1
39841       ],
39842       [
39843         "Venezuela",
39844         "ve",
39845         "58"
39846       ],
39847       [
39848         "Vietnam (Việt Nam)",
39849         "vn",
39850         "84"
39851       ],
39852       [
39853         "Wallis and Futuna (Wallis-et-Futuna)",
39854         "wf",
39855         "681"
39856       ],
39857       [
39858         "Western Sahara (‫الصحراء الغربية‬‎)",
39859         "eh",
39860         "212",
39861         1
39862       ],
39863       [
39864         "Yemen (‫اليمن‬‎)",
39865         "ye",
39866         "967"
39867       ],
39868       [
39869         "Zambia",
39870         "zm",
39871         "260"
39872       ],
39873       [
39874         "Zimbabwe",
39875         "zw",
39876         "263"
39877       ],
39878       [
39879         "Åland Islands",
39880         "ax",
39881         "358",
39882         1
39883       ]
39884   ];
39885   
39886   return d;
39887 }/**
39888 *    This script refer to:
39889 *    Title: International Telephone Input
39890 *    Author: Jack O'Connor
39891 *    Code version:  v12.1.12
39892 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39893 **/
39894
39895 /**
39896  * @class Roo.bootstrap.PhoneInput
39897  * @extends Roo.bootstrap.TriggerField
39898  * An input with International dial-code selection
39899  
39900  * @cfg {String} defaultDialCode default '+852'
39901  * @cfg {Array} preferedCountries default []
39902   
39903  * @constructor
39904  * Create a new PhoneInput.
39905  * @param {Object} config Configuration options
39906  */
39907
39908 Roo.bootstrap.PhoneInput = function(config) {
39909     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39910 };
39911
39912 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39913         
39914         listWidth: undefined,
39915         
39916         selectedClass: 'active',
39917         
39918         invalidClass : "has-warning",
39919         
39920         validClass: 'has-success',
39921         
39922         allowed: '0123456789',
39923         
39924         max_length: 15,
39925         
39926         /**
39927          * @cfg {String} defaultDialCode The default dial code when initializing the input
39928          */
39929         defaultDialCode: '+852',
39930         
39931         /**
39932          * @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
39933          */
39934         preferedCountries: false,
39935         
39936         getAutoCreate : function()
39937         {
39938             var data = Roo.bootstrap.PhoneInputData();
39939             var align = this.labelAlign || this.parentLabelAlign();
39940             var id = Roo.id();
39941             
39942             this.allCountries = [];
39943             this.dialCodeMapping = [];
39944             
39945             for (var i = 0; i < data.length; i++) {
39946               var c = data[i];
39947               this.allCountries[i] = {
39948                 name: c[0],
39949                 iso2: c[1],
39950                 dialCode: c[2],
39951                 priority: c[3] || 0,
39952                 areaCodes: c[4] || null
39953               };
39954               this.dialCodeMapping[c[2]] = {
39955                   name: c[0],
39956                   iso2: c[1],
39957                   priority: c[3] || 0,
39958                   areaCodes: c[4] || null
39959               };
39960             }
39961             
39962             var cfg = {
39963                 cls: 'form-group',
39964                 cn: []
39965             };
39966             
39967             var input =  {
39968                 tag: 'input',
39969                 id : id,
39970                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39971                 maxlength: this.max_length,
39972                 cls : 'form-control tel-input',
39973                 autocomplete: 'new-password'
39974             };
39975             
39976             var hiddenInput = {
39977                 tag: 'input',
39978                 type: 'hidden',
39979                 cls: 'hidden-tel-input'
39980             };
39981             
39982             if (this.name) {
39983                 hiddenInput.name = this.name;
39984             }
39985             
39986             if (this.disabled) {
39987                 input.disabled = true;
39988             }
39989             
39990             var flag_container = {
39991                 tag: 'div',
39992                 cls: 'flag-box',
39993                 cn: [
39994                     {
39995                         tag: 'div',
39996                         cls: 'flag'
39997                     },
39998                     {
39999                         tag: 'div',
40000                         cls: 'caret'
40001                     }
40002                 ]
40003             };
40004             
40005             var box = {
40006                 tag: 'div',
40007                 cls: this.hasFeedback ? 'has-feedback' : '',
40008                 cn: [
40009                     hiddenInput,
40010                     input,
40011                     {
40012                         tag: 'input',
40013                         cls: 'dial-code-holder',
40014                         disabled: true
40015                     }
40016                 ]
40017             };
40018             
40019             var container = {
40020                 cls: 'roo-select2-container input-group',
40021                 cn: [
40022                     flag_container,
40023                     box
40024                 ]
40025             };
40026             
40027             if (this.fieldLabel.length) {
40028                 var indicator = {
40029                     tag: 'i',
40030                     tooltip: 'This field is required'
40031                 };
40032                 
40033                 var label = {
40034                     tag: 'label',
40035                     'for':  id,
40036                     cls: 'control-label',
40037                     cn: []
40038                 };
40039                 
40040                 var label_text = {
40041                     tag: 'span',
40042                     html: this.fieldLabel
40043                 };
40044                 
40045                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40046                 label.cn = [
40047                     indicator,
40048                     label_text
40049                 ];
40050                 
40051                 if(this.indicatorpos == 'right') {
40052                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40053                     label.cn = [
40054                         label_text,
40055                         indicator
40056                     ];
40057                 }
40058                 
40059                 if(align == 'left') {
40060                     container = {
40061                         tag: 'div',
40062                         cn: [
40063                             container
40064                         ]
40065                     };
40066                     
40067                     if(this.labelWidth > 12){
40068                         label.style = "width: " + this.labelWidth + 'px';
40069                     }
40070                     if(this.labelWidth < 13 && this.labelmd == 0){
40071                         this.labelmd = this.labelWidth;
40072                     }
40073                     if(this.labellg > 0){
40074                         label.cls += ' col-lg-' + this.labellg;
40075                         input.cls += ' col-lg-' + (12 - this.labellg);
40076                     }
40077                     if(this.labelmd > 0){
40078                         label.cls += ' col-md-' + this.labelmd;
40079                         container.cls += ' col-md-' + (12 - this.labelmd);
40080                     }
40081                     if(this.labelsm > 0){
40082                         label.cls += ' col-sm-' + this.labelsm;
40083                         container.cls += ' col-sm-' + (12 - this.labelsm);
40084                     }
40085                     if(this.labelxs > 0){
40086                         label.cls += ' col-xs-' + this.labelxs;
40087                         container.cls += ' col-xs-' + (12 - this.labelxs);
40088                     }
40089                 }
40090             }
40091             
40092             cfg.cn = [
40093                 label,
40094                 container
40095             ];
40096             
40097             var settings = this;
40098             
40099             ['xs','sm','md','lg'].map(function(size){
40100                 if (settings[size]) {
40101                     cfg.cls += ' col-' + size + '-' + settings[size];
40102                 }
40103             });
40104             
40105             this.store = new Roo.data.Store({
40106                 proxy : new Roo.data.MemoryProxy({}),
40107                 reader : new Roo.data.JsonReader({
40108                     fields : [
40109                         {
40110                             'name' : 'name',
40111                             'type' : 'string'
40112                         },
40113                         {
40114                             'name' : 'iso2',
40115                             'type' : 'string'
40116                         },
40117                         {
40118                             'name' : 'dialCode',
40119                             'type' : 'string'
40120                         },
40121                         {
40122                             'name' : 'priority',
40123                             'type' : 'string'
40124                         },
40125                         {
40126                             'name' : 'areaCodes',
40127                             'type' : 'string'
40128                         }
40129                     ]
40130                 })
40131             });
40132             
40133             if(!this.preferedCountries) {
40134                 this.preferedCountries = [
40135                     'hk',
40136                     'gb',
40137                     'us'
40138                 ];
40139             }
40140             
40141             var p = this.preferedCountries.reverse();
40142             
40143             if(p) {
40144                 for (var i = 0; i < p.length; i++) {
40145                     for (var j = 0; j < this.allCountries.length; j++) {
40146                         if(this.allCountries[j].iso2 == p[i]) {
40147                             var t = this.allCountries[j];
40148                             this.allCountries.splice(j,1);
40149                             this.allCountries.unshift(t);
40150                         }
40151                     } 
40152                 }
40153             }
40154             
40155             this.store.proxy.data = {
40156                 success: true,
40157                 data: this.allCountries
40158             };
40159             
40160             return cfg;
40161         },
40162         
40163         initEvents : function()
40164         {
40165             this.createList();
40166             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40167             
40168             this.indicator = this.indicatorEl();
40169             this.flag = this.flagEl();
40170             this.dialCodeHolder = this.dialCodeHolderEl();
40171             
40172             this.trigger = this.el.select('div.flag-box',true).first();
40173             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40174             
40175             var _this = this;
40176             
40177             (function(){
40178                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40179                 _this.list.setWidth(lw);
40180             }).defer(100);
40181             
40182             this.list.on('mouseover', this.onViewOver, this);
40183             this.list.on('mousemove', this.onViewMove, this);
40184             this.inputEl().on("keyup", this.onKeyUp, this);
40185             this.inputEl().on("keypress", this.onKeyPress, this);
40186             
40187             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40188
40189             this.view = new Roo.View(this.list, this.tpl, {
40190                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40191             });
40192             
40193             this.view.on('click', this.onViewClick, this);
40194             this.setValue(this.defaultDialCode);
40195         },
40196         
40197         onTriggerClick : function(e)
40198         {
40199             Roo.log('trigger click');
40200             if(this.disabled){
40201                 return;
40202             }
40203             
40204             if(this.isExpanded()){
40205                 this.collapse();
40206                 this.hasFocus = false;
40207             }else {
40208                 this.store.load({});
40209                 this.hasFocus = true;
40210                 this.expand();
40211             }
40212         },
40213         
40214         isExpanded : function()
40215         {
40216             return this.list.isVisible();
40217         },
40218         
40219         collapse : function()
40220         {
40221             if(!this.isExpanded()){
40222                 return;
40223             }
40224             this.list.hide();
40225             Roo.get(document).un('mousedown', this.collapseIf, this);
40226             Roo.get(document).un('mousewheel', this.collapseIf, this);
40227             this.fireEvent('collapse', this);
40228             this.validate();
40229         },
40230         
40231         expand : function()
40232         {
40233             Roo.log('expand');
40234
40235             if(this.isExpanded() || !this.hasFocus){
40236                 return;
40237             }
40238             
40239             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40240             this.list.setWidth(lw);
40241             
40242             this.list.show();
40243             this.restrictHeight();
40244             
40245             Roo.get(document).on('mousedown', this.collapseIf, this);
40246             Roo.get(document).on('mousewheel', this.collapseIf, this);
40247             
40248             this.fireEvent('expand', this);
40249         },
40250         
40251         restrictHeight : function()
40252         {
40253             this.list.alignTo(this.inputEl(), this.listAlign);
40254             this.list.alignTo(this.inputEl(), this.listAlign);
40255         },
40256         
40257         onViewOver : function(e, t)
40258         {
40259             if(this.inKeyMode){
40260                 return;
40261             }
40262             var item = this.view.findItemFromChild(t);
40263             
40264             if(item){
40265                 var index = this.view.indexOf(item);
40266                 this.select(index, false);
40267             }
40268         },
40269
40270         // private
40271         onViewClick : function(view, doFocus, el, e)
40272         {
40273             var index = this.view.getSelectedIndexes()[0];
40274             
40275             var r = this.store.getAt(index);
40276             
40277             if(r){
40278                 this.onSelect(r, index);
40279             }
40280             if(doFocus !== false && !this.blockFocus){
40281                 this.inputEl().focus();
40282             }
40283         },
40284         
40285         onViewMove : function(e, t)
40286         {
40287             this.inKeyMode = false;
40288         },
40289         
40290         select : function(index, scrollIntoView)
40291         {
40292             this.selectedIndex = index;
40293             this.view.select(index);
40294             if(scrollIntoView !== false){
40295                 var el = this.view.getNode(index);
40296                 if(el){
40297                     this.list.scrollChildIntoView(el, false);
40298                 }
40299             }
40300         },
40301         
40302         createList : function()
40303         {
40304             this.list = Roo.get(document.body).createChild({
40305                 tag: 'ul',
40306                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40307                 style: 'display:none'
40308             });
40309             
40310             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40311         },
40312         
40313         collapseIf : function(e)
40314         {
40315             var in_combo  = e.within(this.el);
40316             var in_list =  e.within(this.list);
40317             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40318             
40319             if (in_combo || in_list || is_list) {
40320                 return;
40321             }
40322             this.collapse();
40323         },
40324         
40325         onSelect : function(record, index)
40326         {
40327             if(this.fireEvent('beforeselect', this, record, index) !== false){
40328                 
40329                 this.setFlagClass(record.data.iso2);
40330                 this.setDialCode(record.data.dialCode);
40331                 this.hasFocus = false;
40332                 this.collapse();
40333                 this.fireEvent('select', this, record, index);
40334             }
40335         },
40336         
40337         flagEl : function()
40338         {
40339             var flag = this.el.select('div.flag',true).first();
40340             if(!flag){
40341                 return false;
40342             }
40343             return flag;
40344         },
40345         
40346         dialCodeHolderEl : function()
40347         {
40348             var d = this.el.select('input.dial-code-holder',true).first();
40349             if(!d){
40350                 return false;
40351             }
40352             return d;
40353         },
40354         
40355         setDialCode : function(v)
40356         {
40357             this.dialCodeHolder.dom.value = '+'+v;
40358         },
40359         
40360         setFlagClass : function(n)
40361         {
40362             this.flag.dom.className = 'flag '+n;
40363         },
40364         
40365         getValue : function()
40366         {
40367             var v = this.inputEl().getValue();
40368             if(this.dialCodeHolder) {
40369                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40370             }
40371             return v;
40372         },
40373         
40374         setValue : function(v)
40375         {
40376             var d = this.getDialCode(v);
40377             
40378             //invalid dial code
40379             if(v.length == 0 || !d || d.length == 0) {
40380                 if(this.rendered){
40381                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40382                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40383                 }
40384                 return;
40385             }
40386             
40387             //valid dial code
40388             this.setFlagClass(this.dialCodeMapping[d].iso2);
40389             this.setDialCode(d);
40390             this.inputEl().dom.value = v.replace('+'+d,'');
40391             this.hiddenEl().dom.value = this.getValue();
40392             
40393             this.validate();
40394         },
40395         
40396         getDialCode : function(v)
40397         {
40398             v = v ||  '';
40399             
40400             if (v.length == 0) {
40401                 return this.dialCodeHolder.dom.value;
40402             }
40403             
40404             var dialCode = "";
40405             if (v.charAt(0) != "+") {
40406                 return false;
40407             }
40408             var numericChars = "";
40409             for (var i = 1; i < v.length; i++) {
40410               var c = v.charAt(i);
40411               if (!isNaN(c)) {
40412                 numericChars += c;
40413                 if (this.dialCodeMapping[numericChars]) {
40414                   dialCode = v.substr(1, i);
40415                 }
40416                 if (numericChars.length == 4) {
40417                   break;
40418                 }
40419               }
40420             }
40421             return dialCode;
40422         },
40423         
40424         reset : function()
40425         {
40426             this.setValue(this.defaultDialCode);
40427             this.validate();
40428         },
40429         
40430         hiddenEl : function()
40431         {
40432             return this.el.select('input.hidden-tel-input',true).first();
40433         },
40434         
40435         // after setting val
40436         onKeyUp : function(e){
40437             this.setValue(this.getValue());
40438         },
40439         
40440         onKeyPress : function(e){
40441             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40442                 e.stopEvent();
40443             }
40444         }
40445         
40446 });
40447 /**
40448  * @class Roo.bootstrap.MoneyField
40449  * @extends Roo.bootstrap.ComboBox
40450  * Bootstrap MoneyField class
40451  * 
40452  * @constructor
40453  * Create a new MoneyField.
40454  * @param {Object} config Configuration options
40455  */
40456
40457 Roo.bootstrap.MoneyField = function(config) {
40458     
40459     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40460     
40461 };
40462
40463 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40464     
40465     /**
40466      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40467      */
40468     allowDecimals : true,
40469     /**
40470      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40471      */
40472     decimalSeparator : ".",
40473     /**
40474      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40475      */
40476     decimalPrecision : 0,
40477     /**
40478      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40479      */
40480     allowNegative : true,
40481     /**
40482      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40483      */
40484     allowZero: true,
40485     /**
40486      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40487      */
40488     minValue : Number.NEGATIVE_INFINITY,
40489     /**
40490      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40491      */
40492     maxValue : Number.MAX_VALUE,
40493     /**
40494      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40495      */
40496     minText : "The minimum value for this field is {0}",
40497     /**
40498      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40499      */
40500     maxText : "The maximum value for this field is {0}",
40501     /**
40502      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40503      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40504      */
40505     nanText : "{0} is not a valid number",
40506     /**
40507      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40508      */
40509     castInt : true,
40510     /**
40511      * @cfg {String} defaults currency of the MoneyField
40512      * value should be in lkey
40513      */
40514     defaultCurrency : false,
40515     /**
40516      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40517      */
40518     thousandsDelimiter : false,
40519     /**
40520      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40521      */
40522     max_length: false,
40523     
40524     inputlg : 9,
40525     inputmd : 9,
40526     inputsm : 9,
40527     inputxs : 6,
40528     
40529     store : false,
40530     
40531     getAutoCreate : function()
40532     {
40533         var align = this.labelAlign || this.parentLabelAlign();
40534         
40535         var id = Roo.id();
40536
40537         var cfg = {
40538             cls: 'form-group',
40539             cn: []
40540         };
40541
40542         var input =  {
40543             tag: 'input',
40544             id : id,
40545             cls : 'form-control roo-money-amount-input',
40546             autocomplete: 'new-password'
40547         };
40548         
40549         var hiddenInput = {
40550             tag: 'input',
40551             type: 'hidden',
40552             id: Roo.id(),
40553             cls: 'hidden-number-input'
40554         };
40555         
40556         if(this.max_length) {
40557             input.maxlength = this.max_length; 
40558         }
40559         
40560         if (this.name) {
40561             hiddenInput.name = this.name;
40562         }
40563
40564         if (this.disabled) {
40565             input.disabled = true;
40566         }
40567
40568         var clg = 12 - this.inputlg;
40569         var cmd = 12 - this.inputmd;
40570         var csm = 12 - this.inputsm;
40571         var cxs = 12 - this.inputxs;
40572         
40573         var container = {
40574             tag : 'div',
40575             cls : 'row roo-money-field',
40576             cn : [
40577                 {
40578                     tag : 'div',
40579                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40580                     cn : [
40581                         {
40582                             tag : 'div',
40583                             cls: 'roo-select2-container input-group',
40584                             cn: [
40585                                 {
40586                                     tag : 'input',
40587                                     cls : 'form-control roo-money-currency-input',
40588                                     autocomplete: 'new-password',
40589                                     readOnly : 1,
40590                                     name : this.currencyName
40591                                 },
40592                                 {
40593                                     tag :'span',
40594                                     cls : 'input-group-addon',
40595                                     cn : [
40596                                         {
40597                                             tag: 'span',
40598                                             cls: 'caret'
40599                                         }
40600                                     ]
40601                                 }
40602                             ]
40603                         }
40604                     ]
40605                 },
40606                 {
40607                     tag : 'div',
40608                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40609                     cn : [
40610                         {
40611                             tag: 'div',
40612                             cls: this.hasFeedback ? 'has-feedback' : '',
40613                             cn: [
40614                                 input
40615                             ]
40616                         }
40617                     ]
40618                 }
40619             ]
40620             
40621         };
40622         
40623         if (this.fieldLabel.length) {
40624             var indicator = {
40625                 tag: 'i',
40626                 tooltip: 'This field is required'
40627             };
40628
40629             var label = {
40630                 tag: 'label',
40631                 'for':  id,
40632                 cls: 'control-label',
40633                 cn: []
40634             };
40635
40636             var label_text = {
40637                 tag: 'span',
40638                 html: this.fieldLabel
40639             };
40640
40641             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40642             label.cn = [
40643                 indicator,
40644                 label_text
40645             ];
40646
40647             if(this.indicatorpos == 'right') {
40648                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40649                 label.cn = [
40650                     label_text,
40651                     indicator
40652                 ];
40653             }
40654
40655             if(align == 'left') {
40656                 container = {
40657                     tag: 'div',
40658                     cn: [
40659                         container
40660                     ]
40661                 };
40662
40663                 if(this.labelWidth > 12){
40664                     label.style = "width: " + this.labelWidth + 'px';
40665                 }
40666                 if(this.labelWidth < 13 && this.labelmd == 0){
40667                     this.labelmd = this.labelWidth;
40668                 }
40669                 if(this.labellg > 0){
40670                     label.cls += ' col-lg-' + this.labellg;
40671                     input.cls += ' col-lg-' + (12 - this.labellg);
40672                 }
40673                 if(this.labelmd > 0){
40674                     label.cls += ' col-md-' + this.labelmd;
40675                     container.cls += ' col-md-' + (12 - this.labelmd);
40676                 }
40677                 if(this.labelsm > 0){
40678                     label.cls += ' col-sm-' + this.labelsm;
40679                     container.cls += ' col-sm-' + (12 - this.labelsm);
40680                 }
40681                 if(this.labelxs > 0){
40682                     label.cls += ' col-xs-' + this.labelxs;
40683                     container.cls += ' col-xs-' + (12 - this.labelxs);
40684                 }
40685             }
40686         }
40687
40688         cfg.cn = [
40689             label,
40690             container,
40691             hiddenInput
40692         ];
40693         
40694         var settings = this;
40695
40696         ['xs','sm','md','lg'].map(function(size){
40697             if (settings[size]) {
40698                 cfg.cls += ' col-' + size + '-' + settings[size];
40699             }
40700         });
40701         
40702         return cfg;
40703     },
40704     
40705     initEvents : function()
40706     {
40707         this.indicator = this.indicatorEl();
40708         
40709         this.initCurrencyEvent();
40710         
40711         this.initNumberEvent();
40712     },
40713     
40714     initCurrencyEvent : function()
40715     {
40716         if (!this.store) {
40717             throw "can not find store for combo";
40718         }
40719         
40720         this.store = Roo.factory(this.store, Roo.data);
40721         this.store.parent = this;
40722         
40723         this.createList();
40724         
40725         this.triggerEl = this.el.select('.input-group-addon', true).first();
40726         
40727         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40728         
40729         var _this = this;
40730         
40731         (function(){
40732             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40733             _this.list.setWidth(lw);
40734         }).defer(100);
40735         
40736         this.list.on('mouseover', this.onViewOver, this);
40737         this.list.on('mousemove', this.onViewMove, this);
40738         this.list.on('scroll', this.onViewScroll, this);
40739         
40740         if(!this.tpl){
40741             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40742         }
40743         
40744         this.view = new Roo.View(this.list, this.tpl, {
40745             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40746         });
40747         
40748         this.view.on('click', this.onViewClick, this);
40749         
40750         this.store.on('beforeload', this.onBeforeLoad, this);
40751         this.store.on('load', this.onLoad, this);
40752         this.store.on('loadexception', this.onLoadException, this);
40753         
40754         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40755             "up" : function(e){
40756                 this.inKeyMode = true;
40757                 this.selectPrev();
40758             },
40759
40760             "down" : function(e){
40761                 if(!this.isExpanded()){
40762                     this.onTriggerClick();
40763                 }else{
40764                     this.inKeyMode = true;
40765                     this.selectNext();
40766                 }
40767             },
40768
40769             "enter" : function(e){
40770                 this.collapse();
40771                 
40772                 if(this.fireEvent("specialkey", this, e)){
40773                     this.onViewClick(false);
40774                 }
40775                 
40776                 return true;
40777             },
40778
40779             "esc" : function(e){
40780                 this.collapse();
40781             },
40782
40783             "tab" : function(e){
40784                 this.collapse();
40785                 
40786                 if(this.fireEvent("specialkey", this, e)){
40787                     this.onViewClick(false);
40788                 }
40789                 
40790                 return true;
40791             },
40792
40793             scope : this,
40794
40795             doRelay : function(foo, bar, hname){
40796                 if(hname == 'down' || this.scope.isExpanded()){
40797                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40798                 }
40799                 return true;
40800             },
40801
40802             forceKeyDown: true
40803         });
40804         
40805         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40806         
40807     },
40808     
40809     initNumberEvent : function(e)
40810     {
40811         this.inputEl().on("keydown" , this.fireKey,  this);
40812         this.inputEl().on("focus", this.onFocus,  this);
40813         this.inputEl().on("blur", this.onBlur,  this);
40814         
40815         this.inputEl().relayEvent('keyup', this);
40816         
40817         if(this.indicator){
40818             this.indicator.addClass('invisible');
40819         }
40820  
40821         this.originalValue = this.getValue();
40822         
40823         if(this.validationEvent == 'keyup'){
40824             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40825             this.inputEl().on('keyup', this.filterValidation, this);
40826         }
40827         else if(this.validationEvent !== false){
40828             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40829         }
40830         
40831         if(this.selectOnFocus){
40832             this.on("focus", this.preFocus, this);
40833             
40834         }
40835         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40836             this.inputEl().on("keypress", this.filterKeys, this);
40837         } else {
40838             this.inputEl().relayEvent('keypress', this);
40839         }
40840         
40841         var allowed = "0123456789";
40842         
40843         if(this.allowDecimals){
40844             allowed += this.decimalSeparator;
40845         }
40846         
40847         if(this.allowNegative){
40848             allowed += "-";
40849         }
40850         
40851         if(this.thousandsDelimiter) {
40852             allowed += ",";
40853         }
40854         
40855         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40856         
40857         var keyPress = function(e){
40858             
40859             var k = e.getKey();
40860             
40861             var c = e.getCharCode();
40862             
40863             if(
40864                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40865                     allowed.indexOf(String.fromCharCode(c)) === -1
40866             ){
40867                 e.stopEvent();
40868                 return;
40869             }
40870             
40871             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40872                 return;
40873             }
40874             
40875             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40876                 e.stopEvent();
40877             }
40878         };
40879         
40880         this.inputEl().on("keypress", keyPress, this);
40881         
40882     },
40883     
40884     onTriggerClick : function(e)
40885     {   
40886         if(this.disabled){
40887             return;
40888         }
40889         
40890         this.page = 0;
40891         this.loadNext = false;
40892         
40893         if(this.isExpanded()){
40894             this.collapse();
40895             return;
40896         }
40897         
40898         this.hasFocus = true;
40899         
40900         if(this.triggerAction == 'all') {
40901             this.doQuery(this.allQuery, true);
40902             return;
40903         }
40904         
40905         this.doQuery(this.getRawValue());
40906     },
40907     
40908     getCurrency : function()
40909     {   
40910         var v = this.currencyEl().getValue();
40911         
40912         return v;
40913     },
40914     
40915     restrictHeight : function()
40916     {
40917         this.list.alignTo(this.currencyEl(), this.listAlign);
40918         this.list.alignTo(this.currencyEl(), this.listAlign);
40919     },
40920     
40921     onViewClick : function(view, doFocus, el, e)
40922     {
40923         var index = this.view.getSelectedIndexes()[0];
40924         
40925         var r = this.store.getAt(index);
40926         
40927         if(r){
40928             this.onSelect(r, index);
40929         }
40930     },
40931     
40932     onSelect : function(record, index){
40933         
40934         if(this.fireEvent('beforeselect', this, record, index) !== false){
40935         
40936             this.setFromCurrencyData(index > -1 ? record.data : false);
40937             
40938             this.collapse();
40939             
40940             this.fireEvent('select', this, record, index);
40941         }
40942     },
40943     
40944     setFromCurrencyData : function(o)
40945     {
40946         var currency = '';
40947         
40948         this.lastCurrency = o;
40949         
40950         if (this.currencyField) {
40951             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40952         } else {
40953             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40954         }
40955         
40956         this.lastSelectionText = currency;
40957         
40958         //setting default currency
40959         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40960             this.setCurrency(this.defaultCurrency);
40961             return;
40962         }
40963         
40964         this.setCurrency(currency);
40965     },
40966     
40967     setFromData : function(o)
40968     {
40969         var c = {};
40970         
40971         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40972         
40973         this.setFromCurrencyData(c);
40974         
40975         var value = '';
40976         
40977         if (this.name) {
40978             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40979         } else {
40980             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40981         }
40982         
40983         this.setValue(value);
40984         
40985     },
40986     
40987     setCurrency : function(v)
40988     {   
40989         this.currencyValue = v;
40990         
40991         if(this.rendered){
40992             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40993             this.validate();
40994         }
40995     },
40996     
40997     setValue : function(v)
40998     {
40999         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41000         
41001         this.value = v;
41002         
41003         if(this.rendered){
41004             
41005             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41006             
41007             this.inputEl().dom.value = (v == '') ? '' :
41008                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41009             
41010             if(!this.allowZero && v === '0') {
41011                 this.hiddenEl().dom.value = '';
41012                 this.inputEl().dom.value = '';
41013             }
41014             
41015             this.validate();
41016         }
41017     },
41018     
41019     getRawValue : function()
41020     {
41021         var v = this.inputEl().getValue();
41022         
41023         return v;
41024     },
41025     
41026     getValue : function()
41027     {
41028         return this.fixPrecision(this.parseValue(this.getRawValue()));
41029     },
41030     
41031     parseValue : function(value)
41032     {
41033         if(this.thousandsDelimiter) {
41034             value += "";
41035             r = new RegExp(",", "g");
41036             value = value.replace(r, "");
41037         }
41038         
41039         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41040         return isNaN(value) ? '' : value;
41041         
41042     },
41043     
41044     fixPrecision : function(value)
41045     {
41046         if(this.thousandsDelimiter) {
41047             value += "";
41048             r = new RegExp(",", "g");
41049             value = value.replace(r, "");
41050         }
41051         
41052         var nan = isNaN(value);
41053         
41054         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41055             return nan ? '' : value;
41056         }
41057         return parseFloat(value).toFixed(this.decimalPrecision);
41058     },
41059     
41060     decimalPrecisionFcn : function(v)
41061     {
41062         return Math.floor(v);
41063     },
41064     
41065     validateValue : function(value)
41066     {
41067         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41068             return false;
41069         }
41070         
41071         var num = this.parseValue(value);
41072         
41073         if(isNaN(num)){
41074             this.markInvalid(String.format(this.nanText, value));
41075             return false;
41076         }
41077         
41078         if(num < this.minValue){
41079             this.markInvalid(String.format(this.minText, this.minValue));
41080             return false;
41081         }
41082         
41083         if(num > this.maxValue){
41084             this.markInvalid(String.format(this.maxText, this.maxValue));
41085             return false;
41086         }
41087         
41088         return true;
41089     },
41090     
41091     validate : function()
41092     {
41093         if(this.disabled || this.allowBlank){
41094             this.markValid();
41095             return true;
41096         }
41097         
41098         var currency = this.getCurrency();
41099         
41100         if(this.validateValue(this.getRawValue()) && currency.length){
41101             this.markValid();
41102             return true;
41103         }
41104         
41105         this.markInvalid();
41106         return false;
41107     },
41108     
41109     getName: function()
41110     {
41111         return this.name;
41112     },
41113     
41114     beforeBlur : function()
41115     {
41116         if(!this.castInt){
41117             return;
41118         }
41119         
41120         var v = this.parseValue(this.getRawValue());
41121         
41122         if(v || v == 0){
41123             this.setValue(v);
41124         }
41125     },
41126     
41127     onBlur : function()
41128     {
41129         this.beforeBlur();
41130         
41131         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41132             //this.el.removeClass(this.focusClass);
41133         }
41134         
41135         this.hasFocus = false;
41136         
41137         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41138             this.validate();
41139         }
41140         
41141         var v = this.getValue();
41142         
41143         if(String(v) !== String(this.startValue)){
41144             this.fireEvent('change', this, v, this.startValue);
41145         }
41146         
41147         this.fireEvent("blur", this);
41148     },
41149     
41150     inputEl : function()
41151     {
41152         return this.el.select('.roo-money-amount-input', true).first();
41153     },
41154     
41155     currencyEl : function()
41156     {
41157         return this.el.select('.roo-money-currency-input', true).first();
41158     },
41159     
41160     hiddenEl : function()
41161     {
41162         return this.el.select('input.hidden-number-input',true).first();
41163     }
41164     
41165 });