roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     }
558    
559 });
560
561  /*
562  * - LGPL
563  *
564  * button
565  * 
566  */
567
568 /**
569  * @class Roo.bootstrap.Button
570  * @extends Roo.bootstrap.Component
571  * Bootstrap Button class
572  * @cfg {String} html The button content
573  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
574  * @cfg {String} size ( lg | sm | xs)
575  * @cfg {String} tag ( a | input | submit)
576  * @cfg {String} href empty or href
577  * @cfg {Boolean} disabled default false;
578  * @cfg {Boolean} isClose default false;
579  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
580  * @cfg {String} badge text for badge
581  * @cfg {String} theme (default|glow)  
582  * @cfg {Boolean} inverse dark themed version
583  * @cfg {Boolean} toggle is it a slidy toggle button
584  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
585  * @cfg {String} ontext text for on slidy toggle state
586  * @cfg {String} offtext text for off slidy toggle state
587  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
588  * @cfg {Boolean} removeClass remove the standard class..
589  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
590  * 
591  * @constructor
592  * Create a new button
593  * @param {Object} config The config object
594  */
595
596
597 Roo.bootstrap.Button = function(config){
598     Roo.bootstrap.Button.superclass.constructor.call(this, config);
599     this.weightClass = ["btn-default", 
600                        "btn-primary", 
601                        "btn-success", 
602                        "btn-info", 
603                        "btn-warning",
604                        "btn-danger",
605                        "btn-link"
606                       ],  
607     this.addEvents({
608         // raw events
609         /**
610          * @event click
611          * When a butotn is pressed
612          * @param {Roo.bootstrap.Button} btn
613          * @param {Roo.EventObject} e
614          */
615         "click" : true,
616          /**
617          * @event toggle
618          * After the button has been toggles
619          * @param {Roo.bootstrap.Button} btn
620          * @param {Roo.EventObject} e
621          * @param {boolean} pressed (also available as button.pressed)
622          */
623         "toggle" : true
624     });
625 };
626
627 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
628     html: false,
629     active: false,
630     weight: '',
631     size: '',
632     tag: 'button',
633     href: '',
634     disabled: false,
635     isClose: false,
636     glyphicon: '',
637     badge: '',
638     theme: 'default',
639     inverse: false,
640     
641     toggle: false,
642     ontext: 'ON',
643     offtext: 'OFF',
644     defaulton: true,
645     preventDefault: true,
646     removeClass: false,
647     name: false,
648     target: false,
649      
650     pressed : null,
651      
652     
653     getAutoCreate : function(){
654         
655         var cfg = {
656             tag : 'button',
657             cls : 'roo-button',
658             html: ''
659         };
660         
661         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
662             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
663             this.tag = 'button';
664         } else {
665             cfg.tag = this.tag;
666         }
667         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
668         
669         if (this.toggle == true) {
670             cfg={
671                 tag: 'div',
672                 cls: 'slider-frame roo-button',
673                 cn: [
674                     {
675                         tag: 'span',
676                         'data-on-text':'ON',
677                         'data-off-text':'OFF',
678                         cls: 'slider-button',
679                         html: this.offtext
680                     }
681                 ]
682             };
683             
684             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
685                 cfg.cls += ' '+this.weight;
686             }
687             
688             return cfg;
689         }
690         
691         if (this.isClose) {
692             cfg.cls += ' close';
693             
694             cfg["aria-hidden"] = true;
695             
696             cfg.html = "&times;";
697             
698             return cfg;
699         }
700         
701          
702         if (this.theme==='default') {
703             cfg.cls = 'btn roo-button';
704             
705             //if (this.parentType != 'Navbar') {
706             this.weight = this.weight.length ?  this.weight : 'default';
707             //}
708             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
709                 
710                 cfg.cls += ' btn-' + this.weight;
711             }
712         } else if (this.theme==='glow') {
713             
714             cfg.tag = 'a';
715             cfg.cls = 'btn-glow roo-button';
716             
717             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
718                 
719                 cfg.cls += ' ' + this.weight;
720             }
721         }
722    
723         
724         if (this.inverse) {
725             this.cls += ' inverse';
726         }
727         
728         
729         if (this.active || this.pressed === true) {
730             cfg.cls += ' active';
731         }
732         
733         if (this.disabled) {
734             cfg.disabled = 'disabled';
735         }
736         
737         if (this.items) {
738             Roo.log('changing to ul' );
739             cfg.tag = 'ul';
740             this.glyphicon = 'caret';
741         }
742         
743         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
744          
745         //gsRoo.log(this.parentType);
746         if (this.parentType === 'Navbar' && !this.parent().bar) {
747             Roo.log('changing to li?');
748             
749             cfg.tag = 'li';
750             
751             cfg.cls = '';
752             cfg.cn =  [{
753                 tag : 'a',
754                 cls : 'roo-button',
755                 html : this.html,
756                 href : this.href || '#'
757             }];
758             if (this.menu) {
759                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
760                 cfg.cls += ' dropdown';
761             }   
762             
763             delete cfg.html;
764             
765         }
766         
767        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
768         
769         if (this.glyphicon) {
770             cfg.html = ' ' + cfg.html;
771             
772             cfg.cn = [
773                 {
774                     tag: 'span',
775                     cls: 'glyphicon glyphicon-' + this.glyphicon
776                 }
777             ];
778         }
779         
780         if (this.badge) {
781             cfg.html += ' ';
782             
783             cfg.tag = 'a';
784             
785 //            cfg.cls='btn roo-button';
786             
787             cfg.href=this.href;
788             
789             var value = cfg.html;
790             
791             if(this.glyphicon){
792                 value = {
793                             tag: 'span',
794                             cls: 'glyphicon glyphicon-' + this.glyphicon,
795                             html: this.html
796                         };
797                 
798             }
799             
800             cfg.cn = [
801                 value,
802                 {
803                     tag: 'span',
804                     cls: 'badge',
805                     html: this.badge
806                 }
807             ];
808             
809             cfg.html='';
810         }
811         
812         if (this.menu) {
813             cfg.cls += ' dropdown';
814             cfg.html = typeof(cfg.html) != 'undefined' ?
815                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
816         }
817         
818         if (cfg.tag !== 'a' && this.href !== '') {
819             throw "Tag must be a to set href.";
820         } else if (this.href.length > 0) {
821             cfg.href = this.href;
822         }
823         
824         if(this.removeClass){
825             cfg.cls = '';
826         }
827         
828         if(this.target){
829             cfg.target = this.target;
830         }
831         
832         return cfg;
833     },
834     initEvents: function() {
835        // Roo.log('init events?');
836 //        Roo.log(this.el.dom);
837         // add the menu...
838         
839         if (typeof (this.menu) != 'undefined') {
840             this.menu.parentType = this.xtype;
841             this.menu.triggerEl = this.el;
842             this.addxtype(Roo.apply({}, this.menu));
843         }
844
845
846        if (this.el.hasClass('roo-button')) {
847             this.el.on('click', this.onClick, this);
848        } else {
849             this.el.select('.roo-button').on('click', this.onClick, this);
850        }
851        
852        if(this.removeClass){
853            this.el.on('click', this.onClick, this);
854        }
855        
856        this.el.enableDisplayMode();
857         
858     },
859     onClick : function(e)
860     {
861         if (this.disabled) {
862             return;
863         }
864         
865         Roo.log('button on click ');
866         if(this.preventDefault){
867             e.preventDefault();
868         }
869         
870         if (this.pressed === true || this.pressed === false) {
871             this.toggleActive(e);
872         }
873         
874         
875         this.fireEvent('click', this, e);
876     },
877     
878     /**
879      * Enables this button
880      */
881     enable : function()
882     {
883         this.disabled = false;
884         this.el.removeClass('disabled');
885     },
886     
887     /**
888      * Disable this button
889      */
890     disable : function()
891     {
892         this.disabled = true;
893         this.el.addClass('disabled');
894     },
895      /**
896      * sets the active state on/off, 
897      * @param {Boolean} state (optional) Force a particular state
898      */
899     setActive : function(v) {
900         
901         this.el[v ? 'addClass' : 'removeClass']('active');
902         this.pressed = v;
903     },
904      /**
905      * toggles the current active state 
906      */
907     toggleActive : function(e)
908     {
909         this.setActive(!this.pressed);
910         this.fireEvent('toggle', this, e, !this.pressed);
911     },
912      /**
913      * get the current active state
914      * @return {boolean} true if it's active
915      */
916     isActive : function()
917     {
918         return this.el.hasClass('active');
919     },
920     /**
921      * set the text of the first selected button
922      */
923     setText : function(str)
924     {
925         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
926     },
927     /**
928      * get the text of the first selected button
929      */
930     getText : function()
931     {
932         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
933     },
934     
935     setWeight : function(str)
936     {
937         this.el.removeClass(this.weightClass);
938         this.el.addClass('btn-' + str);        
939     }
940     
941     
942 });
943
944  /*
945  * - LGPL
946  *
947  * column
948  * 
949  */
950
951 /**
952  * @class Roo.bootstrap.Column
953  * @extends Roo.bootstrap.Component
954  * Bootstrap Column class
955  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
956  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
957  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
958  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
959  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
960  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
961  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
962  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
963  *
964  * 
965  * @cfg {Boolean} hidden (true|false) hide the element
966  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
967  * @cfg {String} fa (ban|check|...) font awesome icon
968  * @cfg {Number} fasize (1|2|....) font awsome size
969
970  * @cfg {String} icon (info-sign|check|...) glyphicon name
971
972  * @cfg {String} html content of column.
973  * 
974  * @constructor
975  * Create a new Column
976  * @param {Object} config The config object
977  */
978
979 Roo.bootstrap.Column = function(config){
980     Roo.bootstrap.Column.superclass.constructor.call(this, config);
981 };
982
983 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
984     
985     xs: false,
986     sm: false,
987     md: false,
988     lg: false,
989     xsoff: false,
990     smoff: false,
991     mdoff: false,
992     lgoff: false,
993     html: '',
994     offset: 0,
995     alert: false,
996     fa: false,
997     icon : false,
998     hidden : false,
999     fasize : 1,
1000     
1001     getAutoCreate : function(){
1002         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1003         
1004         cfg = {
1005             tag: 'div',
1006             cls: 'column'
1007         };
1008         
1009         var settings=this;
1010         ['xs','sm','md','lg'].map(function(size){
1011             //Roo.log( size + ':' + settings[size]);
1012             
1013             if (settings[size+'off'] !== false) {
1014                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1015             }
1016             
1017             if (settings[size] === false) {
1018                 return;
1019             }
1020             
1021             if (!settings[size]) { // 0 = hidden
1022                 cfg.cls += ' hidden-' + size;
1023                 return;
1024             }
1025             cfg.cls += ' col-' + size + '-' + settings[size];
1026             
1027         });
1028         
1029         if (this.hidden) {
1030             cfg.cls += ' hidden';
1031         }
1032         
1033         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1034             cfg.cls +=' alert alert-' + this.alert;
1035         }
1036         
1037         
1038         if (this.html.length) {
1039             cfg.html = this.html;
1040         }
1041         if (this.fa) {
1042             var fasize = '';
1043             if (this.fasize > 1) {
1044                 fasize = ' fa-' + this.fasize + 'x';
1045             }
1046             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1047             
1048             
1049         }
1050         if (this.icon) {
1051             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1052         }
1053         
1054         return cfg;
1055     }
1056    
1057 });
1058
1059  
1060
1061  /*
1062  * - LGPL
1063  *
1064  * page container.
1065  * 
1066  */
1067
1068
1069 /**
1070  * @class Roo.bootstrap.Container
1071  * @extends Roo.bootstrap.Component
1072  * Bootstrap Container class
1073  * @cfg {Boolean} jumbotron is it a jumbotron element
1074  * @cfg {String} html content of element
1075  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1076  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1077  * @cfg {String} header content of header (for panel)
1078  * @cfg {String} footer content of footer (for panel)
1079  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1080  * @cfg {String} tag (header|aside|section) type of HTML tag.
1081  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1082  * @cfg {String} fa font awesome icon
1083  * @cfg {String} icon (info-sign|check|...) glyphicon name
1084  * @cfg {Boolean} hidden (true|false) hide the element
1085  * @cfg {Boolean} expandable (true|false) default false
1086  * @cfg {Boolean} expanded (true|false) default true
1087  * @cfg {String} rheader contet on the right of header
1088  * @cfg {Boolean} clickable (true|false) default false
1089
1090  *     
1091  * @constructor
1092  * Create a new Container
1093  * @param {Object} config The config object
1094  */
1095
1096 Roo.bootstrap.Container = function(config){
1097     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1098     
1099     this.addEvents({
1100         // raw events
1101          /**
1102          * @event expand
1103          * After the panel has been expand
1104          * 
1105          * @param {Roo.bootstrap.Container} this
1106          */
1107         "expand" : true,
1108         /**
1109          * @event collapse
1110          * After the panel has been collapsed
1111          * 
1112          * @param {Roo.bootstrap.Container} this
1113          */
1114         "collapse" : true,
1115         /**
1116          * @event click
1117          * When a element is chick
1118          * @param {Roo.bootstrap.Container} this
1119          * @param {Roo.EventObject} e
1120          */
1121         "click" : true
1122     });
1123 };
1124
1125 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1126     
1127     jumbotron : false,
1128     well: '',
1129     panel : '',
1130     header: '',
1131     footer : '',
1132     sticky: '',
1133     tag : false,
1134     alert : false,
1135     fa: false,
1136     icon : false,
1137     expandable : false,
1138     rheader : '',
1139     expanded : true,
1140     clickable: false,
1141   
1142      
1143     getChildContainer : function() {
1144         
1145         if(!this.el){
1146             return false;
1147         }
1148         
1149         if (this.panel.length) {
1150             return this.el.select('.panel-body',true).first();
1151         }
1152         
1153         return this.el;
1154     },
1155     
1156     
1157     getAutoCreate : function(){
1158         
1159         var cfg = {
1160             tag : this.tag || 'div',
1161             html : '',
1162             cls : ''
1163         };
1164         if (this.jumbotron) {
1165             cfg.cls = 'jumbotron';
1166         }
1167         
1168         
1169         
1170         // - this is applied by the parent..
1171         //if (this.cls) {
1172         //    cfg.cls = this.cls + '';
1173         //}
1174         
1175         if (this.sticky.length) {
1176             
1177             var bd = Roo.get(document.body);
1178             if (!bd.hasClass('bootstrap-sticky')) {
1179                 bd.addClass('bootstrap-sticky');
1180                 Roo.select('html',true).setStyle('height', '100%');
1181             }
1182              
1183             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1184         }
1185         
1186         
1187         if (this.well.length) {
1188             switch (this.well) {
1189                 case 'lg':
1190                 case 'sm':
1191                     cfg.cls +=' well well-' +this.well;
1192                     break;
1193                 default:
1194                     cfg.cls +=' well';
1195                     break;
1196             }
1197         }
1198         
1199         if (this.hidden) {
1200             cfg.cls += ' hidden';
1201         }
1202         
1203         
1204         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1205             cfg.cls +=' alert alert-' + this.alert;
1206         }
1207         
1208         var body = cfg;
1209         
1210         if (this.panel.length) {
1211             cfg.cls += ' panel panel-' + this.panel;
1212             cfg.cn = [];
1213             if (this.header.length) {
1214                 
1215                 var h = [];
1216                 
1217                 if(this.expandable){
1218                     
1219                     cfg.cls = cfg.cls + ' expandable';
1220                     
1221                     h.push({
1222                         tag: 'i',
1223                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1224                     });
1225                     
1226                 }
1227                 
1228                 h.push(
1229                     {
1230                         tag: 'span',
1231                         cls : 'panel-title',
1232                         html : (this.expandable ? '&nbsp;' : '') + this.header
1233                     },
1234                     {
1235                         tag: 'span',
1236                         cls: 'panel-header-right',
1237                         html: this.rheader
1238                     }
1239                 );
1240                 
1241                 cfg.cn.push({
1242                     cls : 'panel-heading',
1243                     style : this.expandable ? 'cursor: pointer' : '',
1244                     cn : h
1245                 });
1246                 
1247             }
1248             
1249             body = false;
1250             cfg.cn.push({
1251                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1252                 html : this.html
1253             });
1254             
1255             
1256             if (this.footer.length) {
1257                 cfg.cn.push({
1258                     cls : 'panel-footer',
1259                     html : this.footer
1260                     
1261                 });
1262             }
1263             
1264         }
1265         
1266         if (body) {
1267             body.html = this.html || cfg.html;
1268             // prefix with the icons..
1269             if (this.fa) {
1270                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1271             }
1272             if (this.icon) {
1273                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1274             }
1275             
1276             
1277         }
1278         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1279             cfg.cls =  'container';
1280         }
1281         
1282         return cfg;
1283     },
1284     
1285     initEvents: function() 
1286     {
1287         if(this.expandable){
1288             var headerEl = this.headerEl();
1289         
1290             if(headerEl){
1291                 headerEl.on('click', this.onToggleClick, this);
1292             }
1293         }
1294         
1295         if(this.clickable){
1296             this.el.on('click', this.onClick, this);
1297         }
1298         
1299     },
1300     
1301     onToggleClick : function()
1302     {
1303         var headerEl = this.headerEl();
1304         
1305         if(!headerEl){
1306             return;
1307         }
1308         
1309         if(this.expanded){
1310             this.collapse();
1311             return;
1312         }
1313         
1314         this.expand();
1315     },
1316     
1317     expand : function()
1318     {
1319         if(this.fireEvent('expand', this)) {
1320             
1321             this.expanded = true;
1322             
1323             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1324             
1325             this.el.select('.panel-body',true).first().removeClass('hide');
1326             
1327             var toggleEl = this.toggleEl();
1328
1329             if(!toggleEl){
1330                 return;
1331             }
1332
1333             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1334         }
1335         
1336     },
1337     
1338     collapse : function()
1339     {
1340         if(this.fireEvent('collapse', this)) {
1341             
1342             this.expanded = false;
1343             
1344             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1345             this.el.select('.panel-body',true).first().addClass('hide');
1346         
1347             var toggleEl = this.toggleEl();
1348
1349             if(!toggleEl){
1350                 return;
1351             }
1352
1353             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1354         }
1355     },
1356     
1357     toggleEl : function()
1358     {
1359         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1360             return;
1361         }
1362         
1363         return this.el.select('.panel-heading .fa',true).first();
1364     },
1365     
1366     headerEl : function()
1367     {
1368         if(!this.el || !this.panel.length || !this.header.length){
1369             return;
1370         }
1371         
1372         return this.el.select('.panel-heading',true).first()
1373     },
1374     
1375     bodyEl : function()
1376     {
1377         if(!this.el || !this.panel.length){
1378             return;
1379         }
1380         
1381         return this.el.select('.panel-body',true).first()
1382     },
1383     
1384     titleEl : function()
1385     {
1386         if(!this.el || !this.panel.length || !this.header.length){
1387             return;
1388         }
1389         
1390         return this.el.select('.panel-title',true).first();
1391     },
1392     
1393     setTitle : function(v)
1394     {
1395         var titleEl = this.titleEl();
1396         
1397         if(!titleEl){
1398             return;
1399         }
1400         
1401         titleEl.dom.innerHTML = v;
1402     },
1403     
1404     getTitle : function()
1405     {
1406         
1407         var titleEl = this.titleEl();
1408         
1409         if(!titleEl){
1410             return '';
1411         }
1412         
1413         return titleEl.dom.innerHTML;
1414     },
1415     
1416     setRightTitle : function(v)
1417     {
1418         var t = this.el.select('.panel-header-right',true).first();
1419         
1420         if(!t){
1421             return;
1422         }
1423         
1424         t.dom.innerHTML = v;
1425     },
1426     
1427     onClick : function(e)
1428     {
1429         e.preventDefault();
1430         
1431         this.fireEvent('click', this, e);
1432     }
1433 });
1434
1435  /*
1436  * - LGPL
1437  *
1438  * image
1439  * 
1440  */
1441
1442
1443 /**
1444  * @class Roo.bootstrap.Img
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Img class
1447  * @cfg {Boolean} imgResponsive false | true
1448  * @cfg {String} border rounded | circle | thumbnail
1449  * @cfg {String} src image source
1450  * @cfg {String} alt image alternative text
1451  * @cfg {String} href a tag href
1452  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1453  * @cfg {String} xsUrl xs image source
1454  * @cfg {String} smUrl sm image source
1455  * @cfg {String} mdUrl md image source
1456  * @cfg {String} lgUrl lg image source
1457  * 
1458  * @constructor
1459  * Create a new Input
1460  * @param {Object} config The config object
1461  */
1462
1463 Roo.bootstrap.Img = function(config){
1464     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1465     
1466     this.addEvents({
1467         // img events
1468         /**
1469          * @event click
1470          * The img click event for the img.
1471          * @param {Roo.EventObject} e
1472          */
1473         "click" : true
1474     });
1475 };
1476
1477 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1478     
1479     imgResponsive: true,
1480     border: '',
1481     src: 'about:blank',
1482     href: false,
1483     target: false,
1484     xsUrl: '',
1485     smUrl: '',
1486     mdUrl: '',
1487     lgUrl: '',
1488
1489     getAutoCreate : function()
1490     {   
1491         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1492             return this.createSingleImg();
1493         }
1494         
1495         var cfg = {
1496             tag: 'div',
1497             cls: 'roo-image-responsive-group',
1498             cn: []
1499         };
1500         var _this = this;
1501         
1502         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1503             
1504             if(!_this[size + 'Url']){
1505                 return;
1506             }
1507             
1508             var img = {
1509                 tag: 'img',
1510                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1511                 html: _this.html || cfg.html,
1512                 src: _this[size + 'Url']
1513             };
1514             
1515             img.cls += ' roo-image-responsive-' + size;
1516             
1517             var s = ['xs', 'sm', 'md', 'lg'];
1518             
1519             s.splice(s.indexOf(size), 1);
1520             
1521             Roo.each(s, function(ss){
1522                 img.cls += ' hidden-' + ss;
1523             });
1524             
1525             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1526                 cfg.cls += ' img-' + _this.border;
1527             }
1528             
1529             if(_this.alt){
1530                 cfg.alt = _this.alt;
1531             }
1532             
1533             if(_this.href){
1534                 var a = {
1535                     tag: 'a',
1536                     href: _this.href,
1537                     cn: [
1538                         img
1539                     ]
1540                 };
1541
1542                 if(this.target){
1543                     a.target = _this.target;
1544                 }
1545             }
1546             
1547             cfg.cn.push((_this.href) ? a : img);
1548             
1549         });
1550         
1551         return cfg;
1552     },
1553     
1554     createSingleImg : function()
1555     {
1556         var cfg = {
1557             tag: 'img',
1558             cls: (this.imgResponsive) ? 'img-responsive' : '',
1559             html : null,
1560             src : 'about:blank'  // just incase src get's set to undefined?!?
1561         };
1562         
1563         cfg.html = this.html || cfg.html;
1564         
1565         cfg.src = this.src || cfg.src;
1566         
1567         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1568             cfg.cls += ' img-' + this.border;
1569         }
1570         
1571         if(this.alt){
1572             cfg.alt = this.alt;
1573         }
1574         
1575         if(this.href){
1576             var a = {
1577                 tag: 'a',
1578                 href: this.href,
1579                 cn: [
1580                     cfg
1581                 ]
1582             };
1583             
1584             if(this.target){
1585                 a.target = this.target;
1586             }
1587             
1588         }
1589         
1590         return (this.href) ? a : cfg;
1591     },
1592     
1593     initEvents: function() 
1594     {
1595         if(!this.href){
1596             this.el.on('click', this.onClick, this);
1597         }
1598         
1599     },
1600     
1601     onClick : function(e)
1602     {
1603         Roo.log('img onclick');
1604         this.fireEvent('click', this, e);
1605     },
1606     /**
1607      * Sets the url of the image - used to update it
1608      * @param {String} url the url of the image
1609      */
1610     
1611     setSrc : function(url)
1612     {
1613         this.src =  url;
1614         
1615         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1616             this.el.dom.src =  url;
1617             return;
1618         }
1619         
1620         this.el.select('img', true).first().dom.src =  url;
1621     }
1622     
1623     
1624    
1625 });
1626
1627  /*
1628  * - LGPL
1629  *
1630  * image
1631  * 
1632  */
1633
1634
1635 /**
1636  * @class Roo.bootstrap.Link
1637  * @extends Roo.bootstrap.Component
1638  * Bootstrap Link Class
1639  * @cfg {String} alt image alternative text
1640  * @cfg {String} href a tag href
1641  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1642  * @cfg {String} html the content of the link.
1643  * @cfg {String} anchor name for the anchor link
1644  * @cfg {String} fa - favicon
1645
1646  * @cfg {Boolean} preventDefault (true | false) default false
1647
1648  * 
1649  * @constructor
1650  * Create a new Input
1651  * @param {Object} config The config object
1652  */
1653
1654 Roo.bootstrap.Link = function(config){
1655     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1656     
1657     this.addEvents({
1658         // img events
1659         /**
1660          * @event click
1661          * The img click event for the img.
1662          * @param {Roo.EventObject} e
1663          */
1664         "click" : true
1665     });
1666 };
1667
1668 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1669     
1670     href: false,
1671     target: false,
1672     preventDefault: false,
1673     anchor : false,
1674     alt : false,
1675     fa: false,
1676
1677
1678     getAutoCreate : function()
1679     {
1680         var html = this.html || '';
1681         
1682         if (this.fa !== false) {
1683             html = '<i class="fa fa-' + this.fa + '"></i>';
1684         }
1685         var cfg = {
1686             tag: 'a'
1687         };
1688         // anchor's do not require html/href...
1689         if (this.anchor === false) {
1690             cfg.html = html;
1691             cfg.href = this.href || '#';
1692         } else {
1693             cfg.name = this.anchor;
1694             if (this.html !== false || this.fa !== false) {
1695                 cfg.html = html;
1696             }
1697             if (this.href !== false) {
1698                 cfg.href = this.href;
1699             }
1700         }
1701         
1702         if(this.alt !== false){
1703             cfg.alt = this.alt;
1704         }
1705         
1706         
1707         if(this.target !== false) {
1708             cfg.target = this.target;
1709         }
1710         
1711         return cfg;
1712     },
1713     
1714     initEvents: function() {
1715         
1716         if(!this.href || this.preventDefault){
1717             this.el.on('click', this.onClick, this);
1718         }
1719     },
1720     
1721     onClick : function(e)
1722     {
1723         if(this.preventDefault){
1724             e.preventDefault();
1725         }
1726         //Roo.log('img onclick');
1727         this.fireEvent('click', this, e);
1728     }
1729    
1730 });
1731
1732  /*
1733  * - LGPL
1734  *
1735  * header
1736  * 
1737  */
1738
1739 /**
1740  * @class Roo.bootstrap.Header
1741  * @extends Roo.bootstrap.Component
1742  * Bootstrap Header class
1743  * @cfg {String} html content of header
1744  * @cfg {Number} level (1|2|3|4|5|6) default 1
1745  * 
1746  * @constructor
1747  * Create a new Header
1748  * @param {Object} config The config object
1749  */
1750
1751
1752 Roo.bootstrap.Header  = function(config){
1753     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1754 };
1755
1756 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1757     
1758     //href : false,
1759     html : false,
1760     level : 1,
1761     
1762     
1763     
1764     getAutoCreate : function(){
1765         
1766         
1767         
1768         var cfg = {
1769             tag: 'h' + (1 *this.level),
1770             html: this.html || ''
1771         } ;
1772         
1773         return cfg;
1774     }
1775    
1776 });
1777
1778  
1779
1780  /*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790  
1791 /**
1792  * @class Roo.bootstrap.MenuMgr
1793  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1794  * @singleton
1795  */
1796 Roo.bootstrap.MenuMgr = function(){
1797    var menus, active, groups = {}, attached = false, lastShow = new Date();
1798
1799    // private - called when first menu is created
1800    function init(){
1801        menus = {};
1802        active = new Roo.util.MixedCollection();
1803        Roo.get(document).addKeyListener(27, function(){
1804            if(active.length > 0){
1805                hideAll();
1806            }
1807        });
1808    }
1809
1810    // private
1811    function hideAll(){
1812        if(active && active.length > 0){
1813            var c = active.clone();
1814            c.each(function(m){
1815                m.hide();
1816            });
1817        }
1818    }
1819
1820    // private
1821    function onHide(m){
1822        active.remove(m);
1823        if(active.length < 1){
1824            Roo.get(document).un("mouseup", onMouseDown);
1825             
1826            attached = false;
1827        }
1828    }
1829
1830    // private
1831    function onShow(m){
1832        var last = active.last();
1833        lastShow = new Date();
1834        active.add(m);
1835        if(!attached){
1836           Roo.get(document).on("mouseup", onMouseDown);
1837            
1838            attached = true;
1839        }
1840        if(m.parentMenu){
1841           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1842           m.parentMenu.activeChild = m;
1843        }else if(last && last.isVisible()){
1844           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1845        }
1846    }
1847
1848    // private
1849    function onBeforeHide(m){
1850        if(m.activeChild){
1851            m.activeChild.hide();
1852        }
1853        if(m.autoHideTimer){
1854            clearTimeout(m.autoHideTimer);
1855            delete m.autoHideTimer;
1856        }
1857    }
1858
1859    // private
1860    function onBeforeShow(m){
1861        var pm = m.parentMenu;
1862        if(!pm && !m.allowOtherMenus){
1863            hideAll();
1864        }else if(pm && pm.activeChild && active != m){
1865            pm.activeChild.hide();
1866        }
1867    }
1868
1869    // private this should really trigger on mouseup..
1870    function onMouseDown(e){
1871         Roo.log("on Mouse Up");
1872         
1873         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1874             Roo.log("MenuManager hideAll");
1875             hideAll();
1876             e.stopEvent();
1877         }
1878         
1879         
1880    }
1881
1882    // private
1883    function onBeforeCheck(mi, state){
1884        if(state){
1885            var g = groups[mi.group];
1886            for(var i = 0, l = g.length; i < l; i++){
1887                if(g[i] != mi){
1888                    g[i].setChecked(false);
1889                }
1890            }
1891        }
1892    }
1893
1894    return {
1895
1896        /**
1897         * Hides all menus that are currently visible
1898         */
1899        hideAll : function(){
1900             hideAll();  
1901        },
1902
1903        // private
1904        register : function(menu){
1905            if(!menus){
1906                init();
1907            }
1908            menus[menu.id] = menu;
1909            menu.on("beforehide", onBeforeHide);
1910            menu.on("hide", onHide);
1911            menu.on("beforeshow", onBeforeShow);
1912            menu.on("show", onShow);
1913            var g = menu.group;
1914            if(g && menu.events["checkchange"]){
1915                if(!groups[g]){
1916                    groups[g] = [];
1917                }
1918                groups[g].push(menu);
1919                menu.on("checkchange", onCheck);
1920            }
1921        },
1922
1923         /**
1924          * Returns a {@link Roo.menu.Menu} object
1925          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1926          * be used to generate and return a new Menu instance.
1927          */
1928        get : function(menu){
1929            if(typeof menu == "string"){ // menu id
1930                return menus[menu];
1931            }else if(menu.events){  // menu instance
1932                return menu;
1933            }
1934            /*else if(typeof menu.length == 'number'){ // array of menu items?
1935                return new Roo.bootstrap.Menu({items:menu});
1936            }else{ // otherwise, must be a config
1937                return new Roo.bootstrap.Menu(menu);
1938            }
1939            */
1940            return false;
1941        },
1942
1943        // private
1944        unregister : function(menu){
1945            delete menus[menu.id];
1946            menu.un("beforehide", onBeforeHide);
1947            menu.un("hide", onHide);
1948            menu.un("beforeshow", onBeforeShow);
1949            menu.un("show", onShow);
1950            var g = menu.group;
1951            if(g && menu.events["checkchange"]){
1952                groups[g].remove(menu);
1953                menu.un("checkchange", onCheck);
1954            }
1955        },
1956
1957        // private
1958        registerCheckable : function(menuItem){
1959            var g = menuItem.group;
1960            if(g){
1961                if(!groups[g]){
1962                    groups[g] = [];
1963                }
1964                groups[g].push(menuItem);
1965                menuItem.on("beforecheckchange", onBeforeCheck);
1966            }
1967        },
1968
1969        // private
1970        unregisterCheckable : function(menuItem){
1971            var g = menuItem.group;
1972            if(g){
1973                groups[g].remove(menuItem);
1974                menuItem.un("beforecheckchange", onBeforeCheck);
1975            }
1976        }
1977    };
1978 }();/*
1979  * - LGPL
1980  *
1981  * menu
1982  * 
1983  */
1984
1985 /**
1986  * @class Roo.bootstrap.Menu
1987  * @extends Roo.bootstrap.Component
1988  * Bootstrap Menu class - container for MenuItems
1989  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1990  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1991  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1992  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1993  * 
1994  * @constructor
1995  * Create a new Menu
1996  * @param {Object} config The config object
1997  */
1998
1999
2000 Roo.bootstrap.Menu = function(config){
2001     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2002     if (this.registerMenu && this.type != 'treeview')  {
2003         Roo.bootstrap.MenuMgr.register(this);
2004     }
2005     this.addEvents({
2006         /**
2007          * @event beforeshow
2008          * Fires before this menu is displayed
2009          * @param {Roo.menu.Menu} this
2010          */
2011         beforeshow : true,
2012         /**
2013          * @event beforehide
2014          * Fires before this menu is hidden
2015          * @param {Roo.menu.Menu} this
2016          */
2017         beforehide : true,
2018         /**
2019          * @event show
2020          * Fires after this menu is displayed
2021          * @param {Roo.menu.Menu} this
2022          */
2023         show : true,
2024         /**
2025          * @event hide
2026          * Fires after this menu is hidden
2027          * @param {Roo.menu.Menu} this
2028          */
2029         hide : true,
2030         /**
2031          * @event click
2032          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2033          * @param {Roo.menu.Menu} this
2034          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2035          * @param {Roo.EventObject} e
2036          */
2037         click : true,
2038         /**
2039          * @event mouseover
2040          * Fires when the mouse is hovering over this menu
2041          * @param {Roo.menu.Menu} this
2042          * @param {Roo.EventObject} e
2043          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2044          */
2045         mouseover : true,
2046         /**
2047          * @event mouseout
2048          * Fires when the mouse exits this menu
2049          * @param {Roo.menu.Menu} this
2050          * @param {Roo.EventObject} e
2051          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2052          */
2053         mouseout : true,
2054         /**
2055          * @event itemclick
2056          * Fires when a menu item contained in this menu is clicked
2057          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2058          * @param {Roo.EventObject} e
2059          */
2060         itemclick: true
2061     });
2062     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2063 };
2064
2065 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2066     
2067    /// html : false,
2068     //align : '',
2069     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2070     type: false,
2071     /**
2072      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2073      */
2074     registerMenu : true,
2075     
2076     menuItems :false, // stores the menu items..
2077     
2078     hidden:true,
2079         
2080     parentMenu : false,
2081     
2082     stopEvent : true,
2083     
2084     isLink : false,
2085     
2086     getChildContainer : function() {
2087         return this.el;  
2088     },
2089     
2090     getAutoCreate : function(){
2091          
2092         //if (['right'].indexOf(this.align)!==-1) {
2093         //    cfg.cn[1].cls += ' pull-right'
2094         //}
2095         
2096         
2097         var cfg = {
2098             tag : 'ul',
2099             cls : 'dropdown-menu' ,
2100             style : 'z-index:1000'
2101             
2102         };
2103         
2104         if (this.type === 'submenu') {
2105             cfg.cls = 'submenu active';
2106         }
2107         if (this.type === 'treeview') {
2108             cfg.cls = 'treeview-menu';
2109         }
2110         
2111         return cfg;
2112     },
2113     initEvents : function() {
2114         
2115        // Roo.log("ADD event");
2116        // Roo.log(this.triggerEl.dom);
2117         
2118         this.triggerEl.on('click', this.onTriggerClick, this);
2119         
2120         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2121         
2122         
2123         if (this.triggerEl.hasClass('nav-item')) {
2124             // dropdown toggle on the 'a' in BS4?
2125             this.triggerEl.addClass('dropdown-toggle').select('.nav-link',true).first().addClass('nav-item');
2126         } else {
2127             this.triggerEl.addClass('dropdown-toggle');
2128         }
2129         if (Roo.isTouch) {
2130             this.el.on('touchstart'  , this.onTouch, this);
2131         }
2132         this.el.on('click' , this.onClick, this);
2133
2134         this.el.on("mouseover", this.onMouseOver, this);
2135         this.el.on("mouseout", this.onMouseOut, this);
2136         
2137     },
2138     
2139     findTargetItem : function(e)
2140     {
2141         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2142         if(!t){
2143             return false;
2144         }
2145         //Roo.log(t);         Roo.log(t.id);
2146         if(t && t.id){
2147             //Roo.log(this.menuitems);
2148             return this.menuitems.get(t.id);
2149             
2150             //return this.items.get(t.menuItemId);
2151         }
2152         
2153         return false;
2154     },
2155     
2156     onTouch : function(e) 
2157     {
2158         Roo.log("menu.onTouch");
2159         //e.stopEvent(); this make the user popdown broken
2160         this.onClick(e);
2161     },
2162     
2163     onClick : function(e)
2164     {
2165         Roo.log("menu.onClick");
2166         
2167         var t = this.findTargetItem(e);
2168         if(!t || t.isContainer){
2169             return;
2170         }
2171         Roo.log(e);
2172         /*
2173         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2174             if(t == this.activeItem && t.shouldDeactivate(e)){
2175                 this.activeItem.deactivate();
2176                 delete this.activeItem;
2177                 return;
2178             }
2179             if(t.canActivate){
2180                 this.setActiveItem(t, true);
2181             }
2182             return;
2183             
2184             
2185         }
2186         */
2187        
2188         Roo.log('pass click event');
2189         
2190         t.onClick(e);
2191         
2192         this.fireEvent("click", this, t, e);
2193         
2194         var _this = this;
2195         
2196         if(!t.href.length || t.href == '#'){
2197             (function() { _this.hide(); }).defer(100);
2198         }
2199         
2200     },
2201     
2202     onMouseOver : function(e){
2203         var t  = this.findTargetItem(e);
2204         //Roo.log(t);
2205         //if(t){
2206         //    if(t.canActivate && !t.disabled){
2207         //        this.setActiveItem(t, true);
2208         //    }
2209         //}
2210         
2211         this.fireEvent("mouseover", this, e, t);
2212     },
2213     isVisible : function(){
2214         return !this.hidden;
2215     },
2216      onMouseOut : function(e){
2217         var t  = this.findTargetItem(e);
2218         
2219         //if(t ){
2220         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2221         //        this.activeItem.deactivate();
2222         //        delete this.activeItem;
2223         //    }
2224         //}
2225         this.fireEvent("mouseout", this, e, t);
2226     },
2227     
2228     
2229     /**
2230      * Displays this menu relative to another element
2231      * @param {String/HTMLElement/Roo.Element} element The element to align to
2232      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2233      * the element (defaults to this.defaultAlign)
2234      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2235      */
2236     show : function(el, pos, parentMenu){
2237         this.parentMenu = parentMenu;
2238         if(!this.el){
2239             this.render();
2240         }
2241         this.fireEvent("beforeshow", this);
2242         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2243     },
2244      /**
2245      * Displays this menu at a specific xy position
2246      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2247      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2248      */
2249     showAt : function(xy, parentMenu, /* private: */_e){
2250         this.parentMenu = parentMenu;
2251         if(!this.el){
2252             this.render();
2253         }
2254         if(_e !== false){
2255             this.fireEvent("beforeshow", this);
2256             //xy = this.el.adjustForConstraints(xy);
2257         }
2258         
2259         //this.el.show();
2260         this.hideMenuItems();
2261         this.hidden = false;
2262         this.triggerEl.addClass('open');
2263         
2264         // reassign x when hitting right
2265         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2266             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2267         }
2268         
2269         // reassign y when hitting bottom
2270         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2271             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2272         }
2273         
2274         // but the list may align on trigger left or trigger top... should it be a properity?
2275         
2276         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2277             this.el.setXY(xy);
2278         }
2279         
2280         this.focus();
2281         this.fireEvent("show", this);
2282     },
2283     
2284     focus : function(){
2285         return;
2286         if(!this.hidden){
2287             this.doFocus.defer(50, this);
2288         }
2289     },
2290
2291     doFocus : function(){
2292         if(!this.hidden){
2293             this.focusEl.focus();
2294         }
2295     },
2296
2297     /**
2298      * Hides this menu and optionally all parent menus
2299      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2300      */
2301     hide : function(deep)
2302     {
2303         
2304         this.hideMenuItems();
2305         if(this.el && this.isVisible()){
2306             this.fireEvent("beforehide", this);
2307             if(this.activeItem){
2308                 this.activeItem.deactivate();
2309                 this.activeItem = null;
2310             }
2311             this.triggerEl.removeClass('open');;
2312             this.hidden = true;
2313             this.fireEvent("hide", this);
2314         }
2315         if(deep === true && this.parentMenu){
2316             this.parentMenu.hide(true);
2317         }
2318     },
2319     
2320     onTriggerClick : function(e)
2321     {
2322         Roo.log('trigger click');
2323         
2324         var target = e.getTarget();
2325         
2326         Roo.log(target.nodeName.toLowerCase());
2327         
2328         if(target.nodeName.toLowerCase() === 'i'){
2329             e.preventDefault();
2330         }
2331         
2332     },
2333     
2334     onTriggerPress  : function(e)
2335     {
2336         Roo.log('trigger press');
2337         //Roo.log(e.getTarget());
2338        // Roo.log(this.triggerEl.dom);
2339        
2340         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2341         var pel = Roo.get(e.getTarget());
2342         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2343             Roo.log('is treeview or dropdown?');
2344             return;
2345         }
2346         
2347         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2348             return;
2349         }
2350         
2351         if (this.isVisible()) {
2352             Roo.log('hide');
2353             this.hide();
2354         } else {
2355             Roo.log('show');
2356             this.show(this.triggerEl, false, false);
2357         }
2358         
2359         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2360             e.stopEvent();
2361         }
2362         
2363     },
2364        
2365     
2366     hideMenuItems : function()
2367     {
2368         Roo.log("hide Menu Items");
2369         if (!this.el) { 
2370             return;
2371         }
2372         //$(backdrop).remove()
2373         this.el.select('.open',true).each(function(aa) {
2374             
2375             aa.removeClass('open');
2376           //var parent = getParent($(this))
2377           //var relatedTarget = { relatedTarget: this }
2378           
2379            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2380           //if (e.isDefaultPrevented()) return
2381            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2382         });
2383     },
2384     addxtypeChild : function (tree, cntr) {
2385         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2386           
2387         this.menuitems.add(comp);
2388         return comp;
2389
2390     },
2391     getEl : function()
2392     {
2393         Roo.log(this.el);
2394         return this.el;
2395     },
2396     
2397     clear : function()
2398     {
2399         this.getEl().dom.innerHTML = '';
2400         this.menuitems.clear();
2401     }
2402 });
2403
2404  
2405  /*
2406  * - LGPL
2407  *
2408  * menu item
2409  * 
2410  */
2411
2412
2413 /**
2414  * @class Roo.bootstrap.MenuItem
2415  * @extends Roo.bootstrap.Component
2416  * Bootstrap MenuItem class
2417  * @cfg {String} html the menu label
2418  * @cfg {String} href the link
2419  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2420  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2421  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2422  * @cfg {String} fa favicon to show on left of menu item.
2423  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2424  * 
2425  * 
2426  * @constructor
2427  * Create a new MenuItem
2428  * @param {Object} config The config object
2429  */
2430
2431
2432 Roo.bootstrap.MenuItem = function(config){
2433     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2434     this.addEvents({
2435         // raw events
2436         /**
2437          * @event click
2438          * The raw click event for the entire grid.
2439          * @param {Roo.bootstrap.MenuItem} this
2440          * @param {Roo.EventObject} e
2441          */
2442         "click" : true
2443     });
2444 };
2445
2446 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2447     
2448     href : false,
2449     html : false,
2450     preventDefault: false,
2451     isContainer : false,
2452     active : false,
2453     fa: false,
2454     
2455     getAutoCreate : function(){
2456         
2457         if(this.isContainer){
2458             return {
2459                 tag: 'li',
2460                 cls: 'dropdown-menu-item dropdown-item'
2461             };
2462         }
2463         var ctag = {
2464             tag: 'span',
2465             html: 'Link'
2466         };
2467         
2468         var anc = {
2469             tag : 'a',
2470             href : '#',
2471             cn : [  ]
2472         };
2473         
2474         if (this.fa !== false) {
2475             anc.cn.push({
2476                 tag : 'i',
2477                 cls : 'fa fa-' + this.fa
2478             });
2479         }
2480         
2481         anc.cn.push(ctag);
2482         
2483         
2484         var cfg= {
2485             tag: 'li',
2486             cls: 'dropdown-menu-item dropdown-item',
2487             cn: [ anc ]
2488         };
2489         if (this.parent().type == 'treeview') {
2490             cfg.cls = 'treeview-menu';
2491         }
2492         if (this.active) {
2493             cfg.cls += ' active';
2494         }
2495         
2496         
2497         
2498         anc.href = this.href || cfg.cn[0].href ;
2499         ctag.html = this.html || cfg.cn[0].html ;
2500         return cfg;
2501     },
2502     
2503     initEvents: function()
2504     {
2505         if (this.parent().type == 'treeview') {
2506             this.el.select('a').on('click', this.onClick, this);
2507         }
2508         
2509         if (this.menu) {
2510             this.menu.parentType = this.xtype;
2511             this.menu.triggerEl = this.el;
2512             this.menu = this.addxtype(Roo.apply({}, this.menu));
2513         }
2514         
2515     },
2516     onClick : function(e)
2517     {
2518         Roo.log('item on click ');
2519         
2520         if(this.preventDefault){
2521             e.preventDefault();
2522         }
2523         //this.parent().hideMenuItems();
2524         
2525         this.fireEvent('click', this, e);
2526     },
2527     getEl : function()
2528     {
2529         return this.el;
2530     } 
2531 });
2532
2533  
2534
2535  /*
2536  * - LGPL
2537  *
2538  * menu separator
2539  * 
2540  */
2541
2542
2543 /**
2544  * @class Roo.bootstrap.MenuSeparator
2545  * @extends Roo.bootstrap.Component
2546  * Bootstrap MenuSeparator class
2547  * 
2548  * @constructor
2549  * Create a new MenuItem
2550  * @param {Object} config The config object
2551  */
2552
2553
2554 Roo.bootstrap.MenuSeparator = function(config){
2555     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2556 };
2557
2558 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2559     
2560     getAutoCreate : function(){
2561         var cfg = {
2562             cls: 'divider',
2563             tag : 'li'
2564         };
2565         
2566         return cfg;
2567     }
2568    
2569 });
2570
2571  
2572
2573  
2574 /*
2575 * Licence: LGPL
2576 */
2577
2578 /**
2579  * @class Roo.bootstrap.Modal
2580  * @extends Roo.bootstrap.Component
2581  * Bootstrap Modal class
2582  * @cfg {String} title Title of dialog
2583  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2584  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2585  * @cfg {Boolean} specificTitle default false
2586  * @cfg {Array} buttons Array of buttons or standard button set..
2587  * @cfg {String} buttonPosition (left|right|center) default right
2588  * @cfg {Boolean} animate default true
2589  * @cfg {Boolean} allow_close default true
2590  * @cfg {Boolean} fitwindow default false
2591  * @cfg {String} size (sm|lg) default empty
2592  * @cfg {Number} max_width set the max width of modal
2593  *
2594  *
2595  * @constructor
2596  * Create a new Modal Dialog
2597  * @param {Object} config The config object
2598  */
2599
2600 Roo.bootstrap.Modal = function(config){
2601     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2602     this.addEvents({
2603         // raw events
2604         /**
2605          * @event btnclick
2606          * The raw btnclick event for the button
2607          * @param {Roo.EventObject} e
2608          */
2609         "btnclick" : true,
2610         /**
2611          * @event resize
2612          * Fire when dialog resize
2613          * @param {Roo.bootstrap.Modal} this
2614          * @param {Roo.EventObject} e
2615          */
2616         "resize" : true
2617     });
2618     this.buttons = this.buttons || [];
2619
2620     if (this.tmpl) {
2621         this.tmpl = Roo.factory(this.tmpl);
2622     }
2623
2624 };
2625
2626 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2627
2628     title : 'test dialog',
2629
2630     buttons : false,
2631
2632     // set on load...
2633
2634     html: false,
2635
2636     tmp: false,
2637
2638     specificTitle: false,
2639
2640     buttonPosition: 'right',
2641
2642     allow_close : true,
2643
2644     animate : true,
2645
2646     fitwindow: false,
2647     
2648      // private
2649     dialogEl: false,
2650     bodyEl:  false,
2651     footerEl:  false,
2652     titleEl:  false,
2653     closeEl:  false,
2654
2655     size: '',
2656     
2657     max_width: 0,
2658     
2659     max_height: 0,
2660     
2661     fit_content: false,
2662
2663     onRender : function(ct, position)
2664     {
2665         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2666
2667         if(!this.el){
2668             var cfg = Roo.apply({},  this.getAutoCreate());
2669             cfg.id = Roo.id();
2670             //if(!cfg.name){
2671             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2672             //}
2673             //if (!cfg.name.length) {
2674             //    delete cfg.name;
2675            // }
2676             if (this.cls) {
2677                 cfg.cls += ' ' + this.cls;
2678             }
2679             if (this.style) {
2680                 cfg.style = this.style;
2681             }
2682             this.el = Roo.get(document.body).createChild(cfg, position);
2683         }
2684         //var type = this.el.dom.type;
2685
2686
2687         if(this.tabIndex !== undefined){
2688             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2689         }
2690
2691         this.dialogEl = this.el.select('.modal-dialog',true).first();
2692         this.bodyEl = this.el.select('.modal-body',true).first();
2693         this.closeEl = this.el.select('.modal-header .close', true).first();
2694         this.headerEl = this.el.select('.modal-header',true).first();
2695         this.titleEl = this.el.select('.modal-title',true).first();
2696         this.footerEl = this.el.select('.modal-footer',true).first();
2697
2698         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2699         
2700         //this.el.addClass("x-dlg-modal");
2701
2702         if (this.buttons.length) {
2703             Roo.each(this.buttons, function(bb) {
2704                 var b = Roo.apply({}, bb);
2705                 b.xns = b.xns || Roo.bootstrap;
2706                 b.xtype = b.xtype || 'Button';
2707                 if (typeof(b.listeners) == 'undefined') {
2708                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2709                 }
2710
2711                 var btn = Roo.factory(b);
2712
2713                 btn.render(this.el.select('.modal-footer div').first());
2714
2715             },this);
2716         }
2717         // render the children.
2718         var nitems = [];
2719
2720         if(typeof(this.items) != 'undefined'){
2721             var items = this.items;
2722             delete this.items;
2723
2724             for(var i =0;i < items.length;i++) {
2725                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2726             }
2727         }
2728
2729         this.items = nitems;
2730
2731         // where are these used - they used to be body/close/footer
2732
2733
2734         this.initEvents();
2735         //this.el.addClass([this.fieldClass, this.cls]);
2736
2737     },
2738
2739     getAutoCreate : function()
2740     {
2741         var bdy = {
2742                 cls : 'modal-body',
2743                 html : this.html || ''
2744         };
2745
2746         var title = {
2747             tag: 'h4',
2748             cls : 'modal-title',
2749             html : this.title
2750         };
2751
2752         if(this.specificTitle){
2753             title = this.title;
2754
2755         };
2756
2757         var header = [];
2758         if (this.allow_close) {
2759             header.push({
2760                 tag: 'button',
2761                 cls : 'close',
2762                 html : '&times'
2763             });
2764         }
2765
2766         header.push(title);
2767
2768         var size = '';
2769
2770         if(this.size.length){
2771             size = 'modal-' + this.size;
2772         }
2773
2774         var modal = {
2775             cls: "modal",
2776              cn : [
2777                 {
2778                     cls: "modal-dialog " + size,
2779                     cn : [
2780                         {
2781                             cls : "modal-content",
2782                             cn : [
2783                                 {
2784                                     cls : 'modal-header',
2785                                     cn : header
2786                                 },
2787                                 bdy,
2788                                 {
2789                                     cls : 'modal-footer',
2790                                     cn : [
2791                                         {
2792                                             tag: 'div',
2793                                             cls: 'btn-' + this.buttonPosition
2794                                         }
2795                                     ]
2796
2797                                 }
2798
2799
2800                             ]
2801
2802                         }
2803                     ]
2804
2805                 }
2806             ]
2807         };
2808
2809         if(this.animate){
2810             modal.cls += ' fade';
2811         }
2812
2813         return modal;
2814
2815     },
2816     getChildContainer : function() {
2817
2818          return this.bodyEl;
2819
2820     },
2821     getButtonContainer : function() {
2822          return this.el.select('.modal-footer div',true).first();
2823
2824     },
2825     initEvents : function()
2826     {
2827         if (this.allow_close) {
2828             this.closeEl.on('click', this.hide, this);
2829         }
2830         Roo.EventManager.onWindowResize(this.resize, this, true);
2831
2832
2833     },
2834
2835     resize : function()
2836     {
2837         this.maskEl.setSize(
2838             Roo.lib.Dom.getViewWidth(true),
2839             Roo.lib.Dom.getViewHeight(true)
2840         );
2841         
2842         if (this.fitwindow) {
2843             this.setSize(
2844                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2845                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2846             );
2847             return;
2848         }
2849         
2850         if(this.max_width !== 0) {
2851             
2852             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2853             
2854             if(this.height) {
2855                 this.setSize(w, this.height);
2856                 return;
2857             }
2858             
2859             if(this.max_height) {
2860                 this.setSize(w,Math.min(
2861                     this.max_height,
2862                     Roo.lib.Dom.getViewportHeight(true) - 60
2863                 ));
2864                 
2865                 return;
2866             }
2867             
2868             if(!this.fit_content) {
2869                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2870                 return;
2871             }
2872             
2873             this.setSize(w, Math.min(
2874                 60 +
2875                 this.headerEl.getHeight() + 
2876                 this.footerEl.getHeight() + 
2877                 this.getChildHeight(this.bodyEl.dom.childNodes),
2878                 Roo.lib.Dom.getViewportHeight(true) - 60)
2879             );
2880         }
2881         
2882     },
2883
2884     setSize : function(w,h)
2885     {
2886         if (!w && !h) {
2887             return;
2888         }
2889         
2890         this.resizeTo(w,h);
2891     },
2892
2893     show : function() {
2894
2895         if (!this.rendered) {
2896             this.render();
2897         }
2898
2899         //this.el.setStyle('display', 'block');
2900         this.el.removeClass('hideing');        
2901         this.el.addClass('show');
2902  
2903         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2904             var _this = this;
2905             (function(){
2906                 this.el.addClass('in');
2907             }).defer(50, this);
2908         }else{
2909             this.el.addClass('in');
2910         }
2911
2912         // not sure how we can show data in here..
2913         //if (this.tmpl) {
2914         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2915         //}
2916
2917         Roo.get(document.body).addClass("x-body-masked");
2918         
2919         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2920         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2921         this.maskEl.addClass('show');
2922         
2923         this.resize();
2924         
2925         this.fireEvent('show', this);
2926
2927         // set zindex here - otherwise it appears to be ignored...
2928         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2929
2930         (function () {
2931             this.items.forEach( function(e) {
2932                 e.layout ? e.layout() : false;
2933
2934             });
2935         }).defer(100,this);
2936
2937     },
2938     hide : function()
2939     {
2940         if(this.fireEvent("beforehide", this) !== false){
2941             this.maskEl.removeClass('show');
2942             Roo.get(document.body).removeClass("x-body-masked");
2943             this.el.removeClass('in');
2944             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2945
2946             if(this.animate){ // why
2947                 this.el.addClass('hideing');
2948                 (function(){
2949                     if (!this.el.hasClass('hideing')) {
2950                         return; // it's been shown again...
2951                     }
2952                     this.el.removeClass('show');
2953                     this.el.removeClass('hideing');
2954                 }).defer(150,this);
2955                 
2956             }else{
2957                  this.el.removeClass('show');
2958             }
2959             this.fireEvent('hide', this);
2960         }
2961     },
2962     isVisible : function()
2963     {
2964         
2965         return this.el.hasClass('show') && !this.el.hasClass('hideing');
2966         
2967     },
2968
2969     addButton : function(str, cb)
2970     {
2971
2972
2973         var b = Roo.apply({}, { html : str } );
2974         b.xns = b.xns || Roo.bootstrap;
2975         b.xtype = b.xtype || 'Button';
2976         if (typeof(b.listeners) == 'undefined') {
2977             b.listeners = { click : cb.createDelegate(this)  };
2978         }
2979
2980         var btn = Roo.factory(b);
2981
2982         btn.render(this.el.select('.modal-footer div').first());
2983
2984         return btn;
2985
2986     },
2987
2988     setDefaultButton : function(btn)
2989     {
2990         //this.el.select('.modal-footer').()
2991     },
2992     diff : false,
2993
2994     resizeTo: function(w,h)
2995     {
2996         // skip.. ?? why??
2997
2998         this.dialogEl.setWidth(w);
2999         if (this.diff === false) {
3000             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3001         }
3002
3003         this.bodyEl.setHeight(h - this.diff);
3004
3005         this.fireEvent('resize', this);
3006
3007     },
3008     setContentSize  : function(w, h)
3009     {
3010
3011     },
3012     onButtonClick: function(btn,e)
3013     {
3014         //Roo.log([a,b,c]);
3015         this.fireEvent('btnclick', btn.name, e);
3016     },
3017      /**
3018      * Set the title of the Dialog
3019      * @param {String} str new Title
3020      */
3021     setTitle: function(str) {
3022         this.titleEl.dom.innerHTML = str;
3023     },
3024     /**
3025      * Set the body of the Dialog
3026      * @param {String} str new Title
3027      */
3028     setBody: function(str) {
3029         this.bodyEl.dom.innerHTML = str;
3030     },
3031     /**
3032      * Set the body of the Dialog using the template
3033      * @param {Obj} data - apply this data to the template and replace the body contents.
3034      */
3035     applyBody: function(obj)
3036     {
3037         if (!this.tmpl) {
3038             Roo.log("Error - using apply Body without a template");
3039             //code
3040         }
3041         this.tmpl.overwrite(this.bodyEl, obj);
3042     },
3043     
3044     getChildHeight : function(child_nodes)
3045     {
3046         if(
3047             !child_nodes ||
3048             child_nodes.length == 0
3049         ) {
3050             return;
3051         }
3052         
3053         var child_height = 0;
3054         
3055         for(var i = 0; i < child_nodes.length; i++) {
3056             
3057             /*
3058             * for modal with tabs...
3059             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3060                 
3061                 var layout_childs = child_nodes[i].childNodes;
3062                 
3063                 for(var j = 0; j < layout_childs.length; j++) {
3064                     
3065                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3066                         
3067                         var layout_body_childs = layout_childs[j].childNodes;
3068                         
3069                         for(var k = 0; k < layout_body_childs.length; k++) {
3070                             
3071                             if(layout_body_childs[k].classList.contains('navbar')) {
3072                                 child_height += layout_body_childs[k].offsetHeight;
3073                                 continue;
3074                             }
3075                             
3076                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3077                                 
3078                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3079                                 
3080                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3081                                     
3082                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3083                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3084                                         continue;
3085                                     }
3086                                     
3087                                 }
3088                                 
3089                             }
3090                             
3091                         }
3092                     }
3093                 }
3094                 continue;
3095             }
3096             */
3097             
3098             child_height += child_nodes[i].offsetHeight;
3099             // Roo.log(child_nodes[i].offsetHeight);
3100         }
3101         
3102         return child_height;
3103     }
3104
3105 });
3106
3107
3108 Roo.apply(Roo.bootstrap.Modal,  {
3109     /**
3110          * Button config that displays a single OK button
3111          * @type Object
3112          */
3113         OK :  [{
3114             name : 'ok',
3115             weight : 'primary',
3116             html : 'OK'
3117         }],
3118         /**
3119          * Button config that displays Yes and No buttons
3120          * @type Object
3121          */
3122         YESNO : [
3123             {
3124                 name  : 'no',
3125                 html : 'No'
3126             },
3127             {
3128                 name  :'yes',
3129                 weight : 'primary',
3130                 html : 'Yes'
3131             }
3132         ],
3133
3134         /**
3135          * Button config that displays OK and Cancel buttons
3136          * @type Object
3137          */
3138         OKCANCEL : [
3139             {
3140                name : 'cancel',
3141                 html : 'Cancel'
3142             },
3143             {
3144                 name : 'ok',
3145                 weight : 'primary',
3146                 html : 'OK'
3147             }
3148         ],
3149         /**
3150          * Button config that displays Yes, No and Cancel buttons
3151          * @type Object
3152          */
3153         YESNOCANCEL : [
3154             {
3155                 name : 'yes',
3156                 weight : 'primary',
3157                 html : 'Yes'
3158             },
3159             {
3160                 name : 'no',
3161                 html : 'No'
3162             },
3163             {
3164                 name : 'cancel',
3165                 html : 'Cancel'
3166             }
3167         ],
3168         
3169         zIndex : 10001
3170 });
3171 /*
3172  * - LGPL
3173  *
3174  * messagebox - can be used as a replace
3175  * 
3176  */
3177 /**
3178  * @class Roo.MessageBox
3179  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3180  * Example usage:
3181  *<pre><code>
3182 // Basic alert:
3183 Roo.Msg.alert('Status', 'Changes saved successfully.');
3184
3185 // Prompt for user data:
3186 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3187     if (btn == 'ok'){
3188         // process text value...
3189     }
3190 });
3191
3192 // Show a dialog using config options:
3193 Roo.Msg.show({
3194    title:'Save Changes?',
3195    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3196    buttons: Roo.Msg.YESNOCANCEL,
3197    fn: processResult,
3198    animEl: 'elId'
3199 });
3200 </code></pre>
3201  * @singleton
3202  */
3203 Roo.bootstrap.MessageBox = function(){
3204     var dlg, opt, mask, waitTimer;
3205     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3206     var buttons, activeTextEl, bwidth;
3207
3208     
3209     // private
3210     var handleButton = function(button){
3211         dlg.hide();
3212         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3213     };
3214
3215     // private
3216     var handleHide = function(){
3217         if(opt && opt.cls){
3218             dlg.el.removeClass(opt.cls);
3219         }
3220         //if(waitTimer){
3221         //    Roo.TaskMgr.stop(waitTimer);
3222         //    waitTimer = null;
3223         //}
3224     };
3225
3226     // private
3227     var updateButtons = function(b){
3228         var width = 0;
3229         if(!b){
3230             buttons["ok"].hide();
3231             buttons["cancel"].hide();
3232             buttons["yes"].hide();
3233             buttons["no"].hide();
3234             //dlg.footer.dom.style.display = 'none';
3235             return width;
3236         }
3237         dlg.footerEl.dom.style.display = '';
3238         for(var k in buttons){
3239             if(typeof buttons[k] != "function"){
3240                 if(b[k]){
3241                     buttons[k].show();
3242                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3243                     width += buttons[k].el.getWidth()+15;
3244                 }else{
3245                     buttons[k].hide();
3246                 }
3247             }
3248         }
3249         return width;
3250     };
3251
3252     // private
3253     var handleEsc = function(d, k, e){
3254         if(opt && opt.closable !== false){
3255             dlg.hide();
3256         }
3257         if(e){
3258             e.stopEvent();
3259         }
3260     };
3261
3262     return {
3263         /**
3264          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3265          * @return {Roo.BasicDialog} The BasicDialog element
3266          */
3267         getDialog : function(){
3268            if(!dlg){
3269                 dlg = new Roo.bootstrap.Modal( {
3270                     //draggable: true,
3271                     //resizable:false,
3272                     //constraintoviewport:false,
3273                     //fixedcenter:true,
3274                     //collapsible : false,
3275                     //shim:true,
3276                     //modal: true,
3277                 //    width: 'auto',
3278                   //  height:100,
3279                     //buttonAlign:"center",
3280                     closeClick : function(){
3281                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3282                             handleButton("no");
3283                         }else{
3284                             handleButton("cancel");
3285                         }
3286                     }
3287                 });
3288                 dlg.render();
3289                 dlg.on("hide", handleHide);
3290                 mask = dlg.mask;
3291                 //dlg.addKeyListener(27, handleEsc);
3292                 buttons = {};
3293                 this.buttons = buttons;
3294                 var bt = this.buttonText;
3295                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3296                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3297                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3298                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3299                 //Roo.log(buttons);
3300                 bodyEl = dlg.bodyEl.createChild({
3301
3302                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3303                         '<textarea class="roo-mb-textarea"></textarea>' +
3304                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3305                 });
3306                 msgEl = bodyEl.dom.firstChild;
3307                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3308                 textboxEl.enableDisplayMode();
3309                 textboxEl.addKeyListener([10,13], function(){
3310                     if(dlg.isVisible() && opt && opt.buttons){
3311                         if(opt.buttons.ok){
3312                             handleButton("ok");
3313                         }else if(opt.buttons.yes){
3314                             handleButton("yes");
3315                         }
3316                     }
3317                 });
3318                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3319                 textareaEl.enableDisplayMode();
3320                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3321                 progressEl.enableDisplayMode();
3322                 
3323                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3324                 var pf = progressEl.dom.firstChild;
3325                 if (pf) {
3326                     pp = Roo.get(pf.firstChild);
3327                     pp.setHeight(pf.offsetHeight);
3328                 }
3329                 
3330             }
3331             return dlg;
3332         },
3333
3334         /**
3335          * Updates the message box body text
3336          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3337          * the XHTML-compliant non-breaking space character '&amp;#160;')
3338          * @return {Roo.MessageBox} This message box
3339          */
3340         updateText : function(text)
3341         {
3342             if(!dlg.isVisible() && !opt.width){
3343                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3344                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3345             }
3346             msgEl.innerHTML = text || '&#160;';
3347       
3348             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3349             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3350             var w = Math.max(
3351                     Math.min(opt.width || cw , this.maxWidth), 
3352                     Math.max(opt.minWidth || this.minWidth, bwidth)
3353             );
3354             if(opt.prompt){
3355                 activeTextEl.setWidth(w);
3356             }
3357             if(dlg.isVisible()){
3358                 dlg.fixedcenter = false;
3359             }
3360             // to big, make it scroll. = But as usual stupid IE does not support
3361             // !important..
3362             
3363             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3364                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3365                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3366             } else {
3367                 bodyEl.dom.style.height = '';
3368                 bodyEl.dom.style.overflowY = '';
3369             }
3370             if (cw > w) {
3371                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3372             } else {
3373                 bodyEl.dom.style.overflowX = '';
3374             }
3375             
3376             dlg.setContentSize(w, bodyEl.getHeight());
3377             if(dlg.isVisible()){
3378                 dlg.fixedcenter = true;
3379             }
3380             return this;
3381         },
3382
3383         /**
3384          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3385          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3386          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3387          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3388          * @return {Roo.MessageBox} This message box
3389          */
3390         updateProgress : function(value, text){
3391             if(text){
3392                 this.updateText(text);
3393             }
3394             
3395             if (pp) { // weird bug on my firefox - for some reason this is not defined
3396                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3397                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3398             }
3399             return this;
3400         },        
3401
3402         /**
3403          * Returns true if the message box is currently displayed
3404          * @return {Boolean} True if the message box is visible, else false
3405          */
3406         isVisible : function(){
3407             return dlg && dlg.isVisible();  
3408         },
3409
3410         /**
3411          * Hides the message box if it is displayed
3412          */
3413         hide : function(){
3414             if(this.isVisible()){
3415                 dlg.hide();
3416             }  
3417         },
3418
3419         /**
3420          * Displays a new message box, or reinitializes an existing message box, based on the config options
3421          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3422          * The following config object properties are supported:
3423          * <pre>
3424 Property    Type             Description
3425 ----------  ---------------  ------------------------------------------------------------------------------------
3426 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3427                                    closes (defaults to undefined)
3428 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3429                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3430 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3431                                    progress and wait dialogs will ignore this property and always hide the
3432                                    close button as they can only be closed programmatically.
3433 cls               String           A custom CSS class to apply to the message box element
3434 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3435                                    displayed (defaults to 75)
3436 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3437                                    function will be btn (the name of the button that was clicked, if applicable,
3438                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3439                                    Progress and wait dialogs will ignore this option since they do not respond to
3440                                    user actions and can only be closed programmatically, so any required function
3441                                    should be called by the same code after it closes the dialog.
3442 icon              String           A CSS class that provides a background image to be used as an icon for
3443                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3444 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3445 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3446 modal             Boolean          False to allow user interaction with the page while the message box is
3447                                    displayed (defaults to true)
3448 msg               String           A string that will replace the existing message box body text (defaults
3449                                    to the XHTML-compliant non-breaking space character '&#160;')
3450 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3451 progress          Boolean          True to display a progress bar (defaults to false)
3452 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3453 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3454 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3455 title             String           The title text
3456 value             String           The string value to set into the active textbox element if displayed
3457 wait              Boolean          True to display a progress bar (defaults to false)
3458 width             Number           The width of the dialog in pixels
3459 </pre>
3460          *
3461          * Example usage:
3462          * <pre><code>
3463 Roo.Msg.show({
3464    title: 'Address',
3465    msg: 'Please enter your address:',
3466    width: 300,
3467    buttons: Roo.MessageBox.OKCANCEL,
3468    multiline: true,
3469    fn: saveAddress,
3470    animEl: 'addAddressBtn'
3471 });
3472 </code></pre>
3473          * @param {Object} config Configuration options
3474          * @return {Roo.MessageBox} This message box
3475          */
3476         show : function(options)
3477         {
3478             
3479             // this causes nightmares if you show one dialog after another
3480             // especially on callbacks..
3481              
3482             if(this.isVisible()){
3483                 
3484                 this.hide();
3485                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3486                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3487                 Roo.log("New Dialog Message:" +  options.msg )
3488                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3489                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3490                 
3491             }
3492             var d = this.getDialog();
3493             opt = options;
3494             d.setTitle(opt.title || "&#160;");
3495             d.closeEl.setDisplayed(opt.closable !== false);
3496             activeTextEl = textboxEl;
3497             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3498             if(opt.prompt){
3499                 if(opt.multiline){
3500                     textboxEl.hide();
3501                     textareaEl.show();
3502                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3503                         opt.multiline : this.defaultTextHeight);
3504                     activeTextEl = textareaEl;
3505                 }else{
3506                     textboxEl.show();
3507                     textareaEl.hide();
3508                 }
3509             }else{
3510                 textboxEl.hide();
3511                 textareaEl.hide();
3512             }
3513             progressEl.setDisplayed(opt.progress === true);
3514             this.updateProgress(0);
3515             activeTextEl.dom.value = opt.value || "";
3516             if(opt.prompt){
3517                 dlg.setDefaultButton(activeTextEl);
3518             }else{
3519                 var bs = opt.buttons;
3520                 var db = null;
3521                 if(bs && bs.ok){
3522                     db = buttons["ok"];
3523                 }else if(bs && bs.yes){
3524                     db = buttons["yes"];
3525                 }
3526                 dlg.setDefaultButton(db);
3527             }
3528             bwidth = updateButtons(opt.buttons);
3529             this.updateText(opt.msg);
3530             if(opt.cls){
3531                 d.el.addClass(opt.cls);
3532             }
3533             d.proxyDrag = opt.proxyDrag === true;
3534             d.modal = opt.modal !== false;
3535             d.mask = opt.modal !== false ? mask : false;
3536             if(!d.isVisible()){
3537                 // force it to the end of the z-index stack so it gets a cursor in FF
3538                 document.body.appendChild(dlg.el.dom);
3539                 d.animateTarget = null;
3540                 d.show(options.animEl);
3541             }
3542             return this;
3543         },
3544
3545         /**
3546          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3547          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3548          * and closing the message box when the process is complete.
3549          * @param {String} title The title bar text
3550          * @param {String} msg The message box body text
3551          * @return {Roo.MessageBox} This message box
3552          */
3553         progress : function(title, msg){
3554             this.show({
3555                 title : title,
3556                 msg : msg,
3557                 buttons: false,
3558                 progress:true,
3559                 closable:false,
3560                 minWidth: this.minProgressWidth,
3561                 modal : true
3562             });
3563             return this;
3564         },
3565
3566         /**
3567          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3568          * If a callback function is passed it will be called after the user clicks the button, and the
3569          * id of the button that was clicked will be passed as the only parameter to the callback
3570          * (could also be the top-right close button).
3571          * @param {String} title The title bar text
3572          * @param {String} msg The message box body text
3573          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3574          * @param {Object} scope (optional) The scope of the callback function
3575          * @return {Roo.MessageBox} This message box
3576          */
3577         alert : function(title, msg, fn, scope)
3578         {
3579             this.show({
3580                 title : title,
3581                 msg : msg,
3582                 buttons: this.OK,
3583                 fn: fn,
3584                 closable : false,
3585                 scope : scope,
3586                 modal : true
3587             });
3588             return this;
3589         },
3590
3591         /**
3592          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3593          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3594          * You are responsible for closing the message box when the process is complete.
3595          * @param {String} msg The message box body text
3596          * @param {String} title (optional) The title bar text
3597          * @return {Roo.MessageBox} This message box
3598          */
3599         wait : function(msg, title){
3600             this.show({
3601                 title : title,
3602                 msg : msg,
3603                 buttons: false,
3604                 closable:false,
3605                 progress:true,
3606                 modal:true,
3607                 width:300,
3608                 wait:true
3609             });
3610             waitTimer = Roo.TaskMgr.start({
3611                 run: function(i){
3612                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3613                 },
3614                 interval: 1000
3615             });
3616             return this;
3617         },
3618
3619         /**
3620          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3621          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3622          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3623          * @param {String} title The title bar text
3624          * @param {String} msg The message box body text
3625          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3626          * @param {Object} scope (optional) The scope of the callback function
3627          * @return {Roo.MessageBox} This message box
3628          */
3629         confirm : function(title, msg, fn, scope){
3630             this.show({
3631                 title : title,
3632                 msg : msg,
3633                 buttons: this.YESNO,
3634                 fn: fn,
3635                 scope : scope,
3636                 modal : true
3637             });
3638             return this;
3639         },
3640
3641         /**
3642          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3643          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3644          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3645          * (could also be the top-right close button) and the text that was entered will be passed as the two
3646          * parameters to the callback.
3647          * @param {String} title The title bar text
3648          * @param {String} msg The message box body text
3649          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3650          * @param {Object} scope (optional) The scope of the callback function
3651          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3652          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3653          * @return {Roo.MessageBox} This message box
3654          */
3655         prompt : function(title, msg, fn, scope, multiline){
3656             this.show({
3657                 title : title,
3658                 msg : msg,
3659                 buttons: this.OKCANCEL,
3660                 fn: fn,
3661                 minWidth:250,
3662                 scope : scope,
3663                 prompt:true,
3664                 multiline: multiline,
3665                 modal : true
3666             });
3667             return this;
3668         },
3669
3670         /**
3671          * Button config that displays a single OK button
3672          * @type Object
3673          */
3674         OK : {ok:true},
3675         /**
3676          * Button config that displays Yes and No buttons
3677          * @type Object
3678          */
3679         YESNO : {yes:true, no:true},
3680         /**
3681          * Button config that displays OK and Cancel buttons
3682          * @type Object
3683          */
3684         OKCANCEL : {ok:true, cancel:true},
3685         /**
3686          * Button config that displays Yes, No and Cancel buttons
3687          * @type Object
3688          */
3689         YESNOCANCEL : {yes:true, no:true, cancel:true},
3690
3691         /**
3692          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3693          * @type Number
3694          */
3695         defaultTextHeight : 75,
3696         /**
3697          * The maximum width in pixels of the message box (defaults to 600)
3698          * @type Number
3699          */
3700         maxWidth : 600,
3701         /**
3702          * The minimum width in pixels of the message box (defaults to 100)
3703          * @type Number
3704          */
3705         minWidth : 100,
3706         /**
3707          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3708          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3709          * @type Number
3710          */
3711         minProgressWidth : 250,
3712         /**
3713          * An object containing the default button text strings that can be overriden for localized language support.
3714          * Supported properties are: ok, cancel, yes and no.
3715          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3716          * @type Object
3717          */
3718         buttonText : {
3719             ok : "OK",
3720             cancel : "Cancel",
3721             yes : "Yes",
3722             no : "No"
3723         }
3724     };
3725 }();
3726
3727 /**
3728  * Shorthand for {@link Roo.MessageBox}
3729  */
3730 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3731 Roo.Msg = Roo.Msg || Roo.MessageBox;
3732 /*
3733  * - LGPL
3734  *
3735  * navbar
3736  * 
3737  */
3738
3739 /**
3740  * @class Roo.bootstrap.Navbar
3741  * @extends Roo.bootstrap.Component
3742  * Bootstrap Navbar class
3743
3744  * @constructor
3745  * Create a new Navbar
3746  * @param {Object} config The config object
3747  */
3748
3749
3750 Roo.bootstrap.Navbar = function(config){
3751     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3752     this.addEvents({
3753         // raw events
3754         /**
3755          * @event beforetoggle
3756          * Fire before toggle the menu
3757          * @param {Roo.EventObject} e
3758          */
3759         "beforetoggle" : true
3760     });
3761 };
3762
3763 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3764     
3765     
3766    
3767     // private
3768     navItems : false,
3769     loadMask : false,
3770     
3771     
3772     getAutoCreate : function(){
3773         
3774         
3775         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3776         
3777     },
3778     
3779     initEvents :function ()
3780     {
3781         //Roo.log(this.el.select('.navbar-toggle',true));
3782         this.el.select('.navbar-toggle',true).on('click', function() {
3783             if(this.fireEvent('beforetoggle', this) !== false){
3784                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3785             }
3786             
3787         }, this);
3788         
3789         var mark = {
3790             tag: "div",
3791             cls:"x-dlg-mask"
3792         };
3793         
3794         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3795         
3796         var size = this.el.getSize();
3797         this.maskEl.setSize(size.width, size.height);
3798         this.maskEl.enableDisplayMode("block");
3799         this.maskEl.hide();
3800         
3801         if(this.loadMask){
3802             this.maskEl.show();
3803         }
3804     },
3805     
3806     
3807     getChildContainer : function()
3808     {
3809         if (this.el.select('.collapse').getCount()) {
3810             return this.el.select('.collapse',true).first();
3811         }
3812         
3813         return this.el;
3814     },
3815     
3816     mask : function()
3817     {
3818         this.maskEl.show();
3819     },
3820     
3821     unmask : function()
3822     {
3823         this.maskEl.hide();
3824     } 
3825     
3826     
3827     
3828     
3829 });
3830
3831
3832
3833  
3834
3835  /*
3836  * - LGPL
3837  *
3838  * navbar
3839  * 
3840  */
3841
3842 /**
3843  * @class Roo.bootstrap.NavSimplebar
3844  * @extends Roo.bootstrap.Navbar
3845  * Bootstrap Sidebar class
3846  *
3847  * @cfg {Boolean} inverse is inverted color
3848  * 
3849  * @cfg {String} type (nav | pills | tabs)
3850  * @cfg {Boolean} arrangement stacked | justified
3851  * @cfg {String} align (left | right) alignment
3852  * 
3853  * @cfg {Boolean} main (true|false) main nav bar? default false
3854  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3855  * 
3856  * @cfg {String} tag (header|footer|nav|div) default is nav 
3857
3858  * 
3859  * 
3860  * 
3861  * @constructor
3862  * Create a new Sidebar
3863  * @param {Object} config The config object
3864  */
3865
3866
3867 Roo.bootstrap.NavSimplebar = function(config){
3868     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3869 };
3870
3871 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3872     
3873     inverse: false,
3874     
3875     type: false,
3876     arrangement: '',
3877     align : false,
3878     
3879     
3880     
3881     main : false,
3882     
3883     
3884     tag : false,
3885     
3886     
3887     getAutoCreate : function(){
3888         
3889         
3890         var cfg = {
3891             tag : this.tag || 'div',
3892             cls : 'navbar'
3893         };
3894           
3895         
3896         cfg.cn = [
3897             {
3898                 cls: 'nav',
3899                 tag : 'ul'
3900             }
3901         ];
3902         
3903          
3904         this.type = this.type || 'nav';
3905         if (['tabs','pills'].indexOf(this.type)!==-1) {
3906             cfg.cn[0].cls += ' nav-' + this.type
3907         
3908         
3909         } else {
3910             if (this.type!=='nav') {
3911                 Roo.log('nav type must be nav/tabs/pills')
3912             }
3913             cfg.cn[0].cls += ' navbar-nav'
3914         }
3915         
3916         
3917         
3918         
3919         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3920             cfg.cn[0].cls += ' nav-' + this.arrangement;
3921         }
3922         
3923         
3924         if (this.align === 'right') {
3925             cfg.cn[0].cls += ' navbar-right';
3926         }
3927         
3928         if (this.inverse) {
3929             cfg.cls += ' navbar-inverse';
3930             
3931         }
3932         
3933         
3934         return cfg;
3935     
3936         
3937     }
3938     
3939     
3940     
3941 });
3942
3943
3944
3945  
3946
3947  
3948        /*
3949  * - LGPL
3950  *
3951  * navbar
3952  * navbar-fixed-top
3953  * navbar-expand-md  fixed-top 
3954  */
3955
3956 /**
3957  * @class Roo.bootstrap.NavHeaderbar
3958  * @extends Roo.bootstrap.NavSimplebar
3959  * Bootstrap Sidebar class
3960  *
3961  * @cfg {String} brand what is brand
3962  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3963  * @cfg {String} brand_href href of the brand
3964  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3965  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3966  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3967  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3968  * 
3969  * @constructor
3970  * Create a new Sidebar
3971  * @param {Object} config The config object
3972  */
3973
3974
3975 Roo.bootstrap.NavHeaderbar = function(config){
3976     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3977       
3978 };
3979
3980 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3981     
3982     position: '',
3983     brand: '',
3984     brand_href: false,
3985     srButton : true,
3986     autohide : false,
3987     desktopCenter : false,
3988    
3989     
3990     getAutoCreate : function(){
3991         
3992         var   cfg = {
3993             tag: this.nav || 'nav',
3994             cls: 'navbar navbar-expand-md',
3995             role: 'navigation',
3996             cn: []
3997         };
3998         
3999         var cn = cfg.cn;
4000         if (this.desktopCenter) {
4001             cn.push({cls : 'container', cn : []});
4002             cn = cn[0].cn;
4003         }
4004         
4005         if(this.srButton){
4006             cn.push({
4007                 tag: 'div',
4008                 cls: 'navbar-header',
4009                 cn: [
4010                     {
4011                         tag: 'button',
4012                         type: 'button',
4013                         cls: 'navbar-toggle navbar-toggler',
4014                         'data-toggle': 'collapse',
4015                         cn: [
4016                             {
4017                                 tag: 'span',
4018                                 cls: 'sr-only',
4019                                 html: 'Toggle navigation'
4020                             },
4021                             {
4022                                 tag: 'span',
4023                                 cls: 'icon-bar navbar-toggler-icon'
4024                             },
4025                             {
4026                                 tag: 'span',
4027                                 cls: 'icon-bar'
4028                             },
4029                             {
4030                                 tag: 'span',
4031                                 cls: 'icon-bar'
4032                             }
4033                         ]
4034                     }
4035                 ]
4036             });
4037         }
4038         
4039         cn.push({
4040             tag: 'div',
4041             cls: 'collapse navbar-collapse',
4042             cn : []
4043         });
4044         
4045         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4046         
4047         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4048             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4049             
4050             // tag can override this..
4051             
4052             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4053         }
4054         
4055         if (this.brand !== '') {
4056             cn[0].cn.push({
4057                 tag: 'a',
4058                 href: this.brand_href ? this.brand_href : '#',
4059                 cls: 'navbar-brand',
4060                 cn: [
4061                 this.brand
4062                 ]
4063             });
4064         }
4065         
4066         if(this.main){
4067             cfg.cls += ' main-nav';
4068         }
4069         
4070         
4071         return cfg;
4072
4073         
4074     },
4075     getHeaderChildContainer : function()
4076     {
4077         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4078             return this.el.select('.navbar-header',true).first();
4079         }
4080         
4081         return this.getChildContainer();
4082     },
4083     
4084     
4085     initEvents : function()
4086     {
4087         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4088         
4089         if (this.autohide) {
4090             
4091             var prevScroll = 0;
4092             var ft = this.el;
4093             
4094             Roo.get(document).on('scroll',function(e) {
4095                 var ns = Roo.get(document).getScroll().top;
4096                 var os = prevScroll;
4097                 prevScroll = ns;
4098                 
4099                 if(ns > os){
4100                     ft.removeClass('slideDown');
4101                     ft.addClass('slideUp');
4102                     return;
4103                 }
4104                 ft.removeClass('slideUp');
4105                 ft.addClass('slideDown');
4106                  
4107               
4108           },this);
4109         }
4110     }    
4111     
4112 });
4113
4114
4115
4116  
4117
4118  /*
4119  * - LGPL
4120  *
4121  * navbar
4122  * 
4123  */
4124
4125 /**
4126  * @class Roo.bootstrap.NavSidebar
4127  * @extends Roo.bootstrap.Navbar
4128  * Bootstrap Sidebar class
4129  * 
4130  * @constructor
4131  * Create a new Sidebar
4132  * @param {Object} config The config object
4133  */
4134
4135
4136 Roo.bootstrap.NavSidebar = function(config){
4137     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4138 };
4139
4140 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4141     
4142     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4143     
4144     getAutoCreate : function(){
4145         
4146         
4147         return  {
4148             tag: 'div',
4149             cls: 'sidebar sidebar-nav'
4150         };
4151     
4152         
4153     }
4154     
4155     
4156     
4157 });
4158
4159
4160
4161  
4162
4163  /*
4164  * - LGPL
4165  *
4166  * nav group
4167  * 
4168  */
4169
4170 /**
4171  * @class Roo.bootstrap.NavGroup
4172  * @extends Roo.bootstrap.Component
4173  * Bootstrap NavGroup class
4174  * @cfg {String} align (left|right)
4175  * @cfg {Boolean} inverse
4176  * @cfg {String} type (nav|pills|tab) default nav
4177  * @cfg {String} navId - reference Id for navbar.
4178
4179  * 
4180  * @constructor
4181  * Create a new nav group
4182  * @param {Object} config The config object
4183  */
4184
4185 Roo.bootstrap.NavGroup = function(config){
4186     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4187     this.navItems = [];
4188    
4189     Roo.bootstrap.NavGroup.register(this);
4190      this.addEvents({
4191         /**
4192              * @event changed
4193              * Fires when the active item changes
4194              * @param {Roo.bootstrap.NavGroup} this
4195              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4196              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4197          */
4198         'changed': true
4199      });
4200     
4201 };
4202
4203 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4204     
4205     align: '',
4206     inverse: false,
4207     form: false,
4208     type: 'nav',
4209     navId : '',
4210     // private
4211     
4212     navItems : false, 
4213     
4214     getAutoCreate : function()
4215     {
4216         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4217         
4218         cfg = {
4219             tag : 'ul',
4220             cls: 'nav' 
4221         };
4222         
4223         if (['tabs','pills'].indexOf(this.type)!==-1) {
4224             cfg.cls += ' nav-' + this.type
4225         } else {
4226             if (this.type!=='nav') {
4227                 Roo.log('nav type must be nav/tabs/pills')
4228             }
4229             cfg.cls += ' navbar-nav mr-auto'
4230         }
4231         
4232         if (this.parent() && this.parent().sidebar) {
4233             cfg = {
4234                 tag: 'ul',
4235                 cls: 'dashboard-menu sidebar-menu'
4236             };
4237             
4238             return cfg;
4239         }
4240         
4241         if (this.form === true) {
4242             cfg = {
4243                 tag: 'form',
4244                 cls: 'navbar-form'
4245             };
4246             
4247             if (this.align === 'right') {
4248                 cfg.cls += ' navbar-right';
4249             } else {
4250                 cfg.cls += ' navbar-left';
4251             }
4252         }
4253         
4254         if (this.align === 'right') {
4255             cfg.cls += ' navbar-right';
4256         }
4257         
4258         if (this.inverse) {
4259             cfg.cls += ' navbar-inverse';
4260             
4261         }
4262         
4263         
4264         return cfg;
4265     },
4266     /**
4267     * sets the active Navigation item
4268     * @param {Roo.bootstrap.NavItem} the new current navitem
4269     */
4270     setActiveItem : function(item)
4271     {
4272         var prev = false;
4273         Roo.each(this.navItems, function(v){
4274             if (v == item) {
4275                 return ;
4276             }
4277             if (v.isActive()) {
4278                 v.setActive(false, true);
4279                 prev = v;
4280                 
4281             }
4282             
4283         });
4284
4285         item.setActive(true, true);
4286         this.fireEvent('changed', this, item, prev);
4287         
4288         
4289     },
4290     /**
4291     * gets the active Navigation item
4292     * @return {Roo.bootstrap.NavItem} the current navitem
4293     */
4294     getActive : function()
4295     {
4296         
4297         var prev = false;
4298         Roo.each(this.navItems, function(v){
4299             
4300             if (v.isActive()) {
4301                 prev = v;
4302                 
4303             }
4304             
4305         });
4306         return prev;
4307     },
4308     
4309     indexOfNav : function()
4310     {
4311         
4312         var prev = false;
4313         Roo.each(this.navItems, function(v,i){
4314             
4315             if (v.isActive()) {
4316                 prev = i;
4317                 
4318             }
4319             
4320         });
4321         return prev;
4322     },
4323     /**
4324     * adds a Navigation item
4325     * @param {Roo.bootstrap.NavItem} the navitem to add
4326     */
4327     addItem : function(cfg)
4328     {
4329         var cn = new Roo.bootstrap.NavItem(cfg);
4330         this.register(cn);
4331         cn.parentId = this.id;
4332         cn.onRender(this.el, null);
4333         return cn;
4334     },
4335     /**
4336     * register a Navigation item
4337     * @param {Roo.bootstrap.NavItem} the navitem to add
4338     */
4339     register : function(item)
4340     {
4341         this.navItems.push( item);
4342         item.navId = this.navId;
4343     
4344     },
4345     
4346     /**
4347     * clear all the Navigation item
4348     */
4349    
4350     clearAll : function()
4351     {
4352         this.navItems = [];
4353         this.el.dom.innerHTML = '';
4354     },
4355     
4356     getNavItem: function(tabId)
4357     {
4358         var ret = false;
4359         Roo.each(this.navItems, function(e) {
4360             if (e.tabId == tabId) {
4361                ret =  e;
4362                return false;
4363             }
4364             return true;
4365             
4366         });
4367         return ret;
4368     },
4369     
4370     setActiveNext : function()
4371     {
4372         var i = this.indexOfNav(this.getActive());
4373         if (i > this.navItems.length) {
4374             return;
4375         }
4376         this.setActiveItem(this.navItems[i+1]);
4377     },
4378     setActivePrev : function()
4379     {
4380         var i = this.indexOfNav(this.getActive());
4381         if (i  < 1) {
4382             return;
4383         }
4384         this.setActiveItem(this.navItems[i-1]);
4385     },
4386     clearWasActive : function(except) {
4387         Roo.each(this.navItems, function(e) {
4388             if (e.tabId != except.tabId && e.was_active) {
4389                e.was_active = false;
4390                return false;
4391             }
4392             return true;
4393             
4394         });
4395     },
4396     getWasActive : function ()
4397     {
4398         var r = false;
4399         Roo.each(this.navItems, function(e) {
4400             if (e.was_active) {
4401                r = e;
4402                return false;
4403             }
4404             return true;
4405             
4406         });
4407         return r;
4408     }
4409     
4410     
4411 });
4412
4413  
4414 Roo.apply(Roo.bootstrap.NavGroup, {
4415     
4416     groups: {},
4417      /**
4418     * register a Navigation Group
4419     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4420     */
4421     register : function(navgrp)
4422     {
4423         this.groups[navgrp.navId] = navgrp;
4424         
4425     },
4426     /**
4427     * fetch a Navigation Group based on the navigation ID
4428     * @param {string} the navgroup to add
4429     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4430     */
4431     get: function(navId) {
4432         if (typeof(this.groups[navId]) == 'undefined') {
4433             return false;
4434             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4435         }
4436         return this.groups[navId] ;
4437     }
4438     
4439     
4440     
4441 });
4442
4443  /*
4444  * - LGPL
4445  *
4446  * row
4447  * 
4448  */
4449
4450 /**
4451  * @class Roo.bootstrap.NavItem
4452  * @extends Roo.bootstrap.Component
4453  * Bootstrap Navbar.NavItem class
4454  * @cfg {String} href  link to
4455  * @cfg {String} html content of button
4456  * @cfg {String} badge text inside badge
4457  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4458  * @cfg {String} glyphicon name of glyphicon
4459  * @cfg {String} icon name of font awesome icon
4460  * @cfg {Boolean} active Is item active
4461  * @cfg {Boolean} disabled Is item disabled
4462  
4463  * @cfg {Boolean} preventDefault (true | false) default false
4464  * @cfg {String} tabId the tab that this item activates.
4465  * @cfg {String} tagtype (a|span) render as a href or span?
4466  * @cfg {Boolean} animateRef (true|false) link to element default false  
4467   
4468  * @constructor
4469  * Create a new Navbar Item
4470  * @param {Object} config The config object
4471  */
4472 Roo.bootstrap.NavItem = function(config){
4473     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4474     this.addEvents({
4475         // raw events
4476         /**
4477          * @event click
4478          * The raw click event for the entire grid.
4479          * @param {Roo.EventObject} e
4480          */
4481         "click" : true,
4482          /**
4483             * @event changed
4484             * Fires when the active item active state changes
4485             * @param {Roo.bootstrap.NavItem} this
4486             * @param {boolean} state the new state
4487              
4488          */
4489         'changed': true,
4490         /**
4491             * @event scrollto
4492             * Fires when scroll to element
4493             * @param {Roo.bootstrap.NavItem} this
4494             * @param {Object} options
4495             * @param {Roo.EventObject} e
4496              
4497          */
4498         'scrollto': true
4499     });
4500    
4501 };
4502
4503 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4504     
4505     href: false,
4506     html: '',
4507     badge: '',
4508     icon: false,
4509     glyphicon: false,
4510     active: false,
4511     preventDefault : false,
4512     tabId : false,
4513     tagtype : 'a',
4514     disabled : false,
4515     animateRef : false,
4516     was_active : false,
4517     
4518     getAutoCreate : function(){
4519          
4520         var cfg = {
4521             tag: 'li',
4522             cls: 'nav-item'
4523             
4524         };
4525         
4526         if (this.active) {
4527             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4528         }
4529         if (this.disabled) {
4530             cfg.cls += ' disabled';
4531         }
4532         
4533         if (this.href || this.html || this.glyphicon || this.icon) {
4534             cfg.cn = [
4535                 {
4536                     tag: this.tagtype,
4537                     href : this.href || "#",
4538                     html: this.html || ''
4539                 }
4540             ];
4541             if (this.tagtype == 'a') {
4542                 cfg.cn[0].cls = 'nav-link';
4543             }
4544             if (this.icon) {
4545                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4546             }
4547
4548             if(this.glyphicon) {
4549                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4550             }
4551             
4552             if (this.menu) {
4553                 
4554                 cfg.cn[0].html += " <span class='caret'></span>";
4555              
4556             }
4557             
4558             if (this.badge !== '') {
4559                  
4560                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4561             }
4562         }
4563         
4564         
4565         
4566         return cfg;
4567     },
4568     initEvents: function() 
4569     {
4570         if (typeof (this.menu) != 'undefined') {
4571             this.menu.parentType = this.xtype;
4572             this.menu.triggerEl = this.el;
4573             this.menu = this.addxtype(Roo.apply({}, this.menu));
4574         }
4575         
4576         this.el.select('a',true).on('click', this.onClick, this);
4577         
4578         if(this.tagtype == 'span'){
4579             this.el.select('span',true).on('click', this.onClick, this);
4580         }
4581        
4582         // at this point parent should be available..
4583         this.parent().register(this);
4584     },
4585     
4586     onClick : function(e)
4587     {
4588         if (e.getTarget('.dropdown-menu-item')) {
4589             // did you click on a menu itemm.... - then don't trigger onclick..
4590             return;
4591         }
4592         
4593         if(
4594                 this.preventDefault || 
4595                 this.href == '#' 
4596         ){
4597             Roo.log("NavItem - prevent Default?");
4598             e.preventDefault();
4599         }
4600         
4601         if (this.disabled) {
4602             return;
4603         }
4604         
4605         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4606         if (tg && tg.transition) {
4607             Roo.log("waiting for the transitionend");
4608             return;
4609         }
4610         
4611         
4612         
4613         //Roo.log("fire event clicked");
4614         if(this.fireEvent('click', this, e) === false){
4615             return;
4616         };
4617         
4618         if(this.tagtype == 'span'){
4619             return;
4620         }
4621         
4622         //Roo.log(this.href);
4623         var ael = this.el.select('a',true).first();
4624         //Roo.log(ael);
4625         
4626         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4627             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4628             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4629                 return; // ignore... - it's a 'hash' to another page.
4630             }
4631             Roo.log("NavItem - prevent Default?");
4632             e.preventDefault();
4633             this.scrollToElement(e);
4634         }
4635         
4636         
4637         var p =  this.parent();
4638    
4639         if (['tabs','pills'].indexOf(p.type)!==-1) {
4640             if (typeof(p.setActiveItem) !== 'undefined') {
4641                 p.setActiveItem(this);
4642             }
4643         }
4644         
4645         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4646         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4647             // remove the collapsed menu expand...
4648             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4649         }
4650     },
4651     
4652     isActive: function () {
4653         return this.active
4654     },
4655     setActive : function(state, fire, is_was_active)
4656     {
4657         if (this.active && !state && this.navId) {
4658             this.was_active = true;
4659             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4660             if (nv) {
4661                 nv.clearWasActive(this);
4662             }
4663             
4664         }
4665         this.active = state;
4666         
4667         if (!state ) {
4668             this.el.removeClass('active');
4669         } else if (!this.el.hasClass('active')) {
4670             this.el.addClass('active');
4671         }
4672         if (fire) {
4673             this.fireEvent('changed', this, state);
4674         }
4675         
4676         // show a panel if it's registered and related..
4677         
4678         if (!this.navId || !this.tabId || !state || is_was_active) {
4679             return;
4680         }
4681         
4682         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4683         if (!tg) {
4684             return;
4685         }
4686         var pan = tg.getPanelByName(this.tabId);
4687         if (!pan) {
4688             return;
4689         }
4690         // if we can not flip to new panel - go back to old nav highlight..
4691         if (false == tg.showPanel(pan)) {
4692             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4693             if (nv) {
4694                 var onav = nv.getWasActive();
4695                 if (onav) {
4696                     onav.setActive(true, false, true);
4697                 }
4698             }
4699             
4700         }
4701         
4702         
4703         
4704     },
4705      // this should not be here...
4706     setDisabled : function(state)
4707     {
4708         this.disabled = state;
4709         if (!state ) {
4710             this.el.removeClass('disabled');
4711         } else if (!this.el.hasClass('disabled')) {
4712             this.el.addClass('disabled');
4713         }
4714         
4715     },
4716     
4717     /**
4718      * Fetch the element to display the tooltip on.
4719      * @return {Roo.Element} defaults to this.el
4720      */
4721     tooltipEl : function()
4722     {
4723         return this.el.select('' + this.tagtype + '', true).first();
4724     },
4725     
4726     scrollToElement : function(e)
4727     {
4728         var c = document.body;
4729         
4730         /*
4731          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4732          */
4733         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4734             c = document.documentElement;
4735         }
4736         
4737         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4738         
4739         if(!target){
4740             return;
4741         }
4742
4743         var o = target.calcOffsetsTo(c);
4744         
4745         var options = {
4746             target : target,
4747             value : o[1]
4748         };
4749         
4750         this.fireEvent('scrollto', this, options, e);
4751         
4752         Roo.get(c).scrollTo('top', options.value, true);
4753         
4754         return;
4755     }
4756 });
4757  
4758
4759  /*
4760  * - LGPL
4761  *
4762  * sidebar item
4763  *
4764  *  li
4765  *    <span> icon </span>
4766  *    <span> text </span>
4767  *    <span>badge </span>
4768  */
4769
4770 /**
4771  * @class Roo.bootstrap.NavSidebarItem
4772  * @extends Roo.bootstrap.NavItem
4773  * Bootstrap Navbar.NavSidebarItem class
4774  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4775  * {Boolean} open is the menu open
4776  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4777  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4778  * {String} buttonSize (sm|md|lg)the extra classes for the button
4779  * {Boolean} showArrow show arrow next to the text (default true)
4780  * @constructor
4781  * Create a new Navbar Button
4782  * @param {Object} config The config object
4783  */
4784 Roo.bootstrap.NavSidebarItem = function(config){
4785     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4786     this.addEvents({
4787         // raw events
4788         /**
4789          * @event click
4790          * The raw click event for the entire grid.
4791          * @param {Roo.EventObject} e
4792          */
4793         "click" : true,
4794          /**
4795             * @event changed
4796             * Fires when the active item active state changes
4797             * @param {Roo.bootstrap.NavSidebarItem} this
4798             * @param {boolean} state the new state
4799              
4800          */
4801         'changed': true
4802     });
4803    
4804 };
4805
4806 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4807     
4808     badgeWeight : 'default',
4809     
4810     open: false,
4811     
4812     buttonView : false,
4813     
4814     buttonWeight : 'default',
4815     
4816     buttonSize : 'md',
4817     
4818     showArrow : true,
4819     
4820     getAutoCreate : function(){
4821         
4822         
4823         var a = {
4824                 tag: 'a',
4825                 href : this.href || '#',
4826                 cls: '',
4827                 html : '',
4828                 cn : []
4829         };
4830         
4831         if(this.buttonView){
4832             a = {
4833                 tag: 'button',
4834                 href : this.href || '#',
4835                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4836                 html : this.html,
4837                 cn : []
4838             };
4839         }
4840         
4841         var cfg = {
4842             tag: 'li',
4843             cls: '',
4844             cn: [ a ]
4845         };
4846         
4847         if (this.active) {
4848             cfg.cls += ' active';
4849         }
4850         
4851         if (this.disabled) {
4852             cfg.cls += ' disabled';
4853         }
4854         if (this.open) {
4855             cfg.cls += ' open x-open';
4856         }
4857         // left icon..
4858         if (this.glyphicon || this.icon) {
4859             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4860             a.cn.push({ tag : 'i', cls : c }) ;
4861         }
4862         
4863         if(!this.buttonView){
4864             var span = {
4865                 tag: 'span',
4866                 html : this.html || ''
4867             };
4868
4869             a.cn.push(span);
4870             
4871         }
4872         
4873         if (this.badge !== '') {
4874             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4875         }
4876         
4877         if (this.menu) {
4878             
4879             if(this.showArrow){
4880                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4881             }
4882             
4883             a.cls += ' dropdown-toggle treeview' ;
4884         }
4885         
4886         return cfg;
4887     },
4888     
4889     initEvents : function()
4890     { 
4891         if (typeof (this.menu) != 'undefined') {
4892             this.menu.parentType = this.xtype;
4893             this.menu.triggerEl = this.el;
4894             this.menu = this.addxtype(Roo.apply({}, this.menu));
4895         }
4896         
4897         this.el.on('click', this.onClick, this);
4898         
4899         if(this.badge !== ''){
4900             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4901         }
4902         
4903     },
4904     
4905     onClick : function(e)
4906     {
4907         if(this.disabled){
4908             e.preventDefault();
4909             return;
4910         }
4911         
4912         if(this.preventDefault){
4913             e.preventDefault();
4914         }
4915         
4916         this.fireEvent('click', this);
4917     },
4918     
4919     disable : function()
4920     {
4921         this.setDisabled(true);
4922     },
4923     
4924     enable : function()
4925     {
4926         this.setDisabled(false);
4927     },
4928     
4929     setDisabled : function(state)
4930     {
4931         if(this.disabled == state){
4932             return;
4933         }
4934         
4935         this.disabled = state;
4936         
4937         if (state) {
4938             this.el.addClass('disabled');
4939             return;
4940         }
4941         
4942         this.el.removeClass('disabled');
4943         
4944         return;
4945     },
4946     
4947     setActive : function(state)
4948     {
4949         if(this.active == state){
4950             return;
4951         }
4952         
4953         this.active = state;
4954         
4955         if (state) {
4956             this.el.addClass('active');
4957             return;
4958         }
4959         
4960         this.el.removeClass('active');
4961         
4962         return;
4963     },
4964     
4965     isActive: function () 
4966     {
4967         return this.active;
4968     },
4969     
4970     setBadge : function(str)
4971     {
4972         if(!this.badgeEl){
4973             return;
4974         }
4975         
4976         this.badgeEl.dom.innerHTML = str;
4977     }
4978     
4979    
4980      
4981  
4982 });
4983  
4984
4985  /*
4986  * - LGPL
4987  *
4988  * row
4989  * 
4990  */
4991
4992 /**
4993  * @class Roo.bootstrap.Row
4994  * @extends Roo.bootstrap.Component
4995  * Bootstrap Row class (contains columns...)
4996  * 
4997  * @constructor
4998  * Create a new Row
4999  * @param {Object} config The config object
5000  */
5001
5002 Roo.bootstrap.Row = function(config){
5003     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5004 };
5005
5006 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5007     
5008     getAutoCreate : function(){
5009        return {
5010             cls: 'row clearfix'
5011        };
5012     }
5013     
5014     
5015 });
5016
5017  
5018
5019  /*
5020  * - LGPL
5021  *
5022  * element
5023  * 
5024  */
5025
5026 /**
5027  * @class Roo.bootstrap.Element
5028  * @extends Roo.bootstrap.Component
5029  * Bootstrap Element class
5030  * @cfg {String} html contents of the element
5031  * @cfg {String} tag tag of the element
5032  * @cfg {String} cls class of the element
5033  * @cfg {Boolean} preventDefault (true|false) default false
5034  * @cfg {Boolean} clickable (true|false) default false
5035  * 
5036  * @constructor
5037  * Create a new Element
5038  * @param {Object} config The config object
5039  */
5040
5041 Roo.bootstrap.Element = function(config){
5042     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5043     
5044     this.addEvents({
5045         // raw events
5046         /**
5047          * @event click
5048          * When a element is chick
5049          * @param {Roo.bootstrap.Element} this
5050          * @param {Roo.EventObject} e
5051          */
5052         "click" : true
5053     });
5054 };
5055
5056 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5057     
5058     tag: 'div',
5059     cls: '',
5060     html: '',
5061     preventDefault: false, 
5062     clickable: false,
5063     
5064     getAutoCreate : function(){
5065         
5066         var cfg = {
5067             tag: this.tag,
5068             // cls: this.cls, double assign in parent class Component.js :: onRender
5069             html: this.html
5070         };
5071         
5072         return cfg;
5073     },
5074     
5075     initEvents: function() 
5076     {
5077         Roo.bootstrap.Element.superclass.initEvents.call(this);
5078         
5079         if(this.clickable){
5080             this.el.on('click', this.onClick, this);
5081         }
5082         
5083     },
5084     
5085     onClick : function(e)
5086     {
5087         if(this.preventDefault){
5088             e.preventDefault();
5089         }
5090         
5091         this.fireEvent('click', this, e);
5092     },
5093     
5094     getValue : function()
5095     {
5096         return this.el.dom.innerHTML;
5097     },
5098     
5099     setValue : function(value)
5100     {
5101         this.el.dom.innerHTML = value;
5102     }
5103    
5104 });
5105
5106  
5107
5108  /*
5109  * - LGPL
5110  *
5111  * pagination
5112  * 
5113  */
5114
5115 /**
5116  * @class Roo.bootstrap.Pagination
5117  * @extends Roo.bootstrap.Component
5118  * Bootstrap Pagination class
5119  * @cfg {String} size xs | sm | md | lg
5120  * @cfg {Boolean} inverse false | true
5121  * 
5122  * @constructor
5123  * Create a new Pagination
5124  * @param {Object} config The config object
5125  */
5126
5127 Roo.bootstrap.Pagination = function(config){
5128     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5129 };
5130
5131 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5132     
5133     cls: false,
5134     size: false,
5135     inverse: false,
5136     
5137     getAutoCreate : function(){
5138         var cfg = {
5139             tag: 'ul',
5140                 cls: 'pagination'
5141         };
5142         if (this.inverse) {
5143             cfg.cls += ' inverse';
5144         }
5145         if (this.html) {
5146             cfg.html=this.html;
5147         }
5148         if (this.cls) {
5149             cfg.cls += " " + this.cls;
5150         }
5151         return cfg;
5152     }
5153    
5154 });
5155
5156  
5157
5158  /*
5159  * - LGPL
5160  *
5161  * Pagination item
5162  * 
5163  */
5164
5165
5166 /**
5167  * @class Roo.bootstrap.PaginationItem
5168  * @extends Roo.bootstrap.Component
5169  * Bootstrap PaginationItem class
5170  * @cfg {String} html text
5171  * @cfg {String} href the link
5172  * @cfg {Boolean} preventDefault (true | false) default true
5173  * @cfg {Boolean} active (true | false) default false
5174  * @cfg {Boolean} disabled default false
5175  * 
5176  * 
5177  * @constructor
5178  * Create a new PaginationItem
5179  * @param {Object} config The config object
5180  */
5181
5182
5183 Roo.bootstrap.PaginationItem = function(config){
5184     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5185     this.addEvents({
5186         // raw events
5187         /**
5188          * @event click
5189          * The raw click event for the entire grid.
5190          * @param {Roo.EventObject} e
5191          */
5192         "click" : true
5193     });
5194 };
5195
5196 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5197     
5198     href : false,
5199     html : false,
5200     preventDefault: true,
5201     active : false,
5202     cls : false,
5203     disabled: false,
5204     
5205     getAutoCreate : function(){
5206         var cfg= {
5207             tag: 'li',
5208             cn: [
5209                 {
5210                     tag : 'a',
5211                     href : this.href ? this.href : '#',
5212                     html : this.html ? this.html : ''
5213                 }
5214             ]
5215         };
5216         
5217         if(this.cls){
5218             cfg.cls = this.cls;
5219         }
5220         
5221         if(this.disabled){
5222             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5223         }
5224         
5225         if(this.active){
5226             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5227         }
5228         
5229         return cfg;
5230     },
5231     
5232     initEvents: function() {
5233         
5234         this.el.on('click', this.onClick, this);
5235         
5236     },
5237     onClick : function(e)
5238     {
5239         Roo.log('PaginationItem on click ');
5240         if(this.preventDefault){
5241             e.preventDefault();
5242         }
5243         
5244         if(this.disabled){
5245             return;
5246         }
5247         
5248         this.fireEvent('click', this, e);
5249     }
5250    
5251 });
5252
5253  
5254
5255  /*
5256  * - LGPL
5257  *
5258  * slider
5259  * 
5260  */
5261
5262
5263 /**
5264  * @class Roo.bootstrap.Slider
5265  * @extends Roo.bootstrap.Component
5266  * Bootstrap Slider class
5267  *    
5268  * @constructor
5269  * Create a new Slider
5270  * @param {Object} config The config object
5271  */
5272
5273 Roo.bootstrap.Slider = function(config){
5274     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5275 };
5276
5277 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5278     
5279     getAutoCreate : function(){
5280         
5281         var cfg = {
5282             tag: 'div',
5283             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5284             cn: [
5285                 {
5286                     tag: 'a',
5287                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5288                 }
5289             ]
5290         };
5291         
5292         return cfg;
5293     }
5294    
5295 });
5296
5297  /*
5298  * Based on:
5299  * Ext JS Library 1.1.1
5300  * Copyright(c) 2006-2007, Ext JS, LLC.
5301  *
5302  * Originally Released Under LGPL - original licence link has changed is not relivant.
5303  *
5304  * Fork - LGPL
5305  * <script type="text/javascript">
5306  */
5307  
5308
5309 /**
5310  * @class Roo.grid.ColumnModel
5311  * @extends Roo.util.Observable
5312  * This is the default implementation of a ColumnModel used by the Grid. It defines
5313  * the columns in the grid.
5314  * <br>Usage:<br>
5315  <pre><code>
5316  var colModel = new Roo.grid.ColumnModel([
5317         {header: "Ticker", width: 60, sortable: true, locked: true},
5318         {header: "Company Name", width: 150, sortable: true},
5319         {header: "Market Cap.", width: 100, sortable: true},
5320         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5321         {header: "Employees", width: 100, sortable: true, resizable: false}
5322  ]);
5323  </code></pre>
5324  * <p>
5325  
5326  * The config options listed for this class are options which may appear in each
5327  * individual column definition.
5328  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5329  * @constructor
5330  * @param {Object} config An Array of column config objects. See this class's
5331  * config objects for details.
5332 */
5333 Roo.grid.ColumnModel = function(config){
5334         /**
5335      * The config passed into the constructor
5336      */
5337     this.config = config;
5338     this.lookup = {};
5339
5340     // if no id, create one
5341     // if the column does not have a dataIndex mapping,
5342     // map it to the order it is in the config
5343     for(var i = 0, len = config.length; i < len; i++){
5344         var c = config[i];
5345         if(typeof c.dataIndex == "undefined"){
5346             c.dataIndex = i;
5347         }
5348         if(typeof c.renderer == "string"){
5349             c.renderer = Roo.util.Format[c.renderer];
5350         }
5351         if(typeof c.id == "undefined"){
5352             c.id = Roo.id();
5353         }
5354         if(c.editor && c.editor.xtype){
5355             c.editor  = Roo.factory(c.editor, Roo.grid);
5356         }
5357         if(c.editor && c.editor.isFormField){
5358             c.editor = new Roo.grid.GridEditor(c.editor);
5359         }
5360         this.lookup[c.id] = c;
5361     }
5362
5363     /**
5364      * The width of columns which have no width specified (defaults to 100)
5365      * @type Number
5366      */
5367     this.defaultWidth = 100;
5368
5369     /**
5370      * Default sortable of columns which have no sortable specified (defaults to false)
5371      * @type Boolean
5372      */
5373     this.defaultSortable = false;
5374
5375     this.addEvents({
5376         /**
5377              * @event widthchange
5378              * Fires when the width of a column changes.
5379              * @param {ColumnModel} this
5380              * @param {Number} columnIndex The column index
5381              * @param {Number} newWidth The new width
5382              */
5383             "widthchange": true,
5384         /**
5385              * @event headerchange
5386              * Fires when the text of a header changes.
5387              * @param {ColumnModel} this
5388              * @param {Number} columnIndex The column index
5389              * @param {Number} newText The new header text
5390              */
5391             "headerchange": true,
5392         /**
5393              * @event hiddenchange
5394              * Fires when a column is hidden or "unhidden".
5395              * @param {ColumnModel} this
5396              * @param {Number} columnIndex The column index
5397              * @param {Boolean} hidden true if hidden, false otherwise
5398              */
5399             "hiddenchange": true,
5400             /**
5401          * @event columnmoved
5402          * Fires when a column is moved.
5403          * @param {ColumnModel} this
5404          * @param {Number} oldIndex
5405          * @param {Number} newIndex
5406          */
5407         "columnmoved" : true,
5408         /**
5409          * @event columlockchange
5410          * Fires when a column's locked state is changed
5411          * @param {ColumnModel} this
5412          * @param {Number} colIndex
5413          * @param {Boolean} locked true if locked
5414          */
5415         "columnlockchange" : true
5416     });
5417     Roo.grid.ColumnModel.superclass.constructor.call(this);
5418 };
5419 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5420     /**
5421      * @cfg {String} header The header text to display in the Grid view.
5422      */
5423     /**
5424      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5425      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5426      * specified, the column's index is used as an index into the Record's data Array.
5427      */
5428     /**
5429      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5430      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5431      */
5432     /**
5433      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5434      * Defaults to the value of the {@link #defaultSortable} property.
5435      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5436      */
5437     /**
5438      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5439      */
5440     /**
5441      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5442      */
5443     /**
5444      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5445      */
5446     /**
5447      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5448      */
5449     /**
5450      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5451      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5452      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5453      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5454      */
5455        /**
5456      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5457      */
5458     /**
5459      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5460      */
5461     /**
5462      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5463      */
5464     /**
5465      * @cfg {String} cursor (Optional)
5466      */
5467     /**
5468      * @cfg {String} tooltip (Optional)
5469      */
5470     /**
5471      * @cfg {Number} xs (Optional)
5472      */
5473     /**
5474      * @cfg {Number} sm (Optional)
5475      */
5476     /**
5477      * @cfg {Number} md (Optional)
5478      */
5479     /**
5480      * @cfg {Number} lg (Optional)
5481      */
5482     /**
5483      * Returns the id of the column at the specified index.
5484      * @param {Number} index The column index
5485      * @return {String} the id
5486      */
5487     getColumnId : function(index){
5488         return this.config[index].id;
5489     },
5490
5491     /**
5492      * Returns the column for a specified id.
5493      * @param {String} id The column id
5494      * @return {Object} the column
5495      */
5496     getColumnById : function(id){
5497         return this.lookup[id];
5498     },
5499
5500     
5501     /**
5502      * Returns the column for a specified dataIndex.
5503      * @param {String} dataIndex The column dataIndex
5504      * @return {Object|Boolean} the column or false if not found
5505      */
5506     getColumnByDataIndex: function(dataIndex){
5507         var index = this.findColumnIndex(dataIndex);
5508         return index > -1 ? this.config[index] : false;
5509     },
5510     
5511     /**
5512      * Returns the index for a specified column id.
5513      * @param {String} id The column id
5514      * @return {Number} the index, or -1 if not found
5515      */
5516     getIndexById : function(id){
5517         for(var i = 0, len = this.config.length; i < len; i++){
5518             if(this.config[i].id == id){
5519                 return i;
5520             }
5521         }
5522         return -1;
5523     },
5524     
5525     /**
5526      * Returns the index for a specified column dataIndex.
5527      * @param {String} dataIndex The column dataIndex
5528      * @return {Number} the index, or -1 if not found
5529      */
5530     
5531     findColumnIndex : function(dataIndex){
5532         for(var i = 0, len = this.config.length; i < len; i++){
5533             if(this.config[i].dataIndex == dataIndex){
5534                 return i;
5535             }
5536         }
5537         return -1;
5538     },
5539     
5540     
5541     moveColumn : function(oldIndex, newIndex){
5542         var c = this.config[oldIndex];
5543         this.config.splice(oldIndex, 1);
5544         this.config.splice(newIndex, 0, c);
5545         this.dataMap = null;
5546         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5547     },
5548
5549     isLocked : function(colIndex){
5550         return this.config[colIndex].locked === true;
5551     },
5552
5553     setLocked : function(colIndex, value, suppressEvent){
5554         if(this.isLocked(colIndex) == value){
5555             return;
5556         }
5557         this.config[colIndex].locked = value;
5558         if(!suppressEvent){
5559             this.fireEvent("columnlockchange", this, colIndex, value);
5560         }
5561     },
5562
5563     getTotalLockedWidth : function(){
5564         var totalWidth = 0;
5565         for(var i = 0; i < this.config.length; i++){
5566             if(this.isLocked(i) && !this.isHidden(i)){
5567                 this.totalWidth += this.getColumnWidth(i);
5568             }
5569         }
5570         return totalWidth;
5571     },
5572
5573     getLockedCount : function(){
5574         for(var i = 0, len = this.config.length; i < len; i++){
5575             if(!this.isLocked(i)){
5576                 return i;
5577             }
5578         }
5579         
5580         return this.config.length;
5581     },
5582
5583     /**
5584      * Returns the number of columns.
5585      * @return {Number}
5586      */
5587     getColumnCount : function(visibleOnly){
5588         if(visibleOnly === true){
5589             var c = 0;
5590             for(var i = 0, len = this.config.length; i < len; i++){
5591                 if(!this.isHidden(i)){
5592                     c++;
5593                 }
5594             }
5595             return c;
5596         }
5597         return this.config.length;
5598     },
5599
5600     /**
5601      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5602      * @param {Function} fn
5603      * @param {Object} scope (optional)
5604      * @return {Array} result
5605      */
5606     getColumnsBy : function(fn, scope){
5607         var r = [];
5608         for(var i = 0, len = this.config.length; i < len; i++){
5609             var c = this.config[i];
5610             if(fn.call(scope||this, c, i) === true){
5611                 r[r.length] = c;
5612             }
5613         }
5614         return r;
5615     },
5616
5617     /**
5618      * Returns true if the specified column is sortable.
5619      * @param {Number} col The column index
5620      * @return {Boolean}
5621      */
5622     isSortable : function(col){
5623         if(typeof this.config[col].sortable == "undefined"){
5624             return this.defaultSortable;
5625         }
5626         return this.config[col].sortable;
5627     },
5628
5629     /**
5630      * Returns the rendering (formatting) function defined for the column.
5631      * @param {Number} col The column index.
5632      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5633      */
5634     getRenderer : function(col){
5635         if(!this.config[col].renderer){
5636             return Roo.grid.ColumnModel.defaultRenderer;
5637         }
5638         return this.config[col].renderer;
5639     },
5640
5641     /**
5642      * Sets the rendering (formatting) function for a column.
5643      * @param {Number} col The column index
5644      * @param {Function} fn The function to use to process the cell's raw data
5645      * to return HTML markup for the grid view. The render function is called with
5646      * the following parameters:<ul>
5647      * <li>Data value.</li>
5648      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5649      * <li>css A CSS style string to apply to the table cell.</li>
5650      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5651      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5652      * <li>Row index</li>
5653      * <li>Column index</li>
5654      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5655      */
5656     setRenderer : function(col, fn){
5657         this.config[col].renderer = fn;
5658     },
5659
5660     /**
5661      * Returns the width for the specified column.
5662      * @param {Number} col The column index
5663      * @return {Number}
5664      */
5665     getColumnWidth : function(col){
5666         return this.config[col].width * 1 || this.defaultWidth;
5667     },
5668
5669     /**
5670      * Sets the width for a column.
5671      * @param {Number} col The column index
5672      * @param {Number} width The new width
5673      */
5674     setColumnWidth : function(col, width, suppressEvent){
5675         this.config[col].width = width;
5676         this.totalWidth = null;
5677         if(!suppressEvent){
5678              this.fireEvent("widthchange", this, col, width);
5679         }
5680     },
5681
5682     /**
5683      * Returns the total width of all columns.
5684      * @param {Boolean} includeHidden True to include hidden column widths
5685      * @return {Number}
5686      */
5687     getTotalWidth : function(includeHidden){
5688         if(!this.totalWidth){
5689             this.totalWidth = 0;
5690             for(var i = 0, len = this.config.length; i < len; i++){
5691                 if(includeHidden || !this.isHidden(i)){
5692                     this.totalWidth += this.getColumnWidth(i);
5693                 }
5694             }
5695         }
5696         return this.totalWidth;
5697     },
5698
5699     /**
5700      * Returns the header for the specified column.
5701      * @param {Number} col The column index
5702      * @return {String}
5703      */
5704     getColumnHeader : function(col){
5705         return this.config[col].header;
5706     },
5707
5708     /**
5709      * Sets the header for a column.
5710      * @param {Number} col The column index
5711      * @param {String} header The new header
5712      */
5713     setColumnHeader : function(col, header){
5714         this.config[col].header = header;
5715         this.fireEvent("headerchange", this, col, header);
5716     },
5717
5718     /**
5719      * Returns the tooltip for the specified column.
5720      * @param {Number} col The column index
5721      * @return {String}
5722      */
5723     getColumnTooltip : function(col){
5724             return this.config[col].tooltip;
5725     },
5726     /**
5727      * Sets the tooltip for a column.
5728      * @param {Number} col The column index
5729      * @param {String} tooltip The new tooltip
5730      */
5731     setColumnTooltip : function(col, tooltip){
5732             this.config[col].tooltip = tooltip;
5733     },
5734
5735     /**
5736      * Returns the dataIndex for the specified column.
5737      * @param {Number} col The column index
5738      * @return {Number}
5739      */
5740     getDataIndex : function(col){
5741         return this.config[col].dataIndex;
5742     },
5743
5744     /**
5745      * Sets the dataIndex for a column.
5746      * @param {Number} col The column index
5747      * @param {Number} dataIndex The new dataIndex
5748      */
5749     setDataIndex : function(col, dataIndex){
5750         this.config[col].dataIndex = dataIndex;
5751     },
5752
5753     
5754     
5755     /**
5756      * Returns true if the cell is editable.
5757      * @param {Number} colIndex The column index
5758      * @param {Number} rowIndex The row index - this is nto actually used..?
5759      * @return {Boolean}
5760      */
5761     isCellEditable : function(colIndex, rowIndex){
5762         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5763     },
5764
5765     /**
5766      * Returns the editor defined for the cell/column.
5767      * return false or null to disable editing.
5768      * @param {Number} colIndex The column index
5769      * @param {Number} rowIndex The row index
5770      * @return {Object}
5771      */
5772     getCellEditor : function(colIndex, rowIndex){
5773         return this.config[colIndex].editor;
5774     },
5775
5776     /**
5777      * Sets if a column is editable.
5778      * @param {Number} col The column index
5779      * @param {Boolean} editable True if the column is editable
5780      */
5781     setEditable : function(col, editable){
5782         this.config[col].editable = editable;
5783     },
5784
5785
5786     /**
5787      * Returns true if the column is hidden.
5788      * @param {Number} colIndex The column index
5789      * @return {Boolean}
5790      */
5791     isHidden : function(colIndex){
5792         return this.config[colIndex].hidden;
5793     },
5794
5795
5796     /**
5797      * Returns true if the column width cannot be changed
5798      */
5799     isFixed : function(colIndex){
5800         return this.config[colIndex].fixed;
5801     },
5802
5803     /**
5804      * Returns true if the column can be resized
5805      * @return {Boolean}
5806      */
5807     isResizable : function(colIndex){
5808         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5809     },
5810     /**
5811      * Sets if a column is hidden.
5812      * @param {Number} colIndex The column index
5813      * @param {Boolean} hidden True if the column is hidden
5814      */
5815     setHidden : function(colIndex, hidden){
5816         this.config[colIndex].hidden = hidden;
5817         this.totalWidth = null;
5818         this.fireEvent("hiddenchange", this, colIndex, hidden);
5819     },
5820
5821     /**
5822      * Sets the editor for a column.
5823      * @param {Number} col The column index
5824      * @param {Object} editor The editor object
5825      */
5826     setEditor : function(col, editor){
5827         this.config[col].editor = editor;
5828     }
5829 });
5830
5831 Roo.grid.ColumnModel.defaultRenderer = function(value)
5832 {
5833     if(typeof value == "object") {
5834         return value;
5835     }
5836         if(typeof value == "string" && value.length < 1){
5837             return "&#160;";
5838         }
5839     
5840         return String.format("{0}", value);
5841 };
5842
5843 // Alias for backwards compatibility
5844 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5845 /*
5846  * Based on:
5847  * Ext JS Library 1.1.1
5848  * Copyright(c) 2006-2007, Ext JS, LLC.
5849  *
5850  * Originally Released Under LGPL - original licence link has changed is not relivant.
5851  *
5852  * Fork - LGPL
5853  * <script type="text/javascript">
5854  */
5855  
5856 /**
5857  * @class Roo.LoadMask
5858  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5859  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5860  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5861  * element's UpdateManager load indicator and will be destroyed after the initial load.
5862  * @constructor
5863  * Create a new LoadMask
5864  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5865  * @param {Object} config The config object
5866  */
5867 Roo.LoadMask = function(el, config){
5868     this.el = Roo.get(el);
5869     Roo.apply(this, config);
5870     if(this.store){
5871         this.store.on('beforeload', this.onBeforeLoad, this);
5872         this.store.on('load', this.onLoad, this);
5873         this.store.on('loadexception', this.onLoadException, this);
5874         this.removeMask = false;
5875     }else{
5876         var um = this.el.getUpdateManager();
5877         um.showLoadIndicator = false; // disable the default indicator
5878         um.on('beforeupdate', this.onBeforeLoad, this);
5879         um.on('update', this.onLoad, this);
5880         um.on('failure', this.onLoad, this);
5881         this.removeMask = true;
5882     }
5883 };
5884
5885 Roo.LoadMask.prototype = {
5886     /**
5887      * @cfg {Boolean} removeMask
5888      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5889      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5890      */
5891     /**
5892      * @cfg {String} msg
5893      * The text to display in a centered loading message box (defaults to 'Loading...')
5894      */
5895     msg : 'Loading...',
5896     /**
5897      * @cfg {String} msgCls
5898      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5899      */
5900     msgCls : 'x-mask-loading',
5901
5902     /**
5903      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5904      * @type Boolean
5905      */
5906     disabled: false,
5907
5908     /**
5909      * Disables the mask to prevent it from being displayed
5910      */
5911     disable : function(){
5912        this.disabled = true;
5913     },
5914
5915     /**
5916      * Enables the mask so that it can be displayed
5917      */
5918     enable : function(){
5919         this.disabled = false;
5920     },
5921     
5922     onLoadException : function()
5923     {
5924         Roo.log(arguments);
5925         
5926         if (typeof(arguments[3]) != 'undefined') {
5927             Roo.MessageBox.alert("Error loading",arguments[3]);
5928         } 
5929         /*
5930         try {
5931             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5932                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5933             }   
5934         } catch(e) {
5935             
5936         }
5937         */
5938     
5939         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5940     },
5941     // private
5942     onLoad : function()
5943     {
5944         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5945     },
5946
5947     // private
5948     onBeforeLoad : function(){
5949         if(!this.disabled){
5950             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5951         }
5952     },
5953
5954     // private
5955     destroy : function(){
5956         if(this.store){
5957             this.store.un('beforeload', this.onBeforeLoad, this);
5958             this.store.un('load', this.onLoad, this);
5959             this.store.un('loadexception', this.onLoadException, this);
5960         }else{
5961             var um = this.el.getUpdateManager();
5962             um.un('beforeupdate', this.onBeforeLoad, this);
5963             um.un('update', this.onLoad, this);
5964             um.un('failure', this.onLoad, this);
5965         }
5966     }
5967 };/*
5968  * - LGPL
5969  *
5970  * table
5971  * 
5972  */
5973
5974 /**
5975  * @class Roo.bootstrap.Table
5976  * @extends Roo.bootstrap.Component
5977  * Bootstrap Table class
5978  * @cfg {String} cls table class
5979  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5980  * @cfg {String} bgcolor Specifies the background color for a table
5981  * @cfg {Number} border Specifies whether the table cells should have borders or not
5982  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5983  * @cfg {Number} cellspacing Specifies the space between cells
5984  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5985  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5986  * @cfg {String} sortable Specifies that the table should be sortable
5987  * @cfg {String} summary Specifies a summary of the content of a table
5988  * @cfg {Number} width Specifies the width of a table
5989  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5990  * 
5991  * @cfg {boolean} striped Should the rows be alternative striped
5992  * @cfg {boolean} bordered Add borders to the table
5993  * @cfg {boolean} hover Add hover highlighting
5994  * @cfg {boolean} condensed Format condensed
5995  * @cfg {boolean} responsive Format condensed
5996  * @cfg {Boolean} loadMask (true|false) default false
5997  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5998  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5999  * @cfg {Boolean} rowSelection (true|false) default false
6000  * @cfg {Boolean} cellSelection (true|false) default false
6001  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6002  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6003  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6004  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6005  
6006  * 
6007  * @constructor
6008  * Create a new Table
6009  * @param {Object} config The config object
6010  */
6011
6012 Roo.bootstrap.Table = function(config){
6013     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6014     
6015   
6016     
6017     // BC...
6018     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6019     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6020     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6021     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6022     
6023     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6024     if (this.sm) {
6025         this.sm.grid = this;
6026         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6027         this.sm = this.selModel;
6028         this.sm.xmodule = this.xmodule || false;
6029     }
6030     
6031     if (this.cm && typeof(this.cm.config) == 'undefined') {
6032         this.colModel = new Roo.grid.ColumnModel(this.cm);
6033         this.cm = this.colModel;
6034         this.cm.xmodule = this.xmodule || false;
6035     }
6036     if (this.store) {
6037         this.store= Roo.factory(this.store, Roo.data);
6038         this.ds = this.store;
6039         this.ds.xmodule = this.xmodule || false;
6040          
6041     }
6042     if (this.footer && this.store) {
6043         this.footer.dataSource = this.ds;
6044         this.footer = Roo.factory(this.footer);
6045     }
6046     
6047     /** @private */
6048     this.addEvents({
6049         /**
6050          * @event cellclick
6051          * Fires when a cell is clicked
6052          * @param {Roo.bootstrap.Table} this
6053          * @param {Roo.Element} el
6054          * @param {Number} rowIndex
6055          * @param {Number} columnIndex
6056          * @param {Roo.EventObject} e
6057          */
6058         "cellclick" : true,
6059         /**
6060          * @event celldblclick
6061          * Fires when a cell is double clicked
6062          * @param {Roo.bootstrap.Table} this
6063          * @param {Roo.Element} el
6064          * @param {Number} rowIndex
6065          * @param {Number} columnIndex
6066          * @param {Roo.EventObject} e
6067          */
6068         "celldblclick" : true,
6069         /**
6070          * @event rowclick
6071          * Fires when a row is clicked
6072          * @param {Roo.bootstrap.Table} this
6073          * @param {Roo.Element} el
6074          * @param {Number} rowIndex
6075          * @param {Roo.EventObject} e
6076          */
6077         "rowclick" : true,
6078         /**
6079          * @event rowdblclick
6080          * Fires when a row is double clicked
6081          * @param {Roo.bootstrap.Table} this
6082          * @param {Roo.Element} el
6083          * @param {Number} rowIndex
6084          * @param {Roo.EventObject} e
6085          */
6086         "rowdblclick" : true,
6087         /**
6088          * @event mouseover
6089          * Fires when a mouseover occur
6090          * @param {Roo.bootstrap.Table} this
6091          * @param {Roo.Element} el
6092          * @param {Number} rowIndex
6093          * @param {Number} columnIndex
6094          * @param {Roo.EventObject} e
6095          */
6096         "mouseover" : true,
6097         /**
6098          * @event mouseout
6099          * Fires when a mouseout occur
6100          * @param {Roo.bootstrap.Table} this
6101          * @param {Roo.Element} el
6102          * @param {Number} rowIndex
6103          * @param {Number} columnIndex
6104          * @param {Roo.EventObject} e
6105          */
6106         "mouseout" : true,
6107         /**
6108          * @event rowclass
6109          * Fires when a row is rendered, so you can change add a style to it.
6110          * @param {Roo.bootstrap.Table} this
6111          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6112          */
6113         'rowclass' : true,
6114           /**
6115          * @event rowsrendered
6116          * Fires when all the  rows have been rendered
6117          * @param {Roo.bootstrap.Table} this
6118          */
6119         'rowsrendered' : true,
6120         /**
6121          * @event contextmenu
6122          * The raw contextmenu event for the entire grid.
6123          * @param {Roo.EventObject} e
6124          */
6125         "contextmenu" : true,
6126         /**
6127          * @event rowcontextmenu
6128          * Fires when a row is right clicked
6129          * @param {Roo.bootstrap.Table} this
6130          * @param {Number} rowIndex
6131          * @param {Roo.EventObject} e
6132          */
6133         "rowcontextmenu" : true,
6134         /**
6135          * @event cellcontextmenu
6136          * Fires when a cell is right clicked
6137          * @param {Roo.bootstrap.Table} this
6138          * @param {Number} rowIndex
6139          * @param {Number} cellIndex
6140          * @param {Roo.EventObject} e
6141          */
6142          "cellcontextmenu" : true,
6143          /**
6144          * @event headercontextmenu
6145          * Fires when a header is right clicked
6146          * @param {Roo.bootstrap.Table} this
6147          * @param {Number} columnIndex
6148          * @param {Roo.EventObject} e
6149          */
6150         "headercontextmenu" : true
6151     });
6152 };
6153
6154 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6155     
6156     cls: false,
6157     align: false,
6158     bgcolor: false,
6159     border: false,
6160     cellpadding: false,
6161     cellspacing: false,
6162     frame: false,
6163     rules: false,
6164     sortable: false,
6165     summary: false,
6166     width: false,
6167     striped : false,
6168     scrollBody : false,
6169     bordered: false,
6170     hover:  false,
6171     condensed : false,
6172     responsive : false,
6173     sm : false,
6174     cm : false,
6175     store : false,
6176     loadMask : false,
6177     footerShow : true,
6178     headerShow : true,
6179   
6180     rowSelection : false,
6181     cellSelection : false,
6182     layout : false,
6183     
6184     // Roo.Element - the tbody
6185     mainBody: false,
6186     // Roo.Element - thead element
6187     mainHead: false,
6188     
6189     container: false, // used by gridpanel...
6190     
6191     lazyLoad : false,
6192     
6193     CSS : Roo.util.CSS,
6194     
6195     auto_hide_footer : false,
6196     
6197     getAutoCreate : function()
6198     {
6199         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6200         
6201         cfg = {
6202             tag: 'table',
6203             cls : 'table',
6204             cn : []
6205         };
6206         if (this.scrollBody) {
6207             cfg.cls += ' table-body-fixed';
6208         }    
6209         if (this.striped) {
6210             cfg.cls += ' table-striped';
6211         }
6212         
6213         if (this.hover) {
6214             cfg.cls += ' table-hover';
6215         }
6216         if (this.bordered) {
6217             cfg.cls += ' table-bordered';
6218         }
6219         if (this.condensed) {
6220             cfg.cls += ' table-condensed';
6221         }
6222         if (this.responsive) {
6223             cfg.cls += ' table-responsive';
6224         }
6225         
6226         if (this.cls) {
6227             cfg.cls+=  ' ' +this.cls;
6228         }
6229         
6230         // this lot should be simplifed...
6231         var _t = this;
6232         var cp = [
6233             'align',
6234             'bgcolor',
6235             'border',
6236             'cellpadding',
6237             'cellspacing',
6238             'frame',
6239             'rules',
6240             'sortable',
6241             'summary',
6242             'width'
6243         ].forEach(function(k) {
6244             if (_t[k]) {
6245                 cfg[k] = _t[k];
6246             }
6247         });
6248         
6249         
6250         if (this.layout) {
6251             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6252         }
6253         
6254         if(this.store || this.cm){
6255             if(this.headerShow){
6256                 cfg.cn.push(this.renderHeader());
6257             }
6258             
6259             cfg.cn.push(this.renderBody());
6260             
6261             if(this.footerShow){
6262                 cfg.cn.push(this.renderFooter());
6263             }
6264             // where does this come from?
6265             //cfg.cls+=  ' TableGrid';
6266         }
6267         
6268         return { cn : [ cfg ] };
6269     },
6270     
6271     initEvents : function()
6272     {   
6273         if(!this.store || !this.cm){
6274             return;
6275         }
6276         if (this.selModel) {
6277             this.selModel.initEvents();
6278         }
6279         
6280         
6281         //Roo.log('initEvents with ds!!!!');
6282         
6283         this.mainBody = this.el.select('tbody', true).first();
6284         this.mainHead = this.el.select('thead', true).first();
6285         this.mainFoot = this.el.select('tfoot', true).first();
6286         
6287         
6288         
6289         var _this = this;
6290         
6291         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6292             e.on('click', _this.sort, _this);
6293         });
6294         
6295         this.mainBody.on("click", this.onClick, this);
6296         this.mainBody.on("dblclick", this.onDblClick, this);
6297         
6298         // why is this done????? = it breaks dialogs??
6299         //this.parent().el.setStyle('position', 'relative');
6300         
6301         
6302         if (this.footer) {
6303             this.footer.parentId = this.id;
6304             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6305             
6306             if(this.lazyLoad){
6307                 this.el.select('tfoot tr td').first().addClass('hide');
6308             }
6309         } 
6310         
6311         if(this.loadMask) {
6312             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6313         }
6314         
6315         this.store.on('load', this.onLoad, this);
6316         this.store.on('beforeload', this.onBeforeLoad, this);
6317         this.store.on('update', this.onUpdate, this);
6318         this.store.on('add', this.onAdd, this);
6319         this.store.on("clear", this.clear, this);
6320         
6321         this.el.on("contextmenu", this.onContextMenu, this);
6322         
6323         this.mainBody.on('scroll', this.onBodyScroll, this);
6324         
6325         this.cm.on("headerchange", this.onHeaderChange, this);
6326         
6327         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6328         
6329     },
6330     
6331     onContextMenu : function(e, t)
6332     {
6333         this.processEvent("contextmenu", e);
6334     },
6335     
6336     processEvent : function(name, e)
6337     {
6338         if (name != 'touchstart' ) {
6339             this.fireEvent(name, e);    
6340         }
6341         
6342         var t = e.getTarget();
6343         
6344         var cell = Roo.get(t);
6345         
6346         if(!cell){
6347             return;
6348         }
6349         
6350         if(cell.findParent('tfoot', false, true)){
6351             return;
6352         }
6353         
6354         if(cell.findParent('thead', false, true)){
6355             
6356             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6357                 cell = Roo.get(t).findParent('th', false, true);
6358                 if (!cell) {
6359                     Roo.log("failed to find th in thead?");
6360                     Roo.log(e.getTarget());
6361                     return;
6362                 }
6363             }
6364             
6365             var cellIndex = cell.dom.cellIndex;
6366             
6367             var ename = name == 'touchstart' ? 'click' : name;
6368             this.fireEvent("header" + ename, this, cellIndex, e);
6369             
6370             return;
6371         }
6372         
6373         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6374             cell = Roo.get(t).findParent('td', false, true);
6375             if (!cell) {
6376                 Roo.log("failed to find th in tbody?");
6377                 Roo.log(e.getTarget());
6378                 return;
6379             }
6380         }
6381         
6382         var row = cell.findParent('tr', false, true);
6383         var cellIndex = cell.dom.cellIndex;
6384         var rowIndex = row.dom.rowIndex - 1;
6385         
6386         if(row !== false){
6387             
6388             this.fireEvent("row" + name, this, rowIndex, e);
6389             
6390             if(cell !== false){
6391             
6392                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6393             }
6394         }
6395         
6396     },
6397     
6398     onMouseover : function(e, el)
6399     {
6400         var cell = Roo.get(el);
6401         
6402         if(!cell){
6403             return;
6404         }
6405         
6406         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6407             cell = cell.findParent('td', false, true);
6408         }
6409         
6410         var row = cell.findParent('tr', false, true);
6411         var cellIndex = cell.dom.cellIndex;
6412         var rowIndex = row.dom.rowIndex - 1; // start from 0
6413         
6414         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6415         
6416     },
6417     
6418     onMouseout : function(e, el)
6419     {
6420         var cell = Roo.get(el);
6421         
6422         if(!cell){
6423             return;
6424         }
6425         
6426         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6427             cell = cell.findParent('td', false, true);
6428         }
6429         
6430         var row = cell.findParent('tr', false, true);
6431         var cellIndex = cell.dom.cellIndex;
6432         var rowIndex = row.dom.rowIndex - 1; // start from 0
6433         
6434         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6435         
6436     },
6437     
6438     onClick : function(e, el)
6439     {
6440         var cell = Roo.get(el);
6441         
6442         if(!cell || (!this.cellSelection && !this.rowSelection)){
6443             return;
6444         }
6445         
6446         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6447             cell = cell.findParent('td', false, true);
6448         }
6449         
6450         if(!cell || typeof(cell) == 'undefined'){
6451             return;
6452         }
6453         
6454         var row = cell.findParent('tr', false, true);
6455         
6456         if(!row || typeof(row) == 'undefined'){
6457             return;
6458         }
6459         
6460         var cellIndex = cell.dom.cellIndex;
6461         var rowIndex = this.getRowIndex(row);
6462         
6463         // why??? - should these not be based on SelectionModel?
6464         if(this.cellSelection){
6465             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6466         }
6467         
6468         if(this.rowSelection){
6469             this.fireEvent('rowclick', this, row, rowIndex, e);
6470         }
6471         
6472         
6473     },
6474         
6475     onDblClick : function(e,el)
6476     {
6477         var cell = Roo.get(el);
6478         
6479         if(!cell || (!this.cellSelection && !this.rowSelection)){
6480             return;
6481         }
6482         
6483         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6484             cell = cell.findParent('td', false, true);
6485         }
6486         
6487         if(!cell || typeof(cell) == 'undefined'){
6488             return;
6489         }
6490         
6491         var row = cell.findParent('tr', false, true);
6492         
6493         if(!row || typeof(row) == 'undefined'){
6494             return;
6495         }
6496         
6497         var cellIndex = cell.dom.cellIndex;
6498         var rowIndex = this.getRowIndex(row);
6499         
6500         if(this.cellSelection){
6501             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6502         }
6503         
6504         if(this.rowSelection){
6505             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6506         }
6507     },
6508     
6509     sort : function(e,el)
6510     {
6511         var col = Roo.get(el);
6512         
6513         if(!col.hasClass('sortable')){
6514             return;
6515         }
6516         
6517         var sort = col.attr('sort');
6518         var dir = 'ASC';
6519         
6520         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6521             dir = 'DESC';
6522         }
6523         
6524         this.store.sortInfo = {field : sort, direction : dir};
6525         
6526         if (this.footer) {
6527             Roo.log("calling footer first");
6528             this.footer.onClick('first');
6529         } else {
6530         
6531             this.store.load({ params : { start : 0 } });
6532         }
6533     },
6534     
6535     renderHeader : function()
6536     {
6537         var header = {
6538             tag: 'thead',
6539             cn : []
6540         };
6541         
6542         var cm = this.cm;
6543         this.totalWidth = 0;
6544         
6545         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6546             
6547             var config = cm.config[i];
6548             
6549             var c = {
6550                 tag: 'th',
6551                 cls : 'x-hcol-' + i,
6552                 style : '',
6553                 html: cm.getColumnHeader(i)
6554             };
6555             
6556             var hh = '';
6557             
6558             if(typeof(config.sortable) != 'undefined' && config.sortable){
6559                 c.cls = 'sortable';
6560                 c.html = '<i class="glyphicon"></i>' + c.html;
6561             }
6562             
6563             if(typeof(config.lgHeader) != 'undefined'){
6564                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6565             }
6566             
6567             if(typeof(config.mdHeader) != 'undefined'){
6568                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6569             }
6570             
6571             if(typeof(config.smHeader) != 'undefined'){
6572                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6573             }
6574             
6575             if(typeof(config.xsHeader) != 'undefined'){
6576                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6577             }
6578             
6579             if(hh.length){
6580                 c.html = hh;
6581             }
6582             
6583             if(typeof(config.tooltip) != 'undefined'){
6584                 c.tooltip = config.tooltip;
6585             }
6586             
6587             if(typeof(config.colspan) != 'undefined'){
6588                 c.colspan = config.colspan;
6589             }
6590             
6591             if(typeof(config.hidden) != 'undefined' && config.hidden){
6592                 c.style += ' display:none;';
6593             }
6594             
6595             if(typeof(config.dataIndex) != 'undefined'){
6596                 c.sort = config.dataIndex;
6597             }
6598             
6599            
6600             
6601             if(typeof(config.align) != 'undefined' && config.align.length){
6602                 c.style += ' text-align:' + config.align + ';';
6603             }
6604             
6605             if(typeof(config.width) != 'undefined'){
6606                 c.style += ' width:' + config.width + 'px;';
6607                 this.totalWidth += config.width;
6608             } else {
6609                 this.totalWidth += 100; // assume minimum of 100 per column?
6610             }
6611             
6612             if(typeof(config.cls) != 'undefined'){
6613                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6614             }
6615             
6616             ['xs','sm','md','lg'].map(function(size){
6617                 
6618                 if(typeof(config[size]) == 'undefined'){
6619                     return;
6620                 }
6621                 
6622                 if (!config[size]) { // 0 = hidden
6623                     c.cls += ' hidden-' + size;
6624                     return;
6625                 }
6626                 
6627                 c.cls += ' col-' + size + '-' + config[size];
6628
6629             });
6630             
6631             header.cn.push(c)
6632         }
6633         
6634         return header;
6635     },
6636     
6637     renderBody : function()
6638     {
6639         var body = {
6640             tag: 'tbody',
6641             cn : [
6642                 {
6643                     tag: 'tr',
6644                     cn : [
6645                         {
6646                             tag : 'td',
6647                             colspan :  this.cm.getColumnCount()
6648                         }
6649                     ]
6650                 }
6651             ]
6652         };
6653         
6654         return body;
6655     },
6656     
6657     renderFooter : function()
6658     {
6659         var footer = {
6660             tag: 'tfoot',
6661             cn : [
6662                 {
6663                     tag: 'tr',
6664                     cn : [
6665                         {
6666                             tag : 'td',
6667                             colspan :  this.cm.getColumnCount()
6668                         }
6669                     ]
6670                 }
6671             ]
6672         };
6673         
6674         return footer;
6675     },
6676     
6677     
6678     
6679     onLoad : function()
6680     {
6681 //        Roo.log('ds onload');
6682         this.clear();
6683         
6684         var _this = this;
6685         var cm = this.cm;
6686         var ds = this.store;
6687         
6688         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6689             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6690             if (_this.store.sortInfo) {
6691                     
6692                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6693                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6694                 }
6695                 
6696                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6697                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6698                 }
6699             }
6700         });
6701         
6702         var tbody =  this.mainBody;
6703               
6704         if(ds.getCount() > 0){
6705             ds.data.each(function(d,rowIndex){
6706                 var row =  this.renderRow(cm, ds, rowIndex);
6707                 
6708                 tbody.createChild(row);
6709                 
6710                 var _this = this;
6711                 
6712                 if(row.cellObjects.length){
6713                     Roo.each(row.cellObjects, function(r){
6714                         _this.renderCellObject(r);
6715                     })
6716                 }
6717                 
6718             }, this);
6719         }
6720         
6721         var tfoot = this.el.select('tfoot', true).first();
6722         
6723         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6724             
6725             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6726             
6727             var total = this.ds.getTotalCount();
6728             
6729             if(this.footer.pageSize < total){
6730                 this.mainFoot.show();
6731             }
6732         }
6733         
6734         Roo.each(this.el.select('tbody td', true).elements, function(e){
6735             e.on('mouseover', _this.onMouseover, _this);
6736         });
6737         
6738         Roo.each(this.el.select('tbody td', true).elements, function(e){
6739             e.on('mouseout', _this.onMouseout, _this);
6740         });
6741         this.fireEvent('rowsrendered', this);
6742         
6743         this.autoSize();
6744     },
6745     
6746     
6747     onUpdate : function(ds,record)
6748     {
6749         this.refreshRow(record);
6750         this.autoSize();
6751     },
6752     
6753     onRemove : function(ds, record, index, isUpdate){
6754         if(isUpdate !== true){
6755             this.fireEvent("beforerowremoved", this, index, record);
6756         }
6757         var bt = this.mainBody.dom;
6758         
6759         var rows = this.el.select('tbody > tr', true).elements;
6760         
6761         if(typeof(rows[index]) != 'undefined'){
6762             bt.removeChild(rows[index].dom);
6763         }
6764         
6765 //        if(bt.rows[index]){
6766 //            bt.removeChild(bt.rows[index]);
6767 //        }
6768         
6769         if(isUpdate !== true){
6770             //this.stripeRows(index);
6771             //this.syncRowHeights(index, index);
6772             //this.layout();
6773             this.fireEvent("rowremoved", this, index, record);
6774         }
6775     },
6776     
6777     onAdd : function(ds, records, rowIndex)
6778     {
6779         //Roo.log('on Add called');
6780         // - note this does not handle multiple adding very well..
6781         var bt = this.mainBody.dom;
6782         for (var i =0 ; i < records.length;i++) {
6783             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6784             //Roo.log(records[i]);
6785             //Roo.log(this.store.getAt(rowIndex+i));
6786             this.insertRow(this.store, rowIndex + i, false);
6787             return;
6788         }
6789         
6790     },
6791     
6792     
6793     refreshRow : function(record){
6794         var ds = this.store, index;
6795         if(typeof record == 'number'){
6796             index = record;
6797             record = ds.getAt(index);
6798         }else{
6799             index = ds.indexOf(record);
6800         }
6801         this.insertRow(ds, index, true);
6802         this.autoSize();
6803         this.onRemove(ds, record, index+1, true);
6804         this.autoSize();
6805         //this.syncRowHeights(index, index);
6806         //this.layout();
6807         this.fireEvent("rowupdated", this, index, record);
6808     },
6809     
6810     insertRow : function(dm, rowIndex, isUpdate){
6811         
6812         if(!isUpdate){
6813             this.fireEvent("beforerowsinserted", this, rowIndex);
6814         }
6815             //var s = this.getScrollState();
6816         var row = this.renderRow(this.cm, this.store, rowIndex);
6817         // insert before rowIndex..
6818         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6819         
6820         var _this = this;
6821                 
6822         if(row.cellObjects.length){
6823             Roo.each(row.cellObjects, function(r){
6824                 _this.renderCellObject(r);
6825             })
6826         }
6827             
6828         if(!isUpdate){
6829             this.fireEvent("rowsinserted", this, rowIndex);
6830             //this.syncRowHeights(firstRow, lastRow);
6831             //this.stripeRows(firstRow);
6832             //this.layout();
6833         }
6834         
6835     },
6836     
6837     
6838     getRowDom : function(rowIndex)
6839     {
6840         var rows = this.el.select('tbody > tr', true).elements;
6841         
6842         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6843         
6844     },
6845     // returns the object tree for a tr..
6846   
6847     
6848     renderRow : function(cm, ds, rowIndex) 
6849     {
6850         var d = ds.getAt(rowIndex);
6851         
6852         var row = {
6853             tag : 'tr',
6854             cls : 'x-row-' + rowIndex,
6855             cn : []
6856         };
6857             
6858         var cellObjects = [];
6859         
6860         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6861             var config = cm.config[i];
6862             
6863             var renderer = cm.getRenderer(i);
6864             var value = '';
6865             var id = false;
6866             
6867             if(typeof(renderer) !== 'undefined'){
6868                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6869             }
6870             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6871             // and are rendered into the cells after the row is rendered - using the id for the element.
6872             
6873             if(typeof(value) === 'object'){
6874                 id = Roo.id();
6875                 cellObjects.push({
6876                     container : id,
6877                     cfg : value 
6878                 })
6879             }
6880             
6881             var rowcfg = {
6882                 record: d,
6883                 rowIndex : rowIndex,
6884                 colIndex : i,
6885                 rowClass : ''
6886             };
6887
6888             this.fireEvent('rowclass', this, rowcfg);
6889             
6890             var td = {
6891                 tag: 'td',
6892                 cls : rowcfg.rowClass + ' x-col-' + i,
6893                 style: '',
6894                 html: (typeof(value) === 'object') ? '' : value
6895             };
6896             
6897             if (id) {
6898                 td.id = id;
6899             }
6900             
6901             if(typeof(config.colspan) != 'undefined'){
6902                 td.colspan = config.colspan;
6903             }
6904             
6905             if(typeof(config.hidden) != 'undefined' && config.hidden){
6906                 td.style += ' display:none;';
6907             }
6908             
6909             if(typeof(config.align) != 'undefined' && config.align.length){
6910                 td.style += ' text-align:' + config.align + ';';
6911             }
6912             if(typeof(config.valign) != 'undefined' && config.valign.length){
6913                 td.style += ' vertical-align:' + config.valign + ';';
6914             }
6915             
6916             if(typeof(config.width) != 'undefined'){
6917                 td.style += ' width:' +  config.width + 'px;';
6918             }
6919             
6920             if(typeof(config.cursor) != 'undefined'){
6921                 td.style += ' cursor:' +  config.cursor + ';';
6922             }
6923             
6924             if(typeof(config.cls) != 'undefined'){
6925                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6926             }
6927             
6928             ['xs','sm','md','lg'].map(function(size){
6929                 
6930                 if(typeof(config[size]) == 'undefined'){
6931                     return;
6932                 }
6933                 
6934                 if (!config[size]) { // 0 = hidden
6935                     td.cls += ' hidden-' + size;
6936                     return;
6937                 }
6938                 
6939                 td.cls += ' col-' + size + '-' + config[size];
6940
6941             });
6942             
6943             row.cn.push(td);
6944            
6945         }
6946         
6947         row.cellObjects = cellObjects;
6948         
6949         return row;
6950           
6951     },
6952     
6953     
6954     
6955     onBeforeLoad : function()
6956     {
6957         
6958     },
6959      /**
6960      * Remove all rows
6961      */
6962     clear : function()
6963     {
6964         this.el.select('tbody', true).first().dom.innerHTML = '';
6965     },
6966     /**
6967      * Show or hide a row.
6968      * @param {Number} rowIndex to show or hide
6969      * @param {Boolean} state hide
6970      */
6971     setRowVisibility : function(rowIndex, state)
6972     {
6973         var bt = this.mainBody.dom;
6974         
6975         var rows = this.el.select('tbody > tr', true).elements;
6976         
6977         if(typeof(rows[rowIndex]) == 'undefined'){
6978             return;
6979         }
6980         rows[rowIndex].dom.style.display = state ? '' : 'none';
6981     },
6982     
6983     
6984     getSelectionModel : function(){
6985         if(!this.selModel){
6986             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6987         }
6988         return this.selModel;
6989     },
6990     /*
6991      * Render the Roo.bootstrap object from renderder
6992      */
6993     renderCellObject : function(r)
6994     {
6995         var _this = this;
6996         
6997         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6998         
6999         var t = r.cfg.render(r.container);
7000         
7001         if(r.cfg.cn){
7002             Roo.each(r.cfg.cn, function(c){
7003                 var child = {
7004                     container: t.getChildContainer(),
7005                     cfg: c
7006                 };
7007                 _this.renderCellObject(child);
7008             })
7009         }
7010     },
7011     
7012     getRowIndex : function(row)
7013     {
7014         var rowIndex = -1;
7015         
7016         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7017             if(el != row){
7018                 return;
7019             }
7020             
7021             rowIndex = index;
7022         });
7023         
7024         return rowIndex;
7025     },
7026      /**
7027      * Returns the grid's underlying element = used by panel.Grid
7028      * @return {Element} The element
7029      */
7030     getGridEl : function(){
7031         return this.el;
7032     },
7033      /**
7034      * Forces a resize - used by panel.Grid
7035      * @return {Element} The element
7036      */
7037     autoSize : function()
7038     {
7039         //var ctr = Roo.get(this.container.dom.parentElement);
7040         var ctr = Roo.get(this.el.dom);
7041         
7042         var thd = this.getGridEl().select('thead',true).first();
7043         var tbd = this.getGridEl().select('tbody', true).first();
7044         var tfd = this.getGridEl().select('tfoot', true).first();
7045         
7046         var cw = ctr.getWidth();
7047         
7048         if (tbd) {
7049             
7050             tbd.setSize(ctr.getWidth(),
7051                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7052             );
7053             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7054             cw -= barsize;
7055         }
7056         cw = Math.max(cw, this.totalWidth);
7057         this.getGridEl().select('tr',true).setWidth(cw);
7058         // resize 'expandable coloumn?
7059         
7060         return; // we doe not have a view in this design..
7061         
7062     },
7063     onBodyScroll: function()
7064     {
7065         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7066         if(this.mainHead){
7067             this.mainHead.setStyle({
7068                 'position' : 'relative',
7069                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7070             });
7071         }
7072         
7073         if(this.lazyLoad){
7074             
7075             var scrollHeight = this.mainBody.dom.scrollHeight;
7076             
7077             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7078             
7079             var height = this.mainBody.getHeight();
7080             
7081             if(scrollHeight - height == scrollTop) {
7082                 
7083                 var total = this.ds.getTotalCount();
7084                 
7085                 if(this.footer.cursor + this.footer.pageSize < total){
7086                     
7087                     this.footer.ds.load({
7088                         params : {
7089                             start : this.footer.cursor + this.footer.pageSize,
7090                             limit : this.footer.pageSize
7091                         },
7092                         add : true
7093                     });
7094                 }
7095             }
7096             
7097         }
7098     },
7099     
7100     onHeaderChange : function()
7101     {
7102         var header = this.renderHeader();
7103         var table = this.el.select('table', true).first();
7104         
7105         this.mainHead.remove();
7106         this.mainHead = table.createChild(header, this.mainBody, false);
7107     },
7108     
7109     onHiddenChange : function(colModel, colIndex, hidden)
7110     {
7111         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7112         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7113         
7114         this.CSS.updateRule(thSelector, "display", "");
7115         this.CSS.updateRule(tdSelector, "display", "");
7116         
7117         if(hidden){
7118             this.CSS.updateRule(thSelector, "display", "none");
7119             this.CSS.updateRule(tdSelector, "display", "none");
7120         }
7121         
7122         this.onHeaderChange();
7123         this.onLoad();
7124     },
7125     
7126     setColumnWidth: function(col_index, width)
7127     {
7128         // width = "md-2 xs-2..."
7129         if(!this.colModel.config[col_index]) {
7130             return;
7131         }
7132         
7133         var w = width.split(" ");
7134         
7135         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7136         
7137         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7138         
7139         
7140         for(var j = 0; j < w.length; j++) {
7141             
7142             if(!w[j]) {
7143                 continue;
7144             }
7145             
7146             var size_cls = w[j].split("-");
7147             
7148             if(!Number.isInteger(size_cls[1] * 1)) {
7149                 continue;
7150             }
7151             
7152             if(!this.colModel.config[col_index][size_cls[0]]) {
7153                 continue;
7154             }
7155             
7156             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7157                 continue;
7158             }
7159             
7160             h_row[0].classList.replace(
7161                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7162                 "col-"+size_cls[0]+"-"+size_cls[1]
7163             );
7164             
7165             for(var i = 0; i < rows.length; i++) {
7166                 
7167                 var size_cls = w[j].split("-");
7168                 
7169                 if(!Number.isInteger(size_cls[1] * 1)) {
7170                     continue;
7171                 }
7172                 
7173                 if(!this.colModel.config[col_index][size_cls[0]]) {
7174                     continue;
7175                 }
7176                 
7177                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7178                     continue;
7179                 }
7180                 
7181                 rows[i].classList.replace(
7182                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7183                     "col-"+size_cls[0]+"-"+size_cls[1]
7184                 );
7185             }
7186             
7187             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7188         }
7189     }
7190 });
7191
7192  
7193
7194  /*
7195  * - LGPL
7196  *
7197  * table cell
7198  * 
7199  */
7200
7201 /**
7202  * @class Roo.bootstrap.TableCell
7203  * @extends Roo.bootstrap.Component
7204  * Bootstrap TableCell class
7205  * @cfg {String} html cell contain text
7206  * @cfg {String} cls cell class
7207  * @cfg {String} tag cell tag (td|th) default td
7208  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7209  * @cfg {String} align Aligns the content in a cell
7210  * @cfg {String} axis Categorizes cells
7211  * @cfg {String} bgcolor Specifies the background color of a cell
7212  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7213  * @cfg {Number} colspan Specifies the number of columns a cell should span
7214  * @cfg {String} headers Specifies one or more header cells a cell is related to
7215  * @cfg {Number} height Sets the height of a cell
7216  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7217  * @cfg {Number} rowspan Sets the number of rows a cell should span
7218  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7219  * @cfg {String} valign Vertical aligns the content in a cell
7220  * @cfg {Number} width Specifies the width of a cell
7221  * 
7222  * @constructor
7223  * Create a new TableCell
7224  * @param {Object} config The config object
7225  */
7226
7227 Roo.bootstrap.TableCell = function(config){
7228     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7229 };
7230
7231 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7232     
7233     html: false,
7234     cls: false,
7235     tag: false,
7236     abbr: false,
7237     align: false,
7238     axis: false,
7239     bgcolor: false,
7240     charoff: false,
7241     colspan: false,
7242     headers: false,
7243     height: false,
7244     nowrap: false,
7245     rowspan: false,
7246     scope: false,
7247     valign: false,
7248     width: false,
7249     
7250     
7251     getAutoCreate : function(){
7252         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7253         
7254         cfg = {
7255             tag: 'td'
7256         };
7257         
7258         if(this.tag){
7259             cfg.tag = this.tag;
7260         }
7261         
7262         if (this.html) {
7263             cfg.html=this.html
7264         }
7265         if (this.cls) {
7266             cfg.cls=this.cls
7267         }
7268         if (this.abbr) {
7269             cfg.abbr=this.abbr
7270         }
7271         if (this.align) {
7272             cfg.align=this.align
7273         }
7274         if (this.axis) {
7275             cfg.axis=this.axis
7276         }
7277         if (this.bgcolor) {
7278             cfg.bgcolor=this.bgcolor
7279         }
7280         if (this.charoff) {
7281             cfg.charoff=this.charoff
7282         }
7283         if (this.colspan) {
7284             cfg.colspan=this.colspan
7285         }
7286         if (this.headers) {
7287             cfg.headers=this.headers
7288         }
7289         if (this.height) {
7290             cfg.height=this.height
7291         }
7292         if (this.nowrap) {
7293             cfg.nowrap=this.nowrap
7294         }
7295         if (this.rowspan) {
7296             cfg.rowspan=this.rowspan
7297         }
7298         if (this.scope) {
7299             cfg.scope=this.scope
7300         }
7301         if (this.valign) {
7302             cfg.valign=this.valign
7303         }
7304         if (this.width) {
7305             cfg.width=this.width
7306         }
7307         
7308         
7309         return cfg;
7310     }
7311    
7312 });
7313
7314  
7315
7316  /*
7317  * - LGPL
7318  *
7319  * table row
7320  * 
7321  */
7322
7323 /**
7324  * @class Roo.bootstrap.TableRow
7325  * @extends Roo.bootstrap.Component
7326  * Bootstrap TableRow class
7327  * @cfg {String} cls row class
7328  * @cfg {String} align Aligns the content in a table row
7329  * @cfg {String} bgcolor Specifies a background color for a table row
7330  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7331  * @cfg {String} valign Vertical aligns the content in a table row
7332  * 
7333  * @constructor
7334  * Create a new TableRow
7335  * @param {Object} config The config object
7336  */
7337
7338 Roo.bootstrap.TableRow = function(config){
7339     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7340 };
7341
7342 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7343     
7344     cls: false,
7345     align: false,
7346     bgcolor: false,
7347     charoff: false,
7348     valign: false,
7349     
7350     getAutoCreate : function(){
7351         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7352         
7353         cfg = {
7354             tag: 'tr'
7355         };
7356             
7357         if(this.cls){
7358             cfg.cls = this.cls;
7359         }
7360         if(this.align){
7361             cfg.align = this.align;
7362         }
7363         if(this.bgcolor){
7364             cfg.bgcolor = this.bgcolor;
7365         }
7366         if(this.charoff){
7367             cfg.charoff = this.charoff;
7368         }
7369         if(this.valign){
7370             cfg.valign = this.valign;
7371         }
7372         
7373         return cfg;
7374     }
7375    
7376 });
7377
7378  
7379
7380  /*
7381  * - LGPL
7382  *
7383  * table body
7384  * 
7385  */
7386
7387 /**
7388  * @class Roo.bootstrap.TableBody
7389  * @extends Roo.bootstrap.Component
7390  * Bootstrap TableBody class
7391  * @cfg {String} cls element class
7392  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7393  * @cfg {String} align Aligns the content inside the element
7394  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7395  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7396  * 
7397  * @constructor
7398  * Create a new TableBody
7399  * @param {Object} config The config object
7400  */
7401
7402 Roo.bootstrap.TableBody = function(config){
7403     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7404 };
7405
7406 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7407     
7408     cls: false,
7409     tag: false,
7410     align: false,
7411     charoff: false,
7412     valign: false,
7413     
7414     getAutoCreate : function(){
7415         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7416         
7417         cfg = {
7418             tag: 'tbody'
7419         };
7420             
7421         if (this.cls) {
7422             cfg.cls=this.cls
7423         }
7424         if(this.tag){
7425             cfg.tag = this.tag;
7426         }
7427         
7428         if(this.align){
7429             cfg.align = this.align;
7430         }
7431         if(this.charoff){
7432             cfg.charoff = this.charoff;
7433         }
7434         if(this.valign){
7435             cfg.valign = this.valign;
7436         }
7437         
7438         return cfg;
7439     }
7440     
7441     
7442 //    initEvents : function()
7443 //    {
7444 //        
7445 //        if(!this.store){
7446 //            return;
7447 //        }
7448 //        
7449 //        this.store = Roo.factory(this.store, Roo.data);
7450 //        this.store.on('load', this.onLoad, this);
7451 //        
7452 //        this.store.load();
7453 //        
7454 //    },
7455 //    
7456 //    onLoad: function () 
7457 //    {   
7458 //        this.fireEvent('load', this);
7459 //    }
7460 //    
7461 //   
7462 });
7463
7464  
7465
7466  /*
7467  * Based on:
7468  * Ext JS Library 1.1.1
7469  * Copyright(c) 2006-2007, Ext JS, LLC.
7470  *
7471  * Originally Released Under LGPL - original licence link has changed is not relivant.
7472  *
7473  * Fork - LGPL
7474  * <script type="text/javascript">
7475  */
7476
7477 // as we use this in bootstrap.
7478 Roo.namespace('Roo.form');
7479  /**
7480  * @class Roo.form.Action
7481  * Internal Class used to handle form actions
7482  * @constructor
7483  * @param {Roo.form.BasicForm} el The form element or its id
7484  * @param {Object} config Configuration options
7485  */
7486
7487  
7488  
7489 // define the action interface
7490 Roo.form.Action = function(form, options){
7491     this.form = form;
7492     this.options = options || {};
7493 };
7494 /**
7495  * Client Validation Failed
7496  * @const 
7497  */
7498 Roo.form.Action.CLIENT_INVALID = 'client';
7499 /**
7500  * Server Validation Failed
7501  * @const 
7502  */
7503 Roo.form.Action.SERVER_INVALID = 'server';
7504  /**
7505  * Connect to Server Failed
7506  * @const 
7507  */
7508 Roo.form.Action.CONNECT_FAILURE = 'connect';
7509 /**
7510  * Reading Data from Server Failed
7511  * @const 
7512  */
7513 Roo.form.Action.LOAD_FAILURE = 'load';
7514
7515 Roo.form.Action.prototype = {
7516     type : 'default',
7517     failureType : undefined,
7518     response : undefined,
7519     result : undefined,
7520
7521     // interface method
7522     run : function(options){
7523
7524     },
7525
7526     // interface method
7527     success : function(response){
7528
7529     },
7530
7531     // interface method
7532     handleResponse : function(response){
7533
7534     },
7535
7536     // default connection failure
7537     failure : function(response){
7538         
7539         this.response = response;
7540         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7541         this.form.afterAction(this, false);
7542     },
7543
7544     processResponse : function(response){
7545         this.response = response;
7546         if(!response.responseText){
7547             return true;
7548         }
7549         this.result = this.handleResponse(response);
7550         return this.result;
7551     },
7552
7553     // utility functions used internally
7554     getUrl : function(appendParams){
7555         var url = this.options.url || this.form.url || this.form.el.dom.action;
7556         if(appendParams){
7557             var p = this.getParams();
7558             if(p){
7559                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7560             }
7561         }
7562         return url;
7563     },
7564
7565     getMethod : function(){
7566         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7567     },
7568
7569     getParams : function(){
7570         var bp = this.form.baseParams;
7571         var p = this.options.params;
7572         if(p){
7573             if(typeof p == "object"){
7574                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7575             }else if(typeof p == 'string' && bp){
7576                 p += '&' + Roo.urlEncode(bp);
7577             }
7578         }else if(bp){
7579             p = Roo.urlEncode(bp);
7580         }
7581         return p;
7582     },
7583
7584     createCallback : function(){
7585         return {
7586             success: this.success,
7587             failure: this.failure,
7588             scope: this,
7589             timeout: (this.form.timeout*1000),
7590             upload: this.form.fileUpload ? this.success : undefined
7591         };
7592     }
7593 };
7594
7595 Roo.form.Action.Submit = function(form, options){
7596     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7597 };
7598
7599 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7600     type : 'submit',
7601
7602     haveProgress : false,
7603     uploadComplete : false,
7604     
7605     // uploadProgress indicator.
7606     uploadProgress : function()
7607     {
7608         if (!this.form.progressUrl) {
7609             return;
7610         }
7611         
7612         if (!this.haveProgress) {
7613             Roo.MessageBox.progress("Uploading", "Uploading");
7614         }
7615         if (this.uploadComplete) {
7616            Roo.MessageBox.hide();
7617            return;
7618         }
7619         
7620         this.haveProgress = true;
7621    
7622         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7623         
7624         var c = new Roo.data.Connection();
7625         c.request({
7626             url : this.form.progressUrl,
7627             params: {
7628                 id : uid
7629             },
7630             method: 'GET',
7631             success : function(req){
7632                //console.log(data);
7633                 var rdata = false;
7634                 var edata;
7635                 try  {
7636                    rdata = Roo.decode(req.responseText)
7637                 } catch (e) {
7638                     Roo.log("Invalid data from server..");
7639                     Roo.log(edata);
7640                     return;
7641                 }
7642                 if (!rdata || !rdata.success) {
7643                     Roo.log(rdata);
7644                     Roo.MessageBox.alert(Roo.encode(rdata));
7645                     return;
7646                 }
7647                 var data = rdata.data;
7648                 
7649                 if (this.uploadComplete) {
7650                    Roo.MessageBox.hide();
7651                    return;
7652                 }
7653                    
7654                 if (data){
7655                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7656                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7657                     );
7658                 }
7659                 this.uploadProgress.defer(2000,this);
7660             },
7661        
7662             failure: function(data) {
7663                 Roo.log('progress url failed ');
7664                 Roo.log(data);
7665             },
7666             scope : this
7667         });
7668            
7669     },
7670     
7671     
7672     run : function()
7673     {
7674         // run get Values on the form, so it syncs any secondary forms.
7675         this.form.getValues();
7676         
7677         var o = this.options;
7678         var method = this.getMethod();
7679         var isPost = method == 'POST';
7680         if(o.clientValidation === false || this.form.isValid()){
7681             
7682             if (this.form.progressUrl) {
7683                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7684                     (new Date() * 1) + '' + Math.random());
7685                     
7686             } 
7687             
7688             
7689             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7690                 form:this.form.el.dom,
7691                 url:this.getUrl(!isPost),
7692                 method: method,
7693                 params:isPost ? this.getParams() : null,
7694                 isUpload: this.form.fileUpload
7695             }));
7696             
7697             this.uploadProgress();
7698
7699         }else if (o.clientValidation !== false){ // client validation failed
7700             this.failureType = Roo.form.Action.CLIENT_INVALID;
7701             this.form.afterAction(this, false);
7702         }
7703     },
7704
7705     success : function(response)
7706     {
7707         this.uploadComplete= true;
7708         if (this.haveProgress) {
7709             Roo.MessageBox.hide();
7710         }
7711         
7712         
7713         var result = this.processResponse(response);
7714         if(result === true || result.success){
7715             this.form.afterAction(this, true);
7716             return;
7717         }
7718         if(result.errors){
7719             this.form.markInvalid(result.errors);
7720             this.failureType = Roo.form.Action.SERVER_INVALID;
7721         }
7722         this.form.afterAction(this, false);
7723     },
7724     failure : function(response)
7725     {
7726         this.uploadComplete= true;
7727         if (this.haveProgress) {
7728             Roo.MessageBox.hide();
7729         }
7730         
7731         this.response = response;
7732         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7733         this.form.afterAction(this, false);
7734     },
7735     
7736     handleResponse : function(response){
7737         if(this.form.errorReader){
7738             var rs = this.form.errorReader.read(response);
7739             var errors = [];
7740             if(rs.records){
7741                 for(var i = 0, len = rs.records.length; i < len; i++) {
7742                     var r = rs.records[i];
7743                     errors[i] = r.data;
7744                 }
7745             }
7746             if(errors.length < 1){
7747                 errors = null;
7748             }
7749             return {
7750                 success : rs.success,
7751                 errors : errors
7752             };
7753         }
7754         var ret = false;
7755         try {
7756             ret = Roo.decode(response.responseText);
7757         } catch (e) {
7758             ret = {
7759                 success: false,
7760                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7761                 errors : []
7762             };
7763         }
7764         return ret;
7765         
7766     }
7767 });
7768
7769
7770 Roo.form.Action.Load = function(form, options){
7771     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7772     this.reader = this.form.reader;
7773 };
7774
7775 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7776     type : 'load',
7777
7778     run : function(){
7779         
7780         Roo.Ajax.request(Roo.apply(
7781                 this.createCallback(), {
7782                     method:this.getMethod(),
7783                     url:this.getUrl(false),
7784                     params:this.getParams()
7785         }));
7786     },
7787
7788     success : function(response){
7789         
7790         var result = this.processResponse(response);
7791         if(result === true || !result.success || !result.data){
7792             this.failureType = Roo.form.Action.LOAD_FAILURE;
7793             this.form.afterAction(this, false);
7794             return;
7795         }
7796         this.form.clearInvalid();
7797         this.form.setValues(result.data);
7798         this.form.afterAction(this, true);
7799     },
7800
7801     handleResponse : function(response){
7802         if(this.form.reader){
7803             var rs = this.form.reader.read(response);
7804             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7805             return {
7806                 success : rs.success,
7807                 data : data
7808             };
7809         }
7810         return Roo.decode(response.responseText);
7811     }
7812 });
7813
7814 Roo.form.Action.ACTION_TYPES = {
7815     'load' : Roo.form.Action.Load,
7816     'submit' : Roo.form.Action.Submit
7817 };/*
7818  * - LGPL
7819  *
7820  * form
7821  *
7822  */
7823
7824 /**
7825  * @class Roo.bootstrap.Form
7826  * @extends Roo.bootstrap.Component
7827  * Bootstrap Form class
7828  * @cfg {String} method  GET | POST (default POST)
7829  * @cfg {String} labelAlign top | left (default top)
7830  * @cfg {String} align left  | right - for navbars
7831  * @cfg {Boolean} loadMask load mask when submit (default true)
7832
7833  *
7834  * @constructor
7835  * Create a new Form
7836  * @param {Object} config The config object
7837  */
7838
7839
7840 Roo.bootstrap.Form = function(config){
7841     
7842     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7843     
7844     Roo.bootstrap.Form.popover.apply();
7845     
7846     this.addEvents({
7847         /**
7848          * @event clientvalidation
7849          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7850          * @param {Form} this
7851          * @param {Boolean} valid true if the form has passed client-side validation
7852          */
7853         clientvalidation: true,
7854         /**
7855          * @event beforeaction
7856          * Fires before any action is performed. Return false to cancel the action.
7857          * @param {Form} this
7858          * @param {Action} action The action to be performed
7859          */
7860         beforeaction: true,
7861         /**
7862          * @event actionfailed
7863          * Fires when an action fails.
7864          * @param {Form} this
7865          * @param {Action} action The action that failed
7866          */
7867         actionfailed : true,
7868         /**
7869          * @event actioncomplete
7870          * Fires when an action is completed.
7871          * @param {Form} this
7872          * @param {Action} action The action that completed
7873          */
7874         actioncomplete : true
7875     });
7876 };
7877
7878 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7879
7880      /**
7881      * @cfg {String} method
7882      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7883      */
7884     method : 'POST',
7885     /**
7886      * @cfg {String} url
7887      * The URL to use for form actions if one isn't supplied in the action options.
7888      */
7889     /**
7890      * @cfg {Boolean} fileUpload
7891      * Set to true if this form is a file upload.
7892      */
7893
7894     /**
7895      * @cfg {Object} baseParams
7896      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7897      */
7898
7899     /**
7900      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7901      */
7902     timeout: 30,
7903     /**
7904      * @cfg {Sting} align (left|right) for navbar forms
7905      */
7906     align : 'left',
7907
7908     // private
7909     activeAction : null,
7910
7911     /**
7912      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7913      * element by passing it or its id or mask the form itself by passing in true.
7914      * @type Mixed
7915      */
7916     waitMsgTarget : false,
7917
7918     loadMask : true,
7919     
7920     /**
7921      * @cfg {Boolean} errorMask (true|false) default false
7922      */
7923     errorMask : false,
7924     
7925     /**
7926      * @cfg {Number} maskOffset Default 100
7927      */
7928     maskOffset : 100,
7929     
7930     /**
7931      * @cfg {Boolean} maskBody
7932      */
7933     maskBody : false,
7934
7935     getAutoCreate : function(){
7936
7937         var cfg = {
7938             tag: 'form',
7939             method : this.method || 'POST',
7940             id : this.id || Roo.id(),
7941             cls : ''
7942         };
7943         if (this.parent().xtype.match(/^Nav/)) {
7944             cfg.cls = 'navbar-form navbar-' + this.align;
7945
7946         }
7947
7948         if (this.labelAlign == 'left' ) {
7949             cfg.cls += ' form-horizontal';
7950         }
7951
7952
7953         return cfg;
7954     },
7955     initEvents : function()
7956     {
7957         this.el.on('submit', this.onSubmit, this);
7958         // this was added as random key presses on the form where triggering form submit.
7959         this.el.on('keypress', function(e) {
7960             if (e.getCharCode() != 13) {
7961                 return true;
7962             }
7963             // we might need to allow it for textareas.. and some other items.
7964             // check e.getTarget().
7965
7966             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7967                 return true;
7968             }
7969
7970             Roo.log("keypress blocked");
7971
7972             e.preventDefault();
7973             return false;
7974         });
7975         
7976     },
7977     // private
7978     onSubmit : function(e){
7979         e.stopEvent();
7980     },
7981
7982      /**
7983      * Returns true if client-side validation on the form is successful.
7984      * @return Boolean
7985      */
7986     isValid : function(){
7987         var items = this.getItems();
7988         var valid = true;
7989         var target = false;
7990         
7991         items.each(function(f){
7992             
7993             if(f.validate()){
7994                 return;
7995             }
7996             
7997             Roo.log('invalid field: ' + f.name);
7998             
7999             valid = false;
8000
8001             if(!target && f.el.isVisible(true)){
8002                 target = f;
8003             }
8004            
8005         });
8006         
8007         if(this.errorMask && !valid){
8008             Roo.bootstrap.Form.popover.mask(this, target);
8009         }
8010         
8011         return valid;
8012     },
8013     
8014     /**
8015      * Returns true if any fields in this form have changed since their original load.
8016      * @return Boolean
8017      */
8018     isDirty : function(){
8019         var dirty = false;
8020         var items = this.getItems();
8021         items.each(function(f){
8022            if(f.isDirty()){
8023                dirty = true;
8024                return false;
8025            }
8026            return true;
8027         });
8028         return dirty;
8029     },
8030      /**
8031      * Performs a predefined action (submit or load) or custom actions you define on this form.
8032      * @param {String} actionName The name of the action type
8033      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8034      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8035      * accept other config options):
8036      * <pre>
8037 Property          Type             Description
8038 ----------------  ---------------  ----------------------------------------------------------------------------------
8039 url               String           The url for the action (defaults to the form's url)
8040 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8041 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8042 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8043                                    validate the form on the client (defaults to false)
8044      * </pre>
8045      * @return {BasicForm} this
8046      */
8047     doAction : function(action, options){
8048         if(typeof action == 'string'){
8049             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8050         }
8051         if(this.fireEvent('beforeaction', this, action) !== false){
8052             this.beforeAction(action);
8053             action.run.defer(100, action);
8054         }
8055         return this;
8056     },
8057
8058     // private
8059     beforeAction : function(action){
8060         var o = action.options;
8061         
8062         if(this.loadMask){
8063             
8064             if(this.maskBody){
8065                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8066             } else {
8067                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8068             }
8069         }
8070         // not really supported yet.. ??
8071
8072         //if(this.waitMsgTarget === true){
8073         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8074         //}else if(this.waitMsgTarget){
8075         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8076         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8077         //}else {
8078         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8079        // }
8080
8081     },
8082
8083     // private
8084     afterAction : function(action, success){
8085         this.activeAction = null;
8086         var o = action.options;
8087
8088         if(this.loadMask){
8089             
8090             if(this.maskBody){
8091                 Roo.get(document.body).unmask();
8092             } else {
8093                 this.el.unmask();
8094             }
8095         }
8096         
8097         //if(this.waitMsgTarget === true){
8098 //            this.el.unmask();
8099         //}else if(this.waitMsgTarget){
8100         //    this.waitMsgTarget.unmask();
8101         //}else{
8102         //    Roo.MessageBox.updateProgress(1);
8103         //    Roo.MessageBox.hide();
8104        // }
8105         //
8106         if(success){
8107             if(o.reset){
8108                 this.reset();
8109             }
8110             Roo.callback(o.success, o.scope, [this, action]);
8111             this.fireEvent('actioncomplete', this, action);
8112
8113         }else{
8114
8115             // failure condition..
8116             // we have a scenario where updates need confirming.
8117             // eg. if a locking scenario exists..
8118             // we look for { errors : { needs_confirm : true }} in the response.
8119             if (
8120                 (typeof(action.result) != 'undefined')  &&
8121                 (typeof(action.result.errors) != 'undefined')  &&
8122                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8123            ){
8124                 var _t = this;
8125                 Roo.log("not supported yet");
8126                  /*
8127
8128                 Roo.MessageBox.confirm(
8129                     "Change requires confirmation",
8130                     action.result.errorMsg,
8131                     function(r) {
8132                         if (r != 'yes') {
8133                             return;
8134                         }
8135                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8136                     }
8137
8138                 );
8139                 */
8140
8141
8142                 return;
8143             }
8144
8145             Roo.callback(o.failure, o.scope, [this, action]);
8146             // show an error message if no failed handler is set..
8147             if (!this.hasListener('actionfailed')) {
8148                 Roo.log("need to add dialog support");
8149                 /*
8150                 Roo.MessageBox.alert("Error",
8151                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8152                         action.result.errorMsg :
8153                         "Saving Failed, please check your entries or try again"
8154                 );
8155                 */
8156             }
8157
8158             this.fireEvent('actionfailed', this, action);
8159         }
8160
8161     },
8162     /**
8163      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8164      * @param {String} id The value to search for
8165      * @return Field
8166      */
8167     findField : function(id){
8168         var items = this.getItems();
8169         var field = items.get(id);
8170         if(!field){
8171              items.each(function(f){
8172                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8173                     field = f;
8174                     return false;
8175                 }
8176                 return true;
8177             });
8178         }
8179         return field || null;
8180     },
8181      /**
8182      * Mark fields in this form invalid in bulk.
8183      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8184      * @return {BasicForm} this
8185      */
8186     markInvalid : function(errors){
8187         if(errors instanceof Array){
8188             for(var i = 0, len = errors.length; i < len; i++){
8189                 var fieldError = errors[i];
8190                 var f = this.findField(fieldError.id);
8191                 if(f){
8192                     f.markInvalid(fieldError.msg);
8193                 }
8194             }
8195         }else{
8196             var field, id;
8197             for(id in errors){
8198                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8199                     field.markInvalid(errors[id]);
8200                 }
8201             }
8202         }
8203         //Roo.each(this.childForms || [], function (f) {
8204         //    f.markInvalid(errors);
8205         //});
8206
8207         return this;
8208     },
8209
8210     /**
8211      * Set values for fields in this form in bulk.
8212      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8213      * @return {BasicForm} this
8214      */
8215     setValues : function(values){
8216         if(values instanceof Array){ // array of objects
8217             for(var i = 0, len = values.length; i < len; i++){
8218                 var v = values[i];
8219                 var f = this.findField(v.id);
8220                 if(f){
8221                     f.setValue(v.value);
8222                     if(this.trackResetOnLoad){
8223                         f.originalValue = f.getValue();
8224                     }
8225                 }
8226             }
8227         }else{ // object hash
8228             var field, id;
8229             for(id in values){
8230                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8231
8232                     if (field.setFromData &&
8233                         field.valueField &&
8234                         field.displayField &&
8235                         // combos' with local stores can
8236                         // be queried via setValue()
8237                         // to set their value..
8238                         (field.store && !field.store.isLocal)
8239                         ) {
8240                         // it's a combo
8241                         var sd = { };
8242                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8243                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8244                         field.setFromData(sd);
8245
8246                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8247                         
8248                         field.setFromData(values);
8249                         
8250                     } else {
8251                         field.setValue(values[id]);
8252                     }
8253
8254
8255                     if(this.trackResetOnLoad){
8256                         field.originalValue = field.getValue();
8257                     }
8258                 }
8259             }
8260         }
8261
8262         //Roo.each(this.childForms || [], function (f) {
8263         //    f.setValues(values);
8264         //});
8265
8266         return this;
8267     },
8268
8269     /**
8270      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8271      * they are returned as an array.
8272      * @param {Boolean} asString
8273      * @return {Object}
8274      */
8275     getValues : function(asString){
8276         //if (this.childForms) {
8277             // copy values from the child forms
8278         //    Roo.each(this.childForms, function (f) {
8279         //        this.setValues(f.getValues());
8280         //    }, this);
8281         //}
8282
8283
8284
8285         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8286         if(asString === true){
8287             return fs;
8288         }
8289         return Roo.urlDecode(fs);
8290     },
8291
8292     /**
8293      * Returns the fields in this form as an object with key/value pairs.
8294      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8295      * @return {Object}
8296      */
8297     getFieldValues : function(with_hidden)
8298     {
8299         var items = this.getItems();
8300         var ret = {};
8301         items.each(function(f){
8302             
8303             if (!f.getName()) {
8304                 return;
8305             }
8306             
8307             var v = f.getValue();
8308             
8309             if (f.inputType =='radio') {
8310                 if (typeof(ret[f.getName()]) == 'undefined') {
8311                     ret[f.getName()] = ''; // empty..
8312                 }
8313
8314                 if (!f.el.dom.checked) {
8315                     return;
8316
8317                 }
8318                 v = f.el.dom.value;
8319
8320             }
8321             
8322             if(f.xtype == 'MoneyField'){
8323                 ret[f.currencyName] = f.getCurrency();
8324             }
8325
8326             // not sure if this supported any more..
8327             if ((typeof(v) == 'object') && f.getRawValue) {
8328                 v = f.getRawValue() ; // dates..
8329             }
8330             // combo boxes where name != hiddenName...
8331             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8332                 ret[f.name] = f.getRawValue();
8333             }
8334             ret[f.getName()] = v;
8335         });
8336
8337         return ret;
8338     },
8339
8340     /**
8341      * Clears all invalid messages in this form.
8342      * @return {BasicForm} this
8343      */
8344     clearInvalid : function(){
8345         var items = this.getItems();
8346
8347         items.each(function(f){
8348            f.clearInvalid();
8349         });
8350
8351         return this;
8352     },
8353
8354     /**
8355      * Resets this form.
8356      * @return {BasicForm} this
8357      */
8358     reset : function(){
8359         var items = this.getItems();
8360         items.each(function(f){
8361             f.reset();
8362         });
8363
8364         Roo.each(this.childForms || [], function (f) {
8365             f.reset();
8366         });
8367
8368
8369         return this;
8370     },
8371     
8372     getItems : function()
8373     {
8374         var r=new Roo.util.MixedCollection(false, function(o){
8375             return o.id || (o.id = Roo.id());
8376         });
8377         var iter = function(el) {
8378             if (el.inputEl) {
8379                 r.add(el);
8380             }
8381             if (!el.items) {
8382                 return;
8383             }
8384             Roo.each(el.items,function(e) {
8385                 iter(e);
8386             });
8387         };
8388
8389         iter(this);
8390         return r;
8391     },
8392     
8393     hideFields : function(items)
8394     {
8395         Roo.each(items, function(i){
8396             
8397             var f = this.findField(i);
8398             
8399             if(!f){
8400                 return;
8401             }
8402             
8403             f.hide();
8404             
8405         }, this);
8406     },
8407     
8408     showFields : 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.show();
8419             
8420         }, this);
8421     }
8422
8423 });
8424
8425 Roo.apply(Roo.bootstrap.Form, {
8426     
8427     popover : {
8428         
8429         padding : 5,
8430         
8431         isApplied : false,
8432         
8433         isMasked : false,
8434         
8435         form : false,
8436         
8437         target : false,
8438         
8439         toolTip : false,
8440         
8441         intervalID : false,
8442         
8443         maskEl : false,
8444         
8445         apply : function()
8446         {
8447             if(this.isApplied){
8448                 return;
8449             }
8450             
8451             this.maskEl = {
8452                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8453                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8454                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8455                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8456             };
8457             
8458             this.maskEl.top.enableDisplayMode("block");
8459             this.maskEl.left.enableDisplayMode("block");
8460             this.maskEl.bottom.enableDisplayMode("block");
8461             this.maskEl.right.enableDisplayMode("block");
8462             
8463             this.toolTip = new Roo.bootstrap.Tooltip({
8464                 cls : 'roo-form-error-popover',
8465                 alignment : {
8466                     'left' : ['r-l', [-2,0], 'right'],
8467                     'right' : ['l-r', [2,0], 'left'],
8468                     'bottom' : ['tl-bl', [0,2], 'top'],
8469                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8470                 }
8471             });
8472             
8473             this.toolTip.render(Roo.get(document.body));
8474
8475             this.toolTip.el.enableDisplayMode("block");
8476             
8477             Roo.get(document.body).on('click', function(){
8478                 this.unmask();
8479             }, this);
8480             
8481             Roo.get(document.body).on('touchstart', function(){
8482                 this.unmask();
8483             }, this);
8484             
8485             this.isApplied = true
8486         },
8487         
8488         mask : function(form, target)
8489         {
8490             this.form = form;
8491             
8492             this.target = target;
8493             
8494             if(!this.form.errorMask || !target.el){
8495                 return;
8496             }
8497             
8498             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8499             
8500             Roo.log(scrollable);
8501             
8502             var ot = this.target.el.calcOffsetsTo(scrollable);
8503             
8504             var scrollTo = ot[1] - this.form.maskOffset;
8505             
8506             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8507             
8508             scrollable.scrollTo('top', scrollTo);
8509             
8510             var box = this.target.el.getBox();
8511             Roo.log(box);
8512             var zIndex = Roo.bootstrap.Modal.zIndex++;
8513
8514             
8515             this.maskEl.top.setStyle('position', 'absolute');
8516             this.maskEl.top.setStyle('z-index', zIndex);
8517             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8518             this.maskEl.top.setLeft(0);
8519             this.maskEl.top.setTop(0);
8520             this.maskEl.top.show();
8521             
8522             this.maskEl.left.setStyle('position', 'absolute');
8523             this.maskEl.left.setStyle('z-index', zIndex);
8524             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8525             this.maskEl.left.setLeft(0);
8526             this.maskEl.left.setTop(box.y - this.padding);
8527             this.maskEl.left.show();
8528
8529             this.maskEl.bottom.setStyle('position', 'absolute');
8530             this.maskEl.bottom.setStyle('z-index', zIndex);
8531             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8532             this.maskEl.bottom.setLeft(0);
8533             this.maskEl.bottom.setTop(box.bottom + this.padding);
8534             this.maskEl.bottom.show();
8535
8536             this.maskEl.right.setStyle('position', 'absolute');
8537             this.maskEl.right.setStyle('z-index', zIndex);
8538             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8539             this.maskEl.right.setLeft(box.right + this.padding);
8540             this.maskEl.right.setTop(box.y - this.padding);
8541             this.maskEl.right.show();
8542
8543             this.toolTip.bindEl = this.target.el;
8544
8545             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8546
8547             var tip = this.target.blankText;
8548
8549             if(this.target.getValue() !== '' ) {
8550                 
8551                 if (this.target.invalidText.length) {
8552                     tip = this.target.invalidText;
8553                 } else if (this.target.regexText.length){
8554                     tip = this.target.regexText;
8555                 }
8556             }
8557
8558             this.toolTip.show(tip);
8559
8560             this.intervalID = window.setInterval(function() {
8561                 Roo.bootstrap.Form.popover.unmask();
8562             }, 10000);
8563
8564             window.onwheel = function(){ return false;};
8565             
8566             (function(){ this.isMasked = true; }).defer(500, this);
8567             
8568         },
8569         
8570         unmask : function()
8571         {
8572             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8573                 return;
8574             }
8575             
8576             this.maskEl.top.setStyle('position', 'absolute');
8577             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8578             this.maskEl.top.hide();
8579
8580             this.maskEl.left.setStyle('position', 'absolute');
8581             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8582             this.maskEl.left.hide();
8583
8584             this.maskEl.bottom.setStyle('position', 'absolute');
8585             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8586             this.maskEl.bottom.hide();
8587
8588             this.maskEl.right.setStyle('position', 'absolute');
8589             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8590             this.maskEl.right.hide();
8591             
8592             this.toolTip.hide();
8593             
8594             this.toolTip.el.hide();
8595             
8596             window.onwheel = function(){ return true;};
8597             
8598             if(this.intervalID){
8599                 window.clearInterval(this.intervalID);
8600                 this.intervalID = false;
8601             }
8602             
8603             this.isMasked = false;
8604             
8605         }
8606         
8607     }
8608     
8609 });
8610
8611 /*
8612  * Based on:
8613  * Ext JS Library 1.1.1
8614  * Copyright(c) 2006-2007, Ext JS, LLC.
8615  *
8616  * Originally Released Under LGPL - original licence link has changed is not relivant.
8617  *
8618  * Fork - LGPL
8619  * <script type="text/javascript">
8620  */
8621 /**
8622  * @class Roo.form.VTypes
8623  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8624  * @singleton
8625  */
8626 Roo.form.VTypes = function(){
8627     // closure these in so they are only created once.
8628     var alpha = /^[a-zA-Z_]+$/;
8629     var alphanum = /^[a-zA-Z0-9_]+$/;
8630     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8631     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8632
8633     // All these messages and functions are configurable
8634     return {
8635         /**
8636          * The function used to validate email addresses
8637          * @param {String} value The email address
8638          */
8639         'email' : function(v){
8640             return email.test(v);
8641         },
8642         /**
8643          * The error text to display when the email validation function returns false
8644          * @type String
8645          */
8646         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8647         /**
8648          * The keystroke filter mask to be applied on email input
8649          * @type RegExp
8650          */
8651         'emailMask' : /[a-z0-9_\.\-@]/i,
8652
8653         /**
8654          * The function used to validate URLs
8655          * @param {String} value The URL
8656          */
8657         'url' : function(v){
8658             return url.test(v);
8659         },
8660         /**
8661          * The error text to display when the url validation function returns false
8662          * @type String
8663          */
8664         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8665         
8666         /**
8667          * The function used to validate alpha values
8668          * @param {String} value The value
8669          */
8670         'alpha' : function(v){
8671             return alpha.test(v);
8672         },
8673         /**
8674          * The error text to display when the alpha validation function returns false
8675          * @type String
8676          */
8677         'alphaText' : 'This field should only contain letters and _',
8678         /**
8679          * The keystroke filter mask to be applied on alpha input
8680          * @type RegExp
8681          */
8682         'alphaMask' : /[a-z_]/i,
8683
8684         /**
8685          * The function used to validate alphanumeric values
8686          * @param {String} value The value
8687          */
8688         'alphanum' : function(v){
8689             return alphanum.test(v);
8690         },
8691         /**
8692          * The error text to display when the alphanumeric validation function returns false
8693          * @type String
8694          */
8695         'alphanumText' : 'This field should only contain letters, numbers and _',
8696         /**
8697          * The keystroke filter mask to be applied on alphanumeric input
8698          * @type RegExp
8699          */
8700         'alphanumMask' : /[a-z0-9_]/i
8701     };
8702 }();/*
8703  * - LGPL
8704  *
8705  * Input
8706  * 
8707  */
8708
8709 /**
8710  * @class Roo.bootstrap.Input
8711  * @extends Roo.bootstrap.Component
8712  * Bootstrap Input class
8713  * @cfg {Boolean} disabled is it disabled
8714  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8715  * @cfg {String} name name of the input
8716  * @cfg {string} fieldLabel - the label associated
8717  * @cfg {string} placeholder - placeholder to put in text.
8718  * @cfg {string}  before - input group add on before
8719  * @cfg {string} after - input group add on after
8720  * @cfg {string} size - (lg|sm) or leave empty..
8721  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8722  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8723  * @cfg {Number} md colspan out of 12 for computer-sized screens
8724  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8725  * @cfg {string} value default value of the input
8726  * @cfg {Number} labelWidth set the width of label 
8727  * @cfg {Number} labellg set the width of label (1-12)
8728  * @cfg {Number} labelmd set the width of label (1-12)
8729  * @cfg {Number} labelsm set the width of label (1-12)
8730  * @cfg {Number} labelxs set the width of label (1-12)
8731  * @cfg {String} labelAlign (top|left)
8732  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8733  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8734  * @cfg {String} indicatorpos (left|right) default left
8735  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8736  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8737
8738  * @cfg {String} align (left|center|right) Default left
8739  * @cfg {Boolean} forceFeedback (true|false) Default false
8740  * 
8741  * @constructor
8742  * Create a new Input
8743  * @param {Object} config The config object
8744  */
8745
8746 Roo.bootstrap.Input = function(config){
8747     
8748     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8749     
8750     this.addEvents({
8751         /**
8752          * @event focus
8753          * Fires when this field receives input focus.
8754          * @param {Roo.form.Field} this
8755          */
8756         focus : true,
8757         /**
8758          * @event blur
8759          * Fires when this field loses input focus.
8760          * @param {Roo.form.Field} this
8761          */
8762         blur : true,
8763         /**
8764          * @event specialkey
8765          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8766          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8767          * @param {Roo.form.Field} this
8768          * @param {Roo.EventObject} e The event object
8769          */
8770         specialkey : true,
8771         /**
8772          * @event change
8773          * Fires just before the field blurs if the field value has changed.
8774          * @param {Roo.form.Field} this
8775          * @param {Mixed} newValue The new value
8776          * @param {Mixed} oldValue The original value
8777          */
8778         change : true,
8779         /**
8780          * @event invalid
8781          * Fires after the field has been marked as invalid.
8782          * @param {Roo.form.Field} this
8783          * @param {String} msg The validation message
8784          */
8785         invalid : true,
8786         /**
8787          * @event valid
8788          * Fires after the field has been validated with no errors.
8789          * @param {Roo.form.Field} this
8790          */
8791         valid : true,
8792          /**
8793          * @event keyup
8794          * Fires after the key up
8795          * @param {Roo.form.Field} this
8796          * @param {Roo.EventObject}  e The event Object
8797          */
8798         keyup : true
8799     });
8800 };
8801
8802 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8803      /**
8804      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8805       automatic validation (defaults to "keyup").
8806      */
8807     validationEvent : "keyup",
8808      /**
8809      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8810      */
8811     validateOnBlur : true,
8812     /**
8813      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8814      */
8815     validationDelay : 250,
8816      /**
8817      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8818      */
8819     focusClass : "x-form-focus",  // not needed???
8820     
8821        
8822     /**
8823      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8824      */
8825     invalidClass : "has-warning",
8826     
8827     /**
8828      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8829      */
8830     validClass : "has-success",
8831     
8832     /**
8833      * @cfg {Boolean} hasFeedback (true|false) default true
8834      */
8835     hasFeedback : true,
8836     
8837     /**
8838      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8839      */
8840     invalidFeedbackClass : "glyphicon-warning-sign",
8841     
8842     /**
8843      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8844      */
8845     validFeedbackClass : "glyphicon-ok",
8846     
8847     /**
8848      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8849      */
8850     selectOnFocus : false,
8851     
8852      /**
8853      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8854      */
8855     maskRe : null,
8856        /**
8857      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8858      */
8859     vtype : null,
8860     
8861       /**
8862      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8863      */
8864     disableKeyFilter : false,
8865     
8866        /**
8867      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8868      */
8869     disabled : false,
8870      /**
8871      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8872      */
8873     allowBlank : true,
8874     /**
8875      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8876      */
8877     blankText : "Please complete this mandatory field",
8878     
8879      /**
8880      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8881      */
8882     minLength : 0,
8883     /**
8884      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8885      */
8886     maxLength : Number.MAX_VALUE,
8887     /**
8888      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8889      */
8890     minLengthText : "The minimum length for this field is {0}",
8891     /**
8892      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8893      */
8894     maxLengthText : "The maximum length for this field is {0}",
8895   
8896     
8897     /**
8898      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8899      * If available, this function will be called only after the basic validators all return true, and will be passed the
8900      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8901      */
8902     validator : null,
8903     /**
8904      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8905      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8906      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8907      */
8908     regex : null,
8909     /**
8910      * @cfg {String} regexText -- Depricated - use Invalid Text
8911      */
8912     regexText : "",
8913     
8914     /**
8915      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8916      */
8917     invalidText : "",
8918     
8919     
8920     
8921     autocomplete: false,
8922     
8923     
8924     fieldLabel : '',
8925     inputType : 'text',
8926     
8927     name : false,
8928     placeholder: false,
8929     before : false,
8930     after : false,
8931     size : false,
8932     hasFocus : false,
8933     preventMark: false,
8934     isFormField : true,
8935     value : '',
8936     labelWidth : 2,
8937     labelAlign : false,
8938     readOnly : false,
8939     align : false,
8940     formatedValue : false,
8941     forceFeedback : false,
8942     
8943     indicatorpos : 'left',
8944     
8945     labellg : 0,
8946     labelmd : 0,
8947     labelsm : 0,
8948     labelxs : 0,
8949     
8950     capture : '',
8951     accept : '',
8952     
8953     parentLabelAlign : function()
8954     {
8955         var parent = this;
8956         while (parent.parent()) {
8957             parent = parent.parent();
8958             if (typeof(parent.labelAlign) !='undefined') {
8959                 return parent.labelAlign;
8960             }
8961         }
8962         return 'left';
8963         
8964     },
8965     
8966     getAutoCreate : function()
8967     {
8968         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8969         
8970         var id = Roo.id();
8971         
8972         var cfg = {};
8973         
8974         if(this.inputType != 'hidden'){
8975             cfg.cls = 'form-group' //input-group
8976         }
8977         
8978         var input =  {
8979             tag: 'input',
8980             id : id,
8981             type : this.inputType,
8982             value : this.value,
8983             cls : 'form-control',
8984             placeholder : this.placeholder || '',
8985             autocomplete : this.autocomplete || 'new-password'
8986         };
8987         
8988         if(this.capture.length){
8989             input.capture = this.capture;
8990         }
8991         
8992         if(this.accept.length){
8993             input.accept = this.accept + "/*";
8994         }
8995         
8996         if(this.align){
8997             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8998         }
8999         
9000         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9001             input.maxLength = this.maxLength;
9002         }
9003         
9004         if (this.disabled) {
9005             input.disabled=true;
9006         }
9007         
9008         if (this.readOnly) {
9009             input.readonly=true;
9010         }
9011         
9012         if (this.name) {
9013             input.name = this.name;
9014         }
9015         
9016         if (this.size) {
9017             input.cls += ' input-' + this.size;
9018         }
9019         
9020         var settings=this;
9021         ['xs','sm','md','lg'].map(function(size){
9022             if (settings[size]) {
9023                 cfg.cls += ' col-' + size + '-' + settings[size];
9024             }
9025         });
9026         
9027         var inputblock = input;
9028         
9029         var feedback = {
9030             tag: 'span',
9031             cls: 'glyphicon form-control-feedback'
9032         };
9033             
9034         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9035             
9036             inputblock = {
9037                 cls : 'has-feedback',
9038                 cn :  [
9039                     input,
9040                     feedback
9041                 ] 
9042             };  
9043         }
9044         
9045         if (this.before || this.after) {
9046             
9047             inputblock = {
9048                 cls : 'input-group',
9049                 cn :  [] 
9050             };
9051             
9052             if (this.before && typeof(this.before) == 'string') {
9053                 
9054                 inputblock.cn.push({
9055                     tag :'span',
9056                     cls : 'roo-input-before input-group-addon',
9057                     html : this.before
9058                 });
9059             }
9060             if (this.before && typeof(this.before) == 'object') {
9061                 this.before = Roo.factory(this.before);
9062                 
9063                 inputblock.cn.push({
9064                     tag :'span',
9065                     cls : 'roo-input-before input-group-' +
9066                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9067                 });
9068             }
9069             
9070             inputblock.cn.push(input);
9071             
9072             if (this.after && typeof(this.after) == 'string') {
9073                 inputblock.cn.push({
9074                     tag :'span',
9075                     cls : 'roo-input-after input-group-addon',
9076                     html : this.after
9077                 });
9078             }
9079             if (this.after && typeof(this.after) == 'object') {
9080                 this.after = Roo.factory(this.after);
9081                 
9082                 inputblock.cn.push({
9083                     tag :'span',
9084                     cls : 'roo-input-after input-group-' +
9085                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9086                 });
9087             }
9088             
9089             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9090                 inputblock.cls += ' has-feedback';
9091                 inputblock.cn.push(feedback);
9092             }
9093         };
9094         
9095         if (align ==='left' && this.fieldLabel.length) {
9096             
9097             cfg.cls += ' roo-form-group-label-left';
9098             
9099             cfg.cn = [
9100                 {
9101                     tag : 'i',
9102                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9103                     tooltip : 'This field is required'
9104                 },
9105                 {
9106                     tag: 'label',
9107                     'for' :  id,
9108                     cls : 'control-label',
9109                     html : this.fieldLabel
9110
9111                 },
9112                 {
9113                     cls : "", 
9114                     cn: [
9115                         inputblock
9116                     ]
9117                 }
9118             ];
9119             
9120             var labelCfg = cfg.cn[1];
9121             var contentCfg = cfg.cn[2];
9122             
9123             if(this.indicatorpos == 'right'){
9124                 cfg.cn = [
9125                     {
9126                         tag: 'label',
9127                         'for' :  id,
9128                         cls : 'control-label',
9129                         cn : [
9130                             {
9131                                 tag : 'span',
9132                                 html : this.fieldLabel
9133                             },
9134                             {
9135                                 tag : 'i',
9136                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9137                                 tooltip : 'This field is required'
9138                             }
9139                         ]
9140                     },
9141                     {
9142                         cls : "",
9143                         cn: [
9144                             inputblock
9145                         ]
9146                     }
9147
9148                 ];
9149                 
9150                 labelCfg = cfg.cn[0];
9151                 contentCfg = cfg.cn[1];
9152             
9153             }
9154             
9155             if(this.labelWidth > 12){
9156                 labelCfg.style = "width: " + this.labelWidth + 'px';
9157             }
9158             
9159             if(this.labelWidth < 13 && this.labelmd == 0){
9160                 this.labelmd = this.labelWidth;
9161             }
9162             
9163             if(this.labellg > 0){
9164                 labelCfg.cls += ' col-lg-' + this.labellg;
9165                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9166             }
9167             
9168             if(this.labelmd > 0){
9169                 labelCfg.cls += ' col-md-' + this.labelmd;
9170                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9171             }
9172             
9173             if(this.labelsm > 0){
9174                 labelCfg.cls += ' col-sm-' + this.labelsm;
9175                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9176             }
9177             
9178             if(this.labelxs > 0){
9179                 labelCfg.cls += ' col-xs-' + this.labelxs;
9180                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9181             }
9182             
9183             
9184         } else if ( this.fieldLabel.length) {
9185                 
9186             cfg.cn = [
9187                 {
9188                     tag : 'i',
9189                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9190                     tooltip : 'This field is required'
9191                 },
9192                 {
9193                     tag: 'label',
9194                    //cls : 'input-group-addon',
9195                     html : this.fieldLabel
9196
9197                 },
9198
9199                inputblock
9200
9201            ];
9202            
9203            if(this.indicatorpos == 'right'){
9204                 
9205                 cfg.cn = [
9206                     {
9207                         tag: 'label',
9208                        //cls : 'input-group-addon',
9209                         html : this.fieldLabel
9210
9211                     },
9212                     {
9213                         tag : 'i',
9214                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9215                         tooltip : 'This field is required'
9216                     },
9217
9218                    inputblock
9219
9220                ];
9221
9222             }
9223
9224         } else {
9225             
9226             cfg.cn = [
9227
9228                     inputblock
9229
9230             ];
9231                 
9232                 
9233         };
9234         
9235         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9236            cfg.cls += ' navbar-form';
9237         }
9238         
9239         if (this.parentType === 'NavGroup') {
9240            cfg.cls += ' navbar-form';
9241            cfg.tag = 'li';
9242         }
9243         
9244         return cfg;
9245         
9246     },
9247     /**
9248      * return the real input element.
9249      */
9250     inputEl: function ()
9251     {
9252         return this.el.select('input.form-control',true).first();
9253     },
9254     
9255     tooltipEl : function()
9256     {
9257         return this.inputEl();
9258     },
9259     
9260     indicatorEl : function()
9261     {
9262         var indicator = this.el.select('i.roo-required-indicator',true).first();
9263         
9264         if(!indicator){
9265             return false;
9266         }
9267         
9268         return indicator;
9269         
9270     },
9271     
9272     setDisabled : function(v)
9273     {
9274         var i  = this.inputEl().dom;
9275         if (!v) {
9276             i.removeAttribute('disabled');
9277             return;
9278             
9279         }
9280         i.setAttribute('disabled','true');
9281     },
9282     initEvents : function()
9283     {
9284           
9285         this.inputEl().on("keydown" , this.fireKey,  this);
9286         this.inputEl().on("focus", this.onFocus,  this);
9287         this.inputEl().on("blur", this.onBlur,  this);
9288         
9289         this.inputEl().relayEvent('keyup', this);
9290         
9291         this.indicator = this.indicatorEl();
9292         
9293         if(this.indicator){
9294             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9295         }
9296  
9297         // reference to original value for reset
9298         this.originalValue = this.getValue();
9299         //Roo.form.TextField.superclass.initEvents.call(this);
9300         if(this.validationEvent == 'keyup'){
9301             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9302             this.inputEl().on('keyup', this.filterValidation, this);
9303         }
9304         else if(this.validationEvent !== false){
9305             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9306         }
9307         
9308         if(this.selectOnFocus){
9309             this.on("focus", this.preFocus, this);
9310             
9311         }
9312         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9313             this.inputEl().on("keypress", this.filterKeys, this);
9314         } else {
9315             this.inputEl().relayEvent('keypress', this);
9316         }
9317        /* if(this.grow){
9318             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9319             this.el.on("click", this.autoSize,  this);
9320         }
9321         */
9322         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9323             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9324         }
9325         
9326         if (typeof(this.before) == 'object') {
9327             this.before.render(this.el.select('.roo-input-before',true).first());
9328         }
9329         if (typeof(this.after) == 'object') {
9330             this.after.render(this.el.select('.roo-input-after',true).first());
9331         }
9332         
9333         this.inputEl().on('change', this.onChange, this);
9334         
9335     },
9336     filterValidation : function(e){
9337         if(!e.isNavKeyPress()){
9338             this.validationTask.delay(this.validationDelay);
9339         }
9340     },
9341      /**
9342      * Validates the field value
9343      * @return {Boolean} True if the value is valid, else false
9344      */
9345     validate : function(){
9346         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9347         if(this.disabled || this.validateValue(this.getRawValue())){
9348             this.markValid();
9349             return true;
9350         }
9351         
9352         this.markInvalid();
9353         return false;
9354     },
9355     
9356     
9357     /**
9358      * Validates a value according to the field's validation rules and marks the field as invalid
9359      * if the validation fails
9360      * @param {Mixed} value The value to validate
9361      * @return {Boolean} True if the value is valid, else false
9362      */
9363     validateValue : function(value)
9364     {
9365         if(this.getVisibilityEl().hasClass('hidden')){
9366             return true;
9367         }
9368         
9369         if(value.length < 1)  { // if it's blank
9370             if(this.allowBlank){
9371                 return true;
9372             }
9373             return false;
9374         }
9375         
9376         if(value.length < this.minLength){
9377             return false;
9378         }
9379         if(value.length > this.maxLength){
9380             return false;
9381         }
9382         if(this.vtype){
9383             var vt = Roo.form.VTypes;
9384             if(!vt[this.vtype](value, this)){
9385                 return false;
9386             }
9387         }
9388         if(typeof this.validator == "function"){
9389             var msg = this.validator(value);
9390             if(msg !== true){
9391                 return false;
9392             }
9393             if (typeof(msg) == 'string') {
9394                 this.invalidText = msg;
9395             }
9396         }
9397         
9398         if(this.regex && !this.regex.test(value)){
9399             return false;
9400         }
9401         
9402         return true;
9403     },
9404     
9405      // private
9406     fireKey : function(e){
9407         //Roo.log('field ' + e.getKey());
9408         if(e.isNavKeyPress()){
9409             this.fireEvent("specialkey", this, e);
9410         }
9411     },
9412     focus : function (selectText){
9413         if(this.rendered){
9414             this.inputEl().focus();
9415             if(selectText === true){
9416                 this.inputEl().dom.select();
9417             }
9418         }
9419         return this;
9420     } ,
9421     
9422     onFocus : function(){
9423         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9424            // this.el.addClass(this.focusClass);
9425         }
9426         if(!this.hasFocus){
9427             this.hasFocus = true;
9428             this.startValue = this.getValue();
9429             this.fireEvent("focus", this);
9430         }
9431     },
9432     
9433     beforeBlur : Roo.emptyFn,
9434
9435     
9436     // private
9437     onBlur : function(){
9438         this.beforeBlur();
9439         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9440             //this.el.removeClass(this.focusClass);
9441         }
9442         this.hasFocus = false;
9443         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9444             this.validate();
9445         }
9446         var v = this.getValue();
9447         if(String(v) !== String(this.startValue)){
9448             this.fireEvent('change', this, v, this.startValue);
9449         }
9450         this.fireEvent("blur", this);
9451     },
9452     
9453     onChange : function(e)
9454     {
9455         var v = this.getValue();
9456         if(String(v) !== String(this.startValue)){
9457             this.fireEvent('change', this, v, this.startValue);
9458         }
9459         
9460     },
9461     
9462     /**
9463      * Resets the current field value to the originally loaded value and clears any validation messages
9464      */
9465     reset : function(){
9466         this.setValue(this.originalValue);
9467         this.validate();
9468     },
9469      /**
9470      * Returns the name of the field
9471      * @return {Mixed} name The name field
9472      */
9473     getName: function(){
9474         return this.name;
9475     },
9476      /**
9477      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9478      * @return {Mixed} value The field value
9479      */
9480     getValue : function(){
9481         
9482         var v = this.inputEl().getValue();
9483         
9484         return v;
9485     },
9486     /**
9487      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9488      * @return {Mixed} value The field value
9489      */
9490     getRawValue : function(){
9491         var v = this.inputEl().getValue();
9492         
9493         return v;
9494     },
9495     
9496     /**
9497      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9498      * @param {Mixed} value The value to set
9499      */
9500     setRawValue : function(v){
9501         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9502     },
9503     
9504     selectText : function(start, end){
9505         var v = this.getRawValue();
9506         if(v.length > 0){
9507             start = start === undefined ? 0 : start;
9508             end = end === undefined ? v.length : end;
9509             var d = this.inputEl().dom;
9510             if(d.setSelectionRange){
9511                 d.setSelectionRange(start, end);
9512             }else if(d.createTextRange){
9513                 var range = d.createTextRange();
9514                 range.moveStart("character", start);
9515                 range.moveEnd("character", v.length-end);
9516                 range.select();
9517             }
9518         }
9519     },
9520     
9521     /**
9522      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9523      * @param {Mixed} value The value to set
9524      */
9525     setValue : function(v){
9526         this.value = v;
9527         if(this.rendered){
9528             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9529             this.validate();
9530         }
9531     },
9532     
9533     /*
9534     processValue : function(value){
9535         if(this.stripCharsRe){
9536             var newValue = value.replace(this.stripCharsRe, '');
9537             if(newValue !== value){
9538                 this.setRawValue(newValue);
9539                 return newValue;
9540             }
9541         }
9542         return value;
9543     },
9544   */
9545     preFocus : function(){
9546         
9547         if(this.selectOnFocus){
9548             this.inputEl().dom.select();
9549         }
9550     },
9551     filterKeys : function(e){
9552         var k = e.getKey();
9553         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9554             return;
9555         }
9556         var c = e.getCharCode(), cc = String.fromCharCode(c);
9557         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9558             return;
9559         }
9560         if(!this.maskRe.test(cc)){
9561             e.stopEvent();
9562         }
9563     },
9564      /**
9565      * Clear any invalid styles/messages for this field
9566      */
9567     clearInvalid : function(){
9568         
9569         if(!this.el || this.preventMark){ // not rendered
9570             return;
9571         }
9572         
9573      
9574         this.el.removeClass(this.invalidClass);
9575         
9576         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9577             
9578             var feedback = this.el.select('.form-control-feedback', true).first();
9579             
9580             if(feedback){
9581                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9582             }
9583             
9584         }
9585         
9586         if(this.indicator){
9587             this.indicator.removeClass('visible');
9588             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9589         }
9590         
9591         this.fireEvent('valid', this);
9592     },
9593     
9594      /**
9595      * Mark this field as valid
9596      */
9597     markValid : function()
9598     {
9599         if(!this.el  || this.preventMark){ // not rendered...
9600             return;
9601         }
9602         
9603         this.el.removeClass([this.invalidClass, this.validClass]);
9604         
9605         var feedback = this.el.select('.form-control-feedback', true).first();
9606             
9607         if(feedback){
9608             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9609         }
9610         
9611         if(this.indicator){
9612             this.indicator.removeClass('visible');
9613             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9614         }
9615         
9616         if(this.disabled){
9617             return;
9618         }
9619         
9620         if(this.allowBlank && !this.getRawValue().length){
9621             return;
9622         }
9623         
9624         this.el.addClass(this.validClass);
9625         
9626         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9627             
9628             var feedback = this.el.select('.form-control-feedback', true).first();
9629             
9630             if(feedback){
9631                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9632                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9633             }
9634             
9635         }
9636         
9637         this.fireEvent('valid', this);
9638     },
9639     
9640      /**
9641      * Mark this field as invalid
9642      * @param {String} msg The validation message
9643      */
9644     markInvalid : function(msg)
9645     {
9646         if(!this.el  || this.preventMark){ // not rendered
9647             return;
9648         }
9649         
9650         this.el.removeClass([this.invalidClass, this.validClass]);
9651         
9652         var feedback = this.el.select('.form-control-feedback', true).first();
9653             
9654         if(feedback){
9655             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9656         }
9657
9658         if(this.disabled){
9659             return;
9660         }
9661         
9662         if(this.allowBlank && !this.getRawValue().length){
9663             return;
9664         }
9665         
9666         if(this.indicator){
9667             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9668             this.indicator.addClass('visible');
9669         }
9670         
9671         this.el.addClass(this.invalidClass);
9672         
9673         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9674             
9675             var feedback = this.el.select('.form-control-feedback', true).first();
9676             
9677             if(feedback){
9678                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9679                 
9680                 if(this.getValue().length || this.forceFeedback){
9681                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9682                 }
9683                 
9684             }
9685             
9686         }
9687         
9688         this.fireEvent('invalid', this, msg);
9689     },
9690     // private
9691     SafariOnKeyDown : function(event)
9692     {
9693         // this is a workaround for a password hang bug on chrome/ webkit.
9694         if (this.inputEl().dom.type != 'password') {
9695             return;
9696         }
9697         
9698         var isSelectAll = false;
9699         
9700         if(this.inputEl().dom.selectionEnd > 0){
9701             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9702         }
9703         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9704             event.preventDefault();
9705             this.setValue('');
9706             return;
9707         }
9708         
9709         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9710             
9711             event.preventDefault();
9712             // this is very hacky as keydown always get's upper case.
9713             //
9714             var cc = String.fromCharCode(event.getCharCode());
9715             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9716             
9717         }
9718     },
9719     adjustWidth : function(tag, w){
9720         tag = tag.toLowerCase();
9721         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9722             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9723                 if(tag == 'input'){
9724                     return w + 2;
9725                 }
9726                 if(tag == 'textarea'){
9727                     return w-2;
9728                 }
9729             }else if(Roo.isOpera){
9730                 if(tag == 'input'){
9731                     return w + 2;
9732                 }
9733                 if(tag == 'textarea'){
9734                     return w-2;
9735                 }
9736             }
9737         }
9738         return w;
9739     },
9740     
9741     setFieldLabel : function(v)
9742     {
9743         if(!this.rendered){
9744             return;
9745         }
9746         
9747         if(this.indicator){
9748             var ar = this.el.select('label > span',true);
9749             
9750             if (ar.elements.length) {
9751                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9752                 this.fieldLabel = v;
9753                 return;
9754             }
9755             
9756             var br = this.el.select('label',true);
9757             
9758             if(br.elements.length) {
9759                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9760                 this.fieldLabel = v;
9761                 return;
9762             }
9763             
9764             Roo.log('Cannot Found any of label > span || label in input');
9765             return;
9766         }
9767         
9768         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9769         this.fieldLabel = v;
9770         
9771         
9772     }
9773 });
9774
9775  
9776 /*
9777  * - LGPL
9778  *
9779  * Input
9780  * 
9781  */
9782
9783 /**
9784  * @class Roo.bootstrap.TextArea
9785  * @extends Roo.bootstrap.Input
9786  * Bootstrap TextArea class
9787  * @cfg {Number} cols Specifies the visible width of a text area
9788  * @cfg {Number} rows Specifies the visible number of lines in a text area
9789  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9790  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9791  * @cfg {string} html text
9792  * 
9793  * @constructor
9794  * Create a new TextArea
9795  * @param {Object} config The config object
9796  */
9797
9798 Roo.bootstrap.TextArea = function(config){
9799     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9800    
9801 };
9802
9803 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9804      
9805     cols : false,
9806     rows : 5,
9807     readOnly : false,
9808     warp : 'soft',
9809     resize : false,
9810     value: false,
9811     html: false,
9812     
9813     getAutoCreate : function(){
9814         
9815         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9816         
9817         var id = Roo.id();
9818         
9819         var cfg = {};
9820         
9821         if(this.inputType != 'hidden'){
9822             cfg.cls = 'form-group' //input-group
9823         }
9824         
9825         var input =  {
9826             tag: 'textarea',
9827             id : id,
9828             warp : this.warp,
9829             rows : this.rows,
9830             value : this.value || '',
9831             html: this.html || '',
9832             cls : 'form-control',
9833             placeholder : this.placeholder || '' 
9834             
9835         };
9836         
9837         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9838             input.maxLength = this.maxLength;
9839         }
9840         
9841         if(this.resize){
9842             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9843         }
9844         
9845         if(this.cols){
9846             input.cols = this.cols;
9847         }
9848         
9849         if (this.readOnly) {
9850             input.readonly = true;
9851         }
9852         
9853         if (this.name) {
9854             input.name = this.name;
9855         }
9856         
9857         if (this.size) {
9858             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9859         }
9860         
9861         var settings=this;
9862         ['xs','sm','md','lg'].map(function(size){
9863             if (settings[size]) {
9864                 cfg.cls += ' col-' + size + '-' + settings[size];
9865             }
9866         });
9867         
9868         var inputblock = input;
9869         
9870         if(this.hasFeedback && !this.allowBlank){
9871             
9872             var feedback = {
9873                 tag: 'span',
9874                 cls: 'glyphicon form-control-feedback'
9875             };
9876
9877             inputblock = {
9878                 cls : 'has-feedback',
9879                 cn :  [
9880                     input,
9881                     feedback
9882                 ] 
9883             };  
9884         }
9885         
9886         
9887         if (this.before || this.after) {
9888             
9889             inputblock = {
9890                 cls : 'input-group',
9891                 cn :  [] 
9892             };
9893             if (this.before) {
9894                 inputblock.cn.push({
9895                     tag :'span',
9896                     cls : 'input-group-addon',
9897                     html : this.before
9898                 });
9899             }
9900             
9901             inputblock.cn.push(input);
9902             
9903             if(this.hasFeedback && !this.allowBlank){
9904                 inputblock.cls += ' has-feedback';
9905                 inputblock.cn.push(feedback);
9906             }
9907             
9908             if (this.after) {
9909                 inputblock.cn.push({
9910                     tag :'span',
9911                     cls : 'input-group-addon',
9912                     html : this.after
9913                 });
9914             }
9915             
9916         }
9917         
9918         if (align ==='left' && this.fieldLabel.length) {
9919             cfg.cn = [
9920                 {
9921                     tag: 'label',
9922                     'for' :  id,
9923                     cls : 'control-label',
9924                     html : this.fieldLabel
9925                 },
9926                 {
9927                     cls : "",
9928                     cn: [
9929                         inputblock
9930                     ]
9931                 }
9932
9933             ];
9934             
9935             if(this.labelWidth > 12){
9936                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9937             }
9938
9939             if(this.labelWidth < 13 && this.labelmd == 0){
9940                 this.labelmd = this.labelWidth;
9941             }
9942
9943             if(this.labellg > 0){
9944                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9945                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9946             }
9947
9948             if(this.labelmd > 0){
9949                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9950                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9951             }
9952
9953             if(this.labelsm > 0){
9954                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9955                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9956             }
9957
9958             if(this.labelxs > 0){
9959                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9960                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9961             }
9962             
9963         } else if ( this.fieldLabel.length) {
9964             cfg.cn = [
9965
9966                {
9967                    tag: 'label',
9968                    //cls : 'input-group-addon',
9969                    html : this.fieldLabel
9970
9971                },
9972
9973                inputblock
9974
9975            ];
9976
9977         } else {
9978
9979             cfg.cn = [
9980
9981                 inputblock
9982
9983             ];
9984                 
9985         }
9986         
9987         if (this.disabled) {
9988             input.disabled=true;
9989         }
9990         
9991         return cfg;
9992         
9993     },
9994     /**
9995      * return the real textarea element.
9996      */
9997     inputEl: function ()
9998     {
9999         return this.el.select('textarea.form-control',true).first();
10000     },
10001     
10002     /**
10003      * Clear any invalid styles/messages for this field
10004      */
10005     clearInvalid : function()
10006     {
10007         
10008         if(!this.el || this.preventMark){ // not rendered
10009             return;
10010         }
10011         
10012         var label = this.el.select('label', true).first();
10013         var icon = this.el.select('i.fa-star', true).first();
10014         
10015         if(label && icon){
10016             icon.remove();
10017         }
10018         
10019         this.el.removeClass(this.invalidClass);
10020         
10021         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10022             
10023             var feedback = this.el.select('.form-control-feedback', true).first();
10024             
10025             if(feedback){
10026                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10027             }
10028             
10029         }
10030         
10031         this.fireEvent('valid', this);
10032     },
10033     
10034      /**
10035      * Mark this field as valid
10036      */
10037     markValid : function()
10038     {
10039         if(!this.el  || this.preventMark){ // not rendered
10040             return;
10041         }
10042         
10043         this.el.removeClass([this.invalidClass, this.validClass]);
10044         
10045         var feedback = this.el.select('.form-control-feedback', true).first();
10046             
10047         if(feedback){
10048             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10049         }
10050
10051         if(this.disabled || this.allowBlank){
10052             return;
10053         }
10054         
10055         var label = this.el.select('label', true).first();
10056         var icon = this.el.select('i.fa-star', true).first();
10057         
10058         if(label && icon){
10059             icon.remove();
10060         }
10061         
10062         this.el.addClass(this.validClass);
10063         
10064         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10065             
10066             var feedback = this.el.select('.form-control-feedback', true).first();
10067             
10068             if(feedback){
10069                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10070                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10071             }
10072             
10073         }
10074         
10075         this.fireEvent('valid', this);
10076     },
10077     
10078      /**
10079      * Mark this field as invalid
10080      * @param {String} msg The validation message
10081      */
10082     markInvalid : function(msg)
10083     {
10084         if(!this.el  || this.preventMark){ // not rendered
10085             return;
10086         }
10087         
10088         this.el.removeClass([this.invalidClass, this.validClass]);
10089         
10090         var feedback = this.el.select('.form-control-feedback', true).first();
10091             
10092         if(feedback){
10093             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10094         }
10095
10096         if(this.disabled || this.allowBlank){
10097             return;
10098         }
10099         
10100         var label = this.el.select('label', true).first();
10101         var icon = this.el.select('i.fa-star', true).first();
10102         
10103         if(!this.getValue().length && label && !icon){
10104             this.el.createChild({
10105                 tag : 'i',
10106                 cls : 'text-danger fa fa-lg fa-star',
10107                 tooltip : 'This field is required',
10108                 style : 'margin-right:5px;'
10109             }, label, true);
10110         }
10111
10112         this.el.addClass(this.invalidClass);
10113         
10114         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10115             
10116             var feedback = this.el.select('.form-control-feedback', true).first();
10117             
10118             if(feedback){
10119                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10120                 
10121                 if(this.getValue().length || this.forceFeedback){
10122                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10123                 }
10124                 
10125             }
10126             
10127         }
10128         
10129         this.fireEvent('invalid', this, msg);
10130     }
10131 });
10132
10133  
10134 /*
10135  * - LGPL
10136  *
10137  * trigger field - base class for combo..
10138  * 
10139  */
10140  
10141 /**
10142  * @class Roo.bootstrap.TriggerField
10143  * @extends Roo.bootstrap.Input
10144  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10145  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10146  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10147  * for which you can provide a custom implementation.  For example:
10148  * <pre><code>
10149 var trigger = new Roo.bootstrap.TriggerField();
10150 trigger.onTriggerClick = myTriggerFn;
10151 trigger.applyTo('my-field');
10152 </code></pre>
10153  *
10154  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10155  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10156  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10157  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10158  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10159
10160  * @constructor
10161  * Create a new TriggerField.
10162  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10163  * to the base TextField)
10164  */
10165 Roo.bootstrap.TriggerField = function(config){
10166     this.mimicing = false;
10167     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10168 };
10169
10170 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10171     /**
10172      * @cfg {String} triggerClass A CSS class to apply to the trigger
10173      */
10174      /**
10175      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10176      */
10177     hideTrigger:false,
10178
10179     /**
10180      * @cfg {Boolean} removable (true|false) special filter default false
10181      */
10182     removable : false,
10183     
10184     /** @cfg {Boolean} grow @hide */
10185     /** @cfg {Number} growMin @hide */
10186     /** @cfg {Number} growMax @hide */
10187
10188     /**
10189      * @hide 
10190      * @method
10191      */
10192     autoSize: Roo.emptyFn,
10193     // private
10194     monitorTab : true,
10195     // private
10196     deferHeight : true,
10197
10198     
10199     actionMode : 'wrap',
10200     
10201     caret : false,
10202     
10203     
10204     getAutoCreate : function(){
10205        
10206         var align = this.labelAlign || this.parentLabelAlign();
10207         
10208         var id = Roo.id();
10209         
10210         var cfg = {
10211             cls: 'form-group' //input-group
10212         };
10213         
10214         
10215         var input =  {
10216             tag: 'input',
10217             id : id,
10218             type : this.inputType,
10219             cls : 'form-control',
10220             autocomplete: 'new-password',
10221             placeholder : this.placeholder || '' 
10222             
10223         };
10224         if (this.name) {
10225             input.name = this.name;
10226         }
10227         if (this.size) {
10228             input.cls += ' input-' + this.size;
10229         }
10230         
10231         if (this.disabled) {
10232             input.disabled=true;
10233         }
10234         
10235         var inputblock = input;
10236         
10237         if(this.hasFeedback && !this.allowBlank){
10238             
10239             var feedback = {
10240                 tag: 'span',
10241                 cls: 'glyphicon form-control-feedback'
10242             };
10243             
10244             if(this.removable && !this.editable && !this.tickable){
10245                 inputblock = {
10246                     cls : 'has-feedback',
10247                     cn :  [
10248                         inputblock,
10249                         {
10250                             tag: 'button',
10251                             html : 'x',
10252                             cls : 'roo-combo-removable-btn close'
10253                         },
10254                         feedback
10255                     ] 
10256                 };
10257             } else {
10258                 inputblock = {
10259                     cls : 'has-feedback',
10260                     cn :  [
10261                         inputblock,
10262                         feedback
10263                     ] 
10264                 };
10265             }
10266
10267         } else {
10268             if(this.removable && !this.editable && !this.tickable){
10269                 inputblock = {
10270                     cls : 'roo-removable',
10271                     cn :  [
10272                         inputblock,
10273                         {
10274                             tag: 'button',
10275                             html : 'x',
10276                             cls : 'roo-combo-removable-btn close'
10277                         }
10278                     ] 
10279                 };
10280             }
10281         }
10282         
10283         if (this.before || this.after) {
10284             
10285             inputblock = {
10286                 cls : 'input-group',
10287                 cn :  [] 
10288             };
10289             if (this.before) {
10290                 inputblock.cn.push({
10291                     tag :'span',
10292                     cls : 'input-group-addon',
10293                     html : this.before
10294                 });
10295             }
10296             
10297             inputblock.cn.push(input);
10298             
10299             if(this.hasFeedback && !this.allowBlank){
10300                 inputblock.cls += ' has-feedback';
10301                 inputblock.cn.push(feedback);
10302             }
10303             
10304             if (this.after) {
10305                 inputblock.cn.push({
10306                     tag :'span',
10307                     cls : 'input-group-addon',
10308                     html : this.after
10309                 });
10310             }
10311             
10312         };
10313         
10314         var box = {
10315             tag: 'div',
10316             cn: [
10317                 {
10318                     tag: 'input',
10319                     type : 'hidden',
10320                     cls: 'form-hidden-field'
10321                 },
10322                 inputblock
10323             ]
10324             
10325         };
10326         
10327         if(this.multiple){
10328             box = {
10329                 tag: 'div',
10330                 cn: [
10331                     {
10332                         tag: 'input',
10333                         type : 'hidden',
10334                         cls: 'form-hidden-field'
10335                     },
10336                     {
10337                         tag: 'ul',
10338                         cls: 'roo-select2-choices',
10339                         cn:[
10340                             {
10341                                 tag: 'li',
10342                                 cls: 'roo-select2-search-field',
10343                                 cn: [
10344
10345                                     inputblock
10346                                 ]
10347                             }
10348                         ]
10349                     }
10350                 ]
10351             }
10352         };
10353         
10354         var combobox = {
10355             cls: 'roo-select2-container input-group',
10356             cn: [
10357                 box
10358 //                {
10359 //                    tag: 'ul',
10360 //                    cls: 'typeahead typeahead-long dropdown-menu',
10361 //                    style: 'display:none'
10362 //                }
10363             ]
10364         };
10365         
10366         if(!this.multiple && this.showToggleBtn){
10367             
10368             var caret = {
10369                         tag: 'span',
10370                         cls: 'caret'
10371              };
10372             if (this.caret != false) {
10373                 caret = {
10374                      tag: 'i',
10375                      cls: 'fa fa-' + this.caret
10376                 };
10377                 
10378             }
10379             
10380             combobox.cn.push({
10381                 tag :'span',
10382                 cls : 'input-group-addon btn dropdown-toggle',
10383                 cn : [
10384                     caret,
10385                     {
10386                         tag: 'span',
10387                         cls: 'combobox-clear',
10388                         cn  : [
10389                             {
10390                                 tag : 'i',
10391                                 cls: 'icon-remove'
10392                             }
10393                         ]
10394                     }
10395                 ]
10396
10397             })
10398         }
10399         
10400         if(this.multiple){
10401             combobox.cls += ' roo-select2-container-multi';
10402         }
10403         
10404         if (align ==='left' && this.fieldLabel.length) {
10405             
10406             cfg.cls += ' roo-form-group-label-left';
10407
10408             cfg.cn = [
10409                 {
10410                     tag : 'i',
10411                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10412                     tooltip : 'This field is required'
10413                 },
10414                 {
10415                     tag: 'label',
10416                     'for' :  id,
10417                     cls : 'control-label',
10418                     html : this.fieldLabel
10419
10420                 },
10421                 {
10422                     cls : "", 
10423                     cn: [
10424                         combobox
10425                     ]
10426                 }
10427
10428             ];
10429             
10430             var labelCfg = cfg.cn[1];
10431             var contentCfg = cfg.cn[2];
10432             
10433             if(this.indicatorpos == 'right'){
10434                 cfg.cn = [
10435                     {
10436                         tag: 'label',
10437                         'for' :  id,
10438                         cls : 'control-label',
10439                         cn : [
10440                             {
10441                                 tag : 'span',
10442                                 html : this.fieldLabel
10443                             },
10444                             {
10445                                 tag : 'i',
10446                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10447                                 tooltip : 'This field is required'
10448                             }
10449                         ]
10450                     },
10451                     {
10452                         cls : "", 
10453                         cn: [
10454                             combobox
10455                         ]
10456                     }
10457
10458                 ];
10459                 
10460                 labelCfg = cfg.cn[0];
10461                 contentCfg = cfg.cn[1];
10462             }
10463             
10464             if(this.labelWidth > 12){
10465                 labelCfg.style = "width: " + this.labelWidth + 'px';
10466             }
10467             
10468             if(this.labelWidth < 13 && this.labelmd == 0){
10469                 this.labelmd = this.labelWidth;
10470             }
10471             
10472             if(this.labellg > 0){
10473                 labelCfg.cls += ' col-lg-' + this.labellg;
10474                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10475             }
10476             
10477             if(this.labelmd > 0){
10478                 labelCfg.cls += ' col-md-' + this.labelmd;
10479                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10480             }
10481             
10482             if(this.labelsm > 0){
10483                 labelCfg.cls += ' col-sm-' + this.labelsm;
10484                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10485             }
10486             
10487             if(this.labelxs > 0){
10488                 labelCfg.cls += ' col-xs-' + this.labelxs;
10489                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10490             }
10491             
10492         } else if ( this.fieldLabel.length) {
10493 //                Roo.log(" label");
10494             cfg.cn = [
10495                 {
10496                    tag : 'i',
10497                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10498                    tooltip : 'This field is required'
10499                },
10500                {
10501                    tag: 'label',
10502                    //cls : 'input-group-addon',
10503                    html : this.fieldLabel
10504
10505                },
10506
10507                combobox
10508
10509             ];
10510             
10511             if(this.indicatorpos == 'right'){
10512                 
10513                 cfg.cn = [
10514                     {
10515                        tag: 'label',
10516                        cn : [
10517                            {
10518                                tag : 'span',
10519                                html : this.fieldLabel
10520                            },
10521                            {
10522                               tag : 'i',
10523                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10524                               tooltip : 'This field is required'
10525                            }
10526                        ]
10527
10528                     },
10529                     combobox
10530
10531                 ];
10532
10533             }
10534
10535         } else {
10536             
10537 //                Roo.log(" no label && no align");
10538                 cfg = combobox
10539                      
10540                 
10541         }
10542         
10543         var settings=this;
10544         ['xs','sm','md','lg'].map(function(size){
10545             if (settings[size]) {
10546                 cfg.cls += ' col-' + size + '-' + settings[size];
10547             }
10548         });
10549         
10550         return cfg;
10551         
10552     },
10553     
10554     
10555     
10556     // private
10557     onResize : function(w, h){
10558 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10559 //        if(typeof w == 'number'){
10560 //            var x = w - this.trigger.getWidth();
10561 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10562 //            this.trigger.setStyle('left', x+'px');
10563 //        }
10564     },
10565
10566     // private
10567     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10568
10569     // private
10570     getResizeEl : function(){
10571         return this.inputEl();
10572     },
10573
10574     // private
10575     getPositionEl : function(){
10576         return this.inputEl();
10577     },
10578
10579     // private
10580     alignErrorIcon : function(){
10581         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10582     },
10583
10584     // private
10585     initEvents : function(){
10586         
10587         this.createList();
10588         
10589         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10590         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10591         if(!this.multiple && this.showToggleBtn){
10592             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10593             if(this.hideTrigger){
10594                 this.trigger.setDisplayed(false);
10595             }
10596             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10597         }
10598         
10599         if(this.multiple){
10600             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10601         }
10602         
10603         if(this.removable && !this.editable && !this.tickable){
10604             var close = this.closeTriggerEl();
10605             
10606             if(close){
10607                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10608                 close.on('click', this.removeBtnClick, this, close);
10609             }
10610         }
10611         
10612         //this.trigger.addClassOnOver('x-form-trigger-over');
10613         //this.trigger.addClassOnClick('x-form-trigger-click');
10614         
10615         //if(!this.width){
10616         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10617         //}
10618     },
10619     
10620     closeTriggerEl : function()
10621     {
10622         var close = this.el.select('.roo-combo-removable-btn', true).first();
10623         return close ? close : false;
10624     },
10625     
10626     removeBtnClick : function(e, h, el)
10627     {
10628         e.preventDefault();
10629         
10630         if(this.fireEvent("remove", this) !== false){
10631             this.reset();
10632             this.fireEvent("afterremove", this)
10633         }
10634     },
10635     
10636     createList : function()
10637     {
10638         this.list = Roo.get(document.body).createChild({
10639             tag: 'ul',
10640             cls: 'typeahead typeahead-long dropdown-menu',
10641             style: 'display:none'
10642         });
10643         
10644         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10645         
10646     },
10647
10648     // private
10649     initTrigger : function(){
10650        
10651     },
10652
10653     // private
10654     onDestroy : function(){
10655         if(this.trigger){
10656             this.trigger.removeAllListeners();
10657           //  this.trigger.remove();
10658         }
10659         //if(this.wrap){
10660         //    this.wrap.remove();
10661         //}
10662         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10663     },
10664
10665     // private
10666     onFocus : function(){
10667         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10668         /*
10669         if(!this.mimicing){
10670             this.wrap.addClass('x-trigger-wrap-focus');
10671             this.mimicing = true;
10672             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10673             if(this.monitorTab){
10674                 this.el.on("keydown", this.checkTab, this);
10675             }
10676         }
10677         */
10678     },
10679
10680     // private
10681     checkTab : function(e){
10682         if(e.getKey() == e.TAB){
10683             this.triggerBlur();
10684         }
10685     },
10686
10687     // private
10688     onBlur : function(){
10689         // do nothing
10690     },
10691
10692     // private
10693     mimicBlur : function(e, t){
10694         /*
10695         if(!this.wrap.contains(t) && this.validateBlur()){
10696             this.triggerBlur();
10697         }
10698         */
10699     },
10700
10701     // private
10702     triggerBlur : function(){
10703         this.mimicing = false;
10704         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10705         if(this.monitorTab){
10706             this.el.un("keydown", this.checkTab, this);
10707         }
10708         //this.wrap.removeClass('x-trigger-wrap-focus');
10709         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10710     },
10711
10712     // private
10713     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10714     validateBlur : function(e, t){
10715         return true;
10716     },
10717
10718     // private
10719     onDisable : function(){
10720         this.inputEl().dom.disabled = true;
10721         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10722         //if(this.wrap){
10723         //    this.wrap.addClass('x-item-disabled');
10724         //}
10725     },
10726
10727     // private
10728     onEnable : function(){
10729         this.inputEl().dom.disabled = false;
10730         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10731         //if(this.wrap){
10732         //    this.el.removeClass('x-item-disabled');
10733         //}
10734     },
10735
10736     // private
10737     onShow : function(){
10738         var ae = this.getActionEl();
10739         
10740         if(ae){
10741             ae.dom.style.display = '';
10742             ae.dom.style.visibility = 'visible';
10743         }
10744     },
10745
10746     // private
10747     
10748     onHide : function(){
10749         var ae = this.getActionEl();
10750         ae.dom.style.display = 'none';
10751     },
10752
10753     /**
10754      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10755      * by an implementing function.
10756      * @method
10757      * @param {EventObject} e
10758      */
10759     onTriggerClick : Roo.emptyFn
10760 });
10761  /*
10762  * Based on:
10763  * Ext JS Library 1.1.1
10764  * Copyright(c) 2006-2007, Ext JS, LLC.
10765  *
10766  * Originally Released Under LGPL - original licence link has changed is not relivant.
10767  *
10768  * Fork - LGPL
10769  * <script type="text/javascript">
10770  */
10771
10772
10773 /**
10774  * @class Roo.data.SortTypes
10775  * @singleton
10776  * Defines the default sorting (casting?) comparison functions used when sorting data.
10777  */
10778 Roo.data.SortTypes = {
10779     /**
10780      * Default sort that does nothing
10781      * @param {Mixed} s The value being converted
10782      * @return {Mixed} The comparison value
10783      */
10784     none : function(s){
10785         return s;
10786     },
10787     
10788     /**
10789      * The regular expression used to strip tags
10790      * @type {RegExp}
10791      * @property
10792      */
10793     stripTagsRE : /<\/?[^>]+>/gi,
10794     
10795     /**
10796      * Strips all HTML tags to sort on text only
10797      * @param {Mixed} s The value being converted
10798      * @return {String} The comparison value
10799      */
10800     asText : function(s){
10801         return String(s).replace(this.stripTagsRE, "");
10802     },
10803     
10804     /**
10805      * Strips all HTML tags to sort on text only - Case insensitive
10806      * @param {Mixed} s The value being converted
10807      * @return {String} The comparison value
10808      */
10809     asUCText : function(s){
10810         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10811     },
10812     
10813     /**
10814      * Case insensitive string
10815      * @param {Mixed} s The value being converted
10816      * @return {String} The comparison value
10817      */
10818     asUCString : function(s) {
10819         return String(s).toUpperCase();
10820     },
10821     
10822     /**
10823      * Date sorting
10824      * @param {Mixed} s The value being converted
10825      * @return {Number} The comparison value
10826      */
10827     asDate : function(s) {
10828         if(!s){
10829             return 0;
10830         }
10831         if(s instanceof Date){
10832             return s.getTime();
10833         }
10834         return Date.parse(String(s));
10835     },
10836     
10837     /**
10838      * Float sorting
10839      * @param {Mixed} s The value being converted
10840      * @return {Float} The comparison value
10841      */
10842     asFloat : function(s) {
10843         var val = parseFloat(String(s).replace(/,/g, ""));
10844         if(isNaN(val)) {
10845             val = 0;
10846         }
10847         return val;
10848     },
10849     
10850     /**
10851      * Integer sorting
10852      * @param {Mixed} s The value being converted
10853      * @return {Number} The comparison value
10854      */
10855     asInt : function(s) {
10856         var val = parseInt(String(s).replace(/,/g, ""));
10857         if(isNaN(val)) {
10858             val = 0;
10859         }
10860         return val;
10861     }
10862 };/*
10863  * Based on:
10864  * Ext JS Library 1.1.1
10865  * Copyright(c) 2006-2007, Ext JS, LLC.
10866  *
10867  * Originally Released Under LGPL - original licence link has changed is not relivant.
10868  *
10869  * Fork - LGPL
10870  * <script type="text/javascript">
10871  */
10872
10873 /**
10874 * @class Roo.data.Record
10875  * Instances of this class encapsulate both record <em>definition</em> information, and record
10876  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10877  * to access Records cached in an {@link Roo.data.Store} object.<br>
10878  * <p>
10879  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10880  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10881  * objects.<br>
10882  * <p>
10883  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10884  * @constructor
10885  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10886  * {@link #create}. The parameters are the same.
10887  * @param {Array} data An associative Array of data values keyed by the field name.
10888  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10889  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10890  * not specified an integer id is generated.
10891  */
10892 Roo.data.Record = function(data, id){
10893     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10894     this.data = data;
10895 };
10896
10897 /**
10898  * Generate a constructor for a specific record layout.
10899  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10900  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10901  * Each field definition object may contain the following properties: <ul>
10902  * <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,
10903  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10904  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10905  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10906  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10907  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10908  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10909  * this may be omitted.</p></li>
10910  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10911  * <ul><li>auto (Default, implies no conversion)</li>
10912  * <li>string</li>
10913  * <li>int</li>
10914  * <li>float</li>
10915  * <li>boolean</li>
10916  * <li>date</li></ul></p></li>
10917  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10918  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10919  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10920  * by the Reader into an object that will be stored in the Record. It is passed the
10921  * following parameters:<ul>
10922  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10923  * </ul></p></li>
10924  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10925  * </ul>
10926  * <br>usage:<br><pre><code>
10927 var TopicRecord = Roo.data.Record.create(
10928     {name: 'title', mapping: 'topic_title'},
10929     {name: 'author', mapping: 'username'},
10930     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10931     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10932     {name: 'lastPoster', mapping: 'user2'},
10933     {name: 'excerpt', mapping: 'post_text'}
10934 );
10935
10936 var myNewRecord = new TopicRecord({
10937     title: 'Do my job please',
10938     author: 'noobie',
10939     totalPosts: 1,
10940     lastPost: new Date(),
10941     lastPoster: 'Animal',
10942     excerpt: 'No way dude!'
10943 });
10944 myStore.add(myNewRecord);
10945 </code></pre>
10946  * @method create
10947  * @static
10948  */
10949 Roo.data.Record.create = function(o){
10950     var f = function(){
10951         f.superclass.constructor.apply(this, arguments);
10952     };
10953     Roo.extend(f, Roo.data.Record);
10954     var p = f.prototype;
10955     p.fields = new Roo.util.MixedCollection(false, function(field){
10956         return field.name;
10957     });
10958     for(var i = 0, len = o.length; i < len; i++){
10959         p.fields.add(new Roo.data.Field(o[i]));
10960     }
10961     f.getField = function(name){
10962         return p.fields.get(name);  
10963     };
10964     return f;
10965 };
10966
10967 Roo.data.Record.AUTO_ID = 1000;
10968 Roo.data.Record.EDIT = 'edit';
10969 Roo.data.Record.REJECT = 'reject';
10970 Roo.data.Record.COMMIT = 'commit';
10971
10972 Roo.data.Record.prototype = {
10973     /**
10974      * Readonly flag - true if this record has been modified.
10975      * @type Boolean
10976      */
10977     dirty : false,
10978     editing : false,
10979     error: null,
10980     modified: null,
10981
10982     // private
10983     join : function(store){
10984         this.store = store;
10985     },
10986
10987     /**
10988      * Set the named field to the specified value.
10989      * @param {String} name The name of the field to set.
10990      * @param {Object} value The value to set the field to.
10991      */
10992     set : function(name, value){
10993         if(this.data[name] == value){
10994             return;
10995         }
10996         this.dirty = true;
10997         if(!this.modified){
10998             this.modified = {};
10999         }
11000         if(typeof this.modified[name] == 'undefined'){
11001             this.modified[name] = this.data[name];
11002         }
11003         this.data[name] = value;
11004         if(!this.editing && this.store){
11005             this.store.afterEdit(this);
11006         }       
11007     },
11008
11009     /**
11010      * Get the value of the named field.
11011      * @param {String} name The name of the field to get the value of.
11012      * @return {Object} The value of the field.
11013      */
11014     get : function(name){
11015         return this.data[name]; 
11016     },
11017
11018     // private
11019     beginEdit : function(){
11020         this.editing = true;
11021         this.modified = {}; 
11022     },
11023
11024     // private
11025     cancelEdit : function(){
11026         this.editing = false;
11027         delete this.modified;
11028     },
11029
11030     // private
11031     endEdit : function(){
11032         this.editing = false;
11033         if(this.dirty && this.store){
11034             this.store.afterEdit(this);
11035         }
11036     },
11037
11038     /**
11039      * Usually called by the {@link Roo.data.Store} which owns the Record.
11040      * Rejects all changes made to the Record since either creation, or the last commit operation.
11041      * Modified fields are reverted to their original values.
11042      * <p>
11043      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11044      * of reject operations.
11045      */
11046     reject : function(){
11047         var m = this.modified;
11048         for(var n in m){
11049             if(typeof m[n] != "function"){
11050                 this.data[n] = m[n];
11051             }
11052         }
11053         this.dirty = false;
11054         delete this.modified;
11055         this.editing = false;
11056         if(this.store){
11057             this.store.afterReject(this);
11058         }
11059     },
11060
11061     /**
11062      * Usually called by the {@link Roo.data.Store} which owns the Record.
11063      * Commits all changes made to the Record since either creation, or the last commit operation.
11064      * <p>
11065      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11066      * of commit operations.
11067      */
11068     commit : function(){
11069         this.dirty = false;
11070         delete this.modified;
11071         this.editing = false;
11072         if(this.store){
11073             this.store.afterCommit(this);
11074         }
11075     },
11076
11077     // private
11078     hasError : function(){
11079         return this.error != null;
11080     },
11081
11082     // private
11083     clearError : function(){
11084         this.error = null;
11085     },
11086
11087     /**
11088      * Creates a copy of this record.
11089      * @param {String} id (optional) A new record id if you don't want to use this record's id
11090      * @return {Record}
11091      */
11092     copy : function(newId) {
11093         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11094     }
11095 };/*
11096  * Based on:
11097  * Ext JS Library 1.1.1
11098  * Copyright(c) 2006-2007, Ext JS, LLC.
11099  *
11100  * Originally Released Under LGPL - original licence link has changed is not relivant.
11101  *
11102  * Fork - LGPL
11103  * <script type="text/javascript">
11104  */
11105
11106
11107
11108 /**
11109  * @class Roo.data.Store
11110  * @extends Roo.util.Observable
11111  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11112  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11113  * <p>
11114  * 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
11115  * has no knowledge of the format of the data returned by the Proxy.<br>
11116  * <p>
11117  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11118  * instances from the data object. These records are cached and made available through accessor functions.
11119  * @constructor
11120  * Creates a new Store.
11121  * @param {Object} config A config object containing the objects needed for the Store to access data,
11122  * and read the data into Records.
11123  */
11124 Roo.data.Store = function(config){
11125     this.data = new Roo.util.MixedCollection(false);
11126     this.data.getKey = function(o){
11127         return o.id;
11128     };
11129     this.baseParams = {};
11130     // private
11131     this.paramNames = {
11132         "start" : "start",
11133         "limit" : "limit",
11134         "sort" : "sort",
11135         "dir" : "dir",
11136         "multisort" : "_multisort"
11137     };
11138
11139     if(config && config.data){
11140         this.inlineData = config.data;
11141         delete config.data;
11142     }
11143
11144     Roo.apply(this, config);
11145     
11146     if(this.reader){ // reader passed
11147         this.reader = Roo.factory(this.reader, Roo.data);
11148         this.reader.xmodule = this.xmodule || false;
11149         if(!this.recordType){
11150             this.recordType = this.reader.recordType;
11151         }
11152         if(this.reader.onMetaChange){
11153             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11154         }
11155     }
11156
11157     if(this.recordType){
11158         this.fields = this.recordType.prototype.fields;
11159     }
11160     this.modified = [];
11161
11162     this.addEvents({
11163         /**
11164          * @event datachanged
11165          * Fires when the data cache has changed, and a widget which is using this Store
11166          * as a Record cache should refresh its view.
11167          * @param {Store} this
11168          */
11169         datachanged : true,
11170         /**
11171          * @event metachange
11172          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11173          * @param {Store} this
11174          * @param {Object} meta The JSON metadata
11175          */
11176         metachange : true,
11177         /**
11178          * @event add
11179          * Fires when Records have been added to the Store
11180          * @param {Store} this
11181          * @param {Roo.data.Record[]} records The array of Records added
11182          * @param {Number} index The index at which the record(s) were added
11183          */
11184         add : true,
11185         /**
11186          * @event remove
11187          * Fires when a Record has been removed from the Store
11188          * @param {Store} this
11189          * @param {Roo.data.Record} record The Record that was removed
11190          * @param {Number} index The index at which the record was removed
11191          */
11192         remove : true,
11193         /**
11194          * @event update
11195          * Fires when a Record has been updated
11196          * @param {Store} this
11197          * @param {Roo.data.Record} record The Record that was updated
11198          * @param {String} operation The update operation being performed.  Value may be one of:
11199          * <pre><code>
11200  Roo.data.Record.EDIT
11201  Roo.data.Record.REJECT
11202  Roo.data.Record.COMMIT
11203          * </code></pre>
11204          */
11205         update : true,
11206         /**
11207          * @event clear
11208          * Fires when the data cache has been cleared.
11209          * @param {Store} this
11210          */
11211         clear : true,
11212         /**
11213          * @event beforeload
11214          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11215          * the load action will be canceled.
11216          * @param {Store} this
11217          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11218          */
11219         beforeload : true,
11220         /**
11221          * @event beforeloadadd
11222          * Fires after a new set of Records has been loaded.
11223          * @param {Store} this
11224          * @param {Roo.data.Record[]} records The Records that were loaded
11225          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11226          */
11227         beforeloadadd : true,
11228         /**
11229          * @event load
11230          * Fires after a new set of Records has been loaded, before they are added to the store.
11231          * @param {Store} this
11232          * @param {Roo.data.Record[]} records The Records that were loaded
11233          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11234          * @params {Object} return from reader
11235          */
11236         load : true,
11237         /**
11238          * @event loadexception
11239          * Fires if an exception occurs in the Proxy during loading.
11240          * Called with the signature of the Proxy's "loadexception" event.
11241          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11242          * 
11243          * @param {Proxy} 
11244          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11245          * @param {Object} load options 
11246          * @param {Object} jsonData from your request (normally this contains the Exception)
11247          */
11248         loadexception : true
11249     });
11250     
11251     if(this.proxy){
11252         this.proxy = Roo.factory(this.proxy, Roo.data);
11253         this.proxy.xmodule = this.xmodule || false;
11254         this.relayEvents(this.proxy,  ["loadexception"]);
11255     }
11256     this.sortToggle = {};
11257     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11258
11259     Roo.data.Store.superclass.constructor.call(this);
11260
11261     if(this.inlineData){
11262         this.loadData(this.inlineData);
11263         delete this.inlineData;
11264     }
11265 };
11266
11267 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11268      /**
11269     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11270     * without a remote query - used by combo/forms at present.
11271     */
11272     
11273     /**
11274     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11275     */
11276     /**
11277     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11278     */
11279     /**
11280     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11281     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11282     */
11283     /**
11284     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11285     * on any HTTP request
11286     */
11287     /**
11288     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11289     */
11290     /**
11291     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11292     */
11293     multiSort: false,
11294     /**
11295     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11296     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11297     */
11298     remoteSort : false,
11299
11300     /**
11301     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11302      * loaded or when a record is removed. (defaults to false).
11303     */
11304     pruneModifiedRecords : false,
11305
11306     // private
11307     lastOptions : null,
11308
11309     /**
11310      * Add Records to the Store and fires the add event.
11311      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11312      */
11313     add : function(records){
11314         records = [].concat(records);
11315         for(var i = 0, len = records.length; i < len; i++){
11316             records[i].join(this);
11317         }
11318         var index = this.data.length;
11319         this.data.addAll(records);
11320         this.fireEvent("add", this, records, index);
11321     },
11322
11323     /**
11324      * Remove a Record from the Store and fires the remove event.
11325      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11326      */
11327     remove : function(record){
11328         var index = this.data.indexOf(record);
11329         this.data.removeAt(index);
11330  
11331         if(this.pruneModifiedRecords){
11332             this.modified.remove(record);
11333         }
11334         this.fireEvent("remove", this, record, index);
11335     },
11336
11337     /**
11338      * Remove all Records from the Store and fires the clear event.
11339      */
11340     removeAll : function(){
11341         this.data.clear();
11342         if(this.pruneModifiedRecords){
11343             this.modified = [];
11344         }
11345         this.fireEvent("clear", this);
11346     },
11347
11348     /**
11349      * Inserts Records to the Store at the given index and fires the add event.
11350      * @param {Number} index The start index at which to insert the passed Records.
11351      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11352      */
11353     insert : function(index, records){
11354         records = [].concat(records);
11355         for(var i = 0, len = records.length; i < len; i++){
11356             this.data.insert(index, records[i]);
11357             records[i].join(this);
11358         }
11359         this.fireEvent("add", this, records, index);
11360     },
11361
11362     /**
11363      * Get the index within the cache of the passed Record.
11364      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11365      * @return {Number} The index of the passed Record. Returns -1 if not found.
11366      */
11367     indexOf : function(record){
11368         return this.data.indexOf(record);
11369     },
11370
11371     /**
11372      * Get the index within the cache of the Record with the passed id.
11373      * @param {String} id The id of the Record to find.
11374      * @return {Number} The index of the Record. Returns -1 if not found.
11375      */
11376     indexOfId : function(id){
11377         return this.data.indexOfKey(id);
11378     },
11379
11380     /**
11381      * Get the Record with the specified id.
11382      * @param {String} id The id of the Record to find.
11383      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11384      */
11385     getById : function(id){
11386         return this.data.key(id);
11387     },
11388
11389     /**
11390      * Get the Record at the specified index.
11391      * @param {Number} index The index of the Record to find.
11392      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11393      */
11394     getAt : function(index){
11395         return this.data.itemAt(index);
11396     },
11397
11398     /**
11399      * Returns a range of Records between specified indices.
11400      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11401      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11402      * @return {Roo.data.Record[]} An array of Records
11403      */
11404     getRange : function(start, end){
11405         return this.data.getRange(start, end);
11406     },
11407
11408     // private
11409     storeOptions : function(o){
11410         o = Roo.apply({}, o);
11411         delete o.callback;
11412         delete o.scope;
11413         this.lastOptions = o;
11414     },
11415
11416     /**
11417      * Loads the Record cache from the configured Proxy using the configured Reader.
11418      * <p>
11419      * If using remote paging, then the first load call must specify the <em>start</em>
11420      * and <em>limit</em> properties in the options.params property to establish the initial
11421      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11422      * <p>
11423      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11424      * and this call will return before the new data has been loaded. Perform any post-processing
11425      * in a callback function, or in a "load" event handler.</strong>
11426      * <p>
11427      * @param {Object} options An object containing properties which control loading options:<ul>
11428      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11429      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11430      * passed the following arguments:<ul>
11431      * <li>r : Roo.data.Record[]</li>
11432      * <li>options: Options object from the load call</li>
11433      * <li>success: Boolean success indicator</li></ul></li>
11434      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11435      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11436      * </ul>
11437      */
11438     load : function(options){
11439         options = options || {};
11440         if(this.fireEvent("beforeload", this, options) !== false){
11441             this.storeOptions(options);
11442             var p = Roo.apply(options.params || {}, this.baseParams);
11443             // if meta was not loaded from remote source.. try requesting it.
11444             if (!this.reader.metaFromRemote) {
11445                 p._requestMeta = 1;
11446             }
11447             if(this.sortInfo && this.remoteSort){
11448                 var pn = this.paramNames;
11449                 p[pn["sort"]] = this.sortInfo.field;
11450                 p[pn["dir"]] = this.sortInfo.direction;
11451             }
11452             if (this.multiSort) {
11453                 var pn = this.paramNames;
11454                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11455             }
11456             
11457             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11458         }
11459     },
11460
11461     /**
11462      * Reloads the Record cache from the configured Proxy using the configured Reader and
11463      * the options from the last load operation performed.
11464      * @param {Object} options (optional) An object containing properties which may override the options
11465      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11466      * the most recently used options are reused).
11467      */
11468     reload : function(options){
11469         this.load(Roo.applyIf(options||{}, this.lastOptions));
11470     },
11471
11472     // private
11473     // Called as a callback by the Reader during a load operation.
11474     loadRecords : function(o, options, success){
11475         if(!o || success === false){
11476             if(success !== false){
11477                 this.fireEvent("load", this, [], options, o);
11478             }
11479             if(options.callback){
11480                 options.callback.call(options.scope || this, [], options, false);
11481             }
11482             return;
11483         }
11484         // if data returned failure - throw an exception.
11485         if (o.success === false) {
11486             // show a message if no listener is registered.
11487             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11488                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11489             }
11490             // loadmask wil be hooked into this..
11491             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11492             return;
11493         }
11494         var r = o.records, t = o.totalRecords || r.length;
11495         
11496         this.fireEvent("beforeloadadd", this, r, options, o);
11497         
11498         if(!options || options.add !== true){
11499             if(this.pruneModifiedRecords){
11500                 this.modified = [];
11501             }
11502             for(var i = 0, len = r.length; i < len; i++){
11503                 r[i].join(this);
11504             }
11505             if(this.snapshot){
11506                 this.data = this.snapshot;
11507                 delete this.snapshot;
11508             }
11509             this.data.clear();
11510             this.data.addAll(r);
11511             this.totalLength = t;
11512             this.applySort();
11513             this.fireEvent("datachanged", this);
11514         }else{
11515             this.totalLength = Math.max(t, this.data.length+r.length);
11516             this.add(r);
11517         }
11518         
11519         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11520                 
11521             var e = new Roo.data.Record({});
11522
11523             e.set(this.parent.displayField, this.parent.emptyTitle);
11524             e.set(this.parent.valueField, '');
11525
11526             this.insert(0, e);
11527         }
11528             
11529         this.fireEvent("load", this, r, options, o);
11530         if(options.callback){
11531             options.callback.call(options.scope || this, r, options, true);
11532         }
11533     },
11534
11535
11536     /**
11537      * Loads data from a passed data block. A Reader which understands the format of the data
11538      * must have been configured in the constructor.
11539      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11540      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11541      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11542      */
11543     loadData : function(o, append){
11544         var r = this.reader.readRecords(o);
11545         this.loadRecords(r, {add: append}, true);
11546     },
11547
11548     /**
11549      * Gets the number of cached records.
11550      * <p>
11551      * <em>If using paging, this may not be the total size of the dataset. If the data object
11552      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11553      * the data set size</em>
11554      */
11555     getCount : function(){
11556         return this.data.length || 0;
11557     },
11558
11559     /**
11560      * Gets the total number of records in the dataset as returned by the server.
11561      * <p>
11562      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11563      * the dataset size</em>
11564      */
11565     getTotalCount : function(){
11566         return this.totalLength || 0;
11567     },
11568
11569     /**
11570      * Returns the sort state of the Store as an object with two properties:
11571      * <pre><code>
11572  field {String} The name of the field by which the Records are sorted
11573  direction {String} The sort order, "ASC" or "DESC"
11574      * </code></pre>
11575      */
11576     getSortState : function(){
11577         return this.sortInfo;
11578     },
11579
11580     // private
11581     applySort : function(){
11582         if(this.sortInfo && !this.remoteSort){
11583             var s = this.sortInfo, f = s.field;
11584             var st = this.fields.get(f).sortType;
11585             var fn = function(r1, r2){
11586                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11587                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11588             };
11589             this.data.sort(s.direction, fn);
11590             if(this.snapshot && this.snapshot != this.data){
11591                 this.snapshot.sort(s.direction, fn);
11592             }
11593         }
11594     },
11595
11596     /**
11597      * Sets the default sort column and order to be used by the next load operation.
11598      * @param {String} fieldName The name of the field to sort by.
11599      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11600      */
11601     setDefaultSort : function(field, dir){
11602         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11603     },
11604
11605     /**
11606      * Sort the Records.
11607      * If remote sorting is used, the sort is performed on the server, and the cache is
11608      * reloaded. If local sorting is used, the cache is sorted internally.
11609      * @param {String} fieldName The name of the field to sort by.
11610      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11611      */
11612     sort : function(fieldName, dir){
11613         var f = this.fields.get(fieldName);
11614         if(!dir){
11615             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11616             
11617             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11618                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11619             }else{
11620                 dir = f.sortDir;
11621             }
11622         }
11623         this.sortToggle[f.name] = dir;
11624         this.sortInfo = {field: f.name, direction: dir};
11625         if(!this.remoteSort){
11626             this.applySort();
11627             this.fireEvent("datachanged", this);
11628         }else{
11629             this.load(this.lastOptions);
11630         }
11631     },
11632
11633     /**
11634      * Calls the specified function for each of the Records in the cache.
11635      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11636      * Returning <em>false</em> aborts and exits the iteration.
11637      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11638      */
11639     each : function(fn, scope){
11640         this.data.each(fn, scope);
11641     },
11642
11643     /**
11644      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11645      * (e.g., during paging).
11646      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11647      */
11648     getModifiedRecords : function(){
11649         return this.modified;
11650     },
11651
11652     // private
11653     createFilterFn : function(property, value, anyMatch){
11654         if(!value.exec){ // not a regex
11655             value = String(value);
11656             if(value.length == 0){
11657                 return false;
11658             }
11659             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11660         }
11661         return function(r){
11662             return value.test(r.data[property]);
11663         };
11664     },
11665
11666     /**
11667      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11668      * @param {String} property A field on your records
11669      * @param {Number} start The record index to start at (defaults to 0)
11670      * @param {Number} end The last record index to include (defaults to length - 1)
11671      * @return {Number} The sum
11672      */
11673     sum : function(property, start, end){
11674         var rs = this.data.items, v = 0;
11675         start = start || 0;
11676         end = (end || end === 0) ? end : rs.length-1;
11677
11678         for(var i = start; i <= end; i++){
11679             v += (rs[i].data[property] || 0);
11680         }
11681         return v;
11682     },
11683
11684     /**
11685      * Filter the records by a specified property.
11686      * @param {String} field A field on your records
11687      * @param {String/RegExp} value Either a string that the field
11688      * should start with or a RegExp to test against the field
11689      * @param {Boolean} anyMatch True to match any part not just the beginning
11690      */
11691     filter : function(property, value, anyMatch){
11692         var fn = this.createFilterFn(property, value, anyMatch);
11693         return fn ? this.filterBy(fn) : this.clearFilter();
11694     },
11695
11696     /**
11697      * Filter by a function. The specified function will be called with each
11698      * record in this data source. If the function returns true the record is included,
11699      * otherwise it is filtered.
11700      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11701      * @param {Object} scope (optional) The scope of the function (defaults to this)
11702      */
11703     filterBy : function(fn, scope){
11704         this.snapshot = this.snapshot || this.data;
11705         this.data = this.queryBy(fn, scope||this);
11706         this.fireEvent("datachanged", this);
11707     },
11708
11709     /**
11710      * Query the records by a specified property.
11711      * @param {String} field A field on your records
11712      * @param {String/RegExp} value Either a string that the field
11713      * should start with or a RegExp to test against the field
11714      * @param {Boolean} anyMatch True to match any part not just the beginning
11715      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11716      */
11717     query : function(property, value, anyMatch){
11718         var fn = this.createFilterFn(property, value, anyMatch);
11719         return fn ? this.queryBy(fn) : this.data.clone();
11720     },
11721
11722     /**
11723      * Query by a function. The specified function will be called with each
11724      * record in this data source. If the function returns true the record is included
11725      * in the results.
11726      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11727      * @param {Object} scope (optional) The scope of the function (defaults to this)
11728       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11729      **/
11730     queryBy : function(fn, scope){
11731         var data = this.snapshot || this.data;
11732         return data.filterBy(fn, scope||this);
11733     },
11734
11735     /**
11736      * Collects unique values for a particular dataIndex from this store.
11737      * @param {String} dataIndex The property to collect
11738      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11739      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11740      * @return {Array} An array of the unique values
11741      **/
11742     collect : function(dataIndex, allowNull, bypassFilter){
11743         var d = (bypassFilter === true && this.snapshot) ?
11744                 this.snapshot.items : this.data.items;
11745         var v, sv, r = [], l = {};
11746         for(var i = 0, len = d.length; i < len; i++){
11747             v = d[i].data[dataIndex];
11748             sv = String(v);
11749             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11750                 l[sv] = true;
11751                 r[r.length] = v;
11752             }
11753         }
11754         return r;
11755     },
11756
11757     /**
11758      * Revert to a view of the Record cache with no filtering applied.
11759      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11760      */
11761     clearFilter : function(suppressEvent){
11762         if(this.snapshot && this.snapshot != this.data){
11763             this.data = this.snapshot;
11764             delete this.snapshot;
11765             if(suppressEvent !== true){
11766                 this.fireEvent("datachanged", this);
11767             }
11768         }
11769     },
11770
11771     // private
11772     afterEdit : function(record){
11773         if(this.modified.indexOf(record) == -1){
11774             this.modified.push(record);
11775         }
11776         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11777     },
11778     
11779     // private
11780     afterReject : function(record){
11781         this.modified.remove(record);
11782         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11783     },
11784
11785     // private
11786     afterCommit : function(record){
11787         this.modified.remove(record);
11788         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11789     },
11790
11791     /**
11792      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11793      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11794      */
11795     commitChanges : function(){
11796         var m = this.modified.slice(0);
11797         this.modified = [];
11798         for(var i = 0, len = m.length; i < len; i++){
11799             m[i].commit();
11800         }
11801     },
11802
11803     /**
11804      * Cancel outstanding changes on all changed records.
11805      */
11806     rejectChanges : function(){
11807         var m = this.modified.slice(0);
11808         this.modified = [];
11809         for(var i = 0, len = m.length; i < len; i++){
11810             m[i].reject();
11811         }
11812     },
11813
11814     onMetaChange : function(meta, rtype, o){
11815         this.recordType = rtype;
11816         this.fields = rtype.prototype.fields;
11817         delete this.snapshot;
11818         this.sortInfo = meta.sortInfo || this.sortInfo;
11819         this.modified = [];
11820         this.fireEvent('metachange', this, this.reader.meta);
11821     },
11822     
11823     moveIndex : function(data, type)
11824     {
11825         var index = this.indexOf(data);
11826         
11827         var newIndex = index + type;
11828         
11829         this.remove(data);
11830         
11831         this.insert(newIndex, data);
11832         
11833     }
11834 });/*
11835  * Based on:
11836  * Ext JS Library 1.1.1
11837  * Copyright(c) 2006-2007, Ext JS, LLC.
11838  *
11839  * Originally Released Under LGPL - original licence link has changed is not relivant.
11840  *
11841  * Fork - LGPL
11842  * <script type="text/javascript">
11843  */
11844
11845 /**
11846  * @class Roo.data.SimpleStore
11847  * @extends Roo.data.Store
11848  * Small helper class to make creating Stores from Array data easier.
11849  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11850  * @cfg {Array} fields An array of field definition objects, or field name strings.
11851  * @cfg {Array} data The multi-dimensional array of data
11852  * @constructor
11853  * @param {Object} config
11854  */
11855 Roo.data.SimpleStore = function(config){
11856     Roo.data.SimpleStore.superclass.constructor.call(this, {
11857         isLocal : true,
11858         reader: new Roo.data.ArrayReader({
11859                 id: config.id
11860             },
11861             Roo.data.Record.create(config.fields)
11862         ),
11863         proxy : new Roo.data.MemoryProxy(config.data)
11864     });
11865     this.load();
11866 };
11867 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11868  * Based on:
11869  * Ext JS Library 1.1.1
11870  * Copyright(c) 2006-2007, Ext JS, LLC.
11871  *
11872  * Originally Released Under LGPL - original licence link has changed is not relivant.
11873  *
11874  * Fork - LGPL
11875  * <script type="text/javascript">
11876  */
11877
11878 /**
11879 /**
11880  * @extends Roo.data.Store
11881  * @class Roo.data.JsonStore
11882  * Small helper class to make creating Stores for JSON data easier. <br/>
11883 <pre><code>
11884 var store = new Roo.data.JsonStore({
11885     url: 'get-images.php',
11886     root: 'images',
11887     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11888 });
11889 </code></pre>
11890  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11891  * JsonReader and HttpProxy (unless inline data is provided).</b>
11892  * @cfg {Array} fields An array of field definition objects, or field name strings.
11893  * @constructor
11894  * @param {Object} config
11895  */
11896 Roo.data.JsonStore = function(c){
11897     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11898         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11899         reader: new Roo.data.JsonReader(c, c.fields)
11900     }));
11901 };
11902 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11903  * Based on:
11904  * Ext JS Library 1.1.1
11905  * Copyright(c) 2006-2007, Ext JS, LLC.
11906  *
11907  * Originally Released Under LGPL - original licence link has changed is not relivant.
11908  *
11909  * Fork - LGPL
11910  * <script type="text/javascript">
11911  */
11912
11913  
11914 Roo.data.Field = function(config){
11915     if(typeof config == "string"){
11916         config = {name: config};
11917     }
11918     Roo.apply(this, config);
11919     
11920     if(!this.type){
11921         this.type = "auto";
11922     }
11923     
11924     var st = Roo.data.SortTypes;
11925     // named sortTypes are supported, here we look them up
11926     if(typeof this.sortType == "string"){
11927         this.sortType = st[this.sortType];
11928     }
11929     
11930     // set default sortType for strings and dates
11931     if(!this.sortType){
11932         switch(this.type){
11933             case "string":
11934                 this.sortType = st.asUCString;
11935                 break;
11936             case "date":
11937                 this.sortType = st.asDate;
11938                 break;
11939             default:
11940                 this.sortType = st.none;
11941         }
11942     }
11943
11944     // define once
11945     var stripRe = /[\$,%]/g;
11946
11947     // prebuilt conversion function for this field, instead of
11948     // switching every time we're reading a value
11949     if(!this.convert){
11950         var cv, dateFormat = this.dateFormat;
11951         switch(this.type){
11952             case "":
11953             case "auto":
11954             case undefined:
11955                 cv = function(v){ return v; };
11956                 break;
11957             case "string":
11958                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11959                 break;
11960             case "int":
11961                 cv = function(v){
11962                     return v !== undefined && v !== null && v !== '' ?
11963                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11964                     };
11965                 break;
11966             case "float":
11967                 cv = function(v){
11968                     return v !== undefined && v !== null && v !== '' ?
11969                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11970                     };
11971                 break;
11972             case "bool":
11973             case "boolean":
11974                 cv = function(v){ return v === true || v === "true" || v == 1; };
11975                 break;
11976             case "date":
11977                 cv = function(v){
11978                     if(!v){
11979                         return '';
11980                     }
11981                     if(v instanceof Date){
11982                         return v;
11983                     }
11984                     if(dateFormat){
11985                         if(dateFormat == "timestamp"){
11986                             return new Date(v*1000);
11987                         }
11988                         return Date.parseDate(v, dateFormat);
11989                     }
11990                     var parsed = Date.parse(v);
11991                     return parsed ? new Date(parsed) : null;
11992                 };
11993              break;
11994             
11995         }
11996         this.convert = cv;
11997     }
11998 };
11999
12000 Roo.data.Field.prototype = {
12001     dateFormat: null,
12002     defaultValue: "",
12003     mapping: null,
12004     sortType : null,
12005     sortDir : "ASC"
12006 };/*
12007  * Based on:
12008  * Ext JS Library 1.1.1
12009  * Copyright(c) 2006-2007, Ext JS, LLC.
12010  *
12011  * Originally Released Under LGPL - original licence link has changed is not relivant.
12012  *
12013  * Fork - LGPL
12014  * <script type="text/javascript">
12015  */
12016  
12017 // Base class for reading structured data from a data source.  This class is intended to be
12018 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12019
12020 /**
12021  * @class Roo.data.DataReader
12022  * Base class for reading structured data from a data source.  This class is intended to be
12023  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12024  */
12025
12026 Roo.data.DataReader = function(meta, recordType){
12027     
12028     this.meta = meta;
12029     
12030     this.recordType = recordType instanceof Array ? 
12031         Roo.data.Record.create(recordType) : recordType;
12032 };
12033
12034 Roo.data.DataReader.prototype = {
12035      /**
12036      * Create an empty record
12037      * @param {Object} data (optional) - overlay some values
12038      * @return {Roo.data.Record} record created.
12039      */
12040     newRow :  function(d) {
12041         var da =  {};
12042         this.recordType.prototype.fields.each(function(c) {
12043             switch( c.type) {
12044                 case 'int' : da[c.name] = 0; break;
12045                 case 'date' : da[c.name] = new Date(); break;
12046                 case 'float' : da[c.name] = 0.0; break;
12047                 case 'boolean' : da[c.name] = false; break;
12048                 default : da[c.name] = ""; break;
12049             }
12050             
12051         });
12052         return new this.recordType(Roo.apply(da, d));
12053     }
12054     
12055 };/*
12056  * Based on:
12057  * Ext JS Library 1.1.1
12058  * Copyright(c) 2006-2007, Ext JS, LLC.
12059  *
12060  * Originally Released Under LGPL - original licence link has changed is not relivant.
12061  *
12062  * Fork - LGPL
12063  * <script type="text/javascript">
12064  */
12065
12066 /**
12067  * @class Roo.data.DataProxy
12068  * @extends Roo.data.Observable
12069  * This class is an abstract base class for implementations which provide retrieval of
12070  * unformatted data objects.<br>
12071  * <p>
12072  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12073  * (of the appropriate type which knows how to parse the data object) to provide a block of
12074  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12075  * <p>
12076  * Custom implementations must implement the load method as described in
12077  * {@link Roo.data.HttpProxy#load}.
12078  */
12079 Roo.data.DataProxy = function(){
12080     this.addEvents({
12081         /**
12082          * @event beforeload
12083          * Fires before a network request is made to retrieve a data object.
12084          * @param {Object} This DataProxy object.
12085          * @param {Object} params The params parameter to the load function.
12086          */
12087         beforeload : true,
12088         /**
12089          * @event load
12090          * Fires before the load method's callback is called.
12091          * @param {Object} This DataProxy object.
12092          * @param {Object} o The data object.
12093          * @param {Object} arg The callback argument object passed to the load function.
12094          */
12095         load : true,
12096         /**
12097          * @event loadexception
12098          * Fires if an Exception occurs during data retrieval.
12099          * @param {Object} This DataProxy object.
12100          * @param {Object} o The data object.
12101          * @param {Object} arg The callback argument object passed to the load function.
12102          * @param {Object} e The Exception.
12103          */
12104         loadexception : true
12105     });
12106     Roo.data.DataProxy.superclass.constructor.call(this);
12107 };
12108
12109 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12110
12111     /**
12112      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12113      */
12114 /*
12115  * Based on:
12116  * Ext JS Library 1.1.1
12117  * Copyright(c) 2006-2007, Ext JS, LLC.
12118  *
12119  * Originally Released Under LGPL - original licence link has changed is not relivant.
12120  *
12121  * Fork - LGPL
12122  * <script type="text/javascript">
12123  */
12124 /**
12125  * @class Roo.data.MemoryProxy
12126  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12127  * to the Reader when its load method is called.
12128  * @constructor
12129  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12130  */
12131 Roo.data.MemoryProxy = function(data){
12132     if (data.data) {
12133         data = data.data;
12134     }
12135     Roo.data.MemoryProxy.superclass.constructor.call(this);
12136     this.data = data;
12137 };
12138
12139 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12140     
12141     /**
12142      * Load data from the requested source (in this case an in-memory
12143      * data object passed to the constructor), read the data object into
12144      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12145      * process that block using the passed callback.
12146      * @param {Object} params This parameter is not used by the MemoryProxy class.
12147      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12148      * object into a block of Roo.data.Records.
12149      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12150      * The function must be passed <ul>
12151      * <li>The Record block object</li>
12152      * <li>The "arg" argument from the load function</li>
12153      * <li>A boolean success indicator</li>
12154      * </ul>
12155      * @param {Object} scope The scope in which to call the callback
12156      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12157      */
12158     load : function(params, reader, callback, scope, arg){
12159         params = params || {};
12160         var result;
12161         try {
12162             result = reader.readRecords(this.data);
12163         }catch(e){
12164             this.fireEvent("loadexception", this, arg, null, e);
12165             callback.call(scope, null, arg, false);
12166             return;
12167         }
12168         callback.call(scope, result, arg, true);
12169     },
12170     
12171     // private
12172     update : function(params, records){
12173         
12174     }
12175 });/*
12176  * Based on:
12177  * Ext JS Library 1.1.1
12178  * Copyright(c) 2006-2007, Ext JS, LLC.
12179  *
12180  * Originally Released Under LGPL - original licence link has changed is not relivant.
12181  *
12182  * Fork - LGPL
12183  * <script type="text/javascript">
12184  */
12185 /**
12186  * @class Roo.data.HttpProxy
12187  * @extends Roo.data.DataProxy
12188  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12189  * configured to reference a certain URL.<br><br>
12190  * <p>
12191  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12192  * from which the running page was served.<br><br>
12193  * <p>
12194  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12195  * <p>
12196  * Be aware that to enable the browser to parse an XML document, the server must set
12197  * the Content-Type header in the HTTP response to "text/xml".
12198  * @constructor
12199  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12200  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12201  * will be used to make the request.
12202  */
12203 Roo.data.HttpProxy = function(conn){
12204     Roo.data.HttpProxy.superclass.constructor.call(this);
12205     // is conn a conn config or a real conn?
12206     this.conn = conn;
12207     this.useAjax = !conn || !conn.events;
12208   
12209 };
12210
12211 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12212     // thse are take from connection...
12213     
12214     /**
12215      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12216      */
12217     /**
12218      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12219      * extra parameters to each request made by this object. (defaults to undefined)
12220      */
12221     /**
12222      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12223      *  to each request made by this object. (defaults to undefined)
12224      */
12225     /**
12226      * @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)
12227      */
12228     /**
12229      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12230      */
12231      /**
12232      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12233      * @type Boolean
12234      */
12235   
12236
12237     /**
12238      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12239      * @type Boolean
12240      */
12241     /**
12242      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12243      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12244      * a finer-grained basis than the DataProxy events.
12245      */
12246     getConnection : function(){
12247         return this.useAjax ? Roo.Ajax : this.conn;
12248     },
12249
12250     /**
12251      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12252      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12253      * process that block using the passed callback.
12254      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12255      * for the request to the remote server.
12256      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12257      * object into a block of Roo.data.Records.
12258      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12259      * The function must be passed <ul>
12260      * <li>The Record block object</li>
12261      * <li>The "arg" argument from the load function</li>
12262      * <li>A boolean success indicator</li>
12263      * </ul>
12264      * @param {Object} scope The scope in which to call the callback
12265      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12266      */
12267     load : function(params, reader, callback, scope, arg){
12268         if(this.fireEvent("beforeload", this, params) !== false){
12269             var  o = {
12270                 params : params || {},
12271                 request: {
12272                     callback : callback,
12273                     scope : scope,
12274                     arg : arg
12275                 },
12276                 reader: reader,
12277                 callback : this.loadResponse,
12278                 scope: this
12279             };
12280             if(this.useAjax){
12281                 Roo.applyIf(o, this.conn);
12282                 if(this.activeRequest){
12283                     Roo.Ajax.abort(this.activeRequest);
12284                 }
12285                 this.activeRequest = Roo.Ajax.request(o);
12286             }else{
12287                 this.conn.request(o);
12288             }
12289         }else{
12290             callback.call(scope||this, null, arg, false);
12291         }
12292     },
12293
12294     // private
12295     loadResponse : function(o, success, response){
12296         delete this.activeRequest;
12297         if(!success){
12298             this.fireEvent("loadexception", this, o, response);
12299             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12300             return;
12301         }
12302         var result;
12303         try {
12304             result = o.reader.read(response);
12305         }catch(e){
12306             this.fireEvent("loadexception", this, o, response, e);
12307             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12308             return;
12309         }
12310         
12311         this.fireEvent("load", this, o, o.request.arg);
12312         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12313     },
12314
12315     // private
12316     update : function(dataSet){
12317
12318     },
12319
12320     // private
12321     updateResponse : function(dataSet){
12322
12323     }
12324 });/*
12325  * Based on:
12326  * Ext JS Library 1.1.1
12327  * Copyright(c) 2006-2007, Ext JS, LLC.
12328  *
12329  * Originally Released Under LGPL - original licence link has changed is not relivant.
12330  *
12331  * Fork - LGPL
12332  * <script type="text/javascript">
12333  */
12334
12335 /**
12336  * @class Roo.data.ScriptTagProxy
12337  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12338  * other than the originating domain of the running page.<br><br>
12339  * <p>
12340  * <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
12341  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12342  * <p>
12343  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12344  * source code that is used as the source inside a &lt;script> tag.<br><br>
12345  * <p>
12346  * In order for the browser to process the returned data, the server must wrap the data object
12347  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12348  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12349  * depending on whether the callback name was passed:
12350  * <p>
12351  * <pre><code>
12352 boolean scriptTag = false;
12353 String cb = request.getParameter("callback");
12354 if (cb != null) {
12355     scriptTag = true;
12356     response.setContentType("text/javascript");
12357 } else {
12358     response.setContentType("application/x-json");
12359 }
12360 Writer out = response.getWriter();
12361 if (scriptTag) {
12362     out.write(cb + "(");
12363 }
12364 out.print(dataBlock.toJsonString());
12365 if (scriptTag) {
12366     out.write(");");
12367 }
12368 </pre></code>
12369  *
12370  * @constructor
12371  * @param {Object} config A configuration object.
12372  */
12373 Roo.data.ScriptTagProxy = function(config){
12374     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12375     Roo.apply(this, config);
12376     this.head = document.getElementsByTagName("head")[0];
12377 };
12378
12379 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12380
12381 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12382     /**
12383      * @cfg {String} url The URL from which to request the data object.
12384      */
12385     /**
12386      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12387      */
12388     timeout : 30000,
12389     /**
12390      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12391      * the server the name of the callback function set up by the load call to process the returned data object.
12392      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12393      * javascript output which calls this named function passing the data object as its only parameter.
12394      */
12395     callbackParam : "callback",
12396     /**
12397      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12398      * name to the request.
12399      */
12400     nocache : true,
12401
12402     /**
12403      * Load data from the configured URL, read the data object into
12404      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12405      * process that block using the passed callback.
12406      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12407      * for the request to the remote server.
12408      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12409      * object into a block of Roo.data.Records.
12410      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12411      * The function must be passed <ul>
12412      * <li>The Record block object</li>
12413      * <li>The "arg" argument from the load function</li>
12414      * <li>A boolean success indicator</li>
12415      * </ul>
12416      * @param {Object} scope The scope in which to call the callback
12417      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12418      */
12419     load : function(params, reader, callback, scope, arg){
12420         if(this.fireEvent("beforeload", this, params) !== false){
12421
12422             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12423
12424             var url = this.url;
12425             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12426             if(this.nocache){
12427                 url += "&_dc=" + (new Date().getTime());
12428             }
12429             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12430             var trans = {
12431                 id : transId,
12432                 cb : "stcCallback"+transId,
12433                 scriptId : "stcScript"+transId,
12434                 params : params,
12435                 arg : arg,
12436                 url : url,
12437                 callback : callback,
12438                 scope : scope,
12439                 reader : reader
12440             };
12441             var conn = this;
12442
12443             window[trans.cb] = function(o){
12444                 conn.handleResponse(o, trans);
12445             };
12446
12447             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12448
12449             if(this.autoAbort !== false){
12450                 this.abort();
12451             }
12452
12453             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12454
12455             var script = document.createElement("script");
12456             script.setAttribute("src", url);
12457             script.setAttribute("type", "text/javascript");
12458             script.setAttribute("id", trans.scriptId);
12459             this.head.appendChild(script);
12460
12461             this.trans = trans;
12462         }else{
12463             callback.call(scope||this, null, arg, false);
12464         }
12465     },
12466
12467     // private
12468     isLoading : function(){
12469         return this.trans ? true : false;
12470     },
12471
12472     /**
12473      * Abort the current server request.
12474      */
12475     abort : function(){
12476         if(this.isLoading()){
12477             this.destroyTrans(this.trans);
12478         }
12479     },
12480
12481     // private
12482     destroyTrans : function(trans, isLoaded){
12483         this.head.removeChild(document.getElementById(trans.scriptId));
12484         clearTimeout(trans.timeoutId);
12485         if(isLoaded){
12486             window[trans.cb] = undefined;
12487             try{
12488                 delete window[trans.cb];
12489             }catch(e){}
12490         }else{
12491             // if hasn't been loaded, wait for load to remove it to prevent script error
12492             window[trans.cb] = function(){
12493                 window[trans.cb] = undefined;
12494                 try{
12495                     delete window[trans.cb];
12496                 }catch(e){}
12497             };
12498         }
12499     },
12500
12501     // private
12502     handleResponse : function(o, trans){
12503         this.trans = false;
12504         this.destroyTrans(trans, true);
12505         var result;
12506         try {
12507             result = trans.reader.readRecords(o);
12508         }catch(e){
12509             this.fireEvent("loadexception", this, o, trans.arg, e);
12510             trans.callback.call(trans.scope||window, null, trans.arg, false);
12511             return;
12512         }
12513         this.fireEvent("load", this, o, trans.arg);
12514         trans.callback.call(trans.scope||window, result, trans.arg, true);
12515     },
12516
12517     // private
12518     handleFailure : function(trans){
12519         this.trans = false;
12520         this.destroyTrans(trans, false);
12521         this.fireEvent("loadexception", this, null, trans.arg);
12522         trans.callback.call(trans.scope||window, null, trans.arg, false);
12523     }
12524 });/*
12525  * Based on:
12526  * Ext JS Library 1.1.1
12527  * Copyright(c) 2006-2007, Ext JS, LLC.
12528  *
12529  * Originally Released Under LGPL - original licence link has changed is not relivant.
12530  *
12531  * Fork - LGPL
12532  * <script type="text/javascript">
12533  */
12534
12535 /**
12536  * @class Roo.data.JsonReader
12537  * @extends Roo.data.DataReader
12538  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12539  * based on mappings in a provided Roo.data.Record constructor.
12540  * 
12541  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12542  * in the reply previously. 
12543  * 
12544  * <p>
12545  * Example code:
12546  * <pre><code>
12547 var RecordDef = Roo.data.Record.create([
12548     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12549     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12550 ]);
12551 var myReader = new Roo.data.JsonReader({
12552     totalProperty: "results",    // The property which contains the total dataset size (optional)
12553     root: "rows",                // The property which contains an Array of row objects
12554     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12555 }, RecordDef);
12556 </code></pre>
12557  * <p>
12558  * This would consume a JSON file like this:
12559  * <pre><code>
12560 { 'results': 2, 'rows': [
12561     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12562     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12563 }
12564 </code></pre>
12565  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12566  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12567  * paged from the remote server.
12568  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12569  * @cfg {String} root name of the property which contains the Array of row objects.
12570  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12571  * @cfg {Array} fields Array of field definition objects
12572  * @constructor
12573  * Create a new JsonReader
12574  * @param {Object} meta Metadata configuration options
12575  * @param {Object} recordType Either an Array of field definition objects,
12576  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12577  */
12578 Roo.data.JsonReader = function(meta, recordType){
12579     
12580     meta = meta || {};
12581     // set some defaults:
12582     Roo.applyIf(meta, {
12583         totalProperty: 'total',
12584         successProperty : 'success',
12585         root : 'data',
12586         id : 'id'
12587     });
12588     
12589     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12590 };
12591 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12592     
12593     /**
12594      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12595      * Used by Store query builder to append _requestMeta to params.
12596      * 
12597      */
12598     metaFromRemote : false,
12599     /**
12600      * This method is only used by a DataProxy which has retrieved data from a remote server.
12601      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12602      * @return {Object} data A data block which is used by an Roo.data.Store object as
12603      * a cache of Roo.data.Records.
12604      */
12605     read : function(response){
12606         var json = response.responseText;
12607        
12608         var o = /* eval:var:o */ eval("("+json+")");
12609         if(!o) {
12610             throw {message: "JsonReader.read: Json object not found"};
12611         }
12612         
12613         if(o.metaData){
12614             
12615             delete this.ef;
12616             this.metaFromRemote = true;
12617             this.meta = o.metaData;
12618             this.recordType = Roo.data.Record.create(o.metaData.fields);
12619             this.onMetaChange(this.meta, this.recordType, o);
12620         }
12621         return this.readRecords(o);
12622     },
12623
12624     // private function a store will implement
12625     onMetaChange : function(meta, recordType, o){
12626
12627     },
12628
12629     /**
12630          * @ignore
12631          */
12632     simpleAccess: function(obj, subsc) {
12633         return obj[subsc];
12634     },
12635
12636         /**
12637          * @ignore
12638          */
12639     getJsonAccessor: function(){
12640         var re = /[\[\.]/;
12641         return function(expr) {
12642             try {
12643                 return(re.test(expr))
12644                     ? new Function("obj", "return obj." + expr)
12645                     : function(obj){
12646                         return obj[expr];
12647                     };
12648             } catch(e){}
12649             return Roo.emptyFn;
12650         };
12651     }(),
12652
12653     /**
12654      * Create a data block containing Roo.data.Records from an XML document.
12655      * @param {Object} o An object which contains an Array of row objects in the property specified
12656      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12657      * which contains the total size of the dataset.
12658      * @return {Object} data A data block which is used by an Roo.data.Store object as
12659      * a cache of Roo.data.Records.
12660      */
12661     readRecords : function(o){
12662         /**
12663          * After any data loads, the raw JSON data is available for further custom processing.
12664          * @type Object
12665          */
12666         this.o = o;
12667         var s = this.meta, Record = this.recordType,
12668             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12669
12670 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12671         if (!this.ef) {
12672             if(s.totalProperty) {
12673                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12674                 }
12675                 if(s.successProperty) {
12676                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12677                 }
12678                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12679                 if (s.id) {
12680                         var g = this.getJsonAccessor(s.id);
12681                         this.getId = function(rec) {
12682                                 var r = g(rec);  
12683                                 return (r === undefined || r === "") ? null : r;
12684                         };
12685                 } else {
12686                         this.getId = function(){return null;};
12687                 }
12688             this.ef = [];
12689             for(var jj = 0; jj < fl; jj++){
12690                 f = fi[jj];
12691                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12692                 this.ef[jj] = this.getJsonAccessor(map);
12693             }
12694         }
12695
12696         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12697         if(s.totalProperty){
12698             var vt = parseInt(this.getTotal(o), 10);
12699             if(!isNaN(vt)){
12700                 totalRecords = vt;
12701             }
12702         }
12703         if(s.successProperty){
12704             var vs = this.getSuccess(o);
12705             if(vs === false || vs === 'false'){
12706                 success = false;
12707             }
12708         }
12709         var records = [];
12710         for(var i = 0; i < c; i++){
12711                 var n = root[i];
12712             var values = {};
12713             var id = this.getId(n);
12714             for(var j = 0; j < fl; j++){
12715                 f = fi[j];
12716             var v = this.ef[j](n);
12717             if (!f.convert) {
12718                 Roo.log('missing convert for ' + f.name);
12719                 Roo.log(f);
12720                 continue;
12721             }
12722             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12723             }
12724             var record = new Record(values, id);
12725             record.json = n;
12726             records[i] = record;
12727         }
12728         return {
12729             raw : o,
12730             success : success,
12731             records : records,
12732             totalRecords : totalRecords
12733         };
12734     }
12735 });/*
12736  * Based on:
12737  * Ext JS Library 1.1.1
12738  * Copyright(c) 2006-2007, Ext JS, LLC.
12739  *
12740  * Originally Released Under LGPL - original licence link has changed is not relivant.
12741  *
12742  * Fork - LGPL
12743  * <script type="text/javascript">
12744  */
12745
12746 /**
12747  * @class Roo.data.ArrayReader
12748  * @extends Roo.data.DataReader
12749  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12750  * Each element of that Array represents a row of data fields. The
12751  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12752  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12753  * <p>
12754  * Example code:.
12755  * <pre><code>
12756 var RecordDef = Roo.data.Record.create([
12757     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12758     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12759 ]);
12760 var myReader = new Roo.data.ArrayReader({
12761     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12762 }, RecordDef);
12763 </code></pre>
12764  * <p>
12765  * This would consume an Array like this:
12766  * <pre><code>
12767 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12768   </code></pre>
12769  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12770  * @constructor
12771  * Create a new JsonReader
12772  * @param {Object} meta Metadata configuration options.
12773  * @param {Object} recordType Either an Array of field definition objects
12774  * as specified to {@link Roo.data.Record#create},
12775  * or an {@link Roo.data.Record} object
12776  * created using {@link Roo.data.Record#create}.
12777  */
12778 Roo.data.ArrayReader = function(meta, recordType){
12779     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12780 };
12781
12782 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12783     /**
12784      * Create a data block containing Roo.data.Records from an XML document.
12785      * @param {Object} o An Array of row objects which represents the dataset.
12786      * @return {Object} data A data block which is used by an Roo.data.Store object as
12787      * a cache of Roo.data.Records.
12788      */
12789     readRecords : function(o){
12790         var sid = this.meta ? this.meta.id : null;
12791         var recordType = this.recordType, fields = recordType.prototype.fields;
12792         var records = [];
12793         var root = o;
12794             for(var i = 0; i < root.length; i++){
12795                     var n = root[i];
12796                 var values = {};
12797                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12798                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12799                 var f = fields.items[j];
12800                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12801                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12802                 v = f.convert(v);
12803                 values[f.name] = v;
12804             }
12805                 var record = new recordType(values, id);
12806                 record.json = n;
12807                 records[records.length] = record;
12808             }
12809             return {
12810                 records : records,
12811                 totalRecords : records.length
12812             };
12813     }
12814 });/*
12815  * - LGPL
12816  * * 
12817  */
12818
12819 /**
12820  * @class Roo.bootstrap.ComboBox
12821  * @extends Roo.bootstrap.TriggerField
12822  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12823  * @cfg {Boolean} append (true|false) default false
12824  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12825  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12826  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12827  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12828  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12829  * @cfg {Boolean} animate default true
12830  * @cfg {Boolean} emptyResultText only for touch device
12831  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12832  * @cfg {String} emptyTitle default ''
12833  * @constructor
12834  * Create a new ComboBox.
12835  * @param {Object} config Configuration options
12836  */
12837 Roo.bootstrap.ComboBox = function(config){
12838     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12839     this.addEvents({
12840         /**
12841          * @event expand
12842          * Fires when the dropdown list is expanded
12843         * @param {Roo.bootstrap.ComboBox} combo This combo box
12844         */
12845         'expand' : true,
12846         /**
12847          * @event collapse
12848          * Fires when the dropdown list is collapsed
12849         * @param {Roo.bootstrap.ComboBox} combo This combo box
12850         */
12851         'collapse' : true,
12852         /**
12853          * @event beforeselect
12854          * Fires before a list item is selected. Return false to cancel the selection.
12855         * @param {Roo.bootstrap.ComboBox} combo This combo box
12856         * @param {Roo.data.Record} record The data record returned from the underlying store
12857         * @param {Number} index The index of the selected item in the dropdown list
12858         */
12859         'beforeselect' : true,
12860         /**
12861          * @event select
12862          * Fires when a list item is selected
12863         * @param {Roo.bootstrap.ComboBox} combo This combo box
12864         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12865         * @param {Number} index The index of the selected item in the dropdown list
12866         */
12867         'select' : true,
12868         /**
12869          * @event beforequery
12870          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12871          * The event object passed has these properties:
12872         * @param {Roo.bootstrap.ComboBox} combo This combo box
12873         * @param {String} query The query
12874         * @param {Boolean} forceAll true to force "all" query
12875         * @param {Boolean} cancel true to cancel the query
12876         * @param {Object} e The query event object
12877         */
12878         'beforequery': true,
12879          /**
12880          * @event add
12881          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12882         * @param {Roo.bootstrap.ComboBox} combo This combo box
12883         */
12884         'add' : true,
12885         /**
12886          * @event edit
12887          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12888         * @param {Roo.bootstrap.ComboBox} combo This combo box
12889         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12890         */
12891         'edit' : true,
12892         /**
12893          * @event remove
12894          * Fires when the remove value from the combobox array
12895         * @param {Roo.bootstrap.ComboBox} combo This combo box
12896         */
12897         'remove' : true,
12898         /**
12899          * @event afterremove
12900          * Fires when the remove value from the combobox array
12901         * @param {Roo.bootstrap.ComboBox} combo This combo box
12902         */
12903         'afterremove' : true,
12904         /**
12905          * @event specialfilter
12906          * Fires when specialfilter
12907             * @param {Roo.bootstrap.ComboBox} combo This combo box
12908             */
12909         'specialfilter' : true,
12910         /**
12911          * @event tick
12912          * Fires when tick the element
12913             * @param {Roo.bootstrap.ComboBox} combo This combo box
12914             */
12915         'tick' : true,
12916         /**
12917          * @event touchviewdisplay
12918          * Fires when touch view require special display (default is using displayField)
12919             * @param {Roo.bootstrap.ComboBox} combo This combo box
12920             * @param {Object} cfg set html .
12921             */
12922         'touchviewdisplay' : true
12923         
12924     });
12925     
12926     this.item = [];
12927     this.tickItems = [];
12928     
12929     this.selectedIndex = -1;
12930     if(this.mode == 'local'){
12931         if(config.queryDelay === undefined){
12932             this.queryDelay = 10;
12933         }
12934         if(config.minChars === undefined){
12935             this.minChars = 0;
12936         }
12937     }
12938 };
12939
12940 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12941      
12942     /**
12943      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12944      * rendering into an Roo.Editor, defaults to false)
12945      */
12946     /**
12947      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12948      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12949      */
12950     /**
12951      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12952      */
12953     /**
12954      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12955      * the dropdown list (defaults to undefined, with no header element)
12956      */
12957
12958      /**
12959      * @cfg {String/Roo.Template} tpl The template to use to render the output
12960      */
12961      
12962      /**
12963      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12964      */
12965     listWidth: undefined,
12966     /**
12967      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12968      * mode = 'remote' or 'text' if mode = 'local')
12969      */
12970     displayField: undefined,
12971     
12972     /**
12973      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12974      * mode = 'remote' or 'value' if mode = 'local'). 
12975      * Note: use of a valueField requires the user make a selection
12976      * in order for a value to be mapped.
12977      */
12978     valueField: undefined,
12979     /**
12980      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12981      */
12982     modalTitle : '',
12983     
12984     /**
12985      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12986      * field's data value (defaults to the underlying DOM element's name)
12987      */
12988     hiddenName: undefined,
12989     /**
12990      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12991      */
12992     listClass: '',
12993     /**
12994      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12995      */
12996     selectedClass: 'active',
12997     
12998     /**
12999      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13000      */
13001     shadow:'sides',
13002     /**
13003      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13004      * anchor positions (defaults to 'tl-bl')
13005      */
13006     listAlign: 'tl-bl?',
13007     /**
13008      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13009      */
13010     maxHeight: 300,
13011     /**
13012      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13013      * query specified by the allQuery config option (defaults to 'query')
13014      */
13015     triggerAction: 'query',
13016     /**
13017      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13018      * (defaults to 4, does not apply if editable = false)
13019      */
13020     minChars : 4,
13021     /**
13022      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13023      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13024      */
13025     typeAhead: false,
13026     /**
13027      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13028      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13029      */
13030     queryDelay: 500,
13031     /**
13032      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13033      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13034      */
13035     pageSize: 0,
13036     /**
13037      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13038      * when editable = true (defaults to false)
13039      */
13040     selectOnFocus:false,
13041     /**
13042      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13043      */
13044     queryParam: 'query',
13045     /**
13046      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13047      * when mode = 'remote' (defaults to 'Loading...')
13048      */
13049     loadingText: 'Loading...',
13050     /**
13051      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13052      */
13053     resizable: false,
13054     /**
13055      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13056      */
13057     handleHeight : 8,
13058     /**
13059      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13060      * traditional select (defaults to true)
13061      */
13062     editable: true,
13063     /**
13064      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13065      */
13066     allQuery: '',
13067     /**
13068      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13069      */
13070     mode: 'remote',
13071     /**
13072      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13073      * listWidth has a higher value)
13074      */
13075     minListWidth : 70,
13076     /**
13077      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13078      * allow the user to set arbitrary text into the field (defaults to false)
13079      */
13080     forceSelection:false,
13081     /**
13082      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13083      * if typeAhead = true (defaults to 250)
13084      */
13085     typeAheadDelay : 250,
13086     /**
13087      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13088      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13089      */
13090     valueNotFoundText : undefined,
13091     /**
13092      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13093      */
13094     blockFocus : false,
13095     
13096     /**
13097      * @cfg {Boolean} disableClear Disable showing of clear button.
13098      */
13099     disableClear : false,
13100     /**
13101      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13102      */
13103     alwaysQuery : false,
13104     
13105     /**
13106      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13107      */
13108     multiple : false,
13109     
13110     /**
13111      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13112      */
13113     invalidClass : "has-warning",
13114     
13115     /**
13116      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13117      */
13118     validClass : "has-success",
13119     
13120     /**
13121      * @cfg {Boolean} specialFilter (true|false) special filter default false
13122      */
13123     specialFilter : false,
13124     
13125     /**
13126      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13127      */
13128     mobileTouchView : true,
13129     
13130     /**
13131      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13132      */
13133     useNativeIOS : false,
13134     
13135     /**
13136      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13137      */
13138     mobile_restrict_height : false,
13139     
13140     ios_options : false,
13141     
13142     //private
13143     addicon : false,
13144     editicon: false,
13145     
13146     page: 0,
13147     hasQuery: false,
13148     append: false,
13149     loadNext: false,
13150     autoFocus : true,
13151     tickable : false,
13152     btnPosition : 'right',
13153     triggerList : true,
13154     showToggleBtn : true,
13155     animate : true,
13156     emptyResultText: 'Empty',
13157     triggerText : 'Select',
13158     emptyTitle : '',
13159     
13160     // element that contains real text value.. (when hidden is used..)
13161     
13162     getAutoCreate : function()
13163     {   
13164         var cfg = false;
13165         //render
13166         /*
13167          * Render classic select for iso
13168          */
13169         
13170         if(Roo.isIOS && this.useNativeIOS){
13171             cfg = this.getAutoCreateNativeIOS();
13172             return cfg;
13173         }
13174         
13175         /*
13176          * Touch Devices
13177          */
13178         
13179         if(Roo.isTouch && this.mobileTouchView){
13180             cfg = this.getAutoCreateTouchView();
13181             return cfg;;
13182         }
13183         
13184         /*
13185          *  Normal ComboBox
13186          */
13187         if(!this.tickable){
13188             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13189             return cfg;
13190         }
13191         
13192         /*
13193          *  ComboBox with tickable selections
13194          */
13195              
13196         var align = this.labelAlign || this.parentLabelAlign();
13197         
13198         cfg = {
13199             cls : 'form-group roo-combobox-tickable' //input-group
13200         };
13201         
13202         var btn_text_select = '';
13203         var btn_text_done = '';
13204         var btn_text_cancel = '';
13205         
13206         if (this.btn_text_show) {
13207             btn_text_select = 'Select';
13208             btn_text_done = 'Done';
13209             btn_text_cancel = 'Cancel'; 
13210         }
13211         
13212         var buttons = {
13213             tag : 'div',
13214             cls : 'tickable-buttons',
13215             cn : [
13216                 {
13217                     tag : 'button',
13218                     type : 'button',
13219                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13220                     //html : this.triggerText
13221                     html: btn_text_select
13222                 },
13223                 {
13224                     tag : 'button',
13225                     type : 'button',
13226                     name : 'ok',
13227                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13228                     //html : 'Done'
13229                     html: btn_text_done
13230                 },
13231                 {
13232                     tag : 'button',
13233                     type : 'button',
13234                     name : 'cancel',
13235                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13236                     //html : 'Cancel'
13237                     html: btn_text_cancel
13238                 }
13239             ]
13240         };
13241         
13242         if(this.editable){
13243             buttons.cn.unshift({
13244                 tag: 'input',
13245                 cls: 'roo-select2-search-field-input'
13246             });
13247         }
13248         
13249         var _this = this;
13250         
13251         Roo.each(buttons.cn, function(c){
13252             if (_this.size) {
13253                 c.cls += ' btn-' + _this.size;
13254             }
13255
13256             if (_this.disabled) {
13257                 c.disabled = true;
13258             }
13259         });
13260         
13261         var box = {
13262             tag: 'div',
13263             cn: [
13264                 {
13265                     tag: 'input',
13266                     type : 'hidden',
13267                     cls: 'form-hidden-field'
13268                 },
13269                 {
13270                     tag: 'ul',
13271                     cls: 'roo-select2-choices',
13272                     cn:[
13273                         {
13274                             tag: 'li',
13275                             cls: 'roo-select2-search-field',
13276                             cn: [
13277                                 buttons
13278                             ]
13279                         }
13280                     ]
13281                 }
13282             ]
13283         };
13284         
13285         var combobox = {
13286             cls: 'roo-select2-container input-group roo-select2-container-multi',
13287             cn: [
13288                 box
13289 //                {
13290 //                    tag: 'ul',
13291 //                    cls: 'typeahead typeahead-long dropdown-menu',
13292 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13293 //                }
13294             ]
13295         };
13296         
13297         if(this.hasFeedback && !this.allowBlank){
13298             
13299             var feedback = {
13300                 tag: 'span',
13301                 cls: 'glyphicon form-control-feedback'
13302             };
13303
13304             combobox.cn.push(feedback);
13305         }
13306         
13307         
13308         if (align ==='left' && this.fieldLabel.length) {
13309             
13310             cfg.cls += ' roo-form-group-label-left';
13311             
13312             cfg.cn = [
13313                 {
13314                     tag : 'i',
13315                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13316                     tooltip : 'This field is required'
13317                 },
13318                 {
13319                     tag: 'label',
13320                     'for' :  id,
13321                     cls : 'control-label',
13322                     html : this.fieldLabel
13323
13324                 },
13325                 {
13326                     cls : "", 
13327                     cn: [
13328                         combobox
13329                     ]
13330                 }
13331
13332             ];
13333             
13334             var labelCfg = cfg.cn[1];
13335             var contentCfg = cfg.cn[2];
13336             
13337
13338             if(this.indicatorpos == 'right'){
13339                 
13340                 cfg.cn = [
13341                     {
13342                         tag: 'label',
13343                         'for' :  id,
13344                         cls : 'control-label',
13345                         cn : [
13346                             {
13347                                 tag : 'span',
13348                                 html : this.fieldLabel
13349                             },
13350                             {
13351                                 tag : 'i',
13352                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13353                                 tooltip : 'This field is required'
13354                             }
13355                         ]
13356                     },
13357                     {
13358                         cls : "",
13359                         cn: [
13360                             combobox
13361                         ]
13362                     }
13363
13364                 ];
13365                 
13366                 
13367                 
13368                 labelCfg = cfg.cn[0];
13369                 contentCfg = cfg.cn[1];
13370             
13371             }
13372             
13373             if(this.labelWidth > 12){
13374                 labelCfg.style = "width: " + this.labelWidth + 'px';
13375             }
13376             
13377             if(this.labelWidth < 13 && this.labelmd == 0){
13378                 this.labelmd = this.labelWidth;
13379             }
13380             
13381             if(this.labellg > 0){
13382                 labelCfg.cls += ' col-lg-' + this.labellg;
13383                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13384             }
13385             
13386             if(this.labelmd > 0){
13387                 labelCfg.cls += ' col-md-' + this.labelmd;
13388                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13389             }
13390             
13391             if(this.labelsm > 0){
13392                 labelCfg.cls += ' col-sm-' + this.labelsm;
13393                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13394             }
13395             
13396             if(this.labelxs > 0){
13397                 labelCfg.cls += ' col-xs-' + this.labelxs;
13398                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13399             }
13400                 
13401                 
13402         } else if ( this.fieldLabel.length) {
13403 //                Roo.log(" label");
13404                  cfg.cn = [
13405                     {
13406                         tag : 'i',
13407                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13408                         tooltip : 'This field is required'
13409                     },
13410                     {
13411                         tag: 'label',
13412                         //cls : 'input-group-addon',
13413                         html : this.fieldLabel
13414                     },
13415                     combobox
13416                 ];
13417                 
13418                 if(this.indicatorpos == 'right'){
13419                     cfg.cn = [
13420                         {
13421                             tag: 'label',
13422                             //cls : 'input-group-addon',
13423                             html : this.fieldLabel
13424                         },
13425                         {
13426                             tag : 'i',
13427                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13428                             tooltip : 'This field is required'
13429                         },
13430                         combobox
13431                     ];
13432                     
13433                 }
13434
13435         } else {
13436             
13437 //                Roo.log(" no label && no align");
13438                 cfg = combobox
13439                      
13440                 
13441         }
13442          
13443         var settings=this;
13444         ['xs','sm','md','lg'].map(function(size){
13445             if (settings[size]) {
13446                 cfg.cls += ' col-' + size + '-' + settings[size];
13447             }
13448         });
13449         
13450         return cfg;
13451         
13452     },
13453     
13454     _initEventsCalled : false,
13455     
13456     // private
13457     initEvents: function()
13458     {   
13459         if (this._initEventsCalled) { // as we call render... prevent looping...
13460             return;
13461         }
13462         this._initEventsCalled = true;
13463         
13464         if (!this.store) {
13465             throw "can not find store for combo";
13466         }
13467         
13468         this.indicator = this.indicatorEl();
13469         
13470         this.store = Roo.factory(this.store, Roo.data);
13471         this.store.parent = this;
13472         
13473         // if we are building from html. then this element is so complex, that we can not really
13474         // use the rendered HTML.
13475         // so we have to trash and replace the previous code.
13476         if (Roo.XComponent.build_from_html) {
13477             // remove this element....
13478             var e = this.el.dom, k=0;
13479             while (e ) { e = e.previousSibling;  ++k;}
13480
13481             this.el.remove();
13482             
13483             this.el=false;
13484             this.rendered = false;
13485             
13486             this.render(this.parent().getChildContainer(true), k);
13487         }
13488         
13489         if(Roo.isIOS && this.useNativeIOS){
13490             this.initIOSView();
13491             return;
13492         }
13493         
13494         /*
13495          * Touch Devices
13496          */
13497         
13498         if(Roo.isTouch && this.mobileTouchView){
13499             this.initTouchView();
13500             return;
13501         }
13502         
13503         if(this.tickable){
13504             this.initTickableEvents();
13505             return;
13506         }
13507         
13508         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13509         
13510         if(this.hiddenName){
13511             
13512             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13513             
13514             this.hiddenField.dom.value =
13515                 this.hiddenValue !== undefined ? this.hiddenValue :
13516                 this.value !== undefined ? this.value : '';
13517
13518             // prevent input submission
13519             this.el.dom.removeAttribute('name');
13520             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13521              
13522              
13523         }
13524         //if(Roo.isGecko){
13525         //    this.el.dom.setAttribute('autocomplete', 'off');
13526         //}
13527         
13528         var cls = 'x-combo-list';
13529         
13530         //this.list = new Roo.Layer({
13531         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13532         //});
13533         
13534         var _this = this;
13535         
13536         (function(){
13537             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13538             _this.list.setWidth(lw);
13539         }).defer(100);
13540         
13541         this.list.on('mouseover', this.onViewOver, this);
13542         this.list.on('mousemove', this.onViewMove, this);
13543         this.list.on('scroll', this.onViewScroll, this);
13544         
13545         /*
13546         this.list.swallowEvent('mousewheel');
13547         this.assetHeight = 0;
13548
13549         if(this.title){
13550             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13551             this.assetHeight += this.header.getHeight();
13552         }
13553
13554         this.innerList = this.list.createChild({cls:cls+'-inner'});
13555         this.innerList.on('mouseover', this.onViewOver, this);
13556         this.innerList.on('mousemove', this.onViewMove, this);
13557         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13558         
13559         if(this.allowBlank && !this.pageSize && !this.disableClear){
13560             this.footer = this.list.createChild({cls:cls+'-ft'});
13561             this.pageTb = new Roo.Toolbar(this.footer);
13562            
13563         }
13564         if(this.pageSize){
13565             this.footer = this.list.createChild({cls:cls+'-ft'});
13566             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13567                     {pageSize: this.pageSize});
13568             
13569         }
13570         
13571         if (this.pageTb && this.allowBlank && !this.disableClear) {
13572             var _this = this;
13573             this.pageTb.add(new Roo.Toolbar.Fill(), {
13574                 cls: 'x-btn-icon x-btn-clear',
13575                 text: '&#160;',
13576                 handler: function()
13577                 {
13578                     _this.collapse();
13579                     _this.clearValue();
13580                     _this.onSelect(false, -1);
13581                 }
13582             });
13583         }
13584         if (this.footer) {
13585             this.assetHeight += this.footer.getHeight();
13586         }
13587         */
13588             
13589         if(!this.tpl){
13590             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13591         }
13592
13593         this.view = new Roo.View(this.list, this.tpl, {
13594             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13595         });
13596         //this.view.wrapEl.setDisplayed(false);
13597         this.view.on('click', this.onViewClick, this);
13598         
13599         
13600         this.store.on('beforeload', this.onBeforeLoad, this);
13601         this.store.on('load', this.onLoad, this);
13602         this.store.on('loadexception', this.onLoadException, this);
13603         /*
13604         if(this.resizable){
13605             this.resizer = new Roo.Resizable(this.list,  {
13606                pinned:true, handles:'se'
13607             });
13608             this.resizer.on('resize', function(r, w, h){
13609                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13610                 this.listWidth = w;
13611                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13612                 this.restrictHeight();
13613             }, this);
13614             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13615         }
13616         */
13617         if(!this.editable){
13618             this.editable = true;
13619             this.setEditable(false);
13620         }
13621         
13622         /*
13623         
13624         if (typeof(this.events.add.listeners) != 'undefined') {
13625             
13626             this.addicon = this.wrap.createChild(
13627                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13628        
13629             this.addicon.on('click', function(e) {
13630                 this.fireEvent('add', this);
13631             }, this);
13632         }
13633         if (typeof(this.events.edit.listeners) != 'undefined') {
13634             
13635             this.editicon = this.wrap.createChild(
13636                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13637             if (this.addicon) {
13638                 this.editicon.setStyle('margin-left', '40px');
13639             }
13640             this.editicon.on('click', function(e) {
13641                 
13642                 // we fire even  if inothing is selected..
13643                 this.fireEvent('edit', this, this.lastData );
13644                 
13645             }, this);
13646         }
13647         */
13648         
13649         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13650             "up" : function(e){
13651                 this.inKeyMode = true;
13652                 this.selectPrev();
13653             },
13654
13655             "down" : function(e){
13656                 if(!this.isExpanded()){
13657                     this.onTriggerClick();
13658                 }else{
13659                     this.inKeyMode = true;
13660                     this.selectNext();
13661                 }
13662             },
13663
13664             "enter" : function(e){
13665 //                this.onViewClick();
13666                 //return true;
13667                 this.collapse();
13668                 
13669                 if(this.fireEvent("specialkey", this, e)){
13670                     this.onViewClick(false);
13671                 }
13672                 
13673                 return true;
13674             },
13675
13676             "esc" : function(e){
13677                 this.collapse();
13678             },
13679
13680             "tab" : function(e){
13681                 this.collapse();
13682                 
13683                 if(this.fireEvent("specialkey", this, e)){
13684                     this.onViewClick(false);
13685                 }
13686                 
13687                 return true;
13688             },
13689
13690             scope : this,
13691
13692             doRelay : function(foo, bar, hname){
13693                 if(hname == 'down' || this.scope.isExpanded()){
13694                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13695                 }
13696                 return true;
13697             },
13698
13699             forceKeyDown: true
13700         });
13701         
13702         
13703         this.queryDelay = Math.max(this.queryDelay || 10,
13704                 this.mode == 'local' ? 10 : 250);
13705         
13706         
13707         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13708         
13709         if(this.typeAhead){
13710             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13711         }
13712         if(this.editable !== false){
13713             this.inputEl().on("keyup", this.onKeyUp, this);
13714         }
13715         if(this.forceSelection){
13716             this.inputEl().on('blur', this.doForce, this);
13717         }
13718         
13719         if(this.multiple){
13720             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13721             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13722         }
13723     },
13724     
13725     initTickableEvents: function()
13726     {   
13727         this.createList();
13728         
13729         if(this.hiddenName){
13730             
13731             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13732             
13733             this.hiddenField.dom.value =
13734                 this.hiddenValue !== undefined ? this.hiddenValue :
13735                 this.value !== undefined ? this.value : '';
13736
13737             // prevent input submission
13738             this.el.dom.removeAttribute('name');
13739             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13740              
13741              
13742         }
13743         
13744 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13745         
13746         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13747         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13748         if(this.triggerList){
13749             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13750         }
13751          
13752         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13753         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13754         
13755         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13756         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13757         
13758         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13759         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13760         
13761         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13762         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13763         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13764         
13765         this.okBtn.hide();
13766         this.cancelBtn.hide();
13767         
13768         var _this = this;
13769         
13770         (function(){
13771             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13772             _this.list.setWidth(lw);
13773         }).defer(100);
13774         
13775         this.list.on('mouseover', this.onViewOver, this);
13776         this.list.on('mousemove', this.onViewMove, this);
13777         
13778         this.list.on('scroll', this.onViewScroll, this);
13779         
13780         if(!this.tpl){
13781             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13782                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13783         }
13784
13785         this.view = new Roo.View(this.list, this.tpl, {
13786             singleSelect:true,
13787             tickable:true,
13788             parent:this,
13789             store: this.store,
13790             selectedClass: this.selectedClass
13791         });
13792         
13793         //this.view.wrapEl.setDisplayed(false);
13794         this.view.on('click', this.onViewClick, this);
13795         
13796         
13797         
13798         this.store.on('beforeload', this.onBeforeLoad, this);
13799         this.store.on('load', this.onLoad, this);
13800         this.store.on('loadexception', this.onLoadException, this);
13801         
13802         if(this.editable){
13803             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13804                 "up" : function(e){
13805                     this.inKeyMode = true;
13806                     this.selectPrev();
13807                 },
13808
13809                 "down" : function(e){
13810                     this.inKeyMode = true;
13811                     this.selectNext();
13812                 },
13813
13814                 "enter" : function(e){
13815                     if(this.fireEvent("specialkey", this, e)){
13816                         this.onViewClick(false);
13817                     }
13818                     
13819                     return true;
13820                 },
13821
13822                 "esc" : function(e){
13823                     this.onTickableFooterButtonClick(e, false, false);
13824                 },
13825
13826                 "tab" : function(e){
13827                     this.fireEvent("specialkey", this, e);
13828                     
13829                     this.onTickableFooterButtonClick(e, false, false);
13830                     
13831                     return true;
13832                 },
13833
13834                 scope : this,
13835
13836                 doRelay : function(e, fn, key){
13837                     if(this.scope.isExpanded()){
13838                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13839                     }
13840                     return true;
13841                 },
13842
13843                 forceKeyDown: true
13844             });
13845         }
13846         
13847         this.queryDelay = Math.max(this.queryDelay || 10,
13848                 this.mode == 'local' ? 10 : 250);
13849         
13850         
13851         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13852         
13853         if(this.typeAhead){
13854             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13855         }
13856         
13857         if(this.editable !== false){
13858             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13859         }
13860         
13861         this.indicator = this.indicatorEl();
13862         
13863         if(this.indicator){
13864             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13865             this.indicator.hide();
13866         }
13867         
13868     },
13869
13870     onDestroy : function(){
13871         if(this.view){
13872             this.view.setStore(null);
13873             this.view.el.removeAllListeners();
13874             this.view.el.remove();
13875             this.view.purgeListeners();
13876         }
13877         if(this.list){
13878             this.list.dom.innerHTML  = '';
13879         }
13880         
13881         if(this.store){
13882             this.store.un('beforeload', this.onBeforeLoad, this);
13883             this.store.un('load', this.onLoad, this);
13884             this.store.un('loadexception', this.onLoadException, this);
13885         }
13886         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13887     },
13888
13889     // private
13890     fireKey : function(e){
13891         if(e.isNavKeyPress() && !this.list.isVisible()){
13892             this.fireEvent("specialkey", this, e);
13893         }
13894     },
13895
13896     // private
13897     onResize: function(w, h){
13898 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13899 //        
13900 //        if(typeof w != 'number'){
13901 //            // we do not handle it!?!?
13902 //            return;
13903 //        }
13904 //        var tw = this.trigger.getWidth();
13905 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13906 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13907 //        var x = w - tw;
13908 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13909 //            
13910 //        //this.trigger.setStyle('left', x+'px');
13911 //        
13912 //        if(this.list && this.listWidth === undefined){
13913 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13914 //            this.list.setWidth(lw);
13915 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13916 //        }
13917         
13918     
13919         
13920     },
13921
13922     /**
13923      * Allow or prevent the user from directly editing the field text.  If false is passed,
13924      * the user will only be able to select from the items defined in the dropdown list.  This method
13925      * is the runtime equivalent of setting the 'editable' config option at config time.
13926      * @param {Boolean} value True to allow the user to directly edit the field text
13927      */
13928     setEditable : function(value){
13929         if(value == this.editable){
13930             return;
13931         }
13932         this.editable = value;
13933         if(!value){
13934             this.inputEl().dom.setAttribute('readOnly', true);
13935             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13936             this.inputEl().addClass('x-combo-noedit');
13937         }else{
13938             this.inputEl().dom.setAttribute('readOnly', false);
13939             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13940             this.inputEl().removeClass('x-combo-noedit');
13941         }
13942     },
13943
13944     // private
13945     
13946     onBeforeLoad : function(combo,opts){
13947         if(!this.hasFocus){
13948             return;
13949         }
13950          if (!opts.add) {
13951             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13952          }
13953         this.restrictHeight();
13954         this.selectedIndex = -1;
13955     },
13956
13957     // private
13958     onLoad : function(){
13959         
13960         this.hasQuery = false;
13961         
13962         if(!this.hasFocus){
13963             return;
13964         }
13965         
13966         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13967             this.loading.hide();
13968         }
13969         
13970         if(this.store.getCount() > 0){
13971             
13972             this.expand();
13973             this.restrictHeight();
13974             if(this.lastQuery == this.allQuery){
13975                 if(this.editable && !this.tickable){
13976                     this.inputEl().dom.select();
13977                 }
13978                 
13979                 if(
13980                     !this.selectByValue(this.value, true) &&
13981                     this.autoFocus && 
13982                     (
13983                         !this.store.lastOptions ||
13984                         typeof(this.store.lastOptions.add) == 'undefined' || 
13985                         this.store.lastOptions.add != true
13986                     )
13987                 ){
13988                     this.select(0, true);
13989                 }
13990             }else{
13991                 if(this.autoFocus){
13992                     this.selectNext();
13993                 }
13994                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13995                     this.taTask.delay(this.typeAheadDelay);
13996                 }
13997             }
13998         }else{
13999             this.onEmptyResults();
14000         }
14001         
14002         //this.el.focus();
14003     },
14004     // private
14005     onLoadException : function()
14006     {
14007         this.hasQuery = false;
14008         
14009         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14010             this.loading.hide();
14011         }
14012         
14013         if(this.tickable && this.editable){
14014             return;
14015         }
14016         
14017         this.collapse();
14018         // only causes errors at present
14019         //Roo.log(this.store.reader.jsonData);
14020         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14021             // fixme
14022             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14023         //}
14024         
14025         
14026     },
14027     // private
14028     onTypeAhead : function(){
14029         if(this.store.getCount() > 0){
14030             var r = this.store.getAt(0);
14031             var newValue = r.data[this.displayField];
14032             var len = newValue.length;
14033             var selStart = this.getRawValue().length;
14034             
14035             if(selStart != len){
14036                 this.setRawValue(newValue);
14037                 this.selectText(selStart, newValue.length);
14038             }
14039         }
14040     },
14041
14042     // private
14043     onSelect : function(record, index){
14044         
14045         if(this.fireEvent('beforeselect', this, record, index) !== false){
14046         
14047             this.setFromData(index > -1 ? record.data : false);
14048             
14049             this.collapse();
14050             this.fireEvent('select', this, record, index);
14051         }
14052     },
14053
14054     /**
14055      * Returns the currently selected field value or empty string if no value is set.
14056      * @return {String} value The selected value
14057      */
14058     getValue : function()
14059     {
14060         if(Roo.isIOS && this.useNativeIOS){
14061             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14062         }
14063         
14064         if(this.multiple){
14065             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14066         }
14067         
14068         if(this.valueField){
14069             return typeof this.value != 'undefined' ? this.value : '';
14070         }else{
14071             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14072         }
14073     },
14074     
14075     getRawValue : function()
14076     {
14077         if(Roo.isIOS && this.useNativeIOS){
14078             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14079         }
14080         
14081         var v = this.inputEl().getValue();
14082         
14083         return v;
14084     },
14085
14086     /**
14087      * Clears any text/value currently set in the field
14088      */
14089     clearValue : function(){
14090         
14091         if(this.hiddenField){
14092             this.hiddenField.dom.value = '';
14093         }
14094         this.value = '';
14095         this.setRawValue('');
14096         this.lastSelectionText = '';
14097         this.lastData = false;
14098         
14099         var close = this.closeTriggerEl();
14100         
14101         if(close){
14102             close.hide();
14103         }
14104         
14105         this.validate();
14106         
14107     },
14108
14109     /**
14110      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14111      * will be displayed in the field.  If the value does not match the data value of an existing item,
14112      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14113      * Otherwise the field will be blank (although the value will still be set).
14114      * @param {String} value The value to match
14115      */
14116     setValue : function(v)
14117     {
14118         if(Roo.isIOS && this.useNativeIOS){
14119             this.setIOSValue(v);
14120             return;
14121         }
14122         
14123         if(this.multiple){
14124             this.syncValue();
14125             return;
14126         }
14127         
14128         var text = v;
14129         if(this.valueField){
14130             var r = this.findRecord(this.valueField, v);
14131             if(r){
14132                 text = r.data[this.displayField];
14133             }else if(this.valueNotFoundText !== undefined){
14134                 text = this.valueNotFoundText;
14135             }
14136         }
14137         this.lastSelectionText = text;
14138         if(this.hiddenField){
14139             this.hiddenField.dom.value = v;
14140         }
14141         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14142         this.value = v;
14143         
14144         var close = this.closeTriggerEl();
14145         
14146         if(close){
14147             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14148         }
14149         
14150         this.validate();
14151     },
14152     /**
14153      * @property {Object} the last set data for the element
14154      */
14155     
14156     lastData : false,
14157     /**
14158      * Sets the value of the field based on a object which is related to the record format for the store.
14159      * @param {Object} value the value to set as. or false on reset?
14160      */
14161     setFromData : function(o){
14162         
14163         if(this.multiple){
14164             this.addItem(o);
14165             return;
14166         }
14167             
14168         var dv = ''; // display value
14169         var vv = ''; // value value..
14170         this.lastData = o;
14171         if (this.displayField) {
14172             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14173         } else {
14174             // this is an error condition!!!
14175             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14176         }
14177         
14178         if(this.valueField){
14179             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14180         }
14181         
14182         var close = this.closeTriggerEl();
14183         
14184         if(close){
14185             if(dv.length || vv * 1 > 0){
14186                 close.show() ;
14187                 this.blockFocus=true;
14188             } else {
14189                 close.hide();
14190             }             
14191         }
14192         
14193         if(this.hiddenField){
14194             this.hiddenField.dom.value = vv;
14195             
14196             this.lastSelectionText = dv;
14197             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14198             this.value = vv;
14199             return;
14200         }
14201         // no hidden field.. - we store the value in 'value', but still display
14202         // display field!!!!
14203         this.lastSelectionText = dv;
14204         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14205         this.value = vv;
14206         
14207         
14208         
14209     },
14210     // private
14211     reset : function(){
14212         // overridden so that last data is reset..
14213         
14214         if(this.multiple){
14215             this.clearItem();
14216             return;
14217         }
14218         
14219         this.setValue(this.originalValue);
14220         //this.clearInvalid();
14221         this.lastData = false;
14222         if (this.view) {
14223             this.view.clearSelections();
14224         }
14225         
14226         this.validate();
14227     },
14228     // private
14229     findRecord : function(prop, value){
14230         var record;
14231         if(this.store.getCount() > 0){
14232             this.store.each(function(r){
14233                 if(r.data[prop] == value){
14234                     record = r;
14235                     return false;
14236                 }
14237                 return true;
14238             });
14239         }
14240         return record;
14241     },
14242     
14243     getName: function()
14244     {
14245         // returns hidden if it's set..
14246         if (!this.rendered) {return ''};
14247         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14248         
14249     },
14250     // private
14251     onViewMove : function(e, t){
14252         this.inKeyMode = false;
14253     },
14254
14255     // private
14256     onViewOver : function(e, t){
14257         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14258             return;
14259         }
14260         var item = this.view.findItemFromChild(t);
14261         
14262         if(item){
14263             var index = this.view.indexOf(item);
14264             this.select(index, false);
14265         }
14266     },
14267
14268     // private
14269     onViewClick : function(view, doFocus, el, e)
14270     {
14271         var index = this.view.getSelectedIndexes()[0];
14272         
14273         var r = this.store.getAt(index);
14274         
14275         if(this.tickable){
14276             
14277             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14278                 return;
14279             }
14280             
14281             var rm = false;
14282             var _this = this;
14283             
14284             Roo.each(this.tickItems, function(v,k){
14285                 
14286                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14287                     Roo.log(v);
14288                     _this.tickItems.splice(k, 1);
14289                     
14290                     if(typeof(e) == 'undefined' && view == false){
14291                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14292                     }
14293                     
14294                     rm = true;
14295                     return;
14296                 }
14297             });
14298             
14299             if(rm){
14300                 return;
14301             }
14302             
14303             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14304                 this.tickItems.push(r.data);
14305             }
14306             
14307             if(typeof(e) == 'undefined' && view == false){
14308                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14309             }
14310                     
14311             return;
14312         }
14313         
14314         if(r){
14315             this.onSelect(r, index);
14316         }
14317         if(doFocus !== false && !this.blockFocus){
14318             this.inputEl().focus();
14319         }
14320     },
14321
14322     // private
14323     restrictHeight : function(){
14324         //this.innerList.dom.style.height = '';
14325         //var inner = this.innerList.dom;
14326         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14327         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14328         //this.list.beginUpdate();
14329         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14330         this.list.alignTo(this.inputEl(), this.listAlign);
14331         this.list.alignTo(this.inputEl(), this.listAlign);
14332         //this.list.endUpdate();
14333     },
14334
14335     // private
14336     onEmptyResults : function(){
14337         
14338         if(this.tickable && this.editable){
14339             this.hasFocus = false;
14340             this.restrictHeight();
14341             return;
14342         }
14343         
14344         this.collapse();
14345     },
14346
14347     /**
14348      * Returns true if the dropdown list is expanded, else false.
14349      */
14350     isExpanded : function(){
14351         return this.list.isVisible();
14352     },
14353
14354     /**
14355      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14356      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14357      * @param {String} value The data value of the item to select
14358      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14359      * selected item if it is not currently in view (defaults to true)
14360      * @return {Boolean} True if the value matched an item in the list, else false
14361      */
14362     selectByValue : function(v, scrollIntoView){
14363         if(v !== undefined && v !== null){
14364             var r = this.findRecord(this.valueField || this.displayField, v);
14365             if(r){
14366                 this.select(this.store.indexOf(r), scrollIntoView);
14367                 return true;
14368             }
14369         }
14370         return false;
14371     },
14372
14373     /**
14374      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14375      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14376      * @param {Number} index The zero-based index of the list item to select
14377      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14378      * selected item if it is not currently in view (defaults to true)
14379      */
14380     select : function(index, scrollIntoView){
14381         this.selectedIndex = index;
14382         this.view.select(index);
14383         if(scrollIntoView !== false){
14384             var el = this.view.getNode(index);
14385             /*
14386              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14387              */
14388             if(el){
14389                 this.list.scrollChildIntoView(el, false);
14390             }
14391         }
14392     },
14393
14394     // private
14395     selectNext : function(){
14396         var ct = this.store.getCount();
14397         if(ct > 0){
14398             if(this.selectedIndex == -1){
14399                 this.select(0);
14400             }else if(this.selectedIndex < ct-1){
14401                 this.select(this.selectedIndex+1);
14402             }
14403         }
14404     },
14405
14406     // private
14407     selectPrev : function(){
14408         var ct = this.store.getCount();
14409         if(ct > 0){
14410             if(this.selectedIndex == -1){
14411                 this.select(0);
14412             }else if(this.selectedIndex != 0){
14413                 this.select(this.selectedIndex-1);
14414             }
14415         }
14416     },
14417
14418     // private
14419     onKeyUp : function(e){
14420         if(this.editable !== false && !e.isSpecialKey()){
14421             this.lastKey = e.getKey();
14422             this.dqTask.delay(this.queryDelay);
14423         }
14424     },
14425
14426     // private
14427     validateBlur : function(){
14428         return !this.list || !this.list.isVisible();   
14429     },
14430
14431     // private
14432     initQuery : function(){
14433         
14434         var v = this.getRawValue();
14435         
14436         if(this.tickable && this.editable){
14437             v = this.tickableInputEl().getValue();
14438         }
14439         
14440         this.doQuery(v);
14441     },
14442
14443     // private
14444     doForce : function(){
14445         if(this.inputEl().dom.value.length > 0){
14446             this.inputEl().dom.value =
14447                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14448              
14449         }
14450     },
14451
14452     /**
14453      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14454      * query allowing the query action to be canceled if needed.
14455      * @param {String} query The SQL query to execute
14456      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14457      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14458      * saved in the current store (defaults to false)
14459      */
14460     doQuery : function(q, forceAll){
14461         
14462         if(q === undefined || q === null){
14463             q = '';
14464         }
14465         var qe = {
14466             query: q,
14467             forceAll: forceAll,
14468             combo: this,
14469             cancel:false
14470         };
14471         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14472             return false;
14473         }
14474         q = qe.query;
14475         
14476         forceAll = qe.forceAll;
14477         if(forceAll === true || (q.length >= this.minChars)){
14478             
14479             this.hasQuery = true;
14480             
14481             if(this.lastQuery != q || this.alwaysQuery){
14482                 this.lastQuery = q;
14483                 if(this.mode == 'local'){
14484                     this.selectedIndex = -1;
14485                     if(forceAll){
14486                         this.store.clearFilter();
14487                     }else{
14488                         
14489                         if(this.specialFilter){
14490                             this.fireEvent('specialfilter', this);
14491                             this.onLoad();
14492                             return;
14493                         }
14494                         
14495                         this.store.filter(this.displayField, q);
14496                     }
14497                     
14498                     this.store.fireEvent("datachanged", this.store);
14499                     
14500                     this.onLoad();
14501                     
14502                     
14503                 }else{
14504                     
14505                     this.store.baseParams[this.queryParam] = q;
14506                     
14507                     var options = {params : this.getParams(q)};
14508                     
14509                     if(this.loadNext){
14510                         options.add = true;
14511                         options.params.start = this.page * this.pageSize;
14512                     }
14513                     
14514                     this.store.load(options);
14515                     
14516                     /*
14517                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14518                      *  we should expand the list on onLoad
14519                      *  so command out it
14520                      */
14521 //                    this.expand();
14522                 }
14523             }else{
14524                 this.selectedIndex = -1;
14525                 this.onLoad();   
14526             }
14527         }
14528         
14529         this.loadNext = false;
14530     },
14531     
14532     // private
14533     getParams : function(q){
14534         var p = {};
14535         //p[this.queryParam] = q;
14536         
14537         if(this.pageSize){
14538             p.start = 0;
14539             p.limit = this.pageSize;
14540         }
14541         return p;
14542     },
14543
14544     /**
14545      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14546      */
14547     collapse : function(){
14548         if(!this.isExpanded()){
14549             return;
14550         }
14551         
14552         this.list.hide();
14553         
14554         this.hasFocus = false;
14555         
14556         if(this.tickable){
14557             this.okBtn.hide();
14558             this.cancelBtn.hide();
14559             this.trigger.show();
14560             
14561             if(this.editable){
14562                 this.tickableInputEl().dom.value = '';
14563                 this.tickableInputEl().blur();
14564             }
14565             
14566         }
14567         
14568         Roo.get(document).un('mousedown', this.collapseIf, this);
14569         Roo.get(document).un('mousewheel', this.collapseIf, this);
14570         if (!this.editable) {
14571             Roo.get(document).un('keydown', this.listKeyPress, this);
14572         }
14573         this.fireEvent('collapse', this);
14574         
14575         this.validate();
14576     },
14577
14578     // private
14579     collapseIf : function(e){
14580         var in_combo  = e.within(this.el);
14581         var in_list =  e.within(this.list);
14582         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14583         
14584         if (in_combo || in_list || is_list) {
14585             //e.stopPropagation();
14586             return;
14587         }
14588         
14589         if(this.tickable){
14590             this.onTickableFooterButtonClick(e, false, false);
14591         }
14592
14593         this.collapse();
14594         
14595     },
14596
14597     /**
14598      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14599      */
14600     expand : function(){
14601        
14602         if(this.isExpanded() || !this.hasFocus){
14603             return;
14604         }
14605         
14606         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14607         this.list.setWidth(lw);
14608         
14609         Roo.log('expand');
14610         
14611         this.list.show();
14612         
14613         this.restrictHeight();
14614         
14615         if(this.tickable){
14616             
14617             this.tickItems = Roo.apply([], this.item);
14618             
14619             this.okBtn.show();
14620             this.cancelBtn.show();
14621             this.trigger.hide();
14622             
14623             if(this.editable){
14624                 this.tickableInputEl().focus();
14625             }
14626             
14627         }
14628         
14629         Roo.get(document).on('mousedown', this.collapseIf, this);
14630         Roo.get(document).on('mousewheel', this.collapseIf, this);
14631         if (!this.editable) {
14632             Roo.get(document).on('keydown', this.listKeyPress, this);
14633         }
14634         
14635         this.fireEvent('expand', this);
14636     },
14637
14638     // private
14639     // Implements the default empty TriggerField.onTriggerClick function
14640     onTriggerClick : function(e)
14641     {
14642         Roo.log('trigger click');
14643         
14644         if(this.disabled || !this.triggerList){
14645             return;
14646         }
14647         
14648         this.page = 0;
14649         this.loadNext = false;
14650         
14651         if(this.isExpanded()){
14652             this.collapse();
14653             if (!this.blockFocus) {
14654                 this.inputEl().focus();
14655             }
14656             
14657         }else {
14658             this.hasFocus = true;
14659             if(this.triggerAction == 'all') {
14660                 this.doQuery(this.allQuery, true);
14661             } else {
14662                 this.doQuery(this.getRawValue());
14663             }
14664             if (!this.blockFocus) {
14665                 this.inputEl().focus();
14666             }
14667         }
14668     },
14669     
14670     onTickableTriggerClick : function(e)
14671     {
14672         if(this.disabled){
14673             return;
14674         }
14675         
14676         this.page = 0;
14677         this.loadNext = false;
14678         this.hasFocus = true;
14679         
14680         if(this.triggerAction == 'all') {
14681             this.doQuery(this.allQuery, true);
14682         } else {
14683             this.doQuery(this.getRawValue());
14684         }
14685     },
14686     
14687     onSearchFieldClick : function(e)
14688     {
14689         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14690             this.onTickableFooterButtonClick(e, false, false);
14691             return;
14692         }
14693         
14694         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14695             return;
14696         }
14697         
14698         this.page = 0;
14699         this.loadNext = false;
14700         this.hasFocus = true;
14701         
14702         if(this.triggerAction == 'all') {
14703             this.doQuery(this.allQuery, true);
14704         } else {
14705             this.doQuery(this.getRawValue());
14706         }
14707     },
14708     
14709     listKeyPress : function(e)
14710     {
14711         //Roo.log('listkeypress');
14712         // scroll to first matching element based on key pres..
14713         if (e.isSpecialKey()) {
14714             return false;
14715         }
14716         var k = String.fromCharCode(e.getKey()).toUpperCase();
14717         //Roo.log(k);
14718         var match  = false;
14719         var csel = this.view.getSelectedNodes();
14720         var cselitem = false;
14721         if (csel.length) {
14722             var ix = this.view.indexOf(csel[0]);
14723             cselitem  = this.store.getAt(ix);
14724             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14725                 cselitem = false;
14726             }
14727             
14728         }
14729         
14730         this.store.each(function(v) { 
14731             if (cselitem) {
14732                 // start at existing selection.
14733                 if (cselitem.id == v.id) {
14734                     cselitem = false;
14735                 }
14736                 return true;
14737             }
14738                 
14739             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14740                 match = this.store.indexOf(v);
14741                 return false;
14742             }
14743             return true;
14744         }, this);
14745         
14746         if (match === false) {
14747             return true; // no more action?
14748         }
14749         // scroll to?
14750         this.view.select(match);
14751         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14752         sn.scrollIntoView(sn.dom.parentNode, false);
14753     },
14754     
14755     onViewScroll : function(e, t){
14756         
14757         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){
14758             return;
14759         }
14760         
14761         this.hasQuery = true;
14762         
14763         this.loading = this.list.select('.loading', true).first();
14764         
14765         if(this.loading === null){
14766             this.list.createChild({
14767                 tag: 'div',
14768                 cls: 'loading roo-select2-more-results roo-select2-active',
14769                 html: 'Loading more results...'
14770             });
14771             
14772             this.loading = this.list.select('.loading', true).first();
14773             
14774             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14775             
14776             this.loading.hide();
14777         }
14778         
14779         this.loading.show();
14780         
14781         var _combo = this;
14782         
14783         this.page++;
14784         this.loadNext = true;
14785         
14786         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14787         
14788         return;
14789     },
14790     
14791     addItem : function(o)
14792     {   
14793         var dv = ''; // display value
14794         
14795         if (this.displayField) {
14796             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14797         } else {
14798             // this is an error condition!!!
14799             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14800         }
14801         
14802         if(!dv.length){
14803             return;
14804         }
14805         
14806         var choice = this.choices.createChild({
14807             tag: 'li',
14808             cls: 'roo-select2-search-choice',
14809             cn: [
14810                 {
14811                     tag: 'div',
14812                     html: dv
14813                 },
14814                 {
14815                     tag: 'a',
14816                     href: '#',
14817                     cls: 'roo-select2-search-choice-close fa fa-times',
14818                     tabindex: '-1'
14819                 }
14820             ]
14821             
14822         }, this.searchField);
14823         
14824         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14825         
14826         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14827         
14828         this.item.push(o);
14829         
14830         this.lastData = o;
14831         
14832         this.syncValue();
14833         
14834         this.inputEl().dom.value = '';
14835         
14836         this.validate();
14837     },
14838     
14839     onRemoveItem : function(e, _self, o)
14840     {
14841         e.preventDefault();
14842         
14843         this.lastItem = Roo.apply([], this.item);
14844         
14845         var index = this.item.indexOf(o.data) * 1;
14846         
14847         if( index < 0){
14848             Roo.log('not this item?!');
14849             return;
14850         }
14851         
14852         this.item.splice(index, 1);
14853         o.item.remove();
14854         
14855         this.syncValue();
14856         
14857         this.fireEvent('remove', this, e);
14858         
14859         this.validate();
14860         
14861     },
14862     
14863     syncValue : function()
14864     {
14865         if(!this.item.length){
14866             this.clearValue();
14867             return;
14868         }
14869             
14870         var value = [];
14871         var _this = this;
14872         Roo.each(this.item, function(i){
14873             if(_this.valueField){
14874                 value.push(i[_this.valueField]);
14875                 return;
14876             }
14877
14878             value.push(i);
14879         });
14880
14881         this.value = value.join(',');
14882
14883         if(this.hiddenField){
14884             this.hiddenField.dom.value = this.value;
14885         }
14886         
14887         this.store.fireEvent("datachanged", this.store);
14888         
14889         this.validate();
14890     },
14891     
14892     clearItem : function()
14893     {
14894         if(!this.multiple){
14895             return;
14896         }
14897         
14898         this.item = [];
14899         
14900         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14901            c.remove();
14902         });
14903         
14904         this.syncValue();
14905         
14906         this.validate();
14907         
14908         if(this.tickable && !Roo.isTouch){
14909             this.view.refresh();
14910         }
14911     },
14912     
14913     inputEl: function ()
14914     {
14915         if(Roo.isIOS && this.useNativeIOS){
14916             return this.el.select('select.roo-ios-select', true).first();
14917         }
14918         
14919         if(Roo.isTouch && this.mobileTouchView){
14920             return this.el.select('input.form-control',true).first();
14921         }
14922         
14923         if(this.tickable){
14924             return this.searchField;
14925         }
14926         
14927         return this.el.select('input.form-control',true).first();
14928     },
14929     
14930     onTickableFooterButtonClick : function(e, btn, el)
14931     {
14932         e.preventDefault();
14933         
14934         this.lastItem = Roo.apply([], this.item);
14935         
14936         if(btn && btn.name == 'cancel'){
14937             this.tickItems = Roo.apply([], this.item);
14938             this.collapse();
14939             return;
14940         }
14941         
14942         this.clearItem();
14943         
14944         var _this = this;
14945         
14946         Roo.each(this.tickItems, function(o){
14947             _this.addItem(o);
14948         });
14949         
14950         this.collapse();
14951         
14952     },
14953     
14954     validate : function()
14955     {
14956         if(this.getVisibilityEl().hasClass('hidden')){
14957             return true;
14958         }
14959         
14960         var v = this.getRawValue();
14961         
14962         if(this.multiple){
14963             v = this.getValue();
14964         }
14965         
14966         if(this.disabled || this.allowBlank || v.length){
14967             this.markValid();
14968             return true;
14969         }
14970         
14971         this.markInvalid();
14972         return false;
14973     },
14974     
14975     tickableInputEl : function()
14976     {
14977         if(!this.tickable || !this.editable){
14978             return this.inputEl();
14979         }
14980         
14981         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14982     },
14983     
14984     
14985     getAutoCreateTouchView : function()
14986     {
14987         var id = Roo.id();
14988         
14989         var cfg = {
14990             cls: 'form-group' //input-group
14991         };
14992         
14993         var input =  {
14994             tag: 'input',
14995             id : id,
14996             type : this.inputType,
14997             cls : 'form-control x-combo-noedit',
14998             autocomplete: 'new-password',
14999             placeholder : this.placeholder || '',
15000             readonly : true
15001         };
15002         
15003         if (this.name) {
15004             input.name = this.name;
15005         }
15006         
15007         if (this.size) {
15008             input.cls += ' input-' + this.size;
15009         }
15010         
15011         if (this.disabled) {
15012             input.disabled = true;
15013         }
15014         
15015         var inputblock = {
15016             cls : '',
15017             cn : [
15018                 input
15019             ]
15020         };
15021         
15022         if(this.before){
15023             inputblock.cls += ' input-group';
15024             
15025             inputblock.cn.unshift({
15026                 tag :'span',
15027                 cls : 'input-group-addon',
15028                 html : this.before
15029             });
15030         }
15031         
15032         if(this.removable && !this.multiple){
15033             inputblock.cls += ' roo-removable';
15034             
15035             inputblock.cn.push({
15036                 tag: 'button',
15037                 html : 'x',
15038                 cls : 'roo-combo-removable-btn close'
15039             });
15040         }
15041
15042         if(this.hasFeedback && !this.allowBlank){
15043             
15044             inputblock.cls += ' has-feedback';
15045             
15046             inputblock.cn.push({
15047                 tag: 'span',
15048                 cls: 'glyphicon form-control-feedback'
15049             });
15050             
15051         }
15052         
15053         if (this.after) {
15054             
15055             inputblock.cls += (this.before) ? '' : ' input-group';
15056             
15057             inputblock.cn.push({
15058                 tag :'span',
15059                 cls : 'input-group-addon',
15060                 html : this.after
15061             });
15062         }
15063
15064         var box = {
15065             tag: 'div',
15066             cn: [
15067                 {
15068                     tag: 'input',
15069                     type : 'hidden',
15070                     cls: 'form-hidden-field'
15071                 },
15072                 inputblock
15073             ]
15074             
15075         };
15076         
15077         if(this.multiple){
15078             box = {
15079                 tag: 'div',
15080                 cn: [
15081                     {
15082                         tag: 'input',
15083                         type : 'hidden',
15084                         cls: 'form-hidden-field'
15085                     },
15086                     {
15087                         tag: 'ul',
15088                         cls: 'roo-select2-choices',
15089                         cn:[
15090                             {
15091                                 tag: 'li',
15092                                 cls: 'roo-select2-search-field',
15093                                 cn: [
15094
15095                                     inputblock
15096                                 ]
15097                             }
15098                         ]
15099                     }
15100                 ]
15101             }
15102         };
15103         
15104         var combobox = {
15105             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15106             cn: [
15107                 box
15108             ]
15109         };
15110         
15111         if(!this.multiple && this.showToggleBtn){
15112             
15113             var caret = {
15114                         tag: 'span',
15115                         cls: 'caret'
15116             };
15117             
15118             if (this.caret != false) {
15119                 caret = {
15120                      tag: 'i',
15121                      cls: 'fa fa-' + this.caret
15122                 };
15123                 
15124             }
15125             
15126             combobox.cn.push({
15127                 tag :'span',
15128                 cls : 'input-group-addon btn dropdown-toggle',
15129                 cn : [
15130                     caret,
15131                     {
15132                         tag: 'span',
15133                         cls: 'combobox-clear',
15134                         cn  : [
15135                             {
15136                                 tag : 'i',
15137                                 cls: 'icon-remove'
15138                             }
15139                         ]
15140                     }
15141                 ]
15142
15143             })
15144         }
15145         
15146         if(this.multiple){
15147             combobox.cls += ' roo-select2-container-multi';
15148         }
15149         
15150         var align = this.labelAlign || this.parentLabelAlign();
15151         
15152         if (align ==='left' && this.fieldLabel.length) {
15153
15154             cfg.cn = [
15155                 {
15156                    tag : 'i',
15157                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15158                    tooltip : 'This field is required'
15159                 },
15160                 {
15161                     tag: 'label',
15162                     cls : 'control-label',
15163                     html : this.fieldLabel
15164
15165                 },
15166                 {
15167                     cls : '', 
15168                     cn: [
15169                         combobox
15170                     ]
15171                 }
15172             ];
15173             
15174             var labelCfg = cfg.cn[1];
15175             var contentCfg = cfg.cn[2];
15176             
15177
15178             if(this.indicatorpos == 'right'){
15179                 cfg.cn = [
15180                     {
15181                         tag: 'label',
15182                         'for' :  id,
15183                         cls : 'control-label',
15184                         cn : [
15185                             {
15186                                 tag : 'span',
15187                                 html : this.fieldLabel
15188                             },
15189                             {
15190                                 tag : 'i',
15191                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15192                                 tooltip : 'This field is required'
15193                             }
15194                         ]
15195                     },
15196                     {
15197                         cls : "",
15198                         cn: [
15199                             combobox
15200                         ]
15201                     }
15202
15203                 ];
15204                 
15205                 labelCfg = cfg.cn[0];
15206                 contentCfg = cfg.cn[1];
15207             }
15208             
15209            
15210             
15211             if(this.labelWidth > 12){
15212                 labelCfg.style = "width: " + this.labelWidth + 'px';
15213             }
15214             
15215             if(this.labelWidth < 13 && this.labelmd == 0){
15216                 this.labelmd = this.labelWidth;
15217             }
15218             
15219             if(this.labellg > 0){
15220                 labelCfg.cls += ' col-lg-' + this.labellg;
15221                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15222             }
15223             
15224             if(this.labelmd > 0){
15225                 labelCfg.cls += ' col-md-' + this.labelmd;
15226                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15227             }
15228             
15229             if(this.labelsm > 0){
15230                 labelCfg.cls += ' col-sm-' + this.labelsm;
15231                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15232             }
15233             
15234             if(this.labelxs > 0){
15235                 labelCfg.cls += ' col-xs-' + this.labelxs;
15236                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15237             }
15238                 
15239                 
15240         } else if ( this.fieldLabel.length) {
15241             cfg.cn = [
15242                 {
15243                    tag : 'i',
15244                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15245                    tooltip : 'This field is required'
15246                 },
15247                 {
15248                     tag: 'label',
15249                     cls : 'control-label',
15250                     html : this.fieldLabel
15251
15252                 },
15253                 {
15254                     cls : '', 
15255                     cn: [
15256                         combobox
15257                     ]
15258                 }
15259             ];
15260             
15261             if(this.indicatorpos == 'right'){
15262                 cfg.cn = [
15263                     {
15264                         tag: 'label',
15265                         cls : 'control-label',
15266                         html : this.fieldLabel,
15267                         cn : [
15268                             {
15269                                tag : 'i',
15270                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15271                                tooltip : 'This field is required'
15272                             }
15273                         ]
15274                     },
15275                     {
15276                         cls : '', 
15277                         cn: [
15278                             combobox
15279                         ]
15280                     }
15281                 ];
15282             }
15283         } else {
15284             cfg.cn = combobox;    
15285         }
15286         
15287         
15288         var settings = this;
15289         
15290         ['xs','sm','md','lg'].map(function(size){
15291             if (settings[size]) {
15292                 cfg.cls += ' col-' + size + '-' + settings[size];
15293             }
15294         });
15295         
15296         return cfg;
15297     },
15298     
15299     initTouchView : function()
15300     {
15301         this.renderTouchView();
15302         
15303         this.touchViewEl.on('scroll', function(){
15304             this.el.dom.scrollTop = 0;
15305         }, this);
15306         
15307         this.originalValue = this.getValue();
15308         
15309         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15310         
15311         this.inputEl().on("click", this.showTouchView, this);
15312         if (this.triggerEl) {
15313             this.triggerEl.on("click", this.showTouchView, this);
15314         }
15315         
15316         
15317         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15318         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15319         
15320         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15321         
15322         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15323         this.store.on('load', this.onTouchViewLoad, this);
15324         this.store.on('loadexception', this.onTouchViewLoadException, this);
15325         
15326         if(this.hiddenName){
15327             
15328             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15329             
15330             this.hiddenField.dom.value =
15331                 this.hiddenValue !== undefined ? this.hiddenValue :
15332                 this.value !== undefined ? this.value : '';
15333         
15334             this.el.dom.removeAttribute('name');
15335             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15336         }
15337         
15338         if(this.multiple){
15339             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15340             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15341         }
15342         
15343         if(this.removable && !this.multiple){
15344             var close = this.closeTriggerEl();
15345             if(close){
15346                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15347                 close.on('click', this.removeBtnClick, this, close);
15348             }
15349         }
15350         /*
15351          * fix the bug in Safari iOS8
15352          */
15353         this.inputEl().on("focus", function(e){
15354             document.activeElement.blur();
15355         }, this);
15356         
15357         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15358         
15359         return;
15360         
15361         
15362     },
15363     
15364     renderTouchView : function()
15365     {
15366         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15367         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15368         
15369         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15370         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15371         
15372         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15373         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15374         this.touchViewBodyEl.setStyle('overflow', 'auto');
15375         
15376         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15377         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15378         
15379         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15380         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15381         
15382     },
15383     
15384     showTouchView : function()
15385     {
15386         if(this.disabled){
15387             return;
15388         }
15389         
15390         this.touchViewHeaderEl.hide();
15391
15392         if(this.modalTitle.length){
15393             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15394             this.touchViewHeaderEl.show();
15395         }
15396
15397         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15398         this.touchViewEl.show();
15399
15400         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15401         
15402         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15403         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15404
15405         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15406
15407         if(this.modalTitle.length){
15408             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15409         }
15410         
15411         this.touchViewBodyEl.setHeight(bodyHeight);
15412
15413         if(this.animate){
15414             var _this = this;
15415             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15416         }else{
15417             this.touchViewEl.addClass('in');
15418         }
15419         
15420         if(this._touchViewMask){
15421             Roo.get(document.body).addClass("x-body-masked");
15422             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15423             this._touchViewMask.setStyle('z-index', 10000);
15424             this._touchViewMask.addClass('show');
15425         }
15426         
15427         this.doTouchViewQuery();
15428         
15429     },
15430     
15431     hideTouchView : function()
15432     {
15433         this.touchViewEl.removeClass('in');
15434
15435         if(this.animate){
15436             var _this = this;
15437             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15438         }else{
15439             this.touchViewEl.setStyle('display', 'none');
15440         }
15441         
15442         if(this._touchViewMask){
15443             this._touchViewMask.removeClass('show');
15444             Roo.get(document.body).removeClass("x-body-masked");
15445         }
15446     },
15447     
15448     setTouchViewValue : function()
15449     {
15450         if(this.multiple){
15451             this.clearItem();
15452         
15453             var _this = this;
15454
15455             Roo.each(this.tickItems, function(o){
15456                 this.addItem(o);
15457             }, this);
15458         }
15459         
15460         this.hideTouchView();
15461     },
15462     
15463     doTouchViewQuery : function()
15464     {
15465         var qe = {
15466             query: '',
15467             forceAll: true,
15468             combo: this,
15469             cancel:false
15470         };
15471         
15472         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15473             return false;
15474         }
15475         
15476         if(!this.alwaysQuery || this.mode == 'local'){
15477             this.onTouchViewLoad();
15478             return;
15479         }
15480         
15481         this.store.load();
15482     },
15483     
15484     onTouchViewBeforeLoad : function(combo,opts)
15485     {
15486         return;
15487     },
15488
15489     // private
15490     onTouchViewLoad : function()
15491     {
15492         if(this.store.getCount() < 1){
15493             this.onTouchViewEmptyResults();
15494             return;
15495         }
15496         
15497         this.clearTouchView();
15498         
15499         var rawValue = this.getRawValue();
15500         
15501         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15502         
15503         this.tickItems = [];
15504         
15505         this.store.data.each(function(d, rowIndex){
15506             var row = this.touchViewListGroup.createChild(template);
15507             
15508             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15509                 row.addClass(d.data.cls);
15510             }
15511             
15512             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15513                 var cfg = {
15514                     data : d.data,
15515                     html : d.data[this.displayField]
15516                 };
15517                 
15518                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15519                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15520                 }
15521             }
15522             row.removeClass('selected');
15523             if(!this.multiple && this.valueField &&
15524                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15525             {
15526                 // radio buttons..
15527                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15528                 row.addClass('selected');
15529             }
15530             
15531             if(this.multiple && this.valueField &&
15532                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15533             {
15534                 
15535                 // checkboxes...
15536                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15537                 this.tickItems.push(d.data);
15538             }
15539             
15540             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15541             
15542         }, this);
15543         
15544         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15545         
15546         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15547
15548         if(this.modalTitle.length){
15549             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15550         }
15551
15552         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15553         
15554         if(this.mobile_restrict_height && listHeight < bodyHeight){
15555             this.touchViewBodyEl.setHeight(listHeight);
15556         }
15557         
15558         var _this = this;
15559         
15560         if(firstChecked && listHeight > bodyHeight){
15561             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15562         }
15563         
15564     },
15565     
15566     onTouchViewLoadException : function()
15567     {
15568         this.hideTouchView();
15569     },
15570     
15571     onTouchViewEmptyResults : function()
15572     {
15573         this.clearTouchView();
15574         
15575         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15576         
15577         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15578         
15579     },
15580     
15581     clearTouchView : function()
15582     {
15583         this.touchViewListGroup.dom.innerHTML = '';
15584     },
15585     
15586     onTouchViewClick : function(e, el, o)
15587     {
15588         e.preventDefault();
15589         
15590         var row = o.row;
15591         var rowIndex = o.rowIndex;
15592         
15593         var r = this.store.getAt(rowIndex);
15594         
15595         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15596             
15597             if(!this.multiple){
15598                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15599                     c.dom.removeAttribute('checked');
15600                 }, this);
15601
15602                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15603
15604                 this.setFromData(r.data);
15605
15606                 var close = this.closeTriggerEl();
15607
15608                 if(close){
15609                     close.show();
15610                 }
15611
15612                 this.hideTouchView();
15613
15614                 this.fireEvent('select', this, r, rowIndex);
15615
15616                 return;
15617             }
15618
15619             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15620                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15621                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15622                 return;
15623             }
15624
15625             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15626             this.addItem(r.data);
15627             this.tickItems.push(r.data);
15628         }
15629     },
15630     
15631     getAutoCreateNativeIOS : function()
15632     {
15633         var cfg = {
15634             cls: 'form-group' //input-group,
15635         };
15636         
15637         var combobox =  {
15638             tag: 'select',
15639             cls : 'roo-ios-select'
15640         };
15641         
15642         if (this.name) {
15643             combobox.name = this.name;
15644         }
15645         
15646         if (this.disabled) {
15647             combobox.disabled = true;
15648         }
15649         
15650         var settings = this;
15651         
15652         ['xs','sm','md','lg'].map(function(size){
15653             if (settings[size]) {
15654                 cfg.cls += ' col-' + size + '-' + settings[size];
15655             }
15656         });
15657         
15658         cfg.cn = combobox;
15659         
15660         return cfg;
15661         
15662     },
15663     
15664     initIOSView : function()
15665     {
15666         this.store.on('load', this.onIOSViewLoad, this);
15667         
15668         return;
15669     },
15670     
15671     onIOSViewLoad : function()
15672     {
15673         if(this.store.getCount() < 1){
15674             return;
15675         }
15676         
15677         this.clearIOSView();
15678         
15679         if(this.allowBlank) {
15680             
15681             var default_text = '-- SELECT --';
15682             
15683             if(this.placeholder.length){
15684                 default_text = this.placeholder;
15685             }
15686             
15687             if(this.emptyTitle.length){
15688                 default_text += ' - ' + this.emptyTitle + ' -';
15689             }
15690             
15691             var opt = this.inputEl().createChild({
15692                 tag: 'option',
15693                 value : 0,
15694                 html : default_text
15695             });
15696             
15697             var o = {};
15698             o[this.valueField] = 0;
15699             o[this.displayField] = default_text;
15700             
15701             this.ios_options.push({
15702                 data : o,
15703                 el : opt
15704             });
15705             
15706         }
15707         
15708         this.store.data.each(function(d, rowIndex){
15709             
15710             var html = '';
15711             
15712             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15713                 html = d.data[this.displayField];
15714             }
15715             
15716             var value = '';
15717             
15718             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15719                 value = d.data[this.valueField];
15720             }
15721             
15722             var option = {
15723                 tag: 'option',
15724                 value : value,
15725                 html : html
15726             };
15727             
15728             if(this.value == d.data[this.valueField]){
15729                 option['selected'] = true;
15730             }
15731             
15732             var opt = this.inputEl().createChild(option);
15733             
15734             this.ios_options.push({
15735                 data : d.data,
15736                 el : opt
15737             });
15738             
15739         }, this);
15740         
15741         this.inputEl().on('change', function(){
15742            this.fireEvent('select', this);
15743         }, this);
15744         
15745     },
15746     
15747     clearIOSView: function()
15748     {
15749         this.inputEl().dom.innerHTML = '';
15750         
15751         this.ios_options = [];
15752     },
15753     
15754     setIOSValue: function(v)
15755     {
15756         this.value = v;
15757         
15758         if(!this.ios_options){
15759             return;
15760         }
15761         
15762         Roo.each(this.ios_options, function(opts){
15763            
15764            opts.el.dom.removeAttribute('selected');
15765            
15766            if(opts.data[this.valueField] != v){
15767                return;
15768            }
15769            
15770            opts.el.dom.setAttribute('selected', true);
15771            
15772         }, this);
15773     }
15774
15775     /** 
15776     * @cfg {Boolean} grow 
15777     * @hide 
15778     */
15779     /** 
15780     * @cfg {Number} growMin 
15781     * @hide 
15782     */
15783     /** 
15784     * @cfg {Number} growMax 
15785     * @hide 
15786     */
15787     /**
15788      * @hide
15789      * @method autoSize
15790      */
15791 });
15792
15793 Roo.apply(Roo.bootstrap.ComboBox,  {
15794     
15795     header : {
15796         tag: 'div',
15797         cls: 'modal-header',
15798         cn: [
15799             {
15800                 tag: 'h4',
15801                 cls: 'modal-title'
15802             }
15803         ]
15804     },
15805     
15806     body : {
15807         tag: 'div',
15808         cls: 'modal-body',
15809         cn: [
15810             {
15811                 tag: 'ul',
15812                 cls: 'list-group'
15813             }
15814         ]
15815     },
15816     
15817     listItemRadio : {
15818         tag: 'li',
15819         cls: 'list-group-item',
15820         cn: [
15821             {
15822                 tag: 'span',
15823                 cls: 'roo-combobox-list-group-item-value'
15824             },
15825             {
15826                 tag: 'div',
15827                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15828                 cn: [
15829                     {
15830                         tag: 'input',
15831                         type: 'radio'
15832                     },
15833                     {
15834                         tag: 'label'
15835                     }
15836                 ]
15837             }
15838         ]
15839     },
15840     
15841     listItemCheckbox : {
15842         tag: 'li',
15843         cls: 'list-group-item',
15844         cn: [
15845             {
15846                 tag: 'span',
15847                 cls: 'roo-combobox-list-group-item-value'
15848             },
15849             {
15850                 tag: 'div',
15851                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15852                 cn: [
15853                     {
15854                         tag: 'input',
15855                         type: 'checkbox'
15856                     },
15857                     {
15858                         tag: 'label'
15859                     }
15860                 ]
15861             }
15862         ]
15863     },
15864     
15865     emptyResult : {
15866         tag: 'div',
15867         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15868     },
15869     
15870     footer : {
15871         tag: 'div',
15872         cls: 'modal-footer',
15873         cn: [
15874             {
15875                 tag: 'div',
15876                 cls: 'row',
15877                 cn: [
15878                     {
15879                         tag: 'div',
15880                         cls: 'col-xs-6 text-left',
15881                         cn: {
15882                             tag: 'button',
15883                             cls: 'btn btn-danger roo-touch-view-cancel',
15884                             html: 'Cancel'
15885                         }
15886                     },
15887                     {
15888                         tag: 'div',
15889                         cls: 'col-xs-6 text-right',
15890                         cn: {
15891                             tag: 'button',
15892                             cls: 'btn btn-success roo-touch-view-ok',
15893                             html: 'OK'
15894                         }
15895                     }
15896                 ]
15897             }
15898         ]
15899         
15900     }
15901 });
15902
15903 Roo.apply(Roo.bootstrap.ComboBox,  {
15904     
15905     touchViewTemplate : {
15906         tag: 'div',
15907         cls: 'modal fade roo-combobox-touch-view',
15908         cn: [
15909             {
15910                 tag: 'div',
15911                 cls: 'modal-dialog',
15912                 style : 'position:fixed', // we have to fix position....
15913                 cn: [
15914                     {
15915                         tag: 'div',
15916                         cls: 'modal-content',
15917                         cn: [
15918                             Roo.bootstrap.ComboBox.header,
15919                             Roo.bootstrap.ComboBox.body,
15920                             Roo.bootstrap.ComboBox.footer
15921                         ]
15922                     }
15923                 ]
15924             }
15925         ]
15926     }
15927 });/*
15928  * Based on:
15929  * Ext JS Library 1.1.1
15930  * Copyright(c) 2006-2007, Ext JS, LLC.
15931  *
15932  * Originally Released Under LGPL - original licence link has changed is not relivant.
15933  *
15934  * Fork - LGPL
15935  * <script type="text/javascript">
15936  */
15937
15938 /**
15939  * @class Roo.View
15940  * @extends Roo.util.Observable
15941  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15942  * This class also supports single and multi selection modes. <br>
15943  * Create a data model bound view:
15944  <pre><code>
15945  var store = new Roo.data.Store(...);
15946
15947  var view = new Roo.View({
15948     el : "my-element",
15949     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15950  
15951     singleSelect: true,
15952     selectedClass: "ydataview-selected",
15953     store: store
15954  });
15955
15956  // listen for node click?
15957  view.on("click", function(vw, index, node, e){
15958  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15959  });
15960
15961  // load XML data
15962  dataModel.load("foobar.xml");
15963  </code></pre>
15964  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15965  * <br><br>
15966  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15967  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15968  * 
15969  * Note: old style constructor is still suported (container, template, config)
15970  * 
15971  * @constructor
15972  * Create a new View
15973  * @param {Object} config The config object
15974  * 
15975  */
15976 Roo.View = function(config, depreciated_tpl, depreciated_config){
15977     
15978     this.parent = false;
15979     
15980     if (typeof(depreciated_tpl) == 'undefined') {
15981         // new way.. - universal constructor.
15982         Roo.apply(this, config);
15983         this.el  = Roo.get(this.el);
15984     } else {
15985         // old format..
15986         this.el  = Roo.get(config);
15987         this.tpl = depreciated_tpl;
15988         Roo.apply(this, depreciated_config);
15989     }
15990     this.wrapEl  = this.el.wrap().wrap();
15991     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15992     
15993     
15994     if(typeof(this.tpl) == "string"){
15995         this.tpl = new Roo.Template(this.tpl);
15996     } else {
15997         // support xtype ctors..
15998         this.tpl = new Roo.factory(this.tpl, Roo);
15999     }
16000     
16001     
16002     this.tpl.compile();
16003     
16004     /** @private */
16005     this.addEvents({
16006         /**
16007          * @event beforeclick
16008          * Fires before a click is processed. Returns false to cancel the default action.
16009          * @param {Roo.View} this
16010          * @param {Number} index The index of the target node
16011          * @param {HTMLElement} node The target node
16012          * @param {Roo.EventObject} e The raw event object
16013          */
16014             "beforeclick" : true,
16015         /**
16016          * @event click
16017          * Fires when a template node is clicked.
16018          * @param {Roo.View} this
16019          * @param {Number} index The index of the target node
16020          * @param {HTMLElement} node The target node
16021          * @param {Roo.EventObject} e The raw event object
16022          */
16023             "click" : true,
16024         /**
16025          * @event dblclick
16026          * Fires when a template node is double clicked.
16027          * @param {Roo.View} this
16028          * @param {Number} index The index of the target node
16029          * @param {HTMLElement} node The target node
16030          * @param {Roo.EventObject} e The raw event object
16031          */
16032             "dblclick" : true,
16033         /**
16034          * @event contextmenu
16035          * Fires when a template node is right clicked.
16036          * @param {Roo.View} this
16037          * @param {Number} index The index of the target node
16038          * @param {HTMLElement} node The target node
16039          * @param {Roo.EventObject} e The raw event object
16040          */
16041             "contextmenu" : true,
16042         /**
16043          * @event selectionchange
16044          * Fires when the selected nodes change.
16045          * @param {Roo.View} this
16046          * @param {Array} selections Array of the selected nodes
16047          */
16048             "selectionchange" : true,
16049     
16050         /**
16051          * @event beforeselect
16052          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16053          * @param {Roo.View} this
16054          * @param {HTMLElement} node The node to be selected
16055          * @param {Array} selections Array of currently selected nodes
16056          */
16057             "beforeselect" : true,
16058         /**
16059          * @event preparedata
16060          * Fires on every row to render, to allow you to change the data.
16061          * @param {Roo.View} this
16062          * @param {Object} data to be rendered (change this)
16063          */
16064           "preparedata" : true
16065           
16066           
16067         });
16068
16069
16070
16071     this.el.on({
16072         "click": this.onClick,
16073         "dblclick": this.onDblClick,
16074         "contextmenu": this.onContextMenu,
16075         scope:this
16076     });
16077
16078     this.selections = [];
16079     this.nodes = [];
16080     this.cmp = new Roo.CompositeElementLite([]);
16081     if(this.store){
16082         this.store = Roo.factory(this.store, Roo.data);
16083         this.setStore(this.store, true);
16084     }
16085     
16086     if ( this.footer && this.footer.xtype) {
16087            
16088          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16089         
16090         this.footer.dataSource = this.store;
16091         this.footer.container = fctr;
16092         this.footer = Roo.factory(this.footer, Roo);
16093         fctr.insertFirst(this.el);
16094         
16095         // this is a bit insane - as the paging toolbar seems to detach the el..
16096 //        dom.parentNode.parentNode.parentNode
16097          // they get detached?
16098     }
16099     
16100     
16101     Roo.View.superclass.constructor.call(this);
16102     
16103     
16104 };
16105
16106 Roo.extend(Roo.View, Roo.util.Observable, {
16107     
16108      /**
16109      * @cfg {Roo.data.Store} store Data store to load data from.
16110      */
16111     store : false,
16112     
16113     /**
16114      * @cfg {String|Roo.Element} el The container element.
16115      */
16116     el : '',
16117     
16118     /**
16119      * @cfg {String|Roo.Template} tpl The template used by this View 
16120      */
16121     tpl : false,
16122     /**
16123      * @cfg {String} dataName the named area of the template to use as the data area
16124      *                          Works with domtemplates roo-name="name"
16125      */
16126     dataName: false,
16127     /**
16128      * @cfg {String} selectedClass The css class to add to selected nodes
16129      */
16130     selectedClass : "x-view-selected",
16131      /**
16132      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16133      */
16134     emptyText : "",
16135     
16136     /**
16137      * @cfg {String} text to display on mask (default Loading)
16138      */
16139     mask : false,
16140     /**
16141      * @cfg {Boolean} multiSelect Allow multiple selection
16142      */
16143     multiSelect : false,
16144     /**
16145      * @cfg {Boolean} singleSelect Allow single selection
16146      */
16147     singleSelect:  false,
16148     
16149     /**
16150      * @cfg {Boolean} toggleSelect - selecting 
16151      */
16152     toggleSelect : false,
16153     
16154     /**
16155      * @cfg {Boolean} tickable - selecting 
16156      */
16157     tickable : false,
16158     
16159     /**
16160      * Returns the element this view is bound to.
16161      * @return {Roo.Element}
16162      */
16163     getEl : function(){
16164         return this.wrapEl;
16165     },
16166     
16167     
16168
16169     /**
16170      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16171      */
16172     refresh : function(){
16173         //Roo.log('refresh');
16174         var t = this.tpl;
16175         
16176         // if we are using something like 'domtemplate', then
16177         // the what gets used is:
16178         // t.applySubtemplate(NAME, data, wrapping data..)
16179         // the outer template then get' applied with
16180         //     the store 'extra data'
16181         // and the body get's added to the
16182         //      roo-name="data" node?
16183         //      <span class='roo-tpl-{name}'></span> ?????
16184         
16185         
16186         
16187         this.clearSelections();
16188         this.el.update("");
16189         var html = [];
16190         var records = this.store.getRange();
16191         if(records.length < 1) {
16192             
16193             // is this valid??  = should it render a template??
16194             
16195             this.el.update(this.emptyText);
16196             return;
16197         }
16198         var el = this.el;
16199         if (this.dataName) {
16200             this.el.update(t.apply(this.store.meta)); //????
16201             el = this.el.child('.roo-tpl-' + this.dataName);
16202         }
16203         
16204         for(var i = 0, len = records.length; i < len; i++){
16205             var data = this.prepareData(records[i].data, i, records[i]);
16206             this.fireEvent("preparedata", this, data, i, records[i]);
16207             
16208             var d = Roo.apply({}, data);
16209             
16210             if(this.tickable){
16211                 Roo.apply(d, {'roo-id' : Roo.id()});
16212                 
16213                 var _this = this;
16214             
16215                 Roo.each(this.parent.item, function(item){
16216                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16217                         return;
16218                     }
16219                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16220                 });
16221             }
16222             
16223             html[html.length] = Roo.util.Format.trim(
16224                 this.dataName ?
16225                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16226                     t.apply(d)
16227             );
16228         }
16229         
16230         
16231         
16232         el.update(html.join(""));
16233         this.nodes = el.dom.childNodes;
16234         this.updateIndexes(0);
16235     },
16236     
16237
16238     /**
16239      * Function to override to reformat the data that is sent to
16240      * the template for each node.
16241      * DEPRICATED - use the preparedata event handler.
16242      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16243      * a JSON object for an UpdateManager bound view).
16244      */
16245     prepareData : function(data, index, record)
16246     {
16247         this.fireEvent("preparedata", this, data, index, record);
16248         return data;
16249     },
16250
16251     onUpdate : function(ds, record){
16252         // Roo.log('on update');   
16253         this.clearSelections();
16254         var index = this.store.indexOf(record);
16255         var n = this.nodes[index];
16256         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16257         n.parentNode.removeChild(n);
16258         this.updateIndexes(index, index);
16259     },
16260
16261     
16262     
16263 // --------- FIXME     
16264     onAdd : function(ds, records, index)
16265     {
16266         //Roo.log(['on Add', ds, records, index] );        
16267         this.clearSelections();
16268         if(this.nodes.length == 0){
16269             this.refresh();
16270             return;
16271         }
16272         var n = this.nodes[index];
16273         for(var i = 0, len = records.length; i < len; i++){
16274             var d = this.prepareData(records[i].data, i, records[i]);
16275             if(n){
16276                 this.tpl.insertBefore(n, d);
16277             }else{
16278                 
16279                 this.tpl.append(this.el, d);
16280             }
16281         }
16282         this.updateIndexes(index);
16283     },
16284
16285     onRemove : function(ds, record, index){
16286        // Roo.log('onRemove');
16287         this.clearSelections();
16288         var el = this.dataName  ?
16289             this.el.child('.roo-tpl-' + this.dataName) :
16290             this.el; 
16291         
16292         el.dom.removeChild(this.nodes[index]);
16293         this.updateIndexes(index);
16294     },
16295
16296     /**
16297      * Refresh an individual node.
16298      * @param {Number} index
16299      */
16300     refreshNode : function(index){
16301         this.onUpdate(this.store, this.store.getAt(index));
16302     },
16303
16304     updateIndexes : function(startIndex, endIndex){
16305         var ns = this.nodes;
16306         startIndex = startIndex || 0;
16307         endIndex = endIndex || ns.length - 1;
16308         for(var i = startIndex; i <= endIndex; i++){
16309             ns[i].nodeIndex = i;
16310         }
16311     },
16312
16313     /**
16314      * Changes the data store this view uses and refresh the view.
16315      * @param {Store} store
16316      */
16317     setStore : function(store, initial){
16318         if(!initial && this.store){
16319             this.store.un("datachanged", this.refresh);
16320             this.store.un("add", this.onAdd);
16321             this.store.un("remove", this.onRemove);
16322             this.store.un("update", this.onUpdate);
16323             this.store.un("clear", this.refresh);
16324             this.store.un("beforeload", this.onBeforeLoad);
16325             this.store.un("load", this.onLoad);
16326             this.store.un("loadexception", this.onLoad);
16327         }
16328         if(store){
16329           
16330             store.on("datachanged", this.refresh, this);
16331             store.on("add", this.onAdd, this);
16332             store.on("remove", this.onRemove, this);
16333             store.on("update", this.onUpdate, this);
16334             store.on("clear", this.refresh, this);
16335             store.on("beforeload", this.onBeforeLoad, this);
16336             store.on("load", this.onLoad, this);
16337             store.on("loadexception", this.onLoad, this);
16338         }
16339         
16340         if(store){
16341             this.refresh();
16342         }
16343     },
16344     /**
16345      * onbeforeLoad - masks the loading area.
16346      *
16347      */
16348     onBeforeLoad : function(store,opts)
16349     {
16350          //Roo.log('onBeforeLoad');   
16351         if (!opts.add) {
16352             this.el.update("");
16353         }
16354         this.el.mask(this.mask ? this.mask : "Loading" ); 
16355     },
16356     onLoad : function ()
16357     {
16358         this.el.unmask();
16359     },
16360     
16361
16362     /**
16363      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16364      * @param {HTMLElement} node
16365      * @return {HTMLElement} The template node
16366      */
16367     findItemFromChild : function(node){
16368         var el = this.dataName  ?
16369             this.el.child('.roo-tpl-' + this.dataName,true) :
16370             this.el.dom; 
16371         
16372         if(!node || node.parentNode == el){
16373                     return node;
16374             }
16375             var p = node.parentNode;
16376             while(p && p != el){
16377             if(p.parentNode == el){
16378                 return p;
16379             }
16380             p = p.parentNode;
16381         }
16382             return null;
16383     },
16384
16385     /** @ignore */
16386     onClick : function(e){
16387         var item = this.findItemFromChild(e.getTarget());
16388         if(item){
16389             var index = this.indexOf(item);
16390             if(this.onItemClick(item, index, e) !== false){
16391                 this.fireEvent("click", this, index, item, e);
16392             }
16393         }else{
16394             this.clearSelections();
16395         }
16396     },
16397
16398     /** @ignore */
16399     onContextMenu : function(e){
16400         var item = this.findItemFromChild(e.getTarget());
16401         if(item){
16402             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16403         }
16404     },
16405
16406     /** @ignore */
16407     onDblClick : function(e){
16408         var item = this.findItemFromChild(e.getTarget());
16409         if(item){
16410             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16411         }
16412     },
16413
16414     onItemClick : function(item, index, e)
16415     {
16416         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16417             return false;
16418         }
16419         if (this.toggleSelect) {
16420             var m = this.isSelected(item) ? 'unselect' : 'select';
16421             //Roo.log(m);
16422             var _t = this;
16423             _t[m](item, true, false);
16424             return true;
16425         }
16426         if(this.multiSelect || this.singleSelect){
16427             if(this.multiSelect && e.shiftKey && this.lastSelection){
16428                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16429             }else{
16430                 this.select(item, this.multiSelect && e.ctrlKey);
16431                 this.lastSelection = item;
16432             }
16433             
16434             if(!this.tickable){
16435                 e.preventDefault();
16436             }
16437             
16438         }
16439         return true;
16440     },
16441
16442     /**
16443      * Get the number of selected nodes.
16444      * @return {Number}
16445      */
16446     getSelectionCount : function(){
16447         return this.selections.length;
16448     },
16449
16450     /**
16451      * Get the currently selected nodes.
16452      * @return {Array} An array of HTMLElements
16453      */
16454     getSelectedNodes : function(){
16455         return this.selections;
16456     },
16457
16458     /**
16459      * Get the indexes of the selected nodes.
16460      * @return {Array}
16461      */
16462     getSelectedIndexes : function(){
16463         var indexes = [], s = this.selections;
16464         for(var i = 0, len = s.length; i < len; i++){
16465             indexes.push(s[i].nodeIndex);
16466         }
16467         return indexes;
16468     },
16469
16470     /**
16471      * Clear all selections
16472      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16473      */
16474     clearSelections : function(suppressEvent){
16475         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16476             this.cmp.elements = this.selections;
16477             this.cmp.removeClass(this.selectedClass);
16478             this.selections = [];
16479             if(!suppressEvent){
16480                 this.fireEvent("selectionchange", this, this.selections);
16481             }
16482         }
16483     },
16484
16485     /**
16486      * Returns true if the passed node is selected
16487      * @param {HTMLElement/Number} node The node or node index
16488      * @return {Boolean}
16489      */
16490     isSelected : function(node){
16491         var s = this.selections;
16492         if(s.length < 1){
16493             return false;
16494         }
16495         node = this.getNode(node);
16496         return s.indexOf(node) !== -1;
16497     },
16498
16499     /**
16500      * Selects nodes.
16501      * @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
16502      * @param {Boolean} keepExisting (optional) true to keep existing selections
16503      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16504      */
16505     select : function(nodeInfo, keepExisting, suppressEvent){
16506         if(nodeInfo instanceof Array){
16507             if(!keepExisting){
16508                 this.clearSelections(true);
16509             }
16510             for(var i = 0, len = nodeInfo.length; i < len; i++){
16511                 this.select(nodeInfo[i], true, true);
16512             }
16513             return;
16514         } 
16515         var node = this.getNode(nodeInfo);
16516         if(!node || this.isSelected(node)){
16517             return; // already selected.
16518         }
16519         if(!keepExisting){
16520             this.clearSelections(true);
16521         }
16522         
16523         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16524             Roo.fly(node).addClass(this.selectedClass);
16525             this.selections.push(node);
16526             if(!suppressEvent){
16527                 this.fireEvent("selectionchange", this, this.selections);
16528             }
16529         }
16530         
16531         
16532     },
16533       /**
16534      * Unselects nodes.
16535      * @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
16536      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16537      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16538      */
16539     unselect : function(nodeInfo, keepExisting, suppressEvent)
16540     {
16541         if(nodeInfo instanceof Array){
16542             Roo.each(this.selections, function(s) {
16543                 this.unselect(s, nodeInfo);
16544             }, this);
16545             return;
16546         }
16547         var node = this.getNode(nodeInfo);
16548         if(!node || !this.isSelected(node)){
16549             //Roo.log("not selected");
16550             return; // not selected.
16551         }
16552         // fireevent???
16553         var ns = [];
16554         Roo.each(this.selections, function(s) {
16555             if (s == node ) {
16556                 Roo.fly(node).removeClass(this.selectedClass);
16557
16558                 return;
16559             }
16560             ns.push(s);
16561         },this);
16562         
16563         this.selections= ns;
16564         this.fireEvent("selectionchange", this, this.selections);
16565     },
16566
16567     /**
16568      * Gets a template node.
16569      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16570      * @return {HTMLElement} The node or null if it wasn't found
16571      */
16572     getNode : function(nodeInfo){
16573         if(typeof nodeInfo == "string"){
16574             return document.getElementById(nodeInfo);
16575         }else if(typeof nodeInfo == "number"){
16576             return this.nodes[nodeInfo];
16577         }
16578         return nodeInfo;
16579     },
16580
16581     /**
16582      * Gets a range template nodes.
16583      * @param {Number} startIndex
16584      * @param {Number} endIndex
16585      * @return {Array} An array of nodes
16586      */
16587     getNodes : function(start, end){
16588         var ns = this.nodes;
16589         start = start || 0;
16590         end = typeof end == "undefined" ? ns.length - 1 : end;
16591         var nodes = [];
16592         if(start <= end){
16593             for(var i = start; i <= end; i++){
16594                 nodes.push(ns[i]);
16595             }
16596         } else{
16597             for(var i = start; i >= end; i--){
16598                 nodes.push(ns[i]);
16599             }
16600         }
16601         return nodes;
16602     },
16603
16604     /**
16605      * Finds the index of the passed node
16606      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16607      * @return {Number} The index of the node or -1
16608      */
16609     indexOf : function(node){
16610         node = this.getNode(node);
16611         if(typeof node.nodeIndex == "number"){
16612             return node.nodeIndex;
16613         }
16614         var ns = this.nodes;
16615         for(var i = 0, len = ns.length; i < len; i++){
16616             if(ns[i] == node){
16617                 return i;
16618             }
16619         }
16620         return -1;
16621     }
16622 });
16623 /*
16624  * - LGPL
16625  *
16626  * based on jquery fullcalendar
16627  * 
16628  */
16629
16630 Roo.bootstrap = Roo.bootstrap || {};
16631 /**
16632  * @class Roo.bootstrap.Calendar
16633  * @extends Roo.bootstrap.Component
16634  * Bootstrap Calendar class
16635  * @cfg {Boolean} loadMask (true|false) default false
16636  * @cfg {Object} header generate the user specific header of the calendar, default false
16637
16638  * @constructor
16639  * Create a new Container
16640  * @param {Object} config The config object
16641  */
16642
16643
16644
16645 Roo.bootstrap.Calendar = function(config){
16646     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16647      this.addEvents({
16648         /**
16649              * @event select
16650              * Fires when a date is selected
16651              * @param {DatePicker} this
16652              * @param {Date} date The selected date
16653              */
16654         'select': true,
16655         /**
16656              * @event monthchange
16657              * Fires when the displayed month changes 
16658              * @param {DatePicker} this
16659              * @param {Date} date The selected month
16660              */
16661         'monthchange': true,
16662         /**
16663              * @event evententer
16664              * Fires when mouse over an event
16665              * @param {Calendar} this
16666              * @param {event} Event
16667              */
16668         'evententer': true,
16669         /**
16670              * @event eventleave
16671              * Fires when the mouse leaves an
16672              * @param {Calendar} this
16673              * @param {event}
16674              */
16675         'eventleave': true,
16676         /**
16677              * @event eventclick
16678              * Fires when the mouse click an
16679              * @param {Calendar} this
16680              * @param {event}
16681              */
16682         'eventclick': true
16683         
16684     });
16685
16686 };
16687
16688 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16689     
16690      /**
16691      * @cfg {Number} startDay
16692      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16693      */
16694     startDay : 0,
16695     
16696     loadMask : false,
16697     
16698     header : false,
16699       
16700     getAutoCreate : function(){
16701         
16702         
16703         var fc_button = function(name, corner, style, content ) {
16704             return Roo.apply({},{
16705                 tag : 'span',
16706                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16707                          (corner.length ?
16708                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16709                             ''
16710                         ),
16711                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16712                 unselectable: 'on'
16713             });
16714         };
16715         
16716         var header = {};
16717         
16718         if(!this.header){
16719             header = {
16720                 tag : 'table',
16721                 cls : 'fc-header',
16722                 style : 'width:100%',
16723                 cn : [
16724                     {
16725                         tag: 'tr',
16726                         cn : [
16727                             {
16728                                 tag : 'td',
16729                                 cls : 'fc-header-left',
16730                                 cn : [
16731                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16732                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16733                                     { tag: 'span', cls: 'fc-header-space' },
16734                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16735
16736
16737                                 ]
16738                             },
16739
16740                             {
16741                                 tag : 'td',
16742                                 cls : 'fc-header-center',
16743                                 cn : [
16744                                     {
16745                                         tag: 'span',
16746                                         cls: 'fc-header-title',
16747                                         cn : {
16748                                             tag: 'H2',
16749                                             html : 'month / year'
16750                                         }
16751                                     }
16752
16753                                 ]
16754                             },
16755                             {
16756                                 tag : 'td',
16757                                 cls : 'fc-header-right',
16758                                 cn : [
16759                               /*      fc_button('month', 'left', '', 'month' ),
16760                                     fc_button('week', '', '', 'week' ),
16761                                     fc_button('day', 'right', '', 'day' )
16762                                 */    
16763
16764                                 ]
16765                             }
16766
16767                         ]
16768                     }
16769                 ]
16770             };
16771         }
16772         
16773         header = this.header;
16774         
16775        
16776         var cal_heads = function() {
16777             var ret = [];
16778             // fixme - handle this.
16779             
16780             for (var i =0; i < Date.dayNames.length; i++) {
16781                 var d = Date.dayNames[i];
16782                 ret.push({
16783                     tag: 'th',
16784                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16785                     html : d.substring(0,3)
16786                 });
16787                 
16788             }
16789             ret[0].cls += ' fc-first';
16790             ret[6].cls += ' fc-last';
16791             return ret;
16792         };
16793         var cal_cell = function(n) {
16794             return  {
16795                 tag: 'td',
16796                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16797                 cn : [
16798                     {
16799                         cn : [
16800                             {
16801                                 cls: 'fc-day-number',
16802                                 html: 'D'
16803                             },
16804                             {
16805                                 cls: 'fc-day-content',
16806                              
16807                                 cn : [
16808                                      {
16809                                         style: 'position: relative;' // height: 17px;
16810                                     }
16811                                 ]
16812                             }
16813                             
16814                             
16815                         ]
16816                     }
16817                 ]
16818                 
16819             }
16820         };
16821         var cal_rows = function() {
16822             
16823             var ret = [];
16824             for (var r = 0; r < 6; r++) {
16825                 var row= {
16826                     tag : 'tr',
16827                     cls : 'fc-week',
16828                     cn : []
16829                 };
16830                 
16831                 for (var i =0; i < Date.dayNames.length; i++) {
16832                     var d = Date.dayNames[i];
16833                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16834
16835                 }
16836                 row.cn[0].cls+=' fc-first';
16837                 row.cn[0].cn[0].style = 'min-height:90px';
16838                 row.cn[6].cls+=' fc-last';
16839                 ret.push(row);
16840                 
16841             }
16842             ret[0].cls += ' fc-first';
16843             ret[4].cls += ' fc-prev-last';
16844             ret[5].cls += ' fc-last';
16845             return ret;
16846             
16847         };
16848         
16849         var cal_table = {
16850             tag: 'table',
16851             cls: 'fc-border-separate',
16852             style : 'width:100%',
16853             cellspacing  : 0,
16854             cn : [
16855                 { 
16856                     tag: 'thead',
16857                     cn : [
16858                         { 
16859                             tag: 'tr',
16860                             cls : 'fc-first fc-last',
16861                             cn : cal_heads()
16862                         }
16863                     ]
16864                 },
16865                 { 
16866                     tag: 'tbody',
16867                     cn : cal_rows()
16868                 }
16869                   
16870             ]
16871         };
16872          
16873          var cfg = {
16874             cls : 'fc fc-ltr',
16875             cn : [
16876                 header,
16877                 {
16878                     cls : 'fc-content',
16879                     style : "position: relative;",
16880                     cn : [
16881                         {
16882                             cls : 'fc-view fc-view-month fc-grid',
16883                             style : 'position: relative',
16884                             unselectable : 'on',
16885                             cn : [
16886                                 {
16887                                     cls : 'fc-event-container',
16888                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16889                                 },
16890                                 cal_table
16891                             ]
16892                         }
16893                     ]
16894     
16895                 }
16896            ] 
16897             
16898         };
16899         
16900          
16901         
16902         return cfg;
16903     },
16904     
16905     
16906     initEvents : function()
16907     {
16908         if(!this.store){
16909             throw "can not find store for calendar";
16910         }
16911         
16912         var mark = {
16913             tag: "div",
16914             cls:"x-dlg-mask",
16915             style: "text-align:center",
16916             cn: [
16917                 {
16918                     tag: "div",
16919                     style: "background-color:white;width:50%;margin:250 auto",
16920                     cn: [
16921                         {
16922                             tag: "img",
16923                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16924                         },
16925                         {
16926                             tag: "span",
16927                             html: "Loading"
16928                         }
16929                         
16930                     ]
16931                 }
16932             ]
16933         };
16934         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16935         
16936         var size = this.el.select('.fc-content', true).first().getSize();
16937         this.maskEl.setSize(size.width, size.height);
16938         this.maskEl.enableDisplayMode("block");
16939         if(!this.loadMask){
16940             this.maskEl.hide();
16941         }
16942         
16943         this.store = Roo.factory(this.store, Roo.data);
16944         this.store.on('load', this.onLoad, this);
16945         this.store.on('beforeload', this.onBeforeLoad, this);
16946         
16947         this.resize();
16948         
16949         this.cells = this.el.select('.fc-day',true);
16950         //Roo.log(this.cells);
16951         this.textNodes = this.el.query('.fc-day-number');
16952         this.cells.addClassOnOver('fc-state-hover');
16953         
16954         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16955         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16956         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16957         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16958         
16959         this.on('monthchange', this.onMonthChange, this);
16960         
16961         this.update(new Date().clearTime());
16962     },
16963     
16964     resize : function() {
16965         var sz  = this.el.getSize();
16966         
16967         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16968         this.el.select('.fc-day-content div',true).setHeight(34);
16969     },
16970     
16971     
16972     // private
16973     showPrevMonth : function(e){
16974         this.update(this.activeDate.add("mo", -1));
16975     },
16976     showToday : function(e){
16977         this.update(new Date().clearTime());
16978     },
16979     // private
16980     showNextMonth : function(e){
16981         this.update(this.activeDate.add("mo", 1));
16982     },
16983
16984     // private
16985     showPrevYear : function(){
16986         this.update(this.activeDate.add("y", -1));
16987     },
16988
16989     // private
16990     showNextYear : function(){
16991         this.update(this.activeDate.add("y", 1));
16992     },
16993
16994     
16995    // private
16996     update : function(date)
16997     {
16998         var vd = this.activeDate;
16999         this.activeDate = date;
17000 //        if(vd && this.el){
17001 //            var t = date.getTime();
17002 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17003 //                Roo.log('using add remove');
17004 //                
17005 //                this.fireEvent('monthchange', this, date);
17006 //                
17007 //                this.cells.removeClass("fc-state-highlight");
17008 //                this.cells.each(function(c){
17009 //                   if(c.dateValue == t){
17010 //                       c.addClass("fc-state-highlight");
17011 //                       setTimeout(function(){
17012 //                            try{c.dom.firstChild.focus();}catch(e){}
17013 //                       }, 50);
17014 //                       return false;
17015 //                   }
17016 //                   return true;
17017 //                });
17018 //                return;
17019 //            }
17020 //        }
17021         
17022         var days = date.getDaysInMonth();
17023         
17024         var firstOfMonth = date.getFirstDateOfMonth();
17025         var startingPos = firstOfMonth.getDay()-this.startDay;
17026         
17027         if(startingPos < this.startDay){
17028             startingPos += 7;
17029         }
17030         
17031         var pm = date.add(Date.MONTH, -1);
17032         var prevStart = pm.getDaysInMonth()-startingPos;
17033 //        
17034         this.cells = this.el.select('.fc-day',true);
17035         this.textNodes = this.el.query('.fc-day-number');
17036         this.cells.addClassOnOver('fc-state-hover');
17037         
17038         var cells = this.cells.elements;
17039         var textEls = this.textNodes;
17040         
17041         Roo.each(cells, function(cell){
17042             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17043         });
17044         
17045         days += startingPos;
17046
17047         // convert everything to numbers so it's fast
17048         var day = 86400000;
17049         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17050         //Roo.log(d);
17051         //Roo.log(pm);
17052         //Roo.log(prevStart);
17053         
17054         var today = new Date().clearTime().getTime();
17055         var sel = date.clearTime().getTime();
17056         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17057         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17058         var ddMatch = this.disabledDatesRE;
17059         var ddText = this.disabledDatesText;
17060         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17061         var ddaysText = this.disabledDaysText;
17062         var format = this.format;
17063         
17064         var setCellClass = function(cal, cell){
17065             cell.row = 0;
17066             cell.events = [];
17067             cell.more = [];
17068             //Roo.log('set Cell Class');
17069             cell.title = "";
17070             var t = d.getTime();
17071             
17072             //Roo.log(d);
17073             
17074             cell.dateValue = t;
17075             if(t == today){
17076                 cell.className += " fc-today";
17077                 cell.className += " fc-state-highlight";
17078                 cell.title = cal.todayText;
17079             }
17080             if(t == sel){
17081                 // disable highlight in other month..
17082                 //cell.className += " fc-state-highlight";
17083                 
17084             }
17085             // disabling
17086             if(t < min) {
17087                 cell.className = " fc-state-disabled";
17088                 cell.title = cal.minText;
17089                 return;
17090             }
17091             if(t > max) {
17092                 cell.className = " fc-state-disabled";
17093                 cell.title = cal.maxText;
17094                 return;
17095             }
17096             if(ddays){
17097                 if(ddays.indexOf(d.getDay()) != -1){
17098                     cell.title = ddaysText;
17099                     cell.className = " fc-state-disabled";
17100                 }
17101             }
17102             if(ddMatch && format){
17103                 var fvalue = d.dateFormat(format);
17104                 if(ddMatch.test(fvalue)){
17105                     cell.title = ddText.replace("%0", fvalue);
17106                     cell.className = " fc-state-disabled";
17107                 }
17108             }
17109             
17110             if (!cell.initialClassName) {
17111                 cell.initialClassName = cell.dom.className;
17112             }
17113             
17114             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17115         };
17116
17117         var i = 0;
17118         
17119         for(; i < startingPos; i++) {
17120             textEls[i].innerHTML = (++prevStart);
17121             d.setDate(d.getDate()+1);
17122             
17123             cells[i].className = "fc-past fc-other-month";
17124             setCellClass(this, cells[i]);
17125         }
17126         
17127         var intDay = 0;
17128         
17129         for(; i < days; i++){
17130             intDay = i - startingPos + 1;
17131             textEls[i].innerHTML = (intDay);
17132             d.setDate(d.getDate()+1);
17133             
17134             cells[i].className = ''; // "x-date-active";
17135             setCellClass(this, cells[i]);
17136         }
17137         var extraDays = 0;
17138         
17139         for(; i < 42; i++) {
17140             textEls[i].innerHTML = (++extraDays);
17141             d.setDate(d.getDate()+1);
17142             
17143             cells[i].className = "fc-future fc-other-month";
17144             setCellClass(this, cells[i]);
17145         }
17146         
17147         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17148         
17149         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17150         
17151         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17152         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17153         
17154         if(totalRows != 6){
17155             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17156             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17157         }
17158         
17159         this.fireEvent('monthchange', this, date);
17160         
17161         
17162         /*
17163         if(!this.internalRender){
17164             var main = this.el.dom.firstChild;
17165             var w = main.offsetWidth;
17166             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17167             Roo.fly(main).setWidth(w);
17168             this.internalRender = true;
17169             // opera does not respect the auto grow header center column
17170             // then, after it gets a width opera refuses to recalculate
17171             // without a second pass
17172             if(Roo.isOpera && !this.secondPass){
17173                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17174                 this.secondPass = true;
17175                 this.update.defer(10, this, [date]);
17176             }
17177         }
17178         */
17179         
17180     },
17181     
17182     findCell : function(dt) {
17183         dt = dt.clearTime().getTime();
17184         var ret = false;
17185         this.cells.each(function(c){
17186             //Roo.log("check " +c.dateValue + '?=' + dt);
17187             if(c.dateValue == dt){
17188                 ret = c;
17189                 return false;
17190             }
17191             return true;
17192         });
17193         
17194         return ret;
17195     },
17196     
17197     findCells : function(ev) {
17198         var s = ev.start.clone().clearTime().getTime();
17199        // Roo.log(s);
17200         var e= ev.end.clone().clearTime().getTime();
17201        // Roo.log(e);
17202         var ret = [];
17203         this.cells.each(function(c){
17204              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17205             
17206             if(c.dateValue > e){
17207                 return ;
17208             }
17209             if(c.dateValue < s){
17210                 return ;
17211             }
17212             ret.push(c);
17213         });
17214         
17215         return ret;    
17216     },
17217     
17218 //    findBestRow: function(cells)
17219 //    {
17220 //        var ret = 0;
17221 //        
17222 //        for (var i =0 ; i < cells.length;i++) {
17223 //            ret  = Math.max(cells[i].rows || 0,ret);
17224 //        }
17225 //        return ret;
17226 //        
17227 //    },
17228     
17229     
17230     addItem : function(ev)
17231     {
17232         // look for vertical location slot in
17233         var cells = this.findCells(ev);
17234         
17235 //        ev.row = this.findBestRow(cells);
17236         
17237         // work out the location.
17238         
17239         var crow = false;
17240         var rows = [];
17241         for(var i =0; i < cells.length; i++) {
17242             
17243             cells[i].row = cells[0].row;
17244             
17245             if(i == 0){
17246                 cells[i].row = cells[i].row + 1;
17247             }
17248             
17249             if (!crow) {
17250                 crow = {
17251                     start : cells[i],
17252                     end :  cells[i]
17253                 };
17254                 continue;
17255             }
17256             if (crow.start.getY() == cells[i].getY()) {
17257                 // on same row.
17258                 crow.end = cells[i];
17259                 continue;
17260             }
17261             // different row.
17262             rows.push(crow);
17263             crow = {
17264                 start: cells[i],
17265                 end : cells[i]
17266             };
17267             
17268         }
17269         
17270         rows.push(crow);
17271         ev.els = [];
17272         ev.rows = rows;
17273         ev.cells = cells;
17274         
17275         cells[0].events.push(ev);
17276         
17277         this.calevents.push(ev);
17278     },
17279     
17280     clearEvents: function() {
17281         
17282         if(!this.calevents){
17283             return;
17284         }
17285         
17286         Roo.each(this.cells.elements, function(c){
17287             c.row = 0;
17288             c.events = [];
17289             c.more = [];
17290         });
17291         
17292         Roo.each(this.calevents, function(e) {
17293             Roo.each(e.els, function(el) {
17294                 el.un('mouseenter' ,this.onEventEnter, this);
17295                 el.un('mouseleave' ,this.onEventLeave, this);
17296                 el.remove();
17297             },this);
17298         },this);
17299         
17300         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17301             e.remove();
17302         });
17303         
17304     },
17305     
17306     renderEvents: function()
17307     {   
17308         var _this = this;
17309         
17310         this.cells.each(function(c) {
17311             
17312             if(c.row < 5){
17313                 return;
17314             }
17315             
17316             var ev = c.events;
17317             
17318             var r = 4;
17319             if(c.row != c.events.length){
17320                 r = 4 - (4 - (c.row - c.events.length));
17321             }
17322             
17323             c.events = ev.slice(0, r);
17324             c.more = ev.slice(r);
17325             
17326             if(c.more.length && c.more.length == 1){
17327                 c.events.push(c.more.pop());
17328             }
17329             
17330             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17331             
17332         });
17333             
17334         this.cells.each(function(c) {
17335             
17336             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17337             
17338             
17339             for (var e = 0; e < c.events.length; e++){
17340                 var ev = c.events[e];
17341                 var rows = ev.rows;
17342                 
17343                 for(var i = 0; i < rows.length; i++) {
17344                 
17345                     // how many rows should it span..
17346
17347                     var  cfg = {
17348                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17349                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17350
17351                         unselectable : "on",
17352                         cn : [
17353                             {
17354                                 cls: 'fc-event-inner',
17355                                 cn : [
17356     //                                {
17357     //                                  tag:'span',
17358     //                                  cls: 'fc-event-time',
17359     //                                  html : cells.length > 1 ? '' : ev.time
17360     //                                },
17361                                     {
17362                                       tag:'span',
17363                                       cls: 'fc-event-title',
17364                                       html : String.format('{0}', ev.title)
17365                                     }
17366
17367
17368                                 ]
17369                             },
17370                             {
17371                                 cls: 'ui-resizable-handle ui-resizable-e',
17372                                 html : '&nbsp;&nbsp;&nbsp'
17373                             }
17374
17375                         ]
17376                     };
17377
17378                     if (i == 0) {
17379                         cfg.cls += ' fc-event-start';
17380                     }
17381                     if ((i+1) == rows.length) {
17382                         cfg.cls += ' fc-event-end';
17383                     }
17384
17385                     var ctr = _this.el.select('.fc-event-container',true).first();
17386                     var cg = ctr.createChild(cfg);
17387
17388                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17389                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17390
17391                     var r = (c.more.length) ? 1 : 0;
17392                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17393                     cg.setWidth(ebox.right - sbox.x -2);
17394
17395                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17396                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17397                     cg.on('click', _this.onEventClick, _this, ev);
17398
17399                     ev.els.push(cg);
17400                     
17401                 }
17402                 
17403             }
17404             
17405             
17406             if(c.more.length){
17407                 var  cfg = {
17408                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17409                     style : 'position: absolute',
17410                     unselectable : "on",
17411                     cn : [
17412                         {
17413                             cls: 'fc-event-inner',
17414                             cn : [
17415                                 {
17416                                   tag:'span',
17417                                   cls: 'fc-event-title',
17418                                   html : 'More'
17419                                 }
17420
17421
17422                             ]
17423                         },
17424                         {
17425                             cls: 'ui-resizable-handle ui-resizable-e',
17426                             html : '&nbsp;&nbsp;&nbsp'
17427                         }
17428
17429                     ]
17430                 };
17431
17432                 var ctr = _this.el.select('.fc-event-container',true).first();
17433                 var cg = ctr.createChild(cfg);
17434
17435                 var sbox = c.select('.fc-day-content',true).first().getBox();
17436                 var ebox = c.select('.fc-day-content',true).first().getBox();
17437                 //Roo.log(cg);
17438                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17439                 cg.setWidth(ebox.right - sbox.x -2);
17440
17441                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17442                 
17443             }
17444             
17445         });
17446         
17447         
17448         
17449     },
17450     
17451     onEventEnter: function (e, el,event,d) {
17452         this.fireEvent('evententer', this, el, event);
17453     },
17454     
17455     onEventLeave: function (e, el,event,d) {
17456         this.fireEvent('eventleave', this, el, event);
17457     },
17458     
17459     onEventClick: function (e, el,event,d) {
17460         this.fireEvent('eventclick', this, el, event);
17461     },
17462     
17463     onMonthChange: function () {
17464         this.store.load();
17465     },
17466     
17467     onMoreEventClick: function(e, el, more)
17468     {
17469         var _this = this;
17470         
17471         this.calpopover.placement = 'right';
17472         this.calpopover.setTitle('More');
17473         
17474         this.calpopover.setContent('');
17475         
17476         var ctr = this.calpopover.el.select('.popover-content', true).first();
17477         
17478         Roo.each(more, function(m){
17479             var cfg = {
17480                 cls : 'fc-event-hori fc-event-draggable',
17481                 html : m.title
17482             };
17483             var cg = ctr.createChild(cfg);
17484             
17485             cg.on('click', _this.onEventClick, _this, m);
17486         });
17487         
17488         this.calpopover.show(el);
17489         
17490         
17491     },
17492     
17493     onLoad: function () 
17494     {   
17495         this.calevents = [];
17496         var cal = this;
17497         
17498         if(this.store.getCount() > 0){
17499             this.store.data.each(function(d){
17500                cal.addItem({
17501                     id : d.data.id,
17502                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17503                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17504                     time : d.data.start_time,
17505                     title : d.data.title,
17506                     description : d.data.description,
17507                     venue : d.data.venue
17508                 });
17509             });
17510         }
17511         
17512         this.renderEvents();
17513         
17514         if(this.calevents.length && this.loadMask){
17515             this.maskEl.hide();
17516         }
17517     },
17518     
17519     onBeforeLoad: function()
17520     {
17521         this.clearEvents();
17522         if(this.loadMask){
17523             this.maskEl.show();
17524         }
17525     }
17526 });
17527
17528  
17529  /*
17530  * - LGPL
17531  *
17532  * element
17533  * 
17534  */
17535
17536 /**
17537  * @class Roo.bootstrap.Popover
17538  * @extends Roo.bootstrap.Component
17539  * Bootstrap Popover class
17540  * @cfg {String} html contents of the popover   (or false to use children..)
17541  * @cfg {String} title of popover (or false to hide)
17542  * @cfg {String} placement how it is placed
17543  * @cfg {String} trigger click || hover (or false to trigger manually)
17544  * @cfg {String} over what (parent or false to trigger manually.)
17545  * @cfg {Number} delay - delay before showing
17546  
17547  * @constructor
17548  * Create a new Popover
17549  * @param {Object} config The config object
17550  */
17551
17552 Roo.bootstrap.Popover = function(config){
17553     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17554     
17555     this.addEvents({
17556         // raw events
17557          /**
17558          * @event show
17559          * After the popover show
17560          * 
17561          * @param {Roo.bootstrap.Popover} this
17562          */
17563         "show" : true,
17564         /**
17565          * @event hide
17566          * After the popover hide
17567          * 
17568          * @param {Roo.bootstrap.Popover} this
17569          */
17570         "hide" : true
17571     });
17572 };
17573
17574 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17575     
17576     title: 'Fill in a title',
17577     html: false,
17578     
17579     placement : 'right',
17580     trigger : 'hover', // hover
17581     
17582     delay : 0,
17583     
17584     over: 'parent',
17585     
17586     can_build_overlaid : false,
17587     
17588     getChildContainer : function()
17589     {
17590         return this.el.select('.popover-content',true).first();
17591     },
17592     
17593     getAutoCreate : function(){
17594          
17595         var cfg = {
17596            cls : 'popover roo-dynamic',
17597            style: 'display:block',
17598            cn : [
17599                 {
17600                     cls : 'arrow'
17601                 },
17602                 {
17603                     cls : 'popover-inner',
17604                     cn : [
17605                         {
17606                             tag: 'h3',
17607                             cls: 'popover-title',
17608                             html : this.title
17609                         },
17610                         {
17611                             cls : 'popover-content',
17612                             html : this.html
17613                         }
17614                     ]
17615                     
17616                 }
17617            ]
17618         };
17619         
17620         return cfg;
17621     },
17622     setTitle: function(str)
17623     {
17624         this.title = str;
17625         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17626     },
17627     setContent: function(str)
17628     {
17629         this.html = str;
17630         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17631     },
17632     // as it get's added to the bottom of the page.
17633     onRender : function(ct, position)
17634     {
17635         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17636         if(!this.el){
17637             var cfg = Roo.apply({},  this.getAutoCreate());
17638             cfg.id = Roo.id();
17639             
17640             if (this.cls) {
17641                 cfg.cls += ' ' + this.cls;
17642             }
17643             if (this.style) {
17644                 cfg.style = this.style;
17645             }
17646             //Roo.log("adding to ");
17647             this.el = Roo.get(document.body).createChild(cfg, position);
17648 //            Roo.log(this.el);
17649         }
17650         this.initEvents();
17651     },
17652     
17653     initEvents : function()
17654     {
17655         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17656         this.el.enableDisplayMode('block');
17657         this.el.hide();
17658         if (this.over === false) {
17659             return; 
17660         }
17661         if (this.triggers === false) {
17662             return;
17663         }
17664         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17665         var triggers = this.trigger ? this.trigger.split(' ') : [];
17666         Roo.each(triggers, function(trigger) {
17667         
17668             if (trigger == 'click') {
17669                 on_el.on('click', this.toggle, this);
17670             } else if (trigger != 'manual') {
17671                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17672                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17673       
17674                 on_el.on(eventIn  ,this.enter, this);
17675                 on_el.on(eventOut, this.leave, this);
17676             }
17677         }, this);
17678         
17679     },
17680     
17681     
17682     // private
17683     timeout : null,
17684     hoverState : null,
17685     
17686     toggle : function () {
17687         this.hoverState == 'in' ? this.leave() : this.enter();
17688     },
17689     
17690     enter : function () {
17691         
17692         clearTimeout(this.timeout);
17693     
17694         this.hoverState = 'in';
17695     
17696         if (!this.delay || !this.delay.show) {
17697             this.show();
17698             return;
17699         }
17700         var _t = this;
17701         this.timeout = setTimeout(function () {
17702             if (_t.hoverState == 'in') {
17703                 _t.show();
17704             }
17705         }, this.delay.show)
17706     },
17707     
17708     leave : function() {
17709         clearTimeout(this.timeout);
17710     
17711         this.hoverState = 'out';
17712     
17713         if (!this.delay || !this.delay.hide) {
17714             this.hide();
17715             return;
17716         }
17717         var _t = this;
17718         this.timeout = setTimeout(function () {
17719             if (_t.hoverState == 'out') {
17720                 _t.hide();
17721             }
17722         }, this.delay.hide)
17723     },
17724     
17725     show : function (on_el)
17726     {
17727         if (!on_el) {
17728             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17729         }
17730         
17731         // set content.
17732         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17733         if (this.html !== false) {
17734             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17735         }
17736         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17737         if (!this.title.length) {
17738             this.el.select('.popover-title',true).hide();
17739         }
17740         
17741         var placement = typeof this.placement == 'function' ?
17742             this.placement.call(this, this.el, on_el) :
17743             this.placement;
17744             
17745         var autoToken = /\s?auto?\s?/i;
17746         var autoPlace = autoToken.test(placement);
17747         if (autoPlace) {
17748             placement = placement.replace(autoToken, '') || 'top';
17749         }
17750         
17751         //this.el.detach()
17752         //this.el.setXY([0,0]);
17753         this.el.show();
17754         this.el.dom.style.display='block';
17755         this.el.addClass(placement);
17756         
17757         //this.el.appendTo(on_el);
17758         
17759         var p = this.getPosition();
17760         var box = this.el.getBox();
17761         
17762         if (autoPlace) {
17763             // fixme..
17764         }
17765         var align = Roo.bootstrap.Popover.alignment[placement];
17766         
17767 //        Roo.log(align);
17768         this.el.alignTo(on_el, align[0],align[1]);
17769         //var arrow = this.el.select('.arrow',true).first();
17770         //arrow.set(align[2], 
17771         
17772         this.el.addClass('in');
17773         
17774         
17775         if (this.el.hasClass('fade')) {
17776             // fade it?
17777         }
17778         
17779         this.hoverState = 'in';
17780         
17781         this.fireEvent('show', this);
17782         
17783     },
17784     hide : function()
17785     {
17786         this.el.setXY([0,0]);
17787         this.el.removeClass('in');
17788         this.el.hide();
17789         this.hoverState = null;
17790         
17791         this.fireEvent('hide', this);
17792     }
17793     
17794 });
17795
17796 Roo.bootstrap.Popover.alignment = {
17797     'left' : ['r-l', [-10,0], 'right'],
17798     'right' : ['l-r', [10,0], 'left'],
17799     'bottom' : ['t-b', [0,10], 'top'],
17800     'top' : [ 'b-t', [0,-10], 'bottom']
17801 };
17802
17803  /*
17804  * - LGPL
17805  *
17806  * Progress
17807  * 
17808  */
17809
17810 /**
17811  * @class Roo.bootstrap.Progress
17812  * @extends Roo.bootstrap.Component
17813  * Bootstrap Progress class
17814  * @cfg {Boolean} striped striped of the progress bar
17815  * @cfg {Boolean} active animated of the progress bar
17816  * 
17817  * 
17818  * @constructor
17819  * Create a new Progress
17820  * @param {Object} config The config object
17821  */
17822
17823 Roo.bootstrap.Progress = function(config){
17824     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17825 };
17826
17827 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17828     
17829     striped : false,
17830     active: false,
17831     
17832     getAutoCreate : function(){
17833         var cfg = {
17834             tag: 'div',
17835             cls: 'progress'
17836         };
17837         
17838         
17839         if(this.striped){
17840             cfg.cls += ' progress-striped';
17841         }
17842       
17843         if(this.active){
17844             cfg.cls += ' active';
17845         }
17846         
17847         
17848         return cfg;
17849     }
17850    
17851 });
17852
17853  
17854
17855  /*
17856  * - LGPL
17857  *
17858  * ProgressBar
17859  * 
17860  */
17861
17862 /**
17863  * @class Roo.bootstrap.ProgressBar
17864  * @extends Roo.bootstrap.Component
17865  * Bootstrap ProgressBar class
17866  * @cfg {Number} aria_valuenow aria-value now
17867  * @cfg {Number} aria_valuemin aria-value min
17868  * @cfg {Number} aria_valuemax aria-value max
17869  * @cfg {String} label label for the progress bar
17870  * @cfg {String} panel (success | info | warning | danger )
17871  * @cfg {String} role role of the progress bar
17872  * @cfg {String} sr_only text
17873  * 
17874  * 
17875  * @constructor
17876  * Create a new ProgressBar
17877  * @param {Object} config The config object
17878  */
17879
17880 Roo.bootstrap.ProgressBar = function(config){
17881     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17882 };
17883
17884 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17885     
17886     aria_valuenow : 0,
17887     aria_valuemin : 0,
17888     aria_valuemax : 100,
17889     label : false,
17890     panel : false,
17891     role : false,
17892     sr_only: false,
17893     
17894     getAutoCreate : function()
17895     {
17896         
17897         var cfg = {
17898             tag: 'div',
17899             cls: 'progress-bar',
17900             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17901         };
17902         
17903         if(this.sr_only){
17904             cfg.cn = {
17905                 tag: 'span',
17906                 cls: 'sr-only',
17907                 html: this.sr_only
17908             }
17909         }
17910         
17911         if(this.role){
17912             cfg.role = this.role;
17913         }
17914         
17915         if(this.aria_valuenow){
17916             cfg['aria-valuenow'] = this.aria_valuenow;
17917         }
17918         
17919         if(this.aria_valuemin){
17920             cfg['aria-valuemin'] = this.aria_valuemin;
17921         }
17922         
17923         if(this.aria_valuemax){
17924             cfg['aria-valuemax'] = this.aria_valuemax;
17925         }
17926         
17927         if(this.label && !this.sr_only){
17928             cfg.html = this.label;
17929         }
17930         
17931         if(this.panel){
17932             cfg.cls += ' progress-bar-' + this.panel;
17933         }
17934         
17935         return cfg;
17936     },
17937     
17938     update : function(aria_valuenow)
17939     {
17940         this.aria_valuenow = aria_valuenow;
17941         
17942         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17943     }
17944    
17945 });
17946
17947  
17948
17949  /*
17950  * - LGPL
17951  *
17952  * column
17953  * 
17954  */
17955
17956 /**
17957  * @class Roo.bootstrap.TabGroup
17958  * @extends Roo.bootstrap.Column
17959  * Bootstrap Column class
17960  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17961  * @cfg {Boolean} carousel true to make the group behave like a carousel
17962  * @cfg {Boolean} bullets show bullets for the panels
17963  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17964  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17965  * @cfg {Boolean} showarrow (true|false) show arrow default true
17966  * 
17967  * @constructor
17968  * Create a new TabGroup
17969  * @param {Object} config The config object
17970  */
17971
17972 Roo.bootstrap.TabGroup = function(config){
17973     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17974     if (!this.navId) {
17975         this.navId = Roo.id();
17976     }
17977     this.tabs = [];
17978     Roo.bootstrap.TabGroup.register(this);
17979     
17980 };
17981
17982 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17983     
17984     carousel : false,
17985     transition : false,
17986     bullets : 0,
17987     timer : 0,
17988     autoslide : false,
17989     slideFn : false,
17990     slideOnTouch : false,
17991     showarrow : true,
17992     
17993     getAutoCreate : function()
17994     {
17995         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17996         
17997         cfg.cls += ' tab-content';
17998         
17999         if (this.carousel) {
18000             cfg.cls += ' carousel slide';
18001             
18002             cfg.cn = [{
18003                cls : 'carousel-inner',
18004                cn : []
18005             }];
18006         
18007             if(this.bullets  && !Roo.isTouch){
18008                 
18009                 var bullets = {
18010                     cls : 'carousel-bullets',
18011                     cn : []
18012                 };
18013                
18014                 if(this.bullets_cls){
18015                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18016                 }
18017                 
18018                 bullets.cn.push({
18019                     cls : 'clear'
18020                 });
18021                 
18022                 cfg.cn[0].cn.push(bullets);
18023             }
18024             
18025             if(this.showarrow){
18026                 cfg.cn[0].cn.push({
18027                     tag : 'div',
18028                     class : 'carousel-arrow',
18029                     cn : [
18030                         {
18031                             tag : 'div',
18032                             class : 'carousel-prev',
18033                             cn : [
18034                                 {
18035                                     tag : 'i',
18036                                     class : 'fa fa-chevron-left'
18037                                 }
18038                             ]
18039                         },
18040                         {
18041                             tag : 'div',
18042                             class : 'carousel-next',
18043                             cn : [
18044                                 {
18045                                     tag : 'i',
18046                                     class : 'fa fa-chevron-right'
18047                                 }
18048                             ]
18049                         }
18050                     ]
18051                 });
18052             }
18053             
18054         }
18055         
18056         return cfg;
18057     },
18058     
18059     initEvents:  function()
18060     {
18061 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18062 //            this.el.on("touchstart", this.onTouchStart, this);
18063 //        }
18064         
18065         if(this.autoslide){
18066             var _this = this;
18067             
18068             this.slideFn = window.setInterval(function() {
18069                 _this.showPanelNext();
18070             }, this.timer);
18071         }
18072         
18073         if(this.showarrow){
18074             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18075             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18076         }
18077         
18078         
18079     },
18080     
18081 //    onTouchStart : function(e, el, o)
18082 //    {
18083 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18084 //            return;
18085 //        }
18086 //        
18087 //        this.showPanelNext();
18088 //    },
18089     
18090     
18091     getChildContainer : function()
18092     {
18093         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18094     },
18095     
18096     /**
18097     * register a Navigation item
18098     * @param {Roo.bootstrap.NavItem} the navitem to add
18099     */
18100     register : function(item)
18101     {
18102         this.tabs.push( item);
18103         item.navId = this.navId; // not really needed..
18104         this.addBullet();
18105     
18106     },
18107     
18108     getActivePanel : function()
18109     {
18110         var r = false;
18111         Roo.each(this.tabs, function(t) {
18112             if (t.active) {
18113                 r = t;
18114                 return false;
18115             }
18116             return null;
18117         });
18118         return r;
18119         
18120     },
18121     getPanelByName : function(n)
18122     {
18123         var r = false;
18124         Roo.each(this.tabs, function(t) {
18125             if (t.tabId == n) {
18126                 r = t;
18127                 return false;
18128             }
18129             return null;
18130         });
18131         return r;
18132     },
18133     indexOfPanel : function(p)
18134     {
18135         var r = false;
18136         Roo.each(this.tabs, function(t,i) {
18137             if (t.tabId == p.tabId) {
18138                 r = i;
18139                 return false;
18140             }
18141             return null;
18142         });
18143         return r;
18144     },
18145     /**
18146      * show a specific panel
18147      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18148      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18149      */
18150     showPanel : function (pan)
18151     {
18152         if(this.transition || typeof(pan) == 'undefined'){
18153             Roo.log("waiting for the transitionend");
18154             return;
18155         }
18156         
18157         if (typeof(pan) == 'number') {
18158             pan = this.tabs[pan];
18159         }
18160         
18161         if (typeof(pan) == 'string') {
18162             pan = this.getPanelByName(pan);
18163         }
18164         
18165         var cur = this.getActivePanel();
18166         
18167         if(!pan || !cur){
18168             Roo.log('pan or acitve pan is undefined');
18169             return false;
18170         }
18171         
18172         if (pan.tabId == this.getActivePanel().tabId) {
18173             return true;
18174         }
18175         
18176         if (false === cur.fireEvent('beforedeactivate')) {
18177             return false;
18178         }
18179         
18180         if(this.bullets > 0 && !Roo.isTouch){
18181             this.setActiveBullet(this.indexOfPanel(pan));
18182         }
18183         
18184         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18185             
18186             this.transition = true;
18187             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18188             var lr = dir == 'next' ? 'left' : 'right';
18189             pan.el.addClass(dir); // or prev
18190             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18191             cur.el.addClass(lr); // or right
18192             pan.el.addClass(lr);
18193             
18194             var _this = this;
18195             cur.el.on('transitionend', function() {
18196                 Roo.log("trans end?");
18197                 
18198                 pan.el.removeClass([lr,dir]);
18199                 pan.setActive(true);
18200                 
18201                 cur.el.removeClass([lr]);
18202                 cur.setActive(false);
18203                 
18204                 _this.transition = false;
18205                 
18206             }, this, { single:  true } );
18207             
18208             return true;
18209         }
18210         
18211         cur.setActive(false);
18212         pan.setActive(true);
18213         
18214         return true;
18215         
18216     },
18217     showPanelNext : function()
18218     {
18219         var i = this.indexOfPanel(this.getActivePanel());
18220         
18221         if (i >= this.tabs.length - 1 && !this.autoslide) {
18222             return;
18223         }
18224         
18225         if (i >= this.tabs.length - 1 && this.autoslide) {
18226             i = -1;
18227         }
18228         
18229         this.showPanel(this.tabs[i+1]);
18230     },
18231     
18232     showPanelPrev : function()
18233     {
18234         var i = this.indexOfPanel(this.getActivePanel());
18235         
18236         if (i  < 1 && !this.autoslide) {
18237             return;
18238         }
18239         
18240         if (i < 1 && this.autoslide) {
18241             i = this.tabs.length;
18242         }
18243         
18244         this.showPanel(this.tabs[i-1]);
18245     },
18246     
18247     
18248     addBullet: function()
18249     {
18250         if(!this.bullets || Roo.isTouch){
18251             return;
18252         }
18253         var ctr = this.el.select('.carousel-bullets',true).first();
18254         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18255         var bullet = ctr.createChild({
18256             cls : 'bullet bullet-' + i
18257         },ctr.dom.lastChild);
18258         
18259         
18260         var _this = this;
18261         
18262         bullet.on('click', (function(e, el, o, ii, t){
18263
18264             e.preventDefault();
18265
18266             this.showPanel(ii);
18267
18268             if(this.autoslide && this.slideFn){
18269                 clearInterval(this.slideFn);
18270                 this.slideFn = window.setInterval(function() {
18271                     _this.showPanelNext();
18272                 }, this.timer);
18273             }
18274
18275         }).createDelegate(this, [i, bullet], true));
18276                 
18277         
18278     },
18279      
18280     setActiveBullet : function(i)
18281     {
18282         if(Roo.isTouch){
18283             return;
18284         }
18285         
18286         Roo.each(this.el.select('.bullet', true).elements, function(el){
18287             el.removeClass('selected');
18288         });
18289
18290         var bullet = this.el.select('.bullet-' + i, true).first();
18291         
18292         if(!bullet){
18293             return;
18294         }
18295         
18296         bullet.addClass('selected');
18297     }
18298     
18299     
18300   
18301 });
18302
18303  
18304
18305  
18306  
18307 Roo.apply(Roo.bootstrap.TabGroup, {
18308     
18309     groups: {},
18310      /**
18311     * register a Navigation Group
18312     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18313     */
18314     register : function(navgrp)
18315     {
18316         this.groups[navgrp.navId] = navgrp;
18317         
18318     },
18319     /**
18320     * fetch a Navigation Group based on the navigation ID
18321     * if one does not exist , it will get created.
18322     * @param {string} the navgroup to add
18323     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18324     */
18325     get: function(navId) {
18326         if (typeof(this.groups[navId]) == 'undefined') {
18327             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18328         }
18329         return this.groups[navId] ;
18330     }
18331     
18332     
18333     
18334 });
18335
18336  /*
18337  * - LGPL
18338  *
18339  * TabPanel
18340  * 
18341  */
18342
18343 /**
18344  * @class Roo.bootstrap.TabPanel
18345  * @extends Roo.bootstrap.Component
18346  * Bootstrap TabPanel class
18347  * @cfg {Boolean} active panel active
18348  * @cfg {String} html panel content
18349  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18350  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18351  * @cfg {String} href click to link..
18352  * 
18353  * 
18354  * @constructor
18355  * Create a new TabPanel
18356  * @param {Object} config The config object
18357  */
18358
18359 Roo.bootstrap.TabPanel = function(config){
18360     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18361     this.addEvents({
18362         /**
18363              * @event changed
18364              * Fires when the active status changes
18365              * @param {Roo.bootstrap.TabPanel} this
18366              * @param {Boolean} state the new state
18367             
18368          */
18369         'changed': true,
18370         /**
18371              * @event beforedeactivate
18372              * Fires before a tab is de-activated - can be used to do validation on a form.
18373              * @param {Roo.bootstrap.TabPanel} this
18374              * @return {Boolean} false if there is an error
18375             
18376          */
18377         'beforedeactivate': true
18378      });
18379     
18380     this.tabId = this.tabId || Roo.id();
18381   
18382 };
18383
18384 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18385     
18386     active: false,
18387     html: false,
18388     tabId: false,
18389     navId : false,
18390     href : '',
18391     
18392     getAutoCreate : function(){
18393         var cfg = {
18394             tag: 'div',
18395             // item is needed for carousel - not sure if it has any effect otherwise
18396             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18397             html: this.html || ''
18398         };
18399         
18400         if(this.active){
18401             cfg.cls += ' active';
18402         }
18403         
18404         if(this.tabId){
18405             cfg.tabId = this.tabId;
18406         }
18407         
18408         
18409         return cfg;
18410     },
18411     
18412     initEvents:  function()
18413     {
18414         var p = this.parent();
18415         
18416         this.navId = this.navId || p.navId;
18417         
18418         if (typeof(this.navId) != 'undefined') {
18419             // not really needed.. but just in case.. parent should be a NavGroup.
18420             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18421             
18422             tg.register(this);
18423             
18424             var i = tg.tabs.length - 1;
18425             
18426             if(this.active && tg.bullets > 0 && i < tg.bullets){
18427                 tg.setActiveBullet(i);
18428             }
18429         }
18430         
18431         this.el.on('click', this.onClick, this);
18432         
18433         if(Roo.isTouch){
18434             this.el.on("touchstart", this.onTouchStart, this);
18435             this.el.on("touchmove", this.onTouchMove, this);
18436             this.el.on("touchend", this.onTouchEnd, this);
18437         }
18438         
18439     },
18440     
18441     onRender : function(ct, position)
18442     {
18443         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18444     },
18445     
18446     setActive : function(state)
18447     {
18448         Roo.log("panel - set active " + this.tabId + "=" + state);
18449         
18450         this.active = state;
18451         if (!state) {
18452             this.el.removeClass('active');
18453             
18454         } else  if (!this.el.hasClass('active')) {
18455             this.el.addClass('active');
18456         }
18457         
18458         this.fireEvent('changed', this, state);
18459     },
18460     
18461     onClick : function(e)
18462     {
18463         e.preventDefault();
18464         
18465         if(!this.href.length){
18466             return;
18467         }
18468         
18469         window.location.href = this.href;
18470     },
18471     
18472     startX : 0,
18473     startY : 0,
18474     endX : 0,
18475     endY : 0,
18476     swiping : false,
18477     
18478     onTouchStart : function(e)
18479     {
18480         this.swiping = false;
18481         
18482         this.startX = e.browserEvent.touches[0].clientX;
18483         this.startY = e.browserEvent.touches[0].clientY;
18484     },
18485     
18486     onTouchMove : function(e)
18487     {
18488         this.swiping = true;
18489         
18490         this.endX = e.browserEvent.touches[0].clientX;
18491         this.endY = e.browserEvent.touches[0].clientY;
18492     },
18493     
18494     onTouchEnd : function(e)
18495     {
18496         if(!this.swiping){
18497             this.onClick(e);
18498             return;
18499         }
18500         
18501         var tabGroup = this.parent();
18502         
18503         if(this.endX > this.startX){ // swiping right
18504             tabGroup.showPanelPrev();
18505             return;
18506         }
18507         
18508         if(this.startX > this.endX){ // swiping left
18509             tabGroup.showPanelNext();
18510             return;
18511         }
18512     }
18513     
18514     
18515 });
18516  
18517
18518  
18519
18520  /*
18521  * - LGPL
18522  *
18523  * DateField
18524  * 
18525  */
18526
18527 /**
18528  * @class Roo.bootstrap.DateField
18529  * @extends Roo.bootstrap.Input
18530  * Bootstrap DateField class
18531  * @cfg {Number} weekStart default 0
18532  * @cfg {String} viewMode default empty, (months|years)
18533  * @cfg {String} minViewMode default empty, (months|years)
18534  * @cfg {Number} startDate default -Infinity
18535  * @cfg {Number} endDate default Infinity
18536  * @cfg {Boolean} todayHighlight default false
18537  * @cfg {Boolean} todayBtn default false
18538  * @cfg {Boolean} calendarWeeks default false
18539  * @cfg {Object} daysOfWeekDisabled default empty
18540  * @cfg {Boolean} singleMode default false (true | false)
18541  * 
18542  * @cfg {Boolean} keyboardNavigation default true
18543  * @cfg {String} language default en
18544  * 
18545  * @constructor
18546  * Create a new DateField
18547  * @param {Object} config The config object
18548  */
18549
18550 Roo.bootstrap.DateField = function(config){
18551     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18552      this.addEvents({
18553             /**
18554              * @event show
18555              * Fires when this field show.
18556              * @param {Roo.bootstrap.DateField} this
18557              * @param {Mixed} date The date value
18558              */
18559             show : true,
18560             /**
18561              * @event show
18562              * Fires when this field hide.
18563              * @param {Roo.bootstrap.DateField} this
18564              * @param {Mixed} date The date value
18565              */
18566             hide : true,
18567             /**
18568              * @event select
18569              * Fires when select a date.
18570              * @param {Roo.bootstrap.DateField} this
18571              * @param {Mixed} date The date value
18572              */
18573             select : true,
18574             /**
18575              * @event beforeselect
18576              * Fires when before select a date.
18577              * @param {Roo.bootstrap.DateField} this
18578              * @param {Mixed} date The date value
18579              */
18580             beforeselect : true
18581         });
18582 };
18583
18584 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18585     
18586     /**
18587      * @cfg {String} format
18588      * The default date format string which can be overriden for localization support.  The format must be
18589      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18590      */
18591     format : "m/d/y",
18592     /**
18593      * @cfg {String} altFormats
18594      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18595      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18596      */
18597     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18598     
18599     weekStart : 0,
18600     
18601     viewMode : '',
18602     
18603     minViewMode : '',
18604     
18605     todayHighlight : false,
18606     
18607     todayBtn: false,
18608     
18609     language: 'en',
18610     
18611     keyboardNavigation: true,
18612     
18613     calendarWeeks: false,
18614     
18615     startDate: -Infinity,
18616     
18617     endDate: Infinity,
18618     
18619     daysOfWeekDisabled: [],
18620     
18621     _events: [],
18622     
18623     singleMode : false,
18624     
18625     UTCDate: function()
18626     {
18627         return new Date(Date.UTC.apply(Date, arguments));
18628     },
18629     
18630     UTCToday: function()
18631     {
18632         var today = new Date();
18633         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18634     },
18635     
18636     getDate: function() {
18637             var d = this.getUTCDate();
18638             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18639     },
18640     
18641     getUTCDate: function() {
18642             return this.date;
18643     },
18644     
18645     setDate: function(d) {
18646             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18647     },
18648     
18649     setUTCDate: function(d) {
18650             this.date = d;
18651             this.setValue(this.formatDate(this.date));
18652     },
18653         
18654     onRender: function(ct, position)
18655     {
18656         
18657         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18658         
18659         this.language = this.language || 'en';
18660         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18661         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18662         
18663         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18664         this.format = this.format || 'm/d/y';
18665         this.isInline = false;
18666         this.isInput = true;
18667         this.component = this.el.select('.add-on', true).first() || false;
18668         this.component = (this.component && this.component.length === 0) ? false : this.component;
18669         this.hasInput = this.component && this.inputEl().length;
18670         
18671         if (typeof(this.minViewMode === 'string')) {
18672             switch (this.minViewMode) {
18673                 case 'months':
18674                     this.minViewMode = 1;
18675                     break;
18676                 case 'years':
18677                     this.minViewMode = 2;
18678                     break;
18679                 default:
18680                     this.minViewMode = 0;
18681                     break;
18682             }
18683         }
18684         
18685         if (typeof(this.viewMode === 'string')) {
18686             switch (this.viewMode) {
18687                 case 'months':
18688                     this.viewMode = 1;
18689                     break;
18690                 case 'years':
18691                     this.viewMode = 2;
18692                     break;
18693                 default:
18694                     this.viewMode = 0;
18695                     break;
18696             }
18697         }
18698                 
18699         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18700         
18701 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18702         
18703         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18704         
18705         this.picker().on('mousedown', this.onMousedown, this);
18706         this.picker().on('click', this.onClick, this);
18707         
18708         this.picker().addClass('datepicker-dropdown');
18709         
18710         this.startViewMode = this.viewMode;
18711         
18712         if(this.singleMode){
18713             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18714                 v.setVisibilityMode(Roo.Element.DISPLAY);
18715                 v.hide();
18716             });
18717             
18718             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18719                 v.setStyle('width', '189px');
18720             });
18721         }
18722         
18723         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18724             if(!this.calendarWeeks){
18725                 v.remove();
18726                 return;
18727             }
18728             
18729             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18730             v.attr('colspan', function(i, val){
18731                 return parseInt(val) + 1;
18732             });
18733         });
18734                         
18735         
18736         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18737         
18738         this.setStartDate(this.startDate);
18739         this.setEndDate(this.endDate);
18740         
18741         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18742         
18743         this.fillDow();
18744         this.fillMonths();
18745         this.update();
18746         this.showMode();
18747         
18748         if(this.isInline) {
18749             this.showPopup();
18750         }
18751     },
18752     
18753     picker : function()
18754     {
18755         return this.pickerEl;
18756 //        return this.el.select('.datepicker', true).first();
18757     },
18758     
18759     fillDow: function()
18760     {
18761         var dowCnt = this.weekStart;
18762         
18763         var dow = {
18764             tag: 'tr',
18765             cn: [
18766                 
18767             ]
18768         };
18769         
18770         if(this.calendarWeeks){
18771             dow.cn.push({
18772                 tag: 'th',
18773                 cls: 'cw',
18774                 html: '&nbsp;'
18775             })
18776         }
18777         
18778         while (dowCnt < this.weekStart + 7) {
18779             dow.cn.push({
18780                 tag: 'th',
18781                 cls: 'dow',
18782                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18783             });
18784         }
18785         
18786         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18787     },
18788     
18789     fillMonths: function()
18790     {    
18791         var i = 0;
18792         var months = this.picker().select('>.datepicker-months td', true).first();
18793         
18794         months.dom.innerHTML = '';
18795         
18796         while (i < 12) {
18797             var month = {
18798                 tag: 'span',
18799                 cls: 'month',
18800                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18801             };
18802             
18803             months.createChild(month);
18804         }
18805         
18806     },
18807     
18808     update: function()
18809     {
18810         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;
18811         
18812         if (this.date < this.startDate) {
18813             this.viewDate = new Date(this.startDate);
18814         } else if (this.date > this.endDate) {
18815             this.viewDate = new Date(this.endDate);
18816         } else {
18817             this.viewDate = new Date(this.date);
18818         }
18819         
18820         this.fill();
18821     },
18822     
18823     fill: function() 
18824     {
18825         var d = new Date(this.viewDate),
18826                 year = d.getUTCFullYear(),
18827                 month = d.getUTCMonth(),
18828                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18829                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18830                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18831                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18832                 currentDate = this.date && this.date.valueOf(),
18833                 today = this.UTCToday();
18834         
18835         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18836         
18837 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18838         
18839 //        this.picker.select('>tfoot th.today').
18840 //                                              .text(dates[this.language].today)
18841 //                                              .toggle(this.todayBtn !== false);
18842     
18843         this.updateNavArrows();
18844         this.fillMonths();
18845                                                 
18846         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18847         
18848         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18849          
18850         prevMonth.setUTCDate(day);
18851         
18852         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18853         
18854         var nextMonth = new Date(prevMonth);
18855         
18856         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18857         
18858         nextMonth = nextMonth.valueOf();
18859         
18860         var fillMonths = false;
18861         
18862         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18863         
18864         while(prevMonth.valueOf() <= nextMonth) {
18865             var clsName = '';
18866             
18867             if (prevMonth.getUTCDay() === this.weekStart) {
18868                 if(fillMonths){
18869                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18870                 }
18871                     
18872                 fillMonths = {
18873                     tag: 'tr',
18874                     cn: []
18875                 };
18876                 
18877                 if(this.calendarWeeks){
18878                     // ISO 8601: First week contains first thursday.
18879                     // ISO also states week starts on Monday, but we can be more abstract here.
18880                     var
18881                     // Start of current week: based on weekstart/current date
18882                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18883                     // Thursday of this week
18884                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18885                     // First Thursday of year, year from thursday
18886                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18887                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18888                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18889                     
18890                     fillMonths.cn.push({
18891                         tag: 'td',
18892                         cls: 'cw',
18893                         html: calWeek
18894                     });
18895                 }
18896             }
18897             
18898             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18899                 clsName += ' old';
18900             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18901                 clsName += ' new';
18902             }
18903             if (this.todayHighlight &&
18904                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18905                 prevMonth.getUTCMonth() == today.getMonth() &&
18906                 prevMonth.getUTCDate() == today.getDate()) {
18907                 clsName += ' today';
18908             }
18909             
18910             if (currentDate && prevMonth.valueOf() === currentDate) {
18911                 clsName += ' active';
18912             }
18913             
18914             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18915                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18916                     clsName += ' disabled';
18917             }
18918             
18919             fillMonths.cn.push({
18920                 tag: 'td',
18921                 cls: 'day ' + clsName,
18922                 html: prevMonth.getDate()
18923             });
18924             
18925             prevMonth.setDate(prevMonth.getDate()+1);
18926         }
18927           
18928         var currentYear = this.date && this.date.getUTCFullYear();
18929         var currentMonth = this.date && this.date.getUTCMonth();
18930         
18931         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18932         
18933         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18934             v.removeClass('active');
18935             
18936             if(currentYear === year && k === currentMonth){
18937                 v.addClass('active');
18938             }
18939             
18940             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18941                 v.addClass('disabled');
18942             }
18943             
18944         });
18945         
18946         
18947         year = parseInt(year/10, 10) * 10;
18948         
18949         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18950         
18951         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18952         
18953         year -= 1;
18954         for (var i = -1; i < 11; i++) {
18955             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18956                 tag: 'span',
18957                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18958                 html: year
18959             });
18960             
18961             year += 1;
18962         }
18963     },
18964     
18965     showMode: function(dir) 
18966     {
18967         if (dir) {
18968             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18969         }
18970         
18971         Roo.each(this.picker().select('>div',true).elements, function(v){
18972             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18973             v.hide();
18974         });
18975         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18976     },
18977     
18978     place: function()
18979     {
18980         if(this.isInline) {
18981             return;
18982         }
18983         
18984         this.picker().removeClass(['bottom', 'top']);
18985         
18986         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18987             /*
18988              * place to the top of element!
18989              *
18990              */
18991             
18992             this.picker().addClass('top');
18993             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18994             
18995             return;
18996         }
18997         
18998         this.picker().addClass('bottom');
18999         
19000         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19001     },
19002     
19003     parseDate : function(value)
19004     {
19005         if(!value || value instanceof Date){
19006             return value;
19007         }
19008         var v = Date.parseDate(value, this.format);
19009         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19010             v = Date.parseDate(value, 'Y-m-d');
19011         }
19012         if(!v && this.altFormats){
19013             if(!this.altFormatsArray){
19014                 this.altFormatsArray = this.altFormats.split("|");
19015             }
19016             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19017                 v = Date.parseDate(value, this.altFormatsArray[i]);
19018             }
19019         }
19020         return v;
19021     },
19022     
19023     formatDate : function(date, fmt)
19024     {   
19025         return (!date || !(date instanceof Date)) ?
19026         date : date.dateFormat(fmt || this.format);
19027     },
19028     
19029     onFocus : function()
19030     {
19031         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19032         this.showPopup();
19033     },
19034     
19035     onBlur : function()
19036     {
19037         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19038         
19039         var d = this.inputEl().getValue();
19040         
19041         this.setValue(d);
19042                 
19043         this.hidePopup();
19044     },
19045     
19046     showPopup : function()
19047     {
19048         this.picker().show();
19049         this.update();
19050         this.place();
19051         
19052         this.fireEvent('showpopup', this, this.date);
19053     },
19054     
19055     hidePopup : function()
19056     {
19057         if(this.isInline) {
19058             return;
19059         }
19060         this.picker().hide();
19061         this.viewMode = this.startViewMode;
19062         this.showMode();
19063         
19064         this.fireEvent('hidepopup', this, this.date);
19065         
19066     },
19067     
19068     onMousedown: function(e)
19069     {
19070         e.stopPropagation();
19071         e.preventDefault();
19072     },
19073     
19074     keyup: function(e)
19075     {
19076         Roo.bootstrap.DateField.superclass.keyup.call(this);
19077         this.update();
19078     },
19079
19080     setValue: function(v)
19081     {
19082         if(this.fireEvent('beforeselect', this, v) !== false){
19083             var d = new Date(this.parseDate(v) ).clearTime();
19084         
19085             if(isNaN(d.getTime())){
19086                 this.date = this.viewDate = '';
19087                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19088                 return;
19089             }
19090
19091             v = this.formatDate(d);
19092
19093             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19094
19095             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19096
19097             this.update();
19098
19099             this.fireEvent('select', this, this.date);
19100         }
19101     },
19102     
19103     getValue: function()
19104     {
19105         return this.formatDate(this.date);
19106     },
19107     
19108     fireKey: function(e)
19109     {
19110         if (!this.picker().isVisible()){
19111             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19112                 this.showPopup();
19113             }
19114             return;
19115         }
19116         
19117         var dateChanged = false,
19118         dir, day, month,
19119         newDate, newViewDate;
19120         
19121         switch(e.keyCode){
19122             case 27: // escape
19123                 this.hidePopup();
19124                 e.preventDefault();
19125                 break;
19126             case 37: // left
19127             case 39: // right
19128                 if (!this.keyboardNavigation) {
19129                     break;
19130                 }
19131                 dir = e.keyCode == 37 ? -1 : 1;
19132                 
19133                 if (e.ctrlKey){
19134                     newDate = this.moveYear(this.date, dir);
19135                     newViewDate = this.moveYear(this.viewDate, dir);
19136                 } else if (e.shiftKey){
19137                     newDate = this.moveMonth(this.date, dir);
19138                     newViewDate = this.moveMonth(this.viewDate, dir);
19139                 } else {
19140                     newDate = new Date(this.date);
19141                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19142                     newViewDate = new Date(this.viewDate);
19143                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19144                 }
19145                 if (this.dateWithinRange(newDate)){
19146                     this.date = newDate;
19147                     this.viewDate = newViewDate;
19148                     this.setValue(this.formatDate(this.date));
19149 //                    this.update();
19150                     e.preventDefault();
19151                     dateChanged = true;
19152                 }
19153                 break;
19154             case 38: // up
19155             case 40: // down
19156                 if (!this.keyboardNavigation) {
19157                     break;
19158                 }
19159                 dir = e.keyCode == 38 ? -1 : 1;
19160                 if (e.ctrlKey){
19161                     newDate = this.moveYear(this.date, dir);
19162                     newViewDate = this.moveYear(this.viewDate, dir);
19163                 } else if (e.shiftKey){
19164                     newDate = this.moveMonth(this.date, dir);
19165                     newViewDate = this.moveMonth(this.viewDate, dir);
19166                 } else {
19167                     newDate = new Date(this.date);
19168                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19169                     newViewDate = new Date(this.viewDate);
19170                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19171                 }
19172                 if (this.dateWithinRange(newDate)){
19173                     this.date = newDate;
19174                     this.viewDate = newViewDate;
19175                     this.setValue(this.formatDate(this.date));
19176 //                    this.update();
19177                     e.preventDefault();
19178                     dateChanged = true;
19179                 }
19180                 break;
19181             case 13: // enter
19182                 this.setValue(this.formatDate(this.date));
19183                 this.hidePopup();
19184                 e.preventDefault();
19185                 break;
19186             case 9: // tab
19187                 this.setValue(this.formatDate(this.date));
19188                 this.hidePopup();
19189                 break;
19190             case 16: // shift
19191             case 17: // ctrl
19192             case 18: // alt
19193                 break;
19194             default :
19195                 this.hidePopup();
19196                 
19197         }
19198     },
19199     
19200     
19201     onClick: function(e) 
19202     {
19203         e.stopPropagation();
19204         e.preventDefault();
19205         
19206         var target = e.getTarget();
19207         
19208         if(target.nodeName.toLowerCase() === 'i'){
19209             target = Roo.get(target).dom.parentNode;
19210         }
19211         
19212         var nodeName = target.nodeName;
19213         var className = target.className;
19214         var html = target.innerHTML;
19215         //Roo.log(nodeName);
19216         
19217         switch(nodeName.toLowerCase()) {
19218             case 'th':
19219                 switch(className) {
19220                     case 'switch':
19221                         this.showMode(1);
19222                         break;
19223                     case 'prev':
19224                     case 'next':
19225                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19226                         switch(this.viewMode){
19227                                 case 0:
19228                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19229                                         break;
19230                                 case 1:
19231                                 case 2:
19232                                         this.viewDate = this.moveYear(this.viewDate, dir);
19233                                         break;
19234                         }
19235                         this.fill();
19236                         break;
19237                     case 'today':
19238                         var date = new Date();
19239                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19240 //                        this.fill()
19241                         this.setValue(this.formatDate(this.date));
19242                         
19243                         this.hidePopup();
19244                         break;
19245                 }
19246                 break;
19247             case 'span':
19248                 if (className.indexOf('disabled') < 0) {
19249                     this.viewDate.setUTCDate(1);
19250                     if (className.indexOf('month') > -1) {
19251                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19252                     } else {
19253                         var year = parseInt(html, 10) || 0;
19254                         this.viewDate.setUTCFullYear(year);
19255                         
19256                     }
19257                     
19258                     if(this.singleMode){
19259                         this.setValue(this.formatDate(this.viewDate));
19260                         this.hidePopup();
19261                         return;
19262                     }
19263                     
19264                     this.showMode(-1);
19265                     this.fill();
19266                 }
19267                 break;
19268                 
19269             case 'td':
19270                 //Roo.log(className);
19271                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19272                     var day = parseInt(html, 10) || 1;
19273                     var year = this.viewDate.getUTCFullYear(),
19274                         month = this.viewDate.getUTCMonth();
19275
19276                     if (className.indexOf('old') > -1) {
19277                         if(month === 0 ){
19278                             month = 11;
19279                             year -= 1;
19280                         }else{
19281                             month -= 1;
19282                         }
19283                     } else if (className.indexOf('new') > -1) {
19284                         if (month == 11) {
19285                             month = 0;
19286                             year += 1;
19287                         } else {
19288                             month += 1;
19289                         }
19290                     }
19291                     //Roo.log([year,month,day]);
19292                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19293                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19294 //                    this.fill();
19295                     //Roo.log(this.formatDate(this.date));
19296                     this.setValue(this.formatDate(this.date));
19297                     this.hidePopup();
19298                 }
19299                 break;
19300         }
19301     },
19302     
19303     setStartDate: function(startDate)
19304     {
19305         this.startDate = startDate || -Infinity;
19306         if (this.startDate !== -Infinity) {
19307             this.startDate = this.parseDate(this.startDate);
19308         }
19309         this.update();
19310         this.updateNavArrows();
19311     },
19312
19313     setEndDate: function(endDate)
19314     {
19315         this.endDate = endDate || Infinity;
19316         if (this.endDate !== Infinity) {
19317             this.endDate = this.parseDate(this.endDate);
19318         }
19319         this.update();
19320         this.updateNavArrows();
19321     },
19322     
19323     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19324     {
19325         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19326         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19327             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19328         }
19329         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19330             return parseInt(d, 10);
19331         });
19332         this.update();
19333         this.updateNavArrows();
19334     },
19335     
19336     updateNavArrows: function() 
19337     {
19338         if(this.singleMode){
19339             return;
19340         }
19341         
19342         var d = new Date(this.viewDate),
19343         year = d.getUTCFullYear(),
19344         month = d.getUTCMonth();
19345         
19346         Roo.each(this.picker().select('.prev', true).elements, function(v){
19347             v.show();
19348             switch (this.viewMode) {
19349                 case 0:
19350
19351                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19352                         v.hide();
19353                     }
19354                     break;
19355                 case 1:
19356                 case 2:
19357                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19358                         v.hide();
19359                     }
19360                     break;
19361             }
19362         });
19363         
19364         Roo.each(this.picker().select('.next', true).elements, function(v){
19365             v.show();
19366             switch (this.viewMode) {
19367                 case 0:
19368
19369                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19370                         v.hide();
19371                     }
19372                     break;
19373                 case 1:
19374                 case 2:
19375                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19376                         v.hide();
19377                     }
19378                     break;
19379             }
19380         })
19381     },
19382     
19383     moveMonth: function(date, dir)
19384     {
19385         if (!dir) {
19386             return date;
19387         }
19388         var new_date = new Date(date.valueOf()),
19389         day = new_date.getUTCDate(),
19390         month = new_date.getUTCMonth(),
19391         mag = Math.abs(dir),
19392         new_month, test;
19393         dir = dir > 0 ? 1 : -1;
19394         if (mag == 1){
19395             test = dir == -1
19396             // If going back one month, make sure month is not current month
19397             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19398             ? function(){
19399                 return new_date.getUTCMonth() == month;
19400             }
19401             // If going forward one month, make sure month is as expected
19402             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19403             : function(){
19404                 return new_date.getUTCMonth() != new_month;
19405             };
19406             new_month = month + dir;
19407             new_date.setUTCMonth(new_month);
19408             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19409             if (new_month < 0 || new_month > 11) {
19410                 new_month = (new_month + 12) % 12;
19411             }
19412         } else {
19413             // For magnitudes >1, move one month at a time...
19414             for (var i=0; i<mag; i++) {
19415                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19416                 new_date = this.moveMonth(new_date, dir);
19417             }
19418             // ...then reset the day, keeping it in the new month
19419             new_month = new_date.getUTCMonth();
19420             new_date.setUTCDate(day);
19421             test = function(){
19422                 return new_month != new_date.getUTCMonth();
19423             };
19424         }
19425         // Common date-resetting loop -- if date is beyond end of month, make it
19426         // end of month
19427         while (test()){
19428             new_date.setUTCDate(--day);
19429             new_date.setUTCMonth(new_month);
19430         }
19431         return new_date;
19432     },
19433
19434     moveYear: function(date, dir)
19435     {
19436         return this.moveMonth(date, dir*12);
19437     },
19438
19439     dateWithinRange: function(date)
19440     {
19441         return date >= this.startDate && date <= this.endDate;
19442     },
19443
19444     
19445     remove: function() 
19446     {
19447         this.picker().remove();
19448     },
19449     
19450     validateValue : function(value)
19451     {
19452         if(this.getVisibilityEl().hasClass('hidden')){
19453             return true;
19454         }
19455         
19456         if(value.length < 1)  {
19457             if(this.allowBlank){
19458                 return true;
19459             }
19460             return false;
19461         }
19462         
19463         if(value.length < this.minLength){
19464             return false;
19465         }
19466         if(value.length > this.maxLength){
19467             return false;
19468         }
19469         if(this.vtype){
19470             var vt = Roo.form.VTypes;
19471             if(!vt[this.vtype](value, this)){
19472                 return false;
19473             }
19474         }
19475         if(typeof this.validator == "function"){
19476             var msg = this.validator(value);
19477             if(msg !== true){
19478                 return false;
19479             }
19480         }
19481         
19482         if(this.regex && !this.regex.test(value)){
19483             return false;
19484         }
19485         
19486         if(typeof(this.parseDate(value)) == 'undefined'){
19487             return false;
19488         }
19489         
19490         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19491             return false;
19492         }      
19493         
19494         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19495             return false;
19496         } 
19497         
19498         
19499         return true;
19500     },
19501     
19502     reset : function()
19503     {
19504         this.date = this.viewDate = '';
19505         
19506         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19507     }
19508    
19509 });
19510
19511 Roo.apply(Roo.bootstrap.DateField,  {
19512     
19513     head : {
19514         tag: 'thead',
19515         cn: [
19516         {
19517             tag: 'tr',
19518             cn: [
19519             {
19520                 tag: 'th',
19521                 cls: 'prev',
19522                 html: '<i class="fa fa-arrow-left"/>'
19523             },
19524             {
19525                 tag: 'th',
19526                 cls: 'switch',
19527                 colspan: '5'
19528             },
19529             {
19530                 tag: 'th',
19531                 cls: 'next',
19532                 html: '<i class="fa fa-arrow-right"/>'
19533             }
19534
19535             ]
19536         }
19537         ]
19538     },
19539     
19540     content : {
19541         tag: 'tbody',
19542         cn: [
19543         {
19544             tag: 'tr',
19545             cn: [
19546             {
19547                 tag: 'td',
19548                 colspan: '7'
19549             }
19550             ]
19551         }
19552         ]
19553     },
19554     
19555     footer : {
19556         tag: 'tfoot',
19557         cn: [
19558         {
19559             tag: 'tr',
19560             cn: [
19561             {
19562                 tag: 'th',
19563                 colspan: '7',
19564                 cls: 'today'
19565             }
19566                     
19567             ]
19568         }
19569         ]
19570     },
19571     
19572     dates:{
19573         en: {
19574             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19575             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19576             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19577             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19578             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19579             today: "Today"
19580         }
19581     },
19582     
19583     modes: [
19584     {
19585         clsName: 'days',
19586         navFnc: 'Month',
19587         navStep: 1
19588     },
19589     {
19590         clsName: 'months',
19591         navFnc: 'FullYear',
19592         navStep: 1
19593     },
19594     {
19595         clsName: 'years',
19596         navFnc: 'FullYear',
19597         navStep: 10
19598     }]
19599 });
19600
19601 Roo.apply(Roo.bootstrap.DateField,  {
19602   
19603     template : {
19604         tag: 'div',
19605         cls: 'datepicker dropdown-menu roo-dynamic',
19606         cn: [
19607         {
19608             tag: 'div',
19609             cls: 'datepicker-days',
19610             cn: [
19611             {
19612                 tag: 'table',
19613                 cls: 'table-condensed',
19614                 cn:[
19615                 Roo.bootstrap.DateField.head,
19616                 {
19617                     tag: 'tbody'
19618                 },
19619                 Roo.bootstrap.DateField.footer
19620                 ]
19621             }
19622             ]
19623         },
19624         {
19625             tag: 'div',
19626             cls: 'datepicker-months',
19627             cn: [
19628             {
19629                 tag: 'table',
19630                 cls: 'table-condensed',
19631                 cn:[
19632                 Roo.bootstrap.DateField.head,
19633                 Roo.bootstrap.DateField.content,
19634                 Roo.bootstrap.DateField.footer
19635                 ]
19636             }
19637             ]
19638         },
19639         {
19640             tag: 'div',
19641             cls: 'datepicker-years',
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     }
19656 });
19657
19658  
19659
19660  /*
19661  * - LGPL
19662  *
19663  * TimeField
19664  * 
19665  */
19666
19667 /**
19668  * @class Roo.bootstrap.TimeField
19669  * @extends Roo.bootstrap.Input
19670  * Bootstrap DateField class
19671  * 
19672  * 
19673  * @constructor
19674  * Create a new TimeField
19675  * @param {Object} config The config object
19676  */
19677
19678 Roo.bootstrap.TimeField = function(config){
19679     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19680     this.addEvents({
19681             /**
19682              * @event show
19683              * Fires when this field show.
19684              * @param {Roo.bootstrap.DateField} thisthis
19685              * @param {Mixed} date The date value
19686              */
19687             show : true,
19688             /**
19689              * @event show
19690              * Fires when this field hide.
19691              * @param {Roo.bootstrap.DateField} this
19692              * @param {Mixed} date The date value
19693              */
19694             hide : true,
19695             /**
19696              * @event select
19697              * Fires when select a date.
19698              * @param {Roo.bootstrap.DateField} this
19699              * @param {Mixed} date The date value
19700              */
19701             select : true
19702         });
19703 };
19704
19705 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19706     
19707     /**
19708      * @cfg {String} format
19709      * The default time format string which can be overriden for localization support.  The format must be
19710      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19711      */
19712     format : "H:i",
19713        
19714     onRender: function(ct, position)
19715     {
19716         
19717         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19718                 
19719         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19720         
19721         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19722         
19723         this.pop = this.picker().select('>.datepicker-time',true).first();
19724         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19725         
19726         this.picker().on('mousedown', this.onMousedown, this);
19727         this.picker().on('click', this.onClick, this);
19728         
19729         this.picker().addClass('datepicker-dropdown');
19730     
19731         this.fillTime();
19732         this.update();
19733             
19734         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19735         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19736         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19737         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19738         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19739         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19740
19741     },
19742     
19743     fireKey: function(e){
19744         if (!this.picker().isVisible()){
19745             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19746                 this.show();
19747             }
19748             return;
19749         }
19750
19751         e.preventDefault();
19752         
19753         switch(e.keyCode){
19754             case 27: // escape
19755                 this.hide();
19756                 break;
19757             case 37: // left
19758             case 39: // right
19759                 this.onTogglePeriod();
19760                 break;
19761             case 38: // up
19762                 this.onIncrementMinutes();
19763                 break;
19764             case 40: // down
19765                 this.onDecrementMinutes();
19766                 break;
19767             case 13: // enter
19768             case 9: // tab
19769                 this.setTime();
19770                 break;
19771         }
19772     },
19773     
19774     onClick: function(e) {
19775         e.stopPropagation();
19776         e.preventDefault();
19777     },
19778     
19779     picker : function()
19780     {
19781         return this.el.select('.datepicker', true).first();
19782     },
19783     
19784     fillTime: function()
19785     {    
19786         var time = this.pop.select('tbody', true).first();
19787         
19788         time.dom.innerHTML = '';
19789         
19790         time.createChild({
19791             tag: 'tr',
19792             cn: [
19793                 {
19794                     tag: 'td',
19795                     cn: [
19796                         {
19797                             tag: 'a',
19798                             href: '#',
19799                             cls: 'btn',
19800                             cn: [
19801                                 {
19802                                     tag: 'span',
19803                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19804                                 }
19805                             ]
19806                         } 
19807                     ]
19808                 },
19809                 {
19810                     tag: 'td',
19811                     cls: 'separator'
19812                 },
19813                 {
19814                     tag: 'td',
19815                     cn: [
19816                         {
19817                             tag: 'a',
19818                             href: '#',
19819                             cls: 'btn',
19820                             cn: [
19821                                 {
19822                                     tag: 'span',
19823                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19824                                 }
19825                             ]
19826                         }
19827                     ]
19828                 },
19829                 {
19830                     tag: 'td',
19831                     cls: 'separator'
19832                 }
19833             ]
19834         });
19835         
19836         time.createChild({
19837             tag: 'tr',
19838             cn: [
19839                 {
19840                     tag: 'td',
19841                     cn: [
19842                         {
19843                             tag: 'span',
19844                             cls: 'timepicker-hour',
19845                             html: '00'
19846                         }  
19847                     ]
19848                 },
19849                 {
19850                     tag: 'td',
19851                     cls: 'separator',
19852                     html: ':'
19853                 },
19854                 {
19855                     tag: 'td',
19856                     cn: [
19857                         {
19858                             tag: 'span',
19859                             cls: 'timepicker-minute',
19860                             html: '00'
19861                         }  
19862                     ]
19863                 },
19864                 {
19865                     tag: 'td',
19866                     cls: 'separator'
19867                 },
19868                 {
19869                     tag: 'td',
19870                     cn: [
19871                         {
19872                             tag: 'button',
19873                             type: 'button',
19874                             cls: 'btn btn-primary period',
19875                             html: 'AM'
19876                             
19877                         }
19878                     ]
19879                 }
19880             ]
19881         });
19882         
19883         time.createChild({
19884             tag: 'tr',
19885             cn: [
19886                 {
19887                     tag: 'td',
19888                     cn: [
19889                         {
19890                             tag: 'a',
19891                             href: '#',
19892                             cls: 'btn',
19893                             cn: [
19894                                 {
19895                                     tag: 'span',
19896                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19897                                 }
19898                             ]
19899                         }
19900                     ]
19901                 },
19902                 {
19903                     tag: 'td',
19904                     cls: 'separator'
19905                 },
19906                 {
19907                     tag: 'td',
19908                     cn: [
19909                         {
19910                             tag: 'a',
19911                             href: '#',
19912                             cls: 'btn',
19913                             cn: [
19914                                 {
19915                                     tag: 'span',
19916                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19917                                 }
19918                             ]
19919                         }
19920                     ]
19921                 },
19922                 {
19923                     tag: 'td',
19924                     cls: 'separator'
19925                 }
19926             ]
19927         });
19928         
19929     },
19930     
19931     update: function()
19932     {
19933         
19934         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19935         
19936         this.fill();
19937     },
19938     
19939     fill: function() 
19940     {
19941         var hours = this.time.getHours();
19942         var minutes = this.time.getMinutes();
19943         var period = 'AM';
19944         
19945         if(hours > 11){
19946             period = 'PM';
19947         }
19948         
19949         if(hours == 0){
19950             hours = 12;
19951         }
19952         
19953         
19954         if(hours > 12){
19955             hours = hours - 12;
19956         }
19957         
19958         if(hours < 10){
19959             hours = '0' + hours;
19960         }
19961         
19962         if(minutes < 10){
19963             minutes = '0' + minutes;
19964         }
19965         
19966         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19967         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19968         this.pop.select('button', true).first().dom.innerHTML = period;
19969         
19970     },
19971     
19972     place: function()
19973     {   
19974         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19975         
19976         var cls = ['bottom'];
19977         
19978         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19979             cls.pop();
19980             cls.push('top');
19981         }
19982         
19983         cls.push('right');
19984         
19985         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19986             cls.pop();
19987             cls.push('left');
19988         }
19989         
19990         this.picker().addClass(cls.join('-'));
19991         
19992         var _this = this;
19993         
19994         Roo.each(cls, function(c){
19995             if(c == 'bottom'){
19996                 _this.picker().setTop(_this.inputEl().getHeight());
19997                 return;
19998             }
19999             if(c == 'top'){
20000                 _this.picker().setTop(0 - _this.picker().getHeight());
20001                 return;
20002             }
20003             
20004             if(c == 'left'){
20005                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20006                 return;
20007             }
20008             if(c == 'right'){
20009                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20010                 return;
20011             }
20012         });
20013         
20014     },
20015   
20016     onFocus : function()
20017     {
20018         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20019         this.show();
20020     },
20021     
20022     onBlur : function()
20023     {
20024         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20025         this.hide();
20026     },
20027     
20028     show : function()
20029     {
20030         this.picker().show();
20031         this.pop.show();
20032         this.update();
20033         this.place();
20034         
20035         this.fireEvent('show', this, this.date);
20036     },
20037     
20038     hide : function()
20039     {
20040         this.picker().hide();
20041         this.pop.hide();
20042         
20043         this.fireEvent('hide', this, this.date);
20044     },
20045     
20046     setTime : function()
20047     {
20048         this.hide();
20049         this.setValue(this.time.format(this.format));
20050         
20051         this.fireEvent('select', this, this.date);
20052         
20053         
20054     },
20055     
20056     onMousedown: function(e){
20057         e.stopPropagation();
20058         e.preventDefault();
20059     },
20060     
20061     onIncrementHours: function()
20062     {
20063         Roo.log('onIncrementHours');
20064         this.time = this.time.add(Date.HOUR, 1);
20065         this.update();
20066         
20067     },
20068     
20069     onDecrementHours: function()
20070     {
20071         Roo.log('onDecrementHours');
20072         this.time = this.time.add(Date.HOUR, -1);
20073         this.update();
20074     },
20075     
20076     onIncrementMinutes: function()
20077     {
20078         Roo.log('onIncrementMinutes');
20079         this.time = this.time.add(Date.MINUTE, 1);
20080         this.update();
20081     },
20082     
20083     onDecrementMinutes: function()
20084     {
20085         Roo.log('onDecrementMinutes');
20086         this.time = this.time.add(Date.MINUTE, -1);
20087         this.update();
20088     },
20089     
20090     onTogglePeriod: function()
20091     {
20092         Roo.log('onTogglePeriod');
20093         this.time = this.time.add(Date.HOUR, 12);
20094         this.update();
20095     }
20096     
20097    
20098 });
20099
20100 Roo.apply(Roo.bootstrap.TimeField,  {
20101     
20102     content : {
20103         tag: 'tbody',
20104         cn: [
20105             {
20106                 tag: 'tr',
20107                 cn: [
20108                 {
20109                     tag: 'td',
20110                     colspan: '7'
20111                 }
20112                 ]
20113             }
20114         ]
20115     },
20116     
20117     footer : {
20118         tag: 'tfoot',
20119         cn: [
20120             {
20121                 tag: 'tr',
20122                 cn: [
20123                 {
20124                     tag: 'th',
20125                     colspan: '7',
20126                     cls: '',
20127                     cn: [
20128                         {
20129                             tag: 'button',
20130                             cls: 'btn btn-info ok',
20131                             html: 'OK'
20132                         }
20133                     ]
20134                 }
20135
20136                 ]
20137             }
20138         ]
20139     }
20140 });
20141
20142 Roo.apply(Roo.bootstrap.TimeField,  {
20143   
20144     template : {
20145         tag: 'div',
20146         cls: 'datepicker dropdown-menu',
20147         cn: [
20148             {
20149                 tag: 'div',
20150                 cls: 'datepicker-time',
20151                 cn: [
20152                 {
20153                     tag: 'table',
20154                     cls: 'table-condensed',
20155                     cn:[
20156                     Roo.bootstrap.TimeField.content,
20157                     Roo.bootstrap.TimeField.footer
20158                     ]
20159                 }
20160                 ]
20161             }
20162         ]
20163     }
20164 });
20165
20166  
20167
20168  /*
20169  * - LGPL
20170  *
20171  * MonthField
20172  * 
20173  */
20174
20175 /**
20176  * @class Roo.bootstrap.MonthField
20177  * @extends Roo.bootstrap.Input
20178  * Bootstrap MonthField class
20179  * 
20180  * @cfg {String} language default en
20181  * 
20182  * @constructor
20183  * Create a new MonthField
20184  * @param {Object} config The config object
20185  */
20186
20187 Roo.bootstrap.MonthField = function(config){
20188     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20189     
20190     this.addEvents({
20191         /**
20192          * @event show
20193          * Fires when this field show.
20194          * @param {Roo.bootstrap.MonthField} this
20195          * @param {Mixed} date The date value
20196          */
20197         show : true,
20198         /**
20199          * @event show
20200          * Fires when this field hide.
20201          * @param {Roo.bootstrap.MonthField} this
20202          * @param {Mixed} date The date value
20203          */
20204         hide : true,
20205         /**
20206          * @event select
20207          * Fires when select a date.
20208          * @param {Roo.bootstrap.MonthField} this
20209          * @param {String} oldvalue The old value
20210          * @param {String} newvalue The new value
20211          */
20212         select : true
20213     });
20214 };
20215
20216 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20217     
20218     onRender: function(ct, position)
20219     {
20220         
20221         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20222         
20223         this.language = this.language || 'en';
20224         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20225         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20226         
20227         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20228         this.isInline = false;
20229         this.isInput = true;
20230         this.component = this.el.select('.add-on', true).first() || false;
20231         this.component = (this.component && this.component.length === 0) ? false : this.component;
20232         this.hasInput = this.component && this.inputEL().length;
20233         
20234         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20235         
20236         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20237         
20238         this.picker().on('mousedown', this.onMousedown, this);
20239         this.picker().on('click', this.onClick, this);
20240         
20241         this.picker().addClass('datepicker-dropdown');
20242         
20243         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20244             v.setStyle('width', '189px');
20245         });
20246         
20247         this.fillMonths();
20248         
20249         this.update();
20250         
20251         if(this.isInline) {
20252             this.show();
20253         }
20254         
20255     },
20256     
20257     setValue: function(v, suppressEvent)
20258     {   
20259         var o = this.getValue();
20260         
20261         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20262         
20263         this.update();
20264
20265         if(suppressEvent !== true){
20266             this.fireEvent('select', this, o, v);
20267         }
20268         
20269     },
20270     
20271     getValue: function()
20272     {
20273         return this.value;
20274     },
20275     
20276     onClick: function(e) 
20277     {
20278         e.stopPropagation();
20279         e.preventDefault();
20280         
20281         var target = e.getTarget();
20282         
20283         if(target.nodeName.toLowerCase() === 'i'){
20284             target = Roo.get(target).dom.parentNode;
20285         }
20286         
20287         var nodeName = target.nodeName;
20288         var className = target.className;
20289         var html = target.innerHTML;
20290         
20291         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20292             return;
20293         }
20294         
20295         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20296         
20297         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20298         
20299         this.hide();
20300                         
20301     },
20302     
20303     picker : function()
20304     {
20305         return this.pickerEl;
20306     },
20307     
20308     fillMonths: function()
20309     {    
20310         var i = 0;
20311         var months = this.picker().select('>.datepicker-months td', true).first();
20312         
20313         months.dom.innerHTML = '';
20314         
20315         while (i < 12) {
20316             var month = {
20317                 tag: 'span',
20318                 cls: 'month',
20319                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20320             };
20321             
20322             months.createChild(month);
20323         }
20324         
20325     },
20326     
20327     update: function()
20328     {
20329         var _this = this;
20330         
20331         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20332             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20333         }
20334         
20335         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20336             e.removeClass('active');
20337             
20338             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20339                 e.addClass('active');
20340             }
20341         })
20342     },
20343     
20344     place: function()
20345     {
20346         if(this.isInline) {
20347             return;
20348         }
20349         
20350         this.picker().removeClass(['bottom', 'top']);
20351         
20352         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20353             /*
20354              * place to the top of element!
20355              *
20356              */
20357             
20358             this.picker().addClass('top');
20359             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20360             
20361             return;
20362         }
20363         
20364         this.picker().addClass('bottom');
20365         
20366         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20367     },
20368     
20369     onFocus : function()
20370     {
20371         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20372         this.show();
20373     },
20374     
20375     onBlur : function()
20376     {
20377         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20378         
20379         var d = this.inputEl().getValue();
20380         
20381         this.setValue(d);
20382                 
20383         this.hide();
20384     },
20385     
20386     show : function()
20387     {
20388         this.picker().show();
20389         this.picker().select('>.datepicker-months', true).first().show();
20390         this.update();
20391         this.place();
20392         
20393         this.fireEvent('show', this, this.date);
20394     },
20395     
20396     hide : function()
20397     {
20398         if(this.isInline) {
20399             return;
20400         }
20401         this.picker().hide();
20402         this.fireEvent('hide', this, this.date);
20403         
20404     },
20405     
20406     onMousedown: function(e)
20407     {
20408         e.stopPropagation();
20409         e.preventDefault();
20410     },
20411     
20412     keyup: function(e)
20413     {
20414         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20415         this.update();
20416     },
20417
20418     fireKey: function(e)
20419     {
20420         if (!this.picker().isVisible()){
20421             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20422                 this.show();
20423             }
20424             return;
20425         }
20426         
20427         var dir;
20428         
20429         switch(e.keyCode){
20430             case 27: // escape
20431                 this.hide();
20432                 e.preventDefault();
20433                 break;
20434             case 37: // left
20435             case 39: // right
20436                 dir = e.keyCode == 37 ? -1 : 1;
20437                 
20438                 this.vIndex = this.vIndex + dir;
20439                 
20440                 if(this.vIndex < 0){
20441                     this.vIndex = 0;
20442                 }
20443                 
20444                 if(this.vIndex > 11){
20445                     this.vIndex = 11;
20446                 }
20447                 
20448                 if(isNaN(this.vIndex)){
20449                     this.vIndex = 0;
20450                 }
20451                 
20452                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20453                 
20454                 break;
20455             case 38: // up
20456             case 40: // down
20457                 
20458                 dir = e.keyCode == 38 ? -1 : 1;
20459                 
20460                 this.vIndex = this.vIndex + dir * 4;
20461                 
20462                 if(this.vIndex < 0){
20463                     this.vIndex = 0;
20464                 }
20465                 
20466                 if(this.vIndex > 11){
20467                     this.vIndex = 11;
20468                 }
20469                 
20470                 if(isNaN(this.vIndex)){
20471                     this.vIndex = 0;
20472                 }
20473                 
20474                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20475                 break;
20476                 
20477             case 13: // enter
20478                 
20479                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20480                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20481                 }
20482                 
20483                 this.hide();
20484                 e.preventDefault();
20485                 break;
20486             case 9: // tab
20487                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20488                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20489                 }
20490                 this.hide();
20491                 break;
20492             case 16: // shift
20493             case 17: // ctrl
20494             case 18: // alt
20495                 break;
20496             default :
20497                 this.hide();
20498                 
20499         }
20500     },
20501     
20502     remove: function() 
20503     {
20504         this.picker().remove();
20505     }
20506    
20507 });
20508
20509 Roo.apply(Roo.bootstrap.MonthField,  {
20510     
20511     content : {
20512         tag: 'tbody',
20513         cn: [
20514         {
20515             tag: 'tr',
20516             cn: [
20517             {
20518                 tag: 'td',
20519                 colspan: '7'
20520             }
20521             ]
20522         }
20523         ]
20524     },
20525     
20526     dates:{
20527         en: {
20528             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20529             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20530         }
20531     }
20532 });
20533
20534 Roo.apply(Roo.bootstrap.MonthField,  {
20535   
20536     template : {
20537         tag: 'div',
20538         cls: 'datepicker dropdown-menu roo-dynamic',
20539         cn: [
20540             {
20541                 tag: 'div',
20542                 cls: 'datepicker-months',
20543                 cn: [
20544                 {
20545                     tag: 'table',
20546                     cls: 'table-condensed',
20547                     cn:[
20548                         Roo.bootstrap.DateField.content
20549                     ]
20550                 }
20551                 ]
20552             }
20553         ]
20554     }
20555 });
20556
20557  
20558
20559  
20560  /*
20561  * - LGPL
20562  *
20563  * CheckBox
20564  * 
20565  */
20566
20567 /**
20568  * @class Roo.bootstrap.CheckBox
20569  * @extends Roo.bootstrap.Input
20570  * Bootstrap CheckBox class
20571  * 
20572  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20573  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20574  * @cfg {String} boxLabel The text that appears beside the checkbox
20575  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20576  * @cfg {Boolean} checked initnal the element
20577  * @cfg {Boolean} inline inline the element (default false)
20578  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20579  * @cfg {String} tooltip label tooltip
20580  * 
20581  * @constructor
20582  * Create a new CheckBox
20583  * @param {Object} config The config object
20584  */
20585
20586 Roo.bootstrap.CheckBox = function(config){
20587     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20588    
20589     this.addEvents({
20590         /**
20591         * @event check
20592         * Fires when the element is checked or unchecked.
20593         * @param {Roo.bootstrap.CheckBox} this This input
20594         * @param {Boolean} checked The new checked value
20595         */
20596        check : true,
20597        /**
20598         * @event click
20599         * Fires when the element is click.
20600         * @param {Roo.bootstrap.CheckBox} this This input
20601         */
20602        click : true
20603     });
20604     
20605 };
20606
20607 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20608   
20609     inputType: 'checkbox',
20610     inputValue: 1,
20611     valueOff: 0,
20612     boxLabel: false,
20613     checked: false,
20614     weight : false,
20615     inline: false,
20616     tooltip : '',
20617     
20618     getAutoCreate : function()
20619     {
20620         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20621         
20622         var id = Roo.id();
20623         
20624         var cfg = {};
20625         
20626         cfg.cls = 'form-group ' + this.inputType; //input-group
20627         
20628         if(this.inline){
20629             cfg.cls += ' ' + this.inputType + '-inline';
20630         }
20631         
20632         var input =  {
20633             tag: 'input',
20634             id : id,
20635             type : this.inputType,
20636             value : this.inputValue,
20637             cls : 'roo-' + this.inputType, //'form-box',
20638             placeholder : this.placeholder || ''
20639             
20640         };
20641         
20642         if(this.inputType != 'radio'){
20643             var hidden =  {
20644                 tag: 'input',
20645                 type : 'hidden',
20646                 cls : 'roo-hidden-value',
20647                 value : this.checked ? this.inputValue : this.valueOff
20648             };
20649         }
20650         
20651             
20652         if (this.weight) { // Validity check?
20653             cfg.cls += " " + this.inputType + "-" + this.weight;
20654         }
20655         
20656         if (this.disabled) {
20657             input.disabled=true;
20658         }
20659         
20660         if(this.checked){
20661             input.checked = this.checked;
20662         }
20663         
20664         if (this.name) {
20665             
20666             input.name = this.name;
20667             
20668             if(this.inputType != 'radio'){
20669                 hidden.name = this.name;
20670                 input.name = '_hidden_' + this.name;
20671             }
20672         }
20673         
20674         if (this.size) {
20675             input.cls += ' input-' + this.size;
20676         }
20677         
20678         var settings=this;
20679         
20680         ['xs','sm','md','lg'].map(function(size){
20681             if (settings[size]) {
20682                 cfg.cls += ' col-' + size + '-' + settings[size];
20683             }
20684         });
20685         
20686         var inputblock = input;
20687          
20688         if (this.before || this.after) {
20689             
20690             inputblock = {
20691                 cls : 'input-group',
20692                 cn :  [] 
20693             };
20694             
20695             if (this.before) {
20696                 inputblock.cn.push({
20697                     tag :'span',
20698                     cls : 'input-group-addon',
20699                     html : this.before
20700                 });
20701             }
20702             
20703             inputblock.cn.push(input);
20704             
20705             if(this.inputType != 'radio'){
20706                 inputblock.cn.push(hidden);
20707             }
20708             
20709             if (this.after) {
20710                 inputblock.cn.push({
20711                     tag :'span',
20712                     cls : 'input-group-addon',
20713                     html : this.after
20714                 });
20715             }
20716             
20717         }
20718         
20719         if (align ==='left' && this.fieldLabel.length) {
20720 //                Roo.log("left and has label");
20721             cfg.cn = [
20722                 {
20723                     tag: 'label',
20724                     'for' :  id,
20725                     cls : 'control-label',
20726                     html : this.fieldLabel
20727                 },
20728                 {
20729                     cls : "", 
20730                     cn: [
20731                         inputblock
20732                     ]
20733                 }
20734             ];
20735             
20736             if(this.labelWidth > 12){
20737                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20738             }
20739             
20740             if(this.labelWidth < 13 && this.labelmd == 0){
20741                 this.labelmd = this.labelWidth;
20742             }
20743             
20744             if(this.labellg > 0){
20745                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20746                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20747             }
20748             
20749             if(this.labelmd > 0){
20750                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20751                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20752             }
20753             
20754             if(this.labelsm > 0){
20755                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20756                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20757             }
20758             
20759             if(this.labelxs > 0){
20760                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20761                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20762             }
20763             
20764         } else if ( this.fieldLabel.length) {
20765 //                Roo.log(" label");
20766                 cfg.cn = [
20767                    
20768                     {
20769                         tag: this.boxLabel ? 'span' : 'label',
20770                         'for': id,
20771                         cls: 'control-label box-input-label',
20772                         //cls : 'input-group-addon',
20773                         html : this.fieldLabel
20774                     },
20775                     
20776                     inputblock
20777                     
20778                 ];
20779
20780         } else {
20781             
20782 //                Roo.log(" no label && no align");
20783                 cfg.cn = [  inputblock ] ;
20784                 
20785                 
20786         }
20787         
20788         if(this.boxLabel){
20789              var boxLabelCfg = {
20790                 tag: 'label',
20791                 //'for': id, // box label is handled by onclick - so no for...
20792                 cls: 'box-label',
20793                 html: this.boxLabel
20794             };
20795             
20796             if(this.tooltip){
20797                 boxLabelCfg.tooltip = this.tooltip;
20798             }
20799              
20800             cfg.cn.push(boxLabelCfg);
20801         }
20802         
20803         if(this.inputType != 'radio'){
20804             cfg.cn.push(hidden);
20805         }
20806         
20807         return cfg;
20808         
20809     },
20810     
20811     /**
20812      * return the real input element.
20813      */
20814     inputEl: function ()
20815     {
20816         return this.el.select('input.roo-' + this.inputType,true).first();
20817     },
20818     hiddenEl: function ()
20819     {
20820         return this.el.select('input.roo-hidden-value',true).first();
20821     },
20822     
20823     labelEl: function()
20824     {
20825         return this.el.select('label.control-label',true).first();
20826     },
20827     /* depricated... */
20828     
20829     label: function()
20830     {
20831         return this.labelEl();
20832     },
20833     
20834     boxLabelEl: function()
20835     {
20836         return this.el.select('label.box-label',true).first();
20837     },
20838     
20839     initEvents : function()
20840     {
20841 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20842         
20843         this.inputEl().on('click', this.onClick,  this);
20844         
20845         if (this.boxLabel) { 
20846             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20847         }
20848         
20849         this.startValue = this.getValue();
20850         
20851         if(this.groupId){
20852             Roo.bootstrap.CheckBox.register(this);
20853         }
20854     },
20855     
20856     onClick : function(e)
20857     {   
20858         if(this.fireEvent('click', this, e) !== false){
20859             this.setChecked(!this.checked);
20860         }
20861         
20862     },
20863     
20864     setChecked : function(state,suppressEvent)
20865     {
20866         this.startValue = this.getValue();
20867
20868         if(this.inputType == 'radio'){
20869             
20870             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20871                 e.dom.checked = false;
20872             });
20873             
20874             this.inputEl().dom.checked = true;
20875             
20876             this.inputEl().dom.value = this.inputValue;
20877             
20878             if(suppressEvent !== true){
20879                 this.fireEvent('check', this, true);
20880             }
20881             
20882             this.validate();
20883             
20884             return;
20885         }
20886         
20887         this.checked = state;
20888         
20889         this.inputEl().dom.checked = state;
20890         
20891         
20892         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20893         
20894         if(suppressEvent !== true){
20895             this.fireEvent('check', this, state);
20896         }
20897         
20898         this.validate();
20899     },
20900     
20901     getValue : function()
20902     {
20903         if(this.inputType == 'radio'){
20904             return this.getGroupValue();
20905         }
20906         
20907         return this.hiddenEl().dom.value;
20908         
20909     },
20910     
20911     getGroupValue : function()
20912     {
20913         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20914             return '';
20915         }
20916         
20917         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20918     },
20919     
20920     setValue : function(v,suppressEvent)
20921     {
20922         if(this.inputType == 'radio'){
20923             this.setGroupValue(v, suppressEvent);
20924             return;
20925         }
20926         
20927         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20928         
20929         this.validate();
20930     },
20931     
20932     setGroupValue : function(v, suppressEvent)
20933     {
20934         this.startValue = this.getValue();
20935         
20936         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20937             e.dom.checked = false;
20938             
20939             if(e.dom.value == v){
20940                 e.dom.checked = true;
20941             }
20942         });
20943         
20944         if(suppressEvent !== true){
20945             this.fireEvent('check', this, true);
20946         }
20947
20948         this.validate();
20949         
20950         return;
20951     },
20952     
20953     validate : function()
20954     {
20955         if(this.getVisibilityEl().hasClass('hidden')){
20956             return true;
20957         }
20958         
20959         if(
20960                 this.disabled || 
20961                 (this.inputType == 'radio' && this.validateRadio()) ||
20962                 (this.inputType == 'checkbox' && this.validateCheckbox())
20963         ){
20964             this.markValid();
20965             return true;
20966         }
20967         
20968         this.markInvalid();
20969         return false;
20970     },
20971     
20972     validateRadio : function()
20973     {
20974         if(this.getVisibilityEl().hasClass('hidden')){
20975             return true;
20976         }
20977         
20978         if(this.allowBlank){
20979             return true;
20980         }
20981         
20982         var valid = false;
20983         
20984         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20985             if(!e.dom.checked){
20986                 return;
20987             }
20988             
20989             valid = true;
20990             
20991             return false;
20992         });
20993         
20994         return valid;
20995     },
20996     
20997     validateCheckbox : function()
20998     {
20999         if(!this.groupId){
21000             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21001             //return (this.getValue() == this.inputValue) ? true : false;
21002         }
21003         
21004         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21005         
21006         if(!group){
21007             return false;
21008         }
21009         
21010         var r = false;
21011         
21012         for(var i in group){
21013             if(group[i].el.isVisible(true)){
21014                 r = false;
21015                 break;
21016             }
21017             
21018             r = true;
21019         }
21020         
21021         for(var i in group){
21022             if(r){
21023                 break;
21024             }
21025             
21026             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21027         }
21028         
21029         return r;
21030     },
21031     
21032     /**
21033      * Mark this field as valid
21034      */
21035     markValid : function()
21036     {
21037         var _this = this;
21038         
21039         this.fireEvent('valid', this);
21040         
21041         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21042         
21043         if(this.groupId){
21044             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21045         }
21046         
21047         if(label){
21048             label.markValid();
21049         }
21050
21051         if(this.inputType == 'radio'){
21052             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21053                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21054                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21055             });
21056             
21057             return;
21058         }
21059
21060         if(!this.groupId){
21061             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21062             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21063             return;
21064         }
21065         
21066         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21067         
21068         if(!group){
21069             return;
21070         }
21071         
21072         for(var i in group){
21073             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21074             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21075         }
21076     },
21077     
21078      /**
21079      * Mark this field as invalid
21080      * @param {String} msg The validation message
21081      */
21082     markInvalid : function(msg)
21083     {
21084         if(this.allowBlank){
21085             return;
21086         }
21087         
21088         var _this = this;
21089         
21090         this.fireEvent('invalid', this, msg);
21091         
21092         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21093         
21094         if(this.groupId){
21095             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21096         }
21097         
21098         if(label){
21099             label.markInvalid();
21100         }
21101             
21102         if(this.inputType == 'radio'){
21103             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21104                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21105                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21106             });
21107             
21108             return;
21109         }
21110         
21111         if(!this.groupId){
21112             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21113             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21114             return;
21115         }
21116         
21117         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21118         
21119         if(!group){
21120             return;
21121         }
21122         
21123         for(var i in group){
21124             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21125             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21126         }
21127         
21128     },
21129     
21130     clearInvalid : function()
21131     {
21132         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21133         
21134         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21135         
21136         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21137         
21138         if (label && label.iconEl) {
21139             label.iconEl.removeClass(label.validClass);
21140             label.iconEl.removeClass(label.invalidClass);
21141         }
21142     },
21143     
21144     disable : function()
21145     {
21146         if(this.inputType != 'radio'){
21147             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21148             return;
21149         }
21150         
21151         var _this = this;
21152         
21153         if(this.rendered){
21154             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21155                 _this.getActionEl().addClass(this.disabledClass);
21156                 e.dom.disabled = true;
21157             });
21158         }
21159         
21160         this.disabled = true;
21161         this.fireEvent("disable", this);
21162         return this;
21163     },
21164
21165     enable : function()
21166     {
21167         if(this.inputType != 'radio'){
21168             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21169             return;
21170         }
21171         
21172         var _this = this;
21173         
21174         if(this.rendered){
21175             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21176                 _this.getActionEl().removeClass(this.disabledClass);
21177                 e.dom.disabled = false;
21178             });
21179         }
21180         
21181         this.disabled = false;
21182         this.fireEvent("enable", this);
21183         return this;
21184     },
21185     
21186     setBoxLabel : function(v)
21187     {
21188         this.boxLabel = v;
21189         
21190         if(this.rendered){
21191             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21192         }
21193     }
21194
21195 });
21196
21197 Roo.apply(Roo.bootstrap.CheckBox, {
21198     
21199     groups: {},
21200     
21201      /**
21202     * register a CheckBox Group
21203     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21204     */
21205     register : function(checkbox)
21206     {
21207         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21208             this.groups[checkbox.groupId] = {};
21209         }
21210         
21211         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21212             return;
21213         }
21214         
21215         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21216         
21217     },
21218     /**
21219     * fetch a CheckBox Group based on the group ID
21220     * @param {string} the group ID
21221     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21222     */
21223     get: function(groupId) {
21224         if (typeof(this.groups[groupId]) == 'undefined') {
21225             return false;
21226         }
21227         
21228         return this.groups[groupId] ;
21229     }
21230     
21231     
21232 });
21233 /*
21234  * - LGPL
21235  *
21236  * RadioItem
21237  * 
21238  */
21239
21240 /**
21241  * @class Roo.bootstrap.Radio
21242  * @extends Roo.bootstrap.Component
21243  * Bootstrap Radio class
21244  * @cfg {String} boxLabel - the label associated
21245  * @cfg {String} value - the value of radio
21246  * 
21247  * @constructor
21248  * Create a new Radio
21249  * @param {Object} config The config object
21250  */
21251 Roo.bootstrap.Radio = function(config){
21252     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21253     
21254 };
21255
21256 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21257     
21258     boxLabel : '',
21259     
21260     value : '',
21261     
21262     getAutoCreate : function()
21263     {
21264         var cfg = {
21265             tag : 'div',
21266             cls : 'form-group radio',
21267             cn : [
21268                 {
21269                     tag : 'label',
21270                     cls : 'box-label',
21271                     html : this.boxLabel
21272                 }
21273             ]
21274         };
21275         
21276         return cfg;
21277     },
21278     
21279     initEvents : function() 
21280     {
21281         this.parent().register(this);
21282         
21283         this.el.on('click', this.onClick, this);
21284         
21285     },
21286     
21287     onClick : function(e)
21288     {
21289         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21290             this.setChecked(true);
21291         }
21292     },
21293     
21294     setChecked : function(state, suppressEvent)
21295     {
21296         this.parent().setValue(this.value, suppressEvent);
21297         
21298     },
21299     
21300     setBoxLabel : function(v)
21301     {
21302         this.boxLabel = v;
21303         
21304         if(this.rendered){
21305             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21306         }
21307     }
21308     
21309 });
21310  
21311
21312  /*
21313  * - LGPL
21314  *
21315  * Input
21316  * 
21317  */
21318
21319 /**
21320  * @class Roo.bootstrap.SecurePass
21321  * @extends Roo.bootstrap.Input
21322  * Bootstrap SecurePass class
21323  *
21324  * 
21325  * @constructor
21326  * Create a new SecurePass
21327  * @param {Object} config The config object
21328  */
21329  
21330 Roo.bootstrap.SecurePass = function (config) {
21331     // these go here, so the translation tool can replace them..
21332     this.errors = {
21333         PwdEmpty: "Please type a password, and then retype it to confirm.",
21334         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21335         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21336         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21337         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21338         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21339         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21340         TooWeak: "Your password is Too Weak."
21341     },
21342     this.meterLabel = "Password strength:";
21343     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21344     this.meterClass = [
21345         "roo-password-meter-tooweak", 
21346         "roo-password-meter-weak", 
21347         "roo-password-meter-medium", 
21348         "roo-password-meter-strong", 
21349         "roo-password-meter-grey"
21350     ];
21351     
21352     this.errors = {};
21353     
21354     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21355 }
21356
21357 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21358     /**
21359      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21360      * {
21361      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21362      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21363      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21364      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21365      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21366      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21367      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21368      * })
21369      */
21370     // private
21371     
21372     meterWidth: 300,
21373     errorMsg :'',    
21374     errors: false,
21375     imageRoot: '/',
21376     /**
21377      * @cfg {String/Object} Label for the strength meter (defaults to
21378      * 'Password strength:')
21379      */
21380     // private
21381     meterLabel: '',
21382     /**
21383      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21384      * ['Weak', 'Medium', 'Strong'])
21385      */
21386     // private    
21387     pwdStrengths: false,    
21388     // private
21389     strength: 0,
21390     // private
21391     _lastPwd: null,
21392     // private
21393     kCapitalLetter: 0,
21394     kSmallLetter: 1,
21395     kDigit: 2,
21396     kPunctuation: 3,
21397     
21398     insecure: false,
21399     // private
21400     initEvents: function ()
21401     {
21402         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21403
21404         if (this.el.is('input[type=password]') && Roo.isSafari) {
21405             this.el.on('keydown', this.SafariOnKeyDown, this);
21406         }
21407
21408         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21409     },
21410     // private
21411     onRender: function (ct, position)
21412     {
21413         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21414         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21415         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21416
21417         this.trigger.createChild({
21418                    cn: [
21419                     {
21420                     //id: 'PwdMeter',
21421                     tag: 'div',
21422                     cls: 'roo-password-meter-grey col-xs-12',
21423                     style: {
21424                         //width: 0,
21425                         //width: this.meterWidth + 'px'                                                
21426                         }
21427                     },
21428                     {                            
21429                          cls: 'roo-password-meter-text'                          
21430                     }
21431                 ]            
21432         });
21433
21434          
21435         if (this.hideTrigger) {
21436             this.trigger.setDisplayed(false);
21437         }
21438         this.setSize(this.width || '', this.height || '');
21439     },
21440     // private
21441     onDestroy: function ()
21442     {
21443         if (this.trigger) {
21444             this.trigger.removeAllListeners();
21445             this.trigger.remove();
21446         }
21447         if (this.wrap) {
21448             this.wrap.remove();
21449         }
21450         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21451     },
21452     // private
21453     checkStrength: function ()
21454     {
21455         var pwd = this.inputEl().getValue();
21456         if (pwd == this._lastPwd) {
21457             return;
21458         }
21459
21460         var strength;
21461         if (this.ClientSideStrongPassword(pwd)) {
21462             strength = 3;
21463         } else if (this.ClientSideMediumPassword(pwd)) {
21464             strength = 2;
21465         } else if (this.ClientSideWeakPassword(pwd)) {
21466             strength = 1;
21467         } else {
21468             strength = 0;
21469         }
21470         
21471         Roo.log('strength1: ' + strength);
21472         
21473         //var pm = this.trigger.child('div/div/div').dom;
21474         var pm = this.trigger.child('div/div');
21475         pm.removeClass(this.meterClass);
21476         pm.addClass(this.meterClass[strength]);
21477                 
21478         
21479         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21480                 
21481         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21482         
21483         this._lastPwd = pwd;
21484     },
21485     reset: function ()
21486     {
21487         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21488         
21489         this._lastPwd = '';
21490         
21491         var pm = this.trigger.child('div/div');
21492         pm.removeClass(this.meterClass);
21493         pm.addClass('roo-password-meter-grey');        
21494         
21495         
21496         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21497         
21498         pt.innerHTML = '';
21499         this.inputEl().dom.type='password';
21500     },
21501     // private
21502     validateValue: function (value)
21503     {
21504         
21505         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21506             return false;
21507         }
21508         if (value.length == 0) {
21509             if (this.allowBlank) {
21510                 this.clearInvalid();
21511                 return true;
21512             }
21513
21514             this.markInvalid(this.errors.PwdEmpty);
21515             this.errorMsg = this.errors.PwdEmpty;
21516             return false;
21517         }
21518         
21519         if(this.insecure){
21520             return true;
21521         }
21522         
21523         if ('[\x21-\x7e]*'.match(value)) {
21524             this.markInvalid(this.errors.PwdBadChar);
21525             this.errorMsg = this.errors.PwdBadChar;
21526             return false;
21527         }
21528         if (value.length < 6) {
21529             this.markInvalid(this.errors.PwdShort);
21530             this.errorMsg = this.errors.PwdShort;
21531             return false;
21532         }
21533         if (value.length > 16) {
21534             this.markInvalid(this.errors.PwdLong);
21535             this.errorMsg = this.errors.PwdLong;
21536             return false;
21537         }
21538         var strength;
21539         if (this.ClientSideStrongPassword(value)) {
21540             strength = 3;
21541         } else if (this.ClientSideMediumPassword(value)) {
21542             strength = 2;
21543         } else if (this.ClientSideWeakPassword(value)) {
21544             strength = 1;
21545         } else {
21546             strength = 0;
21547         }
21548
21549         
21550         if (strength < 2) {
21551             //this.markInvalid(this.errors.TooWeak);
21552             this.errorMsg = this.errors.TooWeak;
21553             //return false;
21554         }
21555         
21556         
21557         console.log('strength2: ' + strength);
21558         
21559         //var pm = this.trigger.child('div/div/div').dom;
21560         
21561         var pm = this.trigger.child('div/div');
21562         pm.removeClass(this.meterClass);
21563         pm.addClass(this.meterClass[strength]);
21564                 
21565         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21566                 
21567         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21568         
21569         this.errorMsg = ''; 
21570         return true;
21571     },
21572     // private
21573     CharacterSetChecks: function (type)
21574     {
21575         this.type = type;
21576         this.fResult = false;
21577     },
21578     // private
21579     isctype: function (character, type)
21580     {
21581         switch (type) {  
21582             case this.kCapitalLetter:
21583                 if (character >= 'A' && character <= 'Z') {
21584                     return true;
21585                 }
21586                 break;
21587             
21588             case this.kSmallLetter:
21589                 if (character >= 'a' && character <= 'z') {
21590                     return true;
21591                 }
21592                 break;
21593             
21594             case this.kDigit:
21595                 if (character >= '0' && character <= '9') {
21596                     return true;
21597                 }
21598                 break;
21599             
21600             case this.kPunctuation:
21601                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21602                     return true;
21603                 }
21604                 break;
21605             
21606             default:
21607                 return false;
21608         }
21609
21610     },
21611     // private
21612     IsLongEnough: function (pwd, size)
21613     {
21614         return !(pwd == null || isNaN(size) || pwd.length < size);
21615     },
21616     // private
21617     SpansEnoughCharacterSets: function (word, nb)
21618     {
21619         if (!this.IsLongEnough(word, nb))
21620         {
21621             return false;
21622         }
21623
21624         var characterSetChecks = new Array(
21625             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21626             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21627         );
21628         
21629         for (var index = 0; index < word.length; ++index) {
21630             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21631                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21632                     characterSetChecks[nCharSet].fResult = true;
21633                     break;
21634                 }
21635             }
21636         }
21637
21638         var nCharSets = 0;
21639         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21640             if (characterSetChecks[nCharSet].fResult) {
21641                 ++nCharSets;
21642             }
21643         }
21644
21645         if (nCharSets < nb) {
21646             return false;
21647         }
21648         return true;
21649     },
21650     // private
21651     ClientSideStrongPassword: function (pwd)
21652     {
21653         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21654     },
21655     // private
21656     ClientSideMediumPassword: function (pwd)
21657     {
21658         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21659     },
21660     // private
21661     ClientSideWeakPassword: function (pwd)
21662     {
21663         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21664     }
21665           
21666 })//<script type="text/javascript">
21667
21668 /*
21669  * Based  Ext JS Library 1.1.1
21670  * Copyright(c) 2006-2007, Ext JS, LLC.
21671  * LGPL
21672  *
21673  */
21674  
21675 /**
21676  * @class Roo.HtmlEditorCore
21677  * @extends Roo.Component
21678  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21679  *
21680  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21681  */
21682
21683 Roo.HtmlEditorCore = function(config){
21684     
21685     
21686     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21687     
21688     
21689     this.addEvents({
21690         /**
21691          * @event initialize
21692          * Fires when the editor is fully initialized (including the iframe)
21693          * @param {Roo.HtmlEditorCore} this
21694          */
21695         initialize: true,
21696         /**
21697          * @event activate
21698          * Fires when the editor is first receives the focus. Any insertion must wait
21699          * until after this event.
21700          * @param {Roo.HtmlEditorCore} this
21701          */
21702         activate: true,
21703          /**
21704          * @event beforesync
21705          * Fires before the textarea is updated with content from the editor iframe. Return false
21706          * to cancel the sync.
21707          * @param {Roo.HtmlEditorCore} this
21708          * @param {String} html
21709          */
21710         beforesync: true,
21711          /**
21712          * @event beforepush
21713          * Fires before the iframe editor is updated with content from the textarea. Return false
21714          * to cancel the push.
21715          * @param {Roo.HtmlEditorCore} this
21716          * @param {String} html
21717          */
21718         beforepush: true,
21719          /**
21720          * @event sync
21721          * Fires when the textarea is updated with content from the editor iframe.
21722          * @param {Roo.HtmlEditorCore} this
21723          * @param {String} html
21724          */
21725         sync: true,
21726          /**
21727          * @event push
21728          * Fires when the iframe editor is updated with content from the textarea.
21729          * @param {Roo.HtmlEditorCore} this
21730          * @param {String} html
21731          */
21732         push: true,
21733         
21734         /**
21735          * @event editorevent
21736          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21737          * @param {Roo.HtmlEditorCore} this
21738          */
21739         editorevent: true
21740         
21741     });
21742     
21743     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21744     
21745     // defaults : white / black...
21746     this.applyBlacklists();
21747     
21748     
21749     
21750 };
21751
21752
21753 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21754
21755
21756      /**
21757      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21758      */
21759     
21760     owner : false,
21761     
21762      /**
21763      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21764      *                        Roo.resizable.
21765      */
21766     resizable : false,
21767      /**
21768      * @cfg {Number} height (in pixels)
21769      */   
21770     height: 300,
21771    /**
21772      * @cfg {Number} width (in pixels)
21773      */   
21774     width: 500,
21775     
21776     /**
21777      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21778      * 
21779      */
21780     stylesheets: false,
21781     
21782     // id of frame..
21783     frameId: false,
21784     
21785     // private properties
21786     validationEvent : false,
21787     deferHeight: true,
21788     initialized : false,
21789     activated : false,
21790     sourceEditMode : false,
21791     onFocus : Roo.emptyFn,
21792     iframePad:3,
21793     hideMode:'offsets',
21794     
21795     clearUp: true,
21796     
21797     // blacklist + whitelisted elements..
21798     black: false,
21799     white: false,
21800      
21801     bodyCls : '',
21802
21803     /**
21804      * Protected method that will not generally be called directly. It
21805      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21806      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21807      */
21808     getDocMarkup : function(){
21809         // body styles..
21810         var st = '';
21811         
21812         // inherit styels from page...?? 
21813         if (this.stylesheets === false) {
21814             
21815             Roo.get(document.head).select('style').each(function(node) {
21816                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21817             });
21818             
21819             Roo.get(document.head).select('link').each(function(node) { 
21820                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21821             });
21822             
21823         } else if (!this.stylesheets.length) {
21824                 // simple..
21825                 st = '<style type="text/css">' +
21826                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21827                    '</style>';
21828         } else { 
21829             st = '<style type="text/css">' +
21830                     this.stylesheets +
21831                 '</style>';
21832         }
21833         
21834         st +=  '<style type="text/css">' +
21835             'IMG { cursor: pointer } ' +
21836         '</style>';
21837
21838         var cls = 'roo-htmleditor-body';
21839         
21840         if(this.bodyCls.length){
21841             cls += ' ' + this.bodyCls;
21842         }
21843         
21844         return '<html><head>' + st  +
21845             //<style type="text/css">' +
21846             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21847             //'</style>' +
21848             ' </head><body class="' +  cls + '"></body></html>';
21849     },
21850
21851     // private
21852     onRender : function(ct, position)
21853     {
21854         var _t = this;
21855         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21856         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21857         
21858         
21859         this.el.dom.style.border = '0 none';
21860         this.el.dom.setAttribute('tabIndex', -1);
21861         this.el.addClass('x-hidden hide');
21862         
21863         
21864         
21865         if(Roo.isIE){ // fix IE 1px bogus margin
21866             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21867         }
21868        
21869         
21870         this.frameId = Roo.id();
21871         
21872          
21873         
21874         var iframe = this.owner.wrap.createChild({
21875             tag: 'iframe',
21876             cls: 'form-control', // bootstrap..
21877             id: this.frameId,
21878             name: this.frameId,
21879             frameBorder : 'no',
21880             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21881         }, this.el
21882         );
21883         
21884         
21885         this.iframe = iframe.dom;
21886
21887          this.assignDocWin();
21888         
21889         this.doc.designMode = 'on';
21890        
21891         this.doc.open();
21892         this.doc.write(this.getDocMarkup());
21893         this.doc.close();
21894
21895         
21896         var task = { // must defer to wait for browser to be ready
21897             run : function(){
21898                 //console.log("run task?" + this.doc.readyState);
21899                 this.assignDocWin();
21900                 if(this.doc.body || this.doc.readyState == 'complete'){
21901                     try {
21902                         this.doc.designMode="on";
21903                     } catch (e) {
21904                         return;
21905                     }
21906                     Roo.TaskMgr.stop(task);
21907                     this.initEditor.defer(10, this);
21908                 }
21909             },
21910             interval : 10,
21911             duration: 10000,
21912             scope: this
21913         };
21914         Roo.TaskMgr.start(task);
21915
21916     },
21917
21918     // private
21919     onResize : function(w, h)
21920     {
21921          Roo.log('resize: ' +w + ',' + h );
21922         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21923         if(!this.iframe){
21924             return;
21925         }
21926         if(typeof w == 'number'){
21927             
21928             this.iframe.style.width = w + 'px';
21929         }
21930         if(typeof h == 'number'){
21931             
21932             this.iframe.style.height = h + 'px';
21933             if(this.doc){
21934                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21935             }
21936         }
21937         
21938     },
21939
21940     /**
21941      * Toggles the editor between standard and source edit mode.
21942      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21943      */
21944     toggleSourceEdit : function(sourceEditMode){
21945         
21946         this.sourceEditMode = sourceEditMode === true;
21947         
21948         if(this.sourceEditMode){
21949  
21950             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21951             
21952         }else{
21953             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21954             //this.iframe.className = '';
21955             this.deferFocus();
21956         }
21957         //this.setSize(this.owner.wrap.getSize());
21958         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21959     },
21960
21961     
21962   
21963
21964     /**
21965      * Protected method that will not generally be called directly. If you need/want
21966      * custom HTML cleanup, this is the method you should override.
21967      * @param {String} html The HTML to be cleaned
21968      * return {String} The cleaned HTML
21969      */
21970     cleanHtml : function(html){
21971         html = String(html);
21972         if(html.length > 5){
21973             if(Roo.isSafari){ // strip safari nonsense
21974                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21975             }
21976         }
21977         if(html == '&nbsp;'){
21978             html = '';
21979         }
21980         return html;
21981     },
21982
21983     /**
21984      * HTML Editor -> Textarea
21985      * Protected method that will not generally be called directly. Syncs the contents
21986      * of the editor iframe with the textarea.
21987      */
21988     syncValue : function(){
21989         if(this.initialized){
21990             var bd = (this.doc.body || this.doc.documentElement);
21991             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21992             var html = bd.innerHTML;
21993             if(Roo.isSafari){
21994                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21995                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21996                 if(m && m[1]){
21997                     html = '<div style="'+m[0]+'">' + html + '</div>';
21998                 }
21999             }
22000             html = this.cleanHtml(html);
22001             // fix up the special chars.. normaly like back quotes in word...
22002             // however we do not want to do this with chinese..
22003             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22004                 var cc = b.charCodeAt();
22005                 if (
22006                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22007                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22008                     (cc >= 0xf900 && cc < 0xfb00 )
22009                 ) {
22010                         return b;
22011                 }
22012                 return "&#"+cc+";" 
22013             });
22014             if(this.owner.fireEvent('beforesync', this, html) !== false){
22015                 this.el.dom.value = html;
22016                 this.owner.fireEvent('sync', this, html);
22017             }
22018         }
22019     },
22020
22021     /**
22022      * Protected method that will not generally be called directly. Pushes the value of the textarea
22023      * into the iframe editor.
22024      */
22025     pushValue : function(){
22026         if(this.initialized){
22027             var v = this.el.dom.value.trim();
22028             
22029 //            if(v.length < 1){
22030 //                v = '&#160;';
22031 //            }
22032             
22033             if(this.owner.fireEvent('beforepush', this, v) !== false){
22034                 var d = (this.doc.body || this.doc.documentElement);
22035                 d.innerHTML = v;
22036                 this.cleanUpPaste();
22037                 this.el.dom.value = d.innerHTML;
22038                 this.owner.fireEvent('push', this, v);
22039             }
22040         }
22041     },
22042
22043     // private
22044     deferFocus : function(){
22045         this.focus.defer(10, this);
22046     },
22047
22048     // doc'ed in Field
22049     focus : function(){
22050         if(this.win && !this.sourceEditMode){
22051             this.win.focus();
22052         }else{
22053             this.el.focus();
22054         }
22055     },
22056     
22057     assignDocWin: function()
22058     {
22059         var iframe = this.iframe;
22060         
22061          if(Roo.isIE){
22062             this.doc = iframe.contentWindow.document;
22063             this.win = iframe.contentWindow;
22064         } else {
22065 //            if (!Roo.get(this.frameId)) {
22066 //                return;
22067 //            }
22068 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22069 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22070             
22071             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22072                 return;
22073             }
22074             
22075             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22076             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22077         }
22078     },
22079     
22080     // private
22081     initEditor : function(){
22082         //console.log("INIT EDITOR");
22083         this.assignDocWin();
22084         
22085         
22086         
22087         this.doc.designMode="on";
22088         this.doc.open();
22089         this.doc.write(this.getDocMarkup());
22090         this.doc.close();
22091         
22092         var dbody = (this.doc.body || this.doc.documentElement);
22093         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22094         // this copies styles from the containing element into thsi one..
22095         // not sure why we need all of this..
22096         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22097         
22098         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22099         //ss['background-attachment'] = 'fixed'; // w3c
22100         dbody.bgProperties = 'fixed'; // ie
22101         //Roo.DomHelper.applyStyles(dbody, ss);
22102         Roo.EventManager.on(this.doc, {
22103             //'mousedown': this.onEditorEvent,
22104             'mouseup': this.onEditorEvent,
22105             'dblclick': this.onEditorEvent,
22106             'click': this.onEditorEvent,
22107             'keyup': this.onEditorEvent,
22108             buffer:100,
22109             scope: this
22110         });
22111         if(Roo.isGecko){
22112             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22113         }
22114         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22115             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22116         }
22117         this.initialized = true;
22118
22119         this.owner.fireEvent('initialize', this);
22120         this.pushValue();
22121     },
22122
22123     // private
22124     onDestroy : function(){
22125         
22126         
22127         
22128         if(this.rendered){
22129             
22130             //for (var i =0; i < this.toolbars.length;i++) {
22131             //    // fixme - ask toolbars for heights?
22132             //    this.toolbars[i].onDestroy();
22133            // }
22134             
22135             //this.wrap.dom.innerHTML = '';
22136             //this.wrap.remove();
22137         }
22138     },
22139
22140     // private
22141     onFirstFocus : function(){
22142         
22143         this.assignDocWin();
22144         
22145         
22146         this.activated = true;
22147          
22148     
22149         if(Roo.isGecko){ // prevent silly gecko errors
22150             this.win.focus();
22151             var s = this.win.getSelection();
22152             if(!s.focusNode || s.focusNode.nodeType != 3){
22153                 var r = s.getRangeAt(0);
22154                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22155                 r.collapse(true);
22156                 this.deferFocus();
22157             }
22158             try{
22159                 this.execCmd('useCSS', true);
22160                 this.execCmd('styleWithCSS', false);
22161             }catch(e){}
22162         }
22163         this.owner.fireEvent('activate', this);
22164     },
22165
22166     // private
22167     adjustFont: function(btn){
22168         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22169         //if(Roo.isSafari){ // safari
22170         //    adjust *= 2;
22171        // }
22172         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22173         if(Roo.isSafari){ // safari
22174             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22175             v =  (v < 10) ? 10 : v;
22176             v =  (v > 48) ? 48 : v;
22177             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22178             
22179         }
22180         
22181         
22182         v = Math.max(1, v+adjust);
22183         
22184         this.execCmd('FontSize', v  );
22185     },
22186
22187     onEditorEvent : function(e)
22188     {
22189         this.owner.fireEvent('editorevent', this, e);
22190       //  this.updateToolbar();
22191         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22192     },
22193
22194     insertTag : function(tg)
22195     {
22196         // could be a bit smarter... -> wrap the current selected tRoo..
22197         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22198             
22199             range = this.createRange(this.getSelection());
22200             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22201             wrappingNode.appendChild(range.extractContents());
22202             range.insertNode(wrappingNode);
22203
22204             return;
22205             
22206             
22207             
22208         }
22209         this.execCmd("formatblock",   tg);
22210         
22211     },
22212     
22213     insertText : function(txt)
22214     {
22215         
22216         
22217         var range = this.createRange();
22218         range.deleteContents();
22219                //alert(Sender.getAttribute('label'));
22220                
22221         range.insertNode(this.doc.createTextNode(txt));
22222     } ,
22223     
22224      
22225
22226     /**
22227      * Executes a Midas editor command on the editor document and performs necessary focus and
22228      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22229      * @param {String} cmd The Midas command
22230      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22231      */
22232     relayCmd : function(cmd, value){
22233         this.win.focus();
22234         this.execCmd(cmd, value);
22235         this.owner.fireEvent('editorevent', this);
22236         //this.updateToolbar();
22237         this.owner.deferFocus();
22238     },
22239
22240     /**
22241      * Executes a Midas editor command directly on the editor document.
22242      * For visual commands, you should use {@link #relayCmd} instead.
22243      * <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     execCmd : function(cmd, value){
22248         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22249         this.syncValue();
22250     },
22251  
22252  
22253    
22254     /**
22255      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22256      * to insert tRoo.
22257      * @param {String} text | dom node.. 
22258      */
22259     insertAtCursor : function(text)
22260     {
22261         
22262         if(!this.activated){
22263             return;
22264         }
22265         /*
22266         if(Roo.isIE){
22267             this.win.focus();
22268             var r = this.doc.selection.createRange();
22269             if(r){
22270                 r.collapse(true);
22271                 r.pasteHTML(text);
22272                 this.syncValue();
22273                 this.deferFocus();
22274             
22275             }
22276             return;
22277         }
22278         */
22279         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22280             this.win.focus();
22281             
22282             
22283             // from jquery ui (MIT licenced)
22284             var range, node;
22285             var win = this.win;
22286             
22287             if (win.getSelection && win.getSelection().getRangeAt) {
22288                 range = win.getSelection().getRangeAt(0);
22289                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22290                 range.insertNode(node);
22291             } else if (win.document.selection && win.document.selection.createRange) {
22292                 // no firefox support
22293                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22294                 win.document.selection.createRange().pasteHTML(txt);
22295             } else {
22296                 // no firefox support
22297                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22298                 this.execCmd('InsertHTML', txt);
22299             } 
22300             
22301             this.syncValue();
22302             
22303             this.deferFocus();
22304         }
22305     },
22306  // private
22307     mozKeyPress : function(e){
22308         if(e.ctrlKey){
22309             var c = e.getCharCode(), cmd;
22310           
22311             if(c > 0){
22312                 c = String.fromCharCode(c).toLowerCase();
22313                 switch(c){
22314                     case 'b':
22315                         cmd = 'bold';
22316                         break;
22317                     case 'i':
22318                         cmd = 'italic';
22319                         break;
22320                     
22321                     case 'u':
22322                         cmd = 'underline';
22323                         break;
22324                     
22325                     case 'v':
22326                         this.cleanUpPaste.defer(100, this);
22327                         return;
22328                         
22329                 }
22330                 if(cmd){
22331                     this.win.focus();
22332                     this.execCmd(cmd);
22333                     this.deferFocus();
22334                     e.preventDefault();
22335                 }
22336                 
22337             }
22338         }
22339     },
22340
22341     // private
22342     fixKeys : function(){ // load time branching for fastest keydown performance
22343         if(Roo.isIE){
22344             return function(e){
22345                 var k = e.getKey(), r;
22346                 if(k == e.TAB){
22347                     e.stopEvent();
22348                     r = this.doc.selection.createRange();
22349                     if(r){
22350                         r.collapse(true);
22351                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22352                         this.deferFocus();
22353                     }
22354                     return;
22355                 }
22356                 
22357                 if(k == e.ENTER){
22358                     r = this.doc.selection.createRange();
22359                     if(r){
22360                         var target = r.parentElement();
22361                         if(!target || target.tagName.toLowerCase() != 'li'){
22362                             e.stopEvent();
22363                             r.pasteHTML('<br />');
22364                             r.collapse(false);
22365                             r.select();
22366                         }
22367                     }
22368                 }
22369                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22370                     this.cleanUpPaste.defer(100, this);
22371                     return;
22372                 }
22373                 
22374                 
22375             };
22376         }else if(Roo.isOpera){
22377             return function(e){
22378                 var k = e.getKey();
22379                 if(k == e.TAB){
22380                     e.stopEvent();
22381                     this.win.focus();
22382                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22383                     this.deferFocus();
22384                 }
22385                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22386                     this.cleanUpPaste.defer(100, this);
22387                     return;
22388                 }
22389                 
22390             };
22391         }else if(Roo.isSafari){
22392             return function(e){
22393                 var k = e.getKey();
22394                 
22395                 if(k == e.TAB){
22396                     e.stopEvent();
22397                     this.execCmd('InsertText','\t');
22398                     this.deferFocus();
22399                     return;
22400                 }
22401                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22402                     this.cleanUpPaste.defer(100, this);
22403                     return;
22404                 }
22405                 
22406              };
22407         }
22408     }(),
22409     
22410     getAllAncestors: function()
22411     {
22412         var p = this.getSelectedNode();
22413         var a = [];
22414         if (!p) {
22415             a.push(p); // push blank onto stack..
22416             p = this.getParentElement();
22417         }
22418         
22419         
22420         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22421             a.push(p);
22422             p = p.parentNode;
22423         }
22424         a.push(this.doc.body);
22425         return a;
22426     },
22427     lastSel : false,
22428     lastSelNode : false,
22429     
22430     
22431     getSelection : function() 
22432     {
22433         this.assignDocWin();
22434         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22435     },
22436     
22437     getSelectedNode: function() 
22438     {
22439         // this may only work on Gecko!!!
22440         
22441         // should we cache this!!!!
22442         
22443         
22444         
22445          
22446         var range = this.createRange(this.getSelection()).cloneRange();
22447         
22448         if (Roo.isIE) {
22449             var parent = range.parentElement();
22450             while (true) {
22451                 var testRange = range.duplicate();
22452                 testRange.moveToElementText(parent);
22453                 if (testRange.inRange(range)) {
22454                     break;
22455                 }
22456                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22457                     break;
22458                 }
22459                 parent = parent.parentElement;
22460             }
22461             return parent;
22462         }
22463         
22464         // is ancestor a text element.
22465         var ac =  range.commonAncestorContainer;
22466         if (ac.nodeType == 3) {
22467             ac = ac.parentNode;
22468         }
22469         
22470         var ar = ac.childNodes;
22471          
22472         var nodes = [];
22473         var other_nodes = [];
22474         var has_other_nodes = false;
22475         for (var i=0;i<ar.length;i++) {
22476             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22477                 continue;
22478             }
22479             // fullly contained node.
22480             
22481             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22482                 nodes.push(ar[i]);
22483                 continue;
22484             }
22485             
22486             // probably selected..
22487             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22488                 other_nodes.push(ar[i]);
22489                 continue;
22490             }
22491             // outer..
22492             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22493                 continue;
22494             }
22495             
22496             
22497             has_other_nodes = true;
22498         }
22499         if (!nodes.length && other_nodes.length) {
22500             nodes= other_nodes;
22501         }
22502         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22503             return false;
22504         }
22505         
22506         return nodes[0];
22507     },
22508     createRange: function(sel)
22509     {
22510         // this has strange effects when using with 
22511         // top toolbar - not sure if it's a great idea.
22512         //this.editor.contentWindow.focus();
22513         if (typeof sel != "undefined") {
22514             try {
22515                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22516             } catch(e) {
22517                 return this.doc.createRange();
22518             }
22519         } else {
22520             return this.doc.createRange();
22521         }
22522     },
22523     getParentElement: function()
22524     {
22525         
22526         this.assignDocWin();
22527         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22528         
22529         var range = this.createRange(sel);
22530          
22531         try {
22532             var p = range.commonAncestorContainer;
22533             while (p.nodeType == 3) { // text node
22534                 p = p.parentNode;
22535             }
22536             return p;
22537         } catch (e) {
22538             return null;
22539         }
22540     
22541     },
22542     /***
22543      *
22544      * Range intersection.. the hard stuff...
22545      *  '-1' = before
22546      *  '0' = hits..
22547      *  '1' = after.
22548      *         [ -- selected range --- ]
22549      *   [fail]                        [fail]
22550      *
22551      *    basically..
22552      *      if end is before start or  hits it. fail.
22553      *      if start is after end or hits it fail.
22554      *
22555      *   if either hits (but other is outside. - then it's not 
22556      *   
22557      *    
22558      **/
22559     
22560     
22561     // @see http://www.thismuchiknow.co.uk/?p=64.
22562     rangeIntersectsNode : function(range, node)
22563     {
22564         var nodeRange = node.ownerDocument.createRange();
22565         try {
22566             nodeRange.selectNode(node);
22567         } catch (e) {
22568             nodeRange.selectNodeContents(node);
22569         }
22570     
22571         var rangeStartRange = range.cloneRange();
22572         rangeStartRange.collapse(true);
22573     
22574         var rangeEndRange = range.cloneRange();
22575         rangeEndRange.collapse(false);
22576     
22577         var nodeStartRange = nodeRange.cloneRange();
22578         nodeStartRange.collapse(true);
22579     
22580         var nodeEndRange = nodeRange.cloneRange();
22581         nodeEndRange.collapse(false);
22582     
22583         return rangeStartRange.compareBoundaryPoints(
22584                  Range.START_TO_START, nodeEndRange) == -1 &&
22585                rangeEndRange.compareBoundaryPoints(
22586                  Range.START_TO_START, nodeStartRange) == 1;
22587         
22588          
22589     },
22590     rangeCompareNode : function(range, node)
22591     {
22592         var nodeRange = node.ownerDocument.createRange();
22593         try {
22594             nodeRange.selectNode(node);
22595         } catch (e) {
22596             nodeRange.selectNodeContents(node);
22597         }
22598         
22599         
22600         range.collapse(true);
22601     
22602         nodeRange.collapse(true);
22603      
22604         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22605         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22606          
22607         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22608         
22609         var nodeIsBefore   =  ss == 1;
22610         var nodeIsAfter    = ee == -1;
22611         
22612         if (nodeIsBefore && nodeIsAfter) {
22613             return 0; // outer
22614         }
22615         if (!nodeIsBefore && nodeIsAfter) {
22616             return 1; //right trailed.
22617         }
22618         
22619         if (nodeIsBefore && !nodeIsAfter) {
22620             return 2;  // left trailed.
22621         }
22622         // fully contined.
22623         return 3;
22624     },
22625
22626     // private? - in a new class?
22627     cleanUpPaste :  function()
22628     {
22629         // cleans up the whole document..
22630         Roo.log('cleanuppaste');
22631         
22632         this.cleanUpChildren(this.doc.body);
22633         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22634         if (clean != this.doc.body.innerHTML) {
22635             this.doc.body.innerHTML = clean;
22636         }
22637         
22638     },
22639     
22640     cleanWordChars : function(input) {// change the chars to hex code
22641         var he = Roo.HtmlEditorCore;
22642         
22643         var output = input;
22644         Roo.each(he.swapCodes, function(sw) { 
22645             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22646             
22647             output = output.replace(swapper, sw[1]);
22648         });
22649         
22650         return output;
22651     },
22652     
22653     
22654     cleanUpChildren : function (n)
22655     {
22656         if (!n.childNodes.length) {
22657             return;
22658         }
22659         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22660            this.cleanUpChild(n.childNodes[i]);
22661         }
22662     },
22663     
22664     
22665         
22666     
22667     cleanUpChild : function (node)
22668     {
22669         var ed = this;
22670         //console.log(node);
22671         if (node.nodeName == "#text") {
22672             // clean up silly Windows -- stuff?
22673             return; 
22674         }
22675         if (node.nodeName == "#comment") {
22676             node.parentNode.removeChild(node);
22677             // clean up silly Windows -- stuff?
22678             return; 
22679         }
22680         var lcname = node.tagName.toLowerCase();
22681         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22682         // whitelist of tags..
22683         
22684         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22685             // remove node.
22686             node.parentNode.removeChild(node);
22687             return;
22688             
22689         }
22690         
22691         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22692         
22693         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22694         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22695         
22696         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22697         //    remove_keep_children = true;
22698         //}
22699         
22700         if (remove_keep_children) {
22701             this.cleanUpChildren(node);
22702             // inserts everything just before this node...
22703             while (node.childNodes.length) {
22704                 var cn = node.childNodes[0];
22705                 node.removeChild(cn);
22706                 node.parentNode.insertBefore(cn, node);
22707             }
22708             node.parentNode.removeChild(node);
22709             return;
22710         }
22711         
22712         if (!node.attributes || !node.attributes.length) {
22713             this.cleanUpChildren(node);
22714             return;
22715         }
22716         
22717         function cleanAttr(n,v)
22718         {
22719             
22720             if (v.match(/^\./) || v.match(/^\//)) {
22721                 return;
22722             }
22723             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22724                 return;
22725             }
22726             if (v.match(/^#/)) {
22727                 return;
22728             }
22729 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22730             node.removeAttribute(n);
22731             
22732         }
22733         
22734         var cwhite = this.cwhite;
22735         var cblack = this.cblack;
22736             
22737         function cleanStyle(n,v)
22738         {
22739             if (v.match(/expression/)) { //XSS?? should we even bother..
22740                 node.removeAttribute(n);
22741                 return;
22742             }
22743             
22744             var parts = v.split(/;/);
22745             var clean = [];
22746             
22747             Roo.each(parts, function(p) {
22748                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22749                 if (!p.length) {
22750                     return true;
22751                 }
22752                 var l = p.split(':').shift().replace(/\s+/g,'');
22753                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22754                 
22755                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22756 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22757                     //node.removeAttribute(n);
22758                     return true;
22759                 }
22760                 //Roo.log()
22761                 // only allow 'c whitelisted system attributes'
22762                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22763 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22764                     //node.removeAttribute(n);
22765                     return true;
22766                 }
22767                 
22768                 
22769                  
22770                 
22771                 clean.push(p);
22772                 return true;
22773             });
22774             if (clean.length) { 
22775                 node.setAttribute(n, clean.join(';'));
22776             } else {
22777                 node.removeAttribute(n);
22778             }
22779             
22780         }
22781         
22782         
22783         for (var i = node.attributes.length-1; i > -1 ; i--) {
22784             var a = node.attributes[i];
22785             //console.log(a);
22786             
22787             if (a.name.toLowerCase().substr(0,2)=='on')  {
22788                 node.removeAttribute(a.name);
22789                 continue;
22790             }
22791             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22792                 node.removeAttribute(a.name);
22793                 continue;
22794             }
22795             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22796                 cleanAttr(a.name,a.value); // fixme..
22797                 continue;
22798             }
22799             if (a.name == 'style') {
22800                 cleanStyle(a.name,a.value);
22801                 continue;
22802             }
22803             /// clean up MS crap..
22804             // tecnically this should be a list of valid class'es..
22805             
22806             
22807             if (a.name == 'class') {
22808                 if (a.value.match(/^Mso/)) {
22809                     node.className = '';
22810                 }
22811                 
22812                 if (a.value.match(/^body$/)) {
22813                     node.className = '';
22814                 }
22815                 continue;
22816             }
22817             
22818             // style cleanup!?
22819             // class cleanup?
22820             
22821         }
22822         
22823         
22824         this.cleanUpChildren(node);
22825         
22826         
22827     },
22828     
22829     /**
22830      * Clean up MS wordisms...
22831      */
22832     cleanWord : function(node)
22833     {
22834         
22835         
22836         if (!node) {
22837             this.cleanWord(this.doc.body);
22838             return;
22839         }
22840         if (node.nodeName == "#text") {
22841             // clean up silly Windows -- stuff?
22842             return; 
22843         }
22844         if (node.nodeName == "#comment") {
22845             node.parentNode.removeChild(node);
22846             // clean up silly Windows -- stuff?
22847             return; 
22848         }
22849         
22850         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22851             node.parentNode.removeChild(node);
22852             return;
22853         }
22854         
22855         // remove - but keep children..
22856         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22857             while (node.childNodes.length) {
22858                 var cn = node.childNodes[0];
22859                 node.removeChild(cn);
22860                 node.parentNode.insertBefore(cn, node);
22861             }
22862             node.parentNode.removeChild(node);
22863             this.iterateChildren(node, this.cleanWord);
22864             return;
22865         }
22866         // clean styles
22867         if (node.className.length) {
22868             
22869             var cn = node.className.split(/\W+/);
22870             var cna = [];
22871             Roo.each(cn, function(cls) {
22872                 if (cls.match(/Mso[a-zA-Z]+/)) {
22873                     return;
22874                 }
22875                 cna.push(cls);
22876             });
22877             node.className = cna.length ? cna.join(' ') : '';
22878             if (!cna.length) {
22879                 node.removeAttribute("class");
22880             }
22881         }
22882         
22883         if (node.hasAttribute("lang")) {
22884             node.removeAttribute("lang");
22885         }
22886         
22887         if (node.hasAttribute("style")) {
22888             
22889             var styles = node.getAttribute("style").split(";");
22890             var nstyle = [];
22891             Roo.each(styles, function(s) {
22892                 if (!s.match(/:/)) {
22893                     return;
22894                 }
22895                 var kv = s.split(":");
22896                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22897                     return;
22898                 }
22899                 // what ever is left... we allow.
22900                 nstyle.push(s);
22901             });
22902             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22903             if (!nstyle.length) {
22904                 node.removeAttribute('style');
22905             }
22906         }
22907         this.iterateChildren(node, this.cleanWord);
22908         
22909         
22910         
22911     },
22912     /**
22913      * iterateChildren of a Node, calling fn each time, using this as the scole..
22914      * @param {DomNode} node node to iterate children of.
22915      * @param {Function} fn method of this class to call on each item.
22916      */
22917     iterateChildren : function(node, fn)
22918     {
22919         if (!node.childNodes.length) {
22920                 return;
22921         }
22922         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22923            fn.call(this, node.childNodes[i])
22924         }
22925     },
22926     
22927     
22928     /**
22929      * cleanTableWidths.
22930      *
22931      * Quite often pasting from word etc.. results in tables with column and widths.
22932      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22933      *
22934      */
22935     cleanTableWidths : function(node)
22936     {
22937          
22938          
22939         if (!node) {
22940             this.cleanTableWidths(this.doc.body);
22941             return;
22942         }
22943         
22944         // ignore list...
22945         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22946             return; 
22947         }
22948         Roo.log(node.tagName);
22949         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22950             this.iterateChildren(node, this.cleanTableWidths);
22951             return;
22952         }
22953         if (node.hasAttribute('width')) {
22954             node.removeAttribute('width');
22955         }
22956         
22957          
22958         if (node.hasAttribute("style")) {
22959             // pretty basic...
22960             
22961             var styles = node.getAttribute("style").split(";");
22962             var nstyle = [];
22963             Roo.each(styles, function(s) {
22964                 if (!s.match(/:/)) {
22965                     return;
22966                 }
22967                 var kv = s.split(":");
22968                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22969                     return;
22970                 }
22971                 // what ever is left... we allow.
22972                 nstyle.push(s);
22973             });
22974             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22975             if (!nstyle.length) {
22976                 node.removeAttribute('style');
22977             }
22978         }
22979         
22980         this.iterateChildren(node, this.cleanTableWidths);
22981         
22982         
22983     },
22984     
22985     
22986     
22987     
22988     domToHTML : function(currentElement, depth, nopadtext) {
22989         
22990         depth = depth || 0;
22991         nopadtext = nopadtext || false;
22992     
22993         if (!currentElement) {
22994             return this.domToHTML(this.doc.body);
22995         }
22996         
22997         //Roo.log(currentElement);
22998         var j;
22999         var allText = false;
23000         var nodeName = currentElement.nodeName;
23001         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23002         
23003         if  (nodeName == '#text') {
23004             
23005             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23006         }
23007         
23008         
23009         var ret = '';
23010         if (nodeName != 'BODY') {
23011              
23012             var i = 0;
23013             // Prints the node tagName, such as <A>, <IMG>, etc
23014             if (tagName) {
23015                 var attr = [];
23016                 for(i = 0; i < currentElement.attributes.length;i++) {
23017                     // quoting?
23018                     var aname = currentElement.attributes.item(i).name;
23019                     if (!currentElement.attributes.item(i).value.length) {
23020                         continue;
23021                     }
23022                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23023                 }
23024                 
23025                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23026             } 
23027             else {
23028                 
23029                 // eack
23030             }
23031         } else {
23032             tagName = false;
23033         }
23034         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23035             return ret;
23036         }
23037         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23038             nopadtext = true;
23039         }
23040         
23041         
23042         // Traverse the tree
23043         i = 0;
23044         var currentElementChild = currentElement.childNodes.item(i);
23045         var allText = true;
23046         var innerHTML  = '';
23047         lastnode = '';
23048         while (currentElementChild) {
23049             // Formatting code (indent the tree so it looks nice on the screen)
23050             var nopad = nopadtext;
23051             if (lastnode == 'SPAN') {
23052                 nopad  = true;
23053             }
23054             // text
23055             if  (currentElementChild.nodeName == '#text') {
23056                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23057                 toadd = nopadtext ? toadd : toadd.trim();
23058                 if (!nopad && toadd.length > 80) {
23059                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23060                 }
23061                 innerHTML  += toadd;
23062                 
23063                 i++;
23064                 currentElementChild = currentElement.childNodes.item(i);
23065                 lastNode = '';
23066                 continue;
23067             }
23068             allText = false;
23069             
23070             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23071                 
23072             // Recursively traverse the tree structure of the child node
23073             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23074             lastnode = currentElementChild.nodeName;
23075             i++;
23076             currentElementChild=currentElement.childNodes.item(i);
23077         }
23078         
23079         ret += innerHTML;
23080         
23081         if (!allText) {
23082                 // The remaining code is mostly for formatting the tree
23083             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23084         }
23085         
23086         
23087         if (tagName) {
23088             ret+= "</"+tagName+">";
23089         }
23090         return ret;
23091         
23092     },
23093         
23094     applyBlacklists : function()
23095     {
23096         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23097         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23098         
23099         this.white = [];
23100         this.black = [];
23101         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23102             if (b.indexOf(tag) > -1) {
23103                 return;
23104             }
23105             this.white.push(tag);
23106             
23107         }, this);
23108         
23109         Roo.each(w, function(tag) {
23110             if (b.indexOf(tag) > -1) {
23111                 return;
23112             }
23113             if (this.white.indexOf(tag) > -1) {
23114                 return;
23115             }
23116             this.white.push(tag);
23117             
23118         }, this);
23119         
23120         
23121         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23122             if (w.indexOf(tag) > -1) {
23123                 return;
23124             }
23125             this.black.push(tag);
23126             
23127         }, this);
23128         
23129         Roo.each(b, function(tag) {
23130             if (w.indexOf(tag) > -1) {
23131                 return;
23132             }
23133             if (this.black.indexOf(tag) > -1) {
23134                 return;
23135             }
23136             this.black.push(tag);
23137             
23138         }, this);
23139         
23140         
23141         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23142         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23143         
23144         this.cwhite = [];
23145         this.cblack = [];
23146         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23147             if (b.indexOf(tag) > -1) {
23148                 return;
23149             }
23150             this.cwhite.push(tag);
23151             
23152         }, this);
23153         
23154         Roo.each(w, function(tag) {
23155             if (b.indexOf(tag) > -1) {
23156                 return;
23157             }
23158             if (this.cwhite.indexOf(tag) > -1) {
23159                 return;
23160             }
23161             this.cwhite.push(tag);
23162             
23163         }, this);
23164         
23165         
23166         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23167             if (w.indexOf(tag) > -1) {
23168                 return;
23169             }
23170             this.cblack.push(tag);
23171             
23172         }, this);
23173         
23174         Roo.each(b, function(tag) {
23175             if (w.indexOf(tag) > -1) {
23176                 return;
23177             }
23178             if (this.cblack.indexOf(tag) > -1) {
23179                 return;
23180             }
23181             this.cblack.push(tag);
23182             
23183         }, this);
23184     },
23185     
23186     setStylesheets : function(stylesheets)
23187     {
23188         if(typeof(stylesheets) == 'string'){
23189             Roo.get(this.iframe.contentDocument.head).createChild({
23190                 tag : 'link',
23191                 rel : 'stylesheet',
23192                 type : 'text/css',
23193                 href : stylesheets
23194             });
23195             
23196             return;
23197         }
23198         var _this = this;
23199      
23200         Roo.each(stylesheets, function(s) {
23201             if(!s.length){
23202                 return;
23203             }
23204             
23205             Roo.get(_this.iframe.contentDocument.head).createChild({
23206                 tag : 'link',
23207                 rel : 'stylesheet',
23208                 type : 'text/css',
23209                 href : s
23210             });
23211         });
23212
23213         
23214     },
23215     
23216     removeStylesheets : function()
23217     {
23218         var _this = this;
23219         
23220         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23221             s.remove();
23222         });
23223     },
23224     
23225     setStyle : function(style)
23226     {
23227         Roo.get(this.iframe.contentDocument.head).createChild({
23228             tag : 'style',
23229             type : 'text/css',
23230             html : style
23231         });
23232
23233         return;
23234     }
23235     
23236     // hide stuff that is not compatible
23237     /**
23238      * @event blur
23239      * @hide
23240      */
23241     /**
23242      * @event change
23243      * @hide
23244      */
23245     /**
23246      * @event focus
23247      * @hide
23248      */
23249     /**
23250      * @event specialkey
23251      * @hide
23252      */
23253     /**
23254      * @cfg {String} fieldClass @hide
23255      */
23256     /**
23257      * @cfg {String} focusClass @hide
23258      */
23259     /**
23260      * @cfg {String} autoCreate @hide
23261      */
23262     /**
23263      * @cfg {String} inputType @hide
23264      */
23265     /**
23266      * @cfg {String} invalidClass @hide
23267      */
23268     /**
23269      * @cfg {String} invalidText @hide
23270      */
23271     /**
23272      * @cfg {String} msgFx @hide
23273      */
23274     /**
23275      * @cfg {String} validateOnBlur @hide
23276      */
23277 });
23278
23279 Roo.HtmlEditorCore.white = [
23280         'area', 'br', 'img', 'input', 'hr', 'wbr',
23281         
23282        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23283        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23284        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23285        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23286        'table',   'ul',         'xmp', 
23287        
23288        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23289       'thead',   'tr', 
23290      
23291       'dir', 'menu', 'ol', 'ul', 'dl',
23292        
23293       'embed',  'object'
23294 ];
23295
23296
23297 Roo.HtmlEditorCore.black = [
23298     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23299         'applet', // 
23300         'base',   'basefont', 'bgsound', 'blink',  'body', 
23301         'frame',  'frameset', 'head',    'html',   'ilayer', 
23302         'iframe', 'layer',  'link',     'meta',    'object',   
23303         'script', 'style' ,'title',  'xml' // clean later..
23304 ];
23305 Roo.HtmlEditorCore.clean = [
23306     'script', 'style', 'title', 'xml'
23307 ];
23308 Roo.HtmlEditorCore.remove = [
23309     'font'
23310 ];
23311 // attributes..
23312
23313 Roo.HtmlEditorCore.ablack = [
23314     'on'
23315 ];
23316     
23317 Roo.HtmlEditorCore.aclean = [ 
23318     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23319 ];
23320
23321 // protocols..
23322 Roo.HtmlEditorCore.pwhite= [
23323         'http',  'https',  'mailto'
23324 ];
23325
23326 // white listed style attributes.
23327 Roo.HtmlEditorCore.cwhite= [
23328       //  'text-align', /// default is to allow most things..
23329       
23330          
23331 //        'font-size'//??
23332 ];
23333
23334 // black listed style attributes.
23335 Roo.HtmlEditorCore.cblack= [
23336       //  'font-size' -- this can be set by the project 
23337 ];
23338
23339
23340 Roo.HtmlEditorCore.swapCodes   =[ 
23341     [    8211, "--" ], 
23342     [    8212, "--" ], 
23343     [    8216,  "'" ],  
23344     [    8217, "'" ],  
23345     [    8220, '"' ],  
23346     [    8221, '"' ],  
23347     [    8226, "*" ],  
23348     [    8230, "..." ]
23349 ]; 
23350
23351     /*
23352  * - LGPL
23353  *
23354  * HtmlEditor
23355  * 
23356  */
23357
23358 /**
23359  * @class Roo.bootstrap.HtmlEditor
23360  * @extends Roo.bootstrap.TextArea
23361  * Bootstrap HtmlEditor class
23362
23363  * @constructor
23364  * Create a new HtmlEditor
23365  * @param {Object} config The config object
23366  */
23367
23368 Roo.bootstrap.HtmlEditor = function(config){
23369     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23370     if (!this.toolbars) {
23371         this.toolbars = [];
23372     }
23373     
23374     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23375     this.addEvents({
23376             /**
23377              * @event initialize
23378              * Fires when the editor is fully initialized (including the iframe)
23379              * @param {HtmlEditor} this
23380              */
23381             initialize: true,
23382             /**
23383              * @event activate
23384              * Fires when the editor is first receives the focus. Any insertion must wait
23385              * until after this event.
23386              * @param {HtmlEditor} this
23387              */
23388             activate: true,
23389              /**
23390              * @event beforesync
23391              * Fires before the textarea is updated with content from the editor iframe. Return false
23392              * to cancel the sync.
23393              * @param {HtmlEditor} this
23394              * @param {String} html
23395              */
23396             beforesync: true,
23397              /**
23398              * @event beforepush
23399              * Fires before the iframe editor is updated with content from the textarea. Return false
23400              * to cancel the push.
23401              * @param {HtmlEditor} this
23402              * @param {String} html
23403              */
23404             beforepush: true,
23405              /**
23406              * @event sync
23407              * Fires when the textarea is updated with content from the editor iframe.
23408              * @param {HtmlEditor} this
23409              * @param {String} html
23410              */
23411             sync: true,
23412              /**
23413              * @event push
23414              * Fires when the iframe editor is updated with content from the textarea.
23415              * @param {HtmlEditor} this
23416              * @param {String} html
23417              */
23418             push: true,
23419              /**
23420              * @event editmodechange
23421              * Fires when the editor switches edit modes
23422              * @param {HtmlEditor} this
23423              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23424              */
23425             editmodechange: true,
23426             /**
23427              * @event editorevent
23428              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23429              * @param {HtmlEditor} this
23430              */
23431             editorevent: true,
23432             /**
23433              * @event firstfocus
23434              * Fires when on first focus - needed by toolbars..
23435              * @param {HtmlEditor} this
23436              */
23437             firstfocus: true,
23438             /**
23439              * @event autosave
23440              * Auto save the htmlEditor value as a file into Events
23441              * @param {HtmlEditor} this
23442              */
23443             autosave: true,
23444             /**
23445              * @event savedpreview
23446              * preview the saved version of htmlEditor
23447              * @param {HtmlEditor} this
23448              */
23449             savedpreview: true
23450         });
23451 };
23452
23453
23454 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23455     
23456     
23457       /**
23458      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23459      */
23460     toolbars : false,
23461     
23462      /**
23463     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23464     */
23465     btns : [],
23466    
23467      /**
23468      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23469      *                        Roo.resizable.
23470      */
23471     resizable : false,
23472      /**
23473      * @cfg {Number} height (in pixels)
23474      */   
23475     height: 300,
23476    /**
23477      * @cfg {Number} width (in pixels)
23478      */   
23479     width: false,
23480     
23481     /**
23482      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23483      * 
23484      */
23485     stylesheets: false,
23486     
23487     // id of frame..
23488     frameId: false,
23489     
23490     // private properties
23491     validationEvent : false,
23492     deferHeight: true,
23493     initialized : false,
23494     activated : false,
23495     
23496     onFocus : Roo.emptyFn,
23497     iframePad:3,
23498     hideMode:'offsets',
23499     
23500     tbContainer : false,
23501     
23502     bodyCls : '',
23503     
23504     toolbarContainer :function() {
23505         return this.wrap.select('.x-html-editor-tb',true).first();
23506     },
23507
23508     /**
23509      * Protected method that will not generally be called directly. It
23510      * is called when the editor creates its toolbar. Override this method if you need to
23511      * add custom toolbar buttons.
23512      * @param {HtmlEditor} editor
23513      */
23514     createToolbar : function(){
23515         Roo.log('renewing');
23516         Roo.log("create toolbars");
23517         
23518         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23519         this.toolbars[0].render(this.toolbarContainer());
23520         
23521         return;
23522         
23523 //        if (!editor.toolbars || !editor.toolbars.length) {
23524 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23525 //        }
23526 //        
23527 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23528 //            editor.toolbars[i] = Roo.factory(
23529 //                    typeof(editor.toolbars[i]) == 'string' ?
23530 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23531 //                Roo.bootstrap.HtmlEditor);
23532 //            editor.toolbars[i].init(editor);
23533 //        }
23534     },
23535
23536      
23537     // private
23538     onRender : function(ct, position)
23539     {
23540        // Roo.log("Call onRender: " + this.xtype);
23541         var _t = this;
23542         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23543       
23544         this.wrap = this.inputEl().wrap({
23545             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23546         });
23547         
23548         this.editorcore.onRender(ct, position);
23549          
23550         if (this.resizable) {
23551             this.resizeEl = new Roo.Resizable(this.wrap, {
23552                 pinned : true,
23553                 wrap: true,
23554                 dynamic : true,
23555                 minHeight : this.height,
23556                 height: this.height,
23557                 handles : this.resizable,
23558                 width: this.width,
23559                 listeners : {
23560                     resize : function(r, w, h) {
23561                         _t.onResize(w,h); // -something
23562                     }
23563                 }
23564             });
23565             
23566         }
23567         this.createToolbar(this);
23568        
23569         
23570         if(!this.width && this.resizable){
23571             this.setSize(this.wrap.getSize());
23572         }
23573         if (this.resizeEl) {
23574             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23575             // should trigger onReize..
23576         }
23577         
23578     },
23579
23580     // private
23581     onResize : function(w, h)
23582     {
23583         Roo.log('resize: ' +w + ',' + h );
23584         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23585         var ew = false;
23586         var eh = false;
23587         
23588         if(this.inputEl() ){
23589             if(typeof w == 'number'){
23590                 var aw = w - this.wrap.getFrameWidth('lr');
23591                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23592                 ew = aw;
23593             }
23594             if(typeof h == 'number'){
23595                  var tbh = -11;  // fixme it needs to tool bar size!
23596                 for (var i =0; i < this.toolbars.length;i++) {
23597                     // fixme - ask toolbars for heights?
23598                     tbh += this.toolbars[i].el.getHeight();
23599                     //if (this.toolbars[i].footer) {
23600                     //    tbh += this.toolbars[i].footer.el.getHeight();
23601                     //}
23602                 }
23603               
23604                 
23605                 
23606                 
23607                 
23608                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23609                 ah -= 5; // knock a few pixes off for look..
23610                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23611                 var eh = ah;
23612             }
23613         }
23614         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23615         this.editorcore.onResize(ew,eh);
23616         
23617     },
23618
23619     /**
23620      * Toggles the editor between standard and source edit mode.
23621      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23622      */
23623     toggleSourceEdit : function(sourceEditMode)
23624     {
23625         this.editorcore.toggleSourceEdit(sourceEditMode);
23626         
23627         if(this.editorcore.sourceEditMode){
23628             Roo.log('editor - showing textarea');
23629             
23630 //            Roo.log('in');
23631 //            Roo.log(this.syncValue());
23632             this.syncValue();
23633             this.inputEl().removeClass(['hide', 'x-hidden']);
23634             this.inputEl().dom.removeAttribute('tabIndex');
23635             this.inputEl().focus();
23636         }else{
23637             Roo.log('editor - hiding textarea');
23638 //            Roo.log('out')
23639 //            Roo.log(this.pushValue()); 
23640             this.pushValue();
23641             
23642             this.inputEl().addClass(['hide', 'x-hidden']);
23643             this.inputEl().dom.setAttribute('tabIndex', -1);
23644             //this.deferFocus();
23645         }
23646          
23647         if(this.resizable){
23648             this.setSize(this.wrap.getSize());
23649         }
23650         
23651         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23652     },
23653  
23654     // private (for BoxComponent)
23655     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23656
23657     // private (for BoxComponent)
23658     getResizeEl : function(){
23659         return this.wrap;
23660     },
23661
23662     // private (for BoxComponent)
23663     getPositionEl : function(){
23664         return this.wrap;
23665     },
23666
23667     // private
23668     initEvents : function(){
23669         this.originalValue = this.getValue();
23670     },
23671
23672 //    /**
23673 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23674 //     * @method
23675 //     */
23676 //    markInvalid : Roo.emptyFn,
23677 //    /**
23678 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23679 //     * @method
23680 //     */
23681 //    clearInvalid : Roo.emptyFn,
23682
23683     setValue : function(v){
23684         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23685         this.editorcore.pushValue();
23686     },
23687
23688      
23689     // private
23690     deferFocus : function(){
23691         this.focus.defer(10, this);
23692     },
23693
23694     // doc'ed in Field
23695     focus : function(){
23696         this.editorcore.focus();
23697         
23698     },
23699       
23700
23701     // private
23702     onDestroy : function(){
23703         
23704         
23705         
23706         if(this.rendered){
23707             
23708             for (var i =0; i < this.toolbars.length;i++) {
23709                 // fixme - ask toolbars for heights?
23710                 this.toolbars[i].onDestroy();
23711             }
23712             
23713             this.wrap.dom.innerHTML = '';
23714             this.wrap.remove();
23715         }
23716     },
23717
23718     // private
23719     onFirstFocus : function(){
23720         //Roo.log("onFirstFocus");
23721         this.editorcore.onFirstFocus();
23722          for (var i =0; i < this.toolbars.length;i++) {
23723             this.toolbars[i].onFirstFocus();
23724         }
23725         
23726     },
23727     
23728     // private
23729     syncValue : function()
23730     {   
23731         this.editorcore.syncValue();
23732     },
23733     
23734     pushValue : function()
23735     {   
23736         this.editorcore.pushValue();
23737     }
23738      
23739     
23740     // hide stuff that is not compatible
23741     /**
23742      * @event blur
23743      * @hide
23744      */
23745     /**
23746      * @event change
23747      * @hide
23748      */
23749     /**
23750      * @event focus
23751      * @hide
23752      */
23753     /**
23754      * @event specialkey
23755      * @hide
23756      */
23757     /**
23758      * @cfg {String} fieldClass @hide
23759      */
23760     /**
23761      * @cfg {String} focusClass @hide
23762      */
23763     /**
23764      * @cfg {String} autoCreate @hide
23765      */
23766     /**
23767      * @cfg {String} inputType @hide
23768      */
23769     /**
23770      * @cfg {String} invalidClass @hide
23771      */
23772     /**
23773      * @cfg {String} invalidText @hide
23774      */
23775     /**
23776      * @cfg {String} msgFx @hide
23777      */
23778     /**
23779      * @cfg {String} validateOnBlur @hide
23780      */
23781 });
23782  
23783     
23784    
23785    
23786    
23787       
23788 Roo.namespace('Roo.bootstrap.htmleditor');
23789 /**
23790  * @class Roo.bootstrap.HtmlEditorToolbar1
23791  * Basic Toolbar
23792  * 
23793  * Usage:
23794  *
23795  new Roo.bootstrap.HtmlEditor({
23796     ....
23797     toolbars : [
23798         new Roo.bootstrap.HtmlEditorToolbar1({
23799             disable : { fonts: 1 , format: 1, ..., ... , ...],
23800             btns : [ .... ]
23801         })
23802     }
23803      
23804  * 
23805  * @cfg {Object} disable List of elements to disable..
23806  * @cfg {Array} btns List of additional buttons.
23807  * 
23808  * 
23809  * NEEDS Extra CSS? 
23810  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23811  */
23812  
23813 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23814 {
23815     
23816     Roo.apply(this, config);
23817     
23818     // default disabled, based on 'good practice'..
23819     this.disable = this.disable || {};
23820     Roo.applyIf(this.disable, {
23821         fontSize : true,
23822         colors : true,
23823         specialElements : true
23824     });
23825     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23826     
23827     this.editor = config.editor;
23828     this.editorcore = config.editor.editorcore;
23829     
23830     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23831     
23832     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23833     // dont call parent... till later.
23834 }
23835 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23836      
23837     bar : true,
23838     
23839     editor : false,
23840     editorcore : false,
23841     
23842     
23843     formats : [
23844         "p" ,  
23845         "h1","h2","h3","h4","h5","h6", 
23846         "pre", "code", 
23847         "abbr", "acronym", "address", "cite", "samp", "var",
23848         'div','span'
23849     ],
23850     
23851     onRender : function(ct, position)
23852     {
23853        // Roo.log("Call onRender: " + this.xtype);
23854         
23855        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23856        Roo.log(this.el);
23857        this.el.dom.style.marginBottom = '0';
23858        var _this = this;
23859        var editorcore = this.editorcore;
23860        var editor= this.editor;
23861        
23862        var children = [];
23863        var btn = function(id,cmd , toggle, handler, html){
23864        
23865             var  event = toggle ? 'toggle' : 'click';
23866        
23867             var a = {
23868                 size : 'sm',
23869                 xtype: 'Button',
23870                 xns: Roo.bootstrap,
23871                 glyphicon : id,
23872                 cmd : id || cmd,
23873                 enableToggle:toggle !== false,
23874                 html : html || '',
23875                 pressed : toggle ? false : null,
23876                 listeners : {}
23877             };
23878             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23879                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23880             };
23881             children.push(a);
23882             return a;
23883        }
23884        
23885     //    var cb_box = function...
23886         
23887         var style = {
23888                 xtype: 'Button',
23889                 size : 'sm',
23890                 xns: Roo.bootstrap,
23891                 glyphicon : 'font',
23892                 //html : 'submit'
23893                 menu : {
23894                     xtype: 'Menu',
23895                     xns: Roo.bootstrap,
23896                     items:  []
23897                 }
23898         };
23899         Roo.each(this.formats, function(f) {
23900             style.menu.items.push({
23901                 xtype :'MenuItem',
23902                 xns: Roo.bootstrap,
23903                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23904                 tagname : f,
23905                 listeners : {
23906                     click : function()
23907                     {
23908                         editorcore.insertTag(this.tagname);
23909                         editor.focus();
23910                     }
23911                 }
23912                 
23913             });
23914         });
23915         children.push(style);   
23916         
23917         btn('bold',false,true);
23918         btn('italic',false,true);
23919         btn('align-left', 'justifyleft',true);
23920         btn('align-center', 'justifycenter',true);
23921         btn('align-right' , 'justifyright',true);
23922         btn('link', false, false, function(btn) {
23923             //Roo.log("create link?");
23924             var url = prompt(this.createLinkText, this.defaultLinkValue);
23925             if(url && url != 'http:/'+'/'){
23926                 this.editorcore.relayCmd('createlink', url);
23927             }
23928         }),
23929         btn('list','insertunorderedlist',true);
23930         btn('pencil', false,true, function(btn){
23931                 Roo.log(this);
23932                 this.toggleSourceEdit(btn.pressed);
23933         });
23934         
23935         if (this.editor.btns.length > 0) {
23936             for (var i = 0; i<this.editor.btns.length; i++) {
23937                 children.push(this.editor.btns[i]);
23938             }
23939         }
23940         
23941         /*
23942         var cog = {
23943                 xtype: 'Button',
23944                 size : 'sm',
23945                 xns: Roo.bootstrap,
23946                 glyphicon : 'cog',
23947                 //html : 'submit'
23948                 menu : {
23949                     xtype: 'Menu',
23950                     xns: Roo.bootstrap,
23951                     items:  []
23952                 }
23953         };
23954         
23955         cog.menu.items.push({
23956             xtype :'MenuItem',
23957             xns: Roo.bootstrap,
23958             html : Clean styles,
23959             tagname : f,
23960             listeners : {
23961                 click : function()
23962                 {
23963                     editorcore.insertTag(this.tagname);
23964                     editor.focus();
23965                 }
23966             }
23967             
23968         });
23969        */
23970         
23971          
23972        this.xtype = 'NavSimplebar';
23973         
23974         for(var i=0;i< children.length;i++) {
23975             
23976             this.buttons.add(this.addxtypeChild(children[i]));
23977             
23978         }
23979         
23980         editor.on('editorevent', this.updateToolbar, this);
23981     },
23982     onBtnClick : function(id)
23983     {
23984        this.editorcore.relayCmd(id);
23985        this.editorcore.focus();
23986     },
23987     
23988     /**
23989      * Protected method that will not generally be called directly. It triggers
23990      * a toolbar update by reading the markup state of the current selection in the editor.
23991      */
23992     updateToolbar: function(){
23993
23994         if(!this.editorcore.activated){
23995             this.editor.onFirstFocus(); // is this neeed?
23996             return;
23997         }
23998
23999         var btns = this.buttons; 
24000         var doc = this.editorcore.doc;
24001         btns.get('bold').setActive(doc.queryCommandState('bold'));
24002         btns.get('italic').setActive(doc.queryCommandState('italic'));
24003         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24004         
24005         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24006         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24007         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24008         
24009         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24010         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24011          /*
24012         
24013         var ans = this.editorcore.getAllAncestors();
24014         if (this.formatCombo) {
24015             
24016             
24017             var store = this.formatCombo.store;
24018             this.formatCombo.setValue("");
24019             for (var i =0; i < ans.length;i++) {
24020                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24021                     // select it..
24022                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24023                     break;
24024                 }
24025             }
24026         }
24027         
24028         
24029         
24030         // hides menus... - so this cant be on a menu...
24031         Roo.bootstrap.MenuMgr.hideAll();
24032         */
24033         Roo.bootstrap.MenuMgr.hideAll();
24034         //this.editorsyncValue();
24035     },
24036     onFirstFocus: function() {
24037         this.buttons.each(function(item){
24038            item.enable();
24039         });
24040     },
24041     toggleSourceEdit : function(sourceEditMode){
24042         
24043           
24044         if(sourceEditMode){
24045             Roo.log("disabling buttons");
24046            this.buttons.each( function(item){
24047                 if(item.cmd != 'pencil'){
24048                     item.disable();
24049                 }
24050             });
24051           
24052         }else{
24053             Roo.log("enabling buttons");
24054             if(this.editorcore.initialized){
24055                 this.buttons.each( function(item){
24056                     item.enable();
24057                 });
24058             }
24059             
24060         }
24061         Roo.log("calling toggole on editor");
24062         // tell the editor that it's been pressed..
24063         this.editor.toggleSourceEdit(sourceEditMode);
24064        
24065     }
24066 });
24067
24068
24069
24070
24071
24072 /**
24073  * @class Roo.bootstrap.Table.AbstractSelectionModel
24074  * @extends Roo.util.Observable
24075  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24076  * implemented by descendant classes.  This class should not be directly instantiated.
24077  * @constructor
24078  */
24079 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24080     this.locked = false;
24081     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24082 };
24083
24084
24085 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24086     /** @ignore Called by the grid automatically. Do not call directly. */
24087     init : function(grid){
24088         this.grid = grid;
24089         this.initEvents();
24090     },
24091
24092     /**
24093      * Locks the selections.
24094      */
24095     lock : function(){
24096         this.locked = true;
24097     },
24098
24099     /**
24100      * Unlocks the selections.
24101      */
24102     unlock : function(){
24103         this.locked = false;
24104     },
24105
24106     /**
24107      * Returns true if the selections are locked.
24108      * @return {Boolean}
24109      */
24110     isLocked : function(){
24111         return this.locked;
24112     }
24113 });
24114 /**
24115  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24116  * @class Roo.bootstrap.Table.RowSelectionModel
24117  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24118  * It supports multiple selections and keyboard selection/navigation. 
24119  * @constructor
24120  * @param {Object} config
24121  */
24122
24123 Roo.bootstrap.Table.RowSelectionModel = function(config){
24124     Roo.apply(this, config);
24125     this.selections = new Roo.util.MixedCollection(false, function(o){
24126         return o.id;
24127     });
24128
24129     this.last = false;
24130     this.lastActive = false;
24131
24132     this.addEvents({
24133         /**
24134              * @event selectionchange
24135              * Fires when the selection changes
24136              * @param {SelectionModel} this
24137              */
24138             "selectionchange" : true,
24139         /**
24140              * @event afterselectionchange
24141              * Fires after the selection changes (eg. by key press or clicking)
24142              * @param {SelectionModel} this
24143              */
24144             "afterselectionchange" : true,
24145         /**
24146              * @event beforerowselect
24147              * Fires when a row is selected being selected, return false to cancel.
24148              * @param {SelectionModel} this
24149              * @param {Number} rowIndex The selected index
24150              * @param {Boolean} keepExisting False if other selections will be cleared
24151              */
24152             "beforerowselect" : true,
24153         /**
24154              * @event rowselect
24155              * Fires when a row is selected.
24156              * @param {SelectionModel} this
24157              * @param {Number} rowIndex The selected index
24158              * @param {Roo.data.Record} r The record
24159              */
24160             "rowselect" : true,
24161         /**
24162              * @event rowdeselect
24163              * Fires when a row is deselected.
24164              * @param {SelectionModel} this
24165              * @param {Number} rowIndex The selected index
24166              */
24167         "rowdeselect" : true
24168     });
24169     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24170     this.locked = false;
24171  };
24172
24173 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24174     /**
24175      * @cfg {Boolean} singleSelect
24176      * True to allow selection of only one row at a time (defaults to false)
24177      */
24178     singleSelect : false,
24179
24180     // private
24181     initEvents : function()
24182     {
24183
24184         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24185         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24186         //}else{ // allow click to work like normal
24187          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24188         //}
24189         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24190         this.grid.on("rowclick", this.handleMouseDown, this);
24191         
24192         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24193             "up" : function(e){
24194                 if(!e.shiftKey){
24195                     this.selectPrevious(e.shiftKey);
24196                 }else if(this.last !== false && this.lastActive !== false){
24197                     var last = this.last;
24198                     this.selectRange(this.last,  this.lastActive-1);
24199                     this.grid.getView().focusRow(this.lastActive);
24200                     if(last !== false){
24201                         this.last = last;
24202                     }
24203                 }else{
24204                     this.selectFirstRow();
24205                 }
24206                 this.fireEvent("afterselectionchange", this);
24207             },
24208             "down" : function(e){
24209                 if(!e.shiftKey){
24210                     this.selectNext(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             scope: this
24224         });
24225         this.grid.store.on('load', function(){
24226             this.selections.clear();
24227         },this);
24228         /*
24229         var view = this.grid.view;
24230         view.on("refresh", this.onRefresh, this);
24231         view.on("rowupdated", this.onRowUpdated, this);
24232         view.on("rowremoved", this.onRemove, this);
24233         */
24234     },
24235
24236     // private
24237     onRefresh : function()
24238     {
24239         var ds = this.grid.store, i, v = this.grid.view;
24240         var s = this.selections;
24241         s.each(function(r){
24242             if((i = ds.indexOfId(r.id)) != -1){
24243                 v.onRowSelect(i);
24244             }else{
24245                 s.remove(r);
24246             }
24247         });
24248     },
24249
24250     // private
24251     onRemove : function(v, index, r){
24252         this.selections.remove(r);
24253     },
24254
24255     // private
24256     onRowUpdated : function(v, index, r){
24257         if(this.isSelected(r)){
24258             v.onRowSelect(index);
24259         }
24260     },
24261
24262     /**
24263      * Select records.
24264      * @param {Array} records The records to select
24265      * @param {Boolean} keepExisting (optional) True to keep existing selections
24266      */
24267     selectRecords : function(records, keepExisting)
24268     {
24269         if(!keepExisting){
24270             this.clearSelections();
24271         }
24272             var ds = this.grid.store;
24273         for(var i = 0, len = records.length; i < len; i++){
24274             this.selectRow(ds.indexOf(records[i]), true);
24275         }
24276     },
24277
24278     /**
24279      * Gets the number of selected rows.
24280      * @return {Number}
24281      */
24282     getCount : function(){
24283         return this.selections.length;
24284     },
24285
24286     /**
24287      * Selects the first row in the grid.
24288      */
24289     selectFirstRow : function(){
24290         this.selectRow(0);
24291     },
24292
24293     /**
24294      * Select the last row.
24295      * @param {Boolean} keepExisting (optional) True to keep existing selections
24296      */
24297     selectLastRow : function(keepExisting){
24298         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24299         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24300     },
24301
24302     /**
24303      * Selects the row immediately following the last selected row.
24304      * @param {Boolean} keepExisting (optional) True to keep existing selections
24305      */
24306     selectNext : function(keepExisting)
24307     {
24308             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24309             this.selectRow(this.last+1, keepExisting);
24310             this.grid.getView().focusRow(this.last);
24311         }
24312     },
24313
24314     /**
24315      * Selects the row that precedes the last selected row.
24316      * @param {Boolean} keepExisting (optional) True to keep existing selections
24317      */
24318     selectPrevious : function(keepExisting){
24319         if(this.last){
24320             this.selectRow(this.last-1, keepExisting);
24321             this.grid.getView().focusRow(this.last);
24322         }
24323     },
24324
24325     /**
24326      * Returns the selected records
24327      * @return {Array} Array of selected records
24328      */
24329     getSelections : function(){
24330         return [].concat(this.selections.items);
24331     },
24332
24333     /**
24334      * Returns the first selected record.
24335      * @return {Record}
24336      */
24337     getSelected : function(){
24338         return this.selections.itemAt(0);
24339     },
24340
24341
24342     /**
24343      * Clears all selections.
24344      */
24345     clearSelections : function(fast)
24346     {
24347         if(this.locked) {
24348             return;
24349         }
24350         if(fast !== true){
24351                 var ds = this.grid.store;
24352             var s = this.selections;
24353             s.each(function(r){
24354                 this.deselectRow(ds.indexOfId(r.id));
24355             }, this);
24356             s.clear();
24357         }else{
24358             this.selections.clear();
24359         }
24360         this.last = false;
24361     },
24362
24363
24364     /**
24365      * Selects all rows.
24366      */
24367     selectAll : function(){
24368         if(this.locked) {
24369             return;
24370         }
24371         this.selections.clear();
24372         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24373             this.selectRow(i, true);
24374         }
24375     },
24376
24377     /**
24378      * Returns True if there is a selection.
24379      * @return {Boolean}
24380      */
24381     hasSelection : function(){
24382         return this.selections.length > 0;
24383     },
24384
24385     /**
24386      * Returns True if the specified row is selected.
24387      * @param {Number/Record} record The record or index of the record to check
24388      * @return {Boolean}
24389      */
24390     isSelected : function(index){
24391             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24392         return (r && this.selections.key(r.id) ? true : false);
24393     },
24394
24395     /**
24396      * Returns True if the specified record id is selected.
24397      * @param {String} id The id of record to check
24398      * @return {Boolean}
24399      */
24400     isIdSelected : function(id){
24401         return (this.selections.key(id) ? true : false);
24402     },
24403
24404
24405     // private
24406     handleMouseDBClick : function(e, t){
24407         
24408     },
24409     // private
24410     handleMouseDown : function(e, t)
24411     {
24412             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24413         if(this.isLocked() || rowIndex < 0 ){
24414             return;
24415         };
24416         if(e.shiftKey && this.last !== false){
24417             var last = this.last;
24418             this.selectRange(last, rowIndex, e.ctrlKey);
24419             this.last = last; // reset the last
24420             t.focus();
24421     
24422         }else{
24423             var isSelected = this.isSelected(rowIndex);
24424             //Roo.log("select row:" + rowIndex);
24425             if(isSelected){
24426                 this.deselectRow(rowIndex);
24427             } else {
24428                         this.selectRow(rowIndex, true);
24429             }
24430     
24431             /*
24432                 if(e.button !== 0 && isSelected){
24433                 alert('rowIndex 2: ' + rowIndex);
24434                     view.focusRow(rowIndex);
24435                 }else if(e.ctrlKey && isSelected){
24436                     this.deselectRow(rowIndex);
24437                 }else if(!isSelected){
24438                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24439                     view.focusRow(rowIndex);
24440                 }
24441             */
24442         }
24443         this.fireEvent("afterselectionchange", this);
24444     },
24445     // private
24446     handleDragableRowClick :  function(grid, rowIndex, e) 
24447     {
24448         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24449             this.selectRow(rowIndex, false);
24450             grid.view.focusRow(rowIndex);
24451              this.fireEvent("afterselectionchange", this);
24452         }
24453     },
24454     
24455     /**
24456      * Selects multiple rows.
24457      * @param {Array} rows Array of the indexes of the row to select
24458      * @param {Boolean} keepExisting (optional) True to keep existing selections
24459      */
24460     selectRows : function(rows, keepExisting){
24461         if(!keepExisting){
24462             this.clearSelections();
24463         }
24464         for(var i = 0, len = rows.length; i < len; i++){
24465             this.selectRow(rows[i], true);
24466         }
24467     },
24468
24469     /**
24470      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24471      * @param {Number} startRow The index of the first row in the range
24472      * @param {Number} endRow The index of the last row in the range
24473      * @param {Boolean} keepExisting (optional) True to retain existing selections
24474      */
24475     selectRange : function(startRow, endRow, keepExisting){
24476         if(this.locked) {
24477             return;
24478         }
24479         if(!keepExisting){
24480             this.clearSelections();
24481         }
24482         if(startRow <= endRow){
24483             for(var i = startRow; i <= endRow; i++){
24484                 this.selectRow(i, true);
24485             }
24486         }else{
24487             for(var i = startRow; i >= endRow; i--){
24488                 this.selectRow(i, true);
24489             }
24490         }
24491     },
24492
24493     /**
24494      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24495      * @param {Number} startRow The index of the first row in the range
24496      * @param {Number} endRow The index of the last row in the range
24497      */
24498     deselectRange : function(startRow, endRow, preventViewNotify){
24499         if(this.locked) {
24500             return;
24501         }
24502         for(var i = startRow; i <= endRow; i++){
24503             this.deselectRow(i, preventViewNotify);
24504         }
24505     },
24506
24507     /**
24508      * Selects a row.
24509      * @param {Number} row The index of the row to select
24510      * @param {Boolean} keepExisting (optional) True to keep existing selections
24511      */
24512     selectRow : function(index, keepExisting, preventViewNotify)
24513     {
24514             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24515             return;
24516         }
24517         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24518             if(!keepExisting || this.singleSelect){
24519                 this.clearSelections();
24520             }
24521             
24522             var r = this.grid.store.getAt(index);
24523             //console.log('selectRow - record id :' + r.id);
24524             
24525             this.selections.add(r);
24526             this.last = this.lastActive = index;
24527             if(!preventViewNotify){
24528                 var proxy = new Roo.Element(
24529                                 this.grid.getRowDom(index)
24530                 );
24531                 proxy.addClass('bg-info info');
24532             }
24533             this.fireEvent("rowselect", this, index, r);
24534             this.fireEvent("selectionchange", this);
24535         }
24536     },
24537
24538     /**
24539      * Deselects a row.
24540      * @param {Number} row The index of the row to deselect
24541      */
24542     deselectRow : function(index, preventViewNotify)
24543     {
24544         if(this.locked) {
24545             return;
24546         }
24547         if(this.last == index){
24548             this.last = false;
24549         }
24550         if(this.lastActive == index){
24551             this.lastActive = false;
24552         }
24553         
24554         var r = this.grid.store.getAt(index);
24555         if (!r) {
24556             return;
24557         }
24558         
24559         this.selections.remove(r);
24560         //.console.log('deselectRow - record id :' + r.id);
24561         if(!preventViewNotify){
24562         
24563             var proxy = new Roo.Element(
24564                 this.grid.getRowDom(index)
24565             );
24566             proxy.removeClass('bg-info info');
24567         }
24568         this.fireEvent("rowdeselect", this, index);
24569         this.fireEvent("selectionchange", this);
24570     },
24571
24572     // private
24573     restoreLast : function(){
24574         if(this._last){
24575             this.last = this._last;
24576         }
24577     },
24578
24579     // private
24580     acceptsNav : function(row, col, cm){
24581         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24582     },
24583
24584     // private
24585     onEditorKey : function(field, e){
24586         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24587         if(k == e.TAB){
24588             e.stopEvent();
24589             ed.completeEdit();
24590             if(e.shiftKey){
24591                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24592             }else{
24593                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24594             }
24595         }else if(k == e.ENTER && !e.ctrlKey){
24596             e.stopEvent();
24597             ed.completeEdit();
24598             if(e.shiftKey){
24599                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24600             }else{
24601                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24602             }
24603         }else if(k == e.ESC){
24604             ed.cancelEdit();
24605         }
24606         if(newCell){
24607             g.startEditing(newCell[0], newCell[1]);
24608         }
24609     }
24610 });
24611 /*
24612  * Based on:
24613  * Ext JS Library 1.1.1
24614  * Copyright(c) 2006-2007, Ext JS, LLC.
24615  *
24616  * Originally Released Under LGPL - original licence link has changed is not relivant.
24617  *
24618  * Fork - LGPL
24619  * <script type="text/javascript">
24620  */
24621  
24622 /**
24623  * @class Roo.bootstrap.PagingToolbar
24624  * @extends Roo.bootstrap.NavSimplebar
24625  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24626  * @constructor
24627  * Create a new PagingToolbar
24628  * @param {Object} config The config object
24629  * @param {Roo.data.Store} store
24630  */
24631 Roo.bootstrap.PagingToolbar = function(config)
24632 {
24633     // old args format still supported... - xtype is prefered..
24634         // created from xtype...
24635     
24636     this.ds = config.dataSource;
24637     
24638     if (config.store && !this.ds) {
24639         this.store= Roo.factory(config.store, Roo.data);
24640         this.ds = this.store;
24641         this.ds.xmodule = this.xmodule || false;
24642     }
24643     
24644     this.toolbarItems = [];
24645     if (config.items) {
24646         this.toolbarItems = config.items;
24647     }
24648     
24649     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24650     
24651     this.cursor = 0;
24652     
24653     if (this.ds) { 
24654         this.bind(this.ds);
24655     }
24656     
24657     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24658     
24659 };
24660
24661 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24662     /**
24663      * @cfg {Roo.data.Store} dataSource
24664      * The underlying data store providing the paged data
24665      */
24666     /**
24667      * @cfg {String/HTMLElement/Element} container
24668      * container The id or element that will contain the toolbar
24669      */
24670     /**
24671      * @cfg {Boolean} displayInfo
24672      * True to display the displayMsg (defaults to false)
24673      */
24674     /**
24675      * @cfg {Number} pageSize
24676      * The number of records to display per page (defaults to 20)
24677      */
24678     pageSize: 20,
24679     /**
24680      * @cfg {String} displayMsg
24681      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24682      */
24683     displayMsg : 'Displaying {0} - {1} of {2}',
24684     /**
24685      * @cfg {String} emptyMsg
24686      * The message to display when no records are found (defaults to "No data to display")
24687      */
24688     emptyMsg : 'No data to display',
24689     /**
24690      * Customizable piece of the default paging text (defaults to "Page")
24691      * @type String
24692      */
24693     beforePageText : "Page",
24694     /**
24695      * Customizable piece of the default paging text (defaults to "of %0")
24696      * @type String
24697      */
24698     afterPageText : "of {0}",
24699     /**
24700      * Customizable piece of the default paging text (defaults to "First Page")
24701      * @type String
24702      */
24703     firstText : "First Page",
24704     /**
24705      * Customizable piece of the default paging text (defaults to "Previous Page")
24706      * @type String
24707      */
24708     prevText : "Previous Page",
24709     /**
24710      * Customizable piece of the default paging text (defaults to "Next Page")
24711      * @type String
24712      */
24713     nextText : "Next Page",
24714     /**
24715      * Customizable piece of the default paging text (defaults to "Last Page")
24716      * @type String
24717      */
24718     lastText : "Last Page",
24719     /**
24720      * Customizable piece of the default paging text (defaults to "Refresh")
24721      * @type String
24722      */
24723     refreshText : "Refresh",
24724
24725     buttons : false,
24726     // private
24727     onRender : function(ct, position) 
24728     {
24729         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24730         this.navgroup.parentId = this.id;
24731         this.navgroup.onRender(this.el, null);
24732         // add the buttons to the navgroup
24733         
24734         if(this.displayInfo){
24735             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24736             this.displayEl = this.el.select('.x-paging-info', true).first();
24737 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24738 //            this.displayEl = navel.el.select('span',true).first();
24739         }
24740         
24741         var _this = this;
24742         
24743         if(this.buttons){
24744             Roo.each(_this.buttons, function(e){ // this might need to use render????
24745                Roo.factory(e).render(_this.el);
24746             });
24747         }
24748             
24749         Roo.each(_this.toolbarItems, function(e) {
24750             _this.navgroup.addItem(e);
24751         });
24752         
24753         
24754         this.first = this.navgroup.addItem({
24755             tooltip: this.firstText,
24756             cls: "prev",
24757             icon : 'fa fa-step-backward',
24758             disabled: true,
24759             preventDefault: true,
24760             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24761         });
24762         
24763         this.prev =  this.navgroup.addItem({
24764             tooltip: this.prevText,
24765             cls: "prev",
24766             icon : 'fa fa-backward',
24767             disabled: true,
24768             preventDefault: true,
24769             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24770         });
24771     //this.addSeparator();
24772         
24773         
24774         var field = this.navgroup.addItem( {
24775             tagtype : 'span',
24776             cls : 'x-paging-position',
24777             
24778             html : this.beforePageText  +
24779                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24780                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24781          } ); //?? escaped?
24782         
24783         this.field = field.el.select('input', true).first();
24784         this.field.on("keydown", this.onPagingKeydown, this);
24785         this.field.on("focus", function(){this.dom.select();});
24786     
24787     
24788         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24789         //this.field.setHeight(18);
24790         //this.addSeparator();
24791         this.next = this.navgroup.addItem({
24792             tooltip: this.nextText,
24793             cls: "next",
24794             html : ' <i class="fa fa-forward">',
24795             disabled: true,
24796             preventDefault: true,
24797             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24798         });
24799         this.last = this.navgroup.addItem({
24800             tooltip: this.lastText,
24801             icon : 'fa fa-step-forward',
24802             cls: "next",
24803             disabled: true,
24804             preventDefault: true,
24805             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24806         });
24807     //this.addSeparator();
24808         this.loading = this.navgroup.addItem({
24809             tooltip: this.refreshText,
24810             icon: 'fa fa-refresh',
24811             preventDefault: true,
24812             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24813         });
24814         
24815     },
24816
24817     // private
24818     updateInfo : function(){
24819         if(this.displayEl){
24820             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24821             var msg = count == 0 ?
24822                 this.emptyMsg :
24823                 String.format(
24824                     this.displayMsg,
24825                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24826                 );
24827             this.displayEl.update(msg);
24828         }
24829     },
24830
24831     // private
24832     onLoad : function(ds, r, o)
24833     {
24834         this.cursor = o.params.start ? o.params.start : 0;
24835         
24836         var d = this.getPageData(),
24837             ap = d.activePage,
24838             ps = d.pages;
24839         
24840         
24841         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24842         this.field.dom.value = ap;
24843         this.first.setDisabled(ap == 1);
24844         this.prev.setDisabled(ap == 1);
24845         this.next.setDisabled(ap == ps);
24846         this.last.setDisabled(ap == ps);
24847         this.loading.enable();
24848         this.updateInfo();
24849     },
24850
24851     // private
24852     getPageData : function(){
24853         var total = this.ds.getTotalCount();
24854         return {
24855             total : total,
24856             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24857             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24858         };
24859     },
24860
24861     // private
24862     onLoadError : function(){
24863         this.loading.enable();
24864     },
24865
24866     // private
24867     onPagingKeydown : function(e){
24868         var k = e.getKey();
24869         var d = this.getPageData();
24870         if(k == e.RETURN){
24871             var v = this.field.dom.value, pageNum;
24872             if(!v || isNaN(pageNum = parseInt(v, 10))){
24873                 this.field.dom.value = d.activePage;
24874                 return;
24875             }
24876             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24877             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24878             e.stopEvent();
24879         }
24880         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))
24881         {
24882           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24883           this.field.dom.value = pageNum;
24884           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24885           e.stopEvent();
24886         }
24887         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24888         {
24889           var v = this.field.dom.value, pageNum; 
24890           var increment = (e.shiftKey) ? 10 : 1;
24891           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24892                 increment *= -1;
24893           }
24894           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24895             this.field.dom.value = d.activePage;
24896             return;
24897           }
24898           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24899           {
24900             this.field.dom.value = parseInt(v, 10) + increment;
24901             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24902             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24903           }
24904           e.stopEvent();
24905         }
24906     },
24907
24908     // private
24909     beforeLoad : function(){
24910         if(this.loading){
24911             this.loading.disable();
24912         }
24913     },
24914
24915     // private
24916     onClick : function(which){
24917         
24918         var ds = this.ds;
24919         if (!ds) {
24920             return;
24921         }
24922         
24923         switch(which){
24924             case "first":
24925                 ds.load({params:{start: 0, limit: this.pageSize}});
24926             break;
24927             case "prev":
24928                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24929             break;
24930             case "next":
24931                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24932             break;
24933             case "last":
24934                 var total = ds.getTotalCount();
24935                 var extra = total % this.pageSize;
24936                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24937                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24938             break;
24939             case "refresh":
24940                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24941             break;
24942         }
24943     },
24944
24945     /**
24946      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24947      * @param {Roo.data.Store} store The data store to unbind
24948      */
24949     unbind : function(ds){
24950         ds.un("beforeload", this.beforeLoad, this);
24951         ds.un("load", this.onLoad, this);
24952         ds.un("loadexception", this.onLoadError, this);
24953         ds.un("remove", this.updateInfo, this);
24954         ds.un("add", this.updateInfo, this);
24955         this.ds = undefined;
24956     },
24957
24958     /**
24959      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24960      * @param {Roo.data.Store} store The data store to bind
24961      */
24962     bind : function(ds){
24963         ds.on("beforeload", this.beforeLoad, this);
24964         ds.on("load", this.onLoad, this);
24965         ds.on("loadexception", this.onLoadError, this);
24966         ds.on("remove", this.updateInfo, this);
24967         ds.on("add", this.updateInfo, this);
24968         this.ds = ds;
24969     }
24970 });/*
24971  * - LGPL
24972  *
24973  * element
24974  * 
24975  */
24976
24977 /**
24978  * @class Roo.bootstrap.MessageBar
24979  * @extends Roo.bootstrap.Component
24980  * Bootstrap MessageBar class
24981  * @cfg {String} html contents of the MessageBar
24982  * @cfg {String} weight (info | success | warning | danger) default info
24983  * @cfg {String} beforeClass insert the bar before the given class
24984  * @cfg {Boolean} closable (true | false) default false
24985  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24986  * 
24987  * @constructor
24988  * Create a new Element
24989  * @param {Object} config The config object
24990  */
24991
24992 Roo.bootstrap.MessageBar = function(config){
24993     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24994 };
24995
24996 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24997     
24998     html: '',
24999     weight: 'info',
25000     closable: false,
25001     fixed: false,
25002     beforeClass: 'bootstrap-sticky-wrap',
25003     
25004     getAutoCreate : function(){
25005         
25006         var cfg = {
25007             tag: 'div',
25008             cls: 'alert alert-dismissable alert-' + this.weight,
25009             cn: [
25010                 {
25011                     tag: 'span',
25012                     cls: 'message',
25013                     html: this.html || ''
25014                 }
25015             ]
25016         };
25017         
25018         if(this.fixed){
25019             cfg.cls += ' alert-messages-fixed';
25020         }
25021         
25022         if(this.closable){
25023             cfg.cn.push({
25024                 tag: 'button',
25025                 cls: 'close',
25026                 html: 'x'
25027             });
25028         }
25029         
25030         return cfg;
25031     },
25032     
25033     onRender : function(ct, position)
25034     {
25035         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25036         
25037         if(!this.el){
25038             var cfg = Roo.apply({},  this.getAutoCreate());
25039             cfg.id = Roo.id();
25040             
25041             if (this.cls) {
25042                 cfg.cls += ' ' + this.cls;
25043             }
25044             if (this.style) {
25045                 cfg.style = this.style;
25046             }
25047             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25048             
25049             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25050         }
25051         
25052         this.el.select('>button.close').on('click', this.hide, this);
25053         
25054     },
25055     
25056     show : function()
25057     {
25058         if (!this.rendered) {
25059             this.render();
25060         }
25061         
25062         this.el.show();
25063         
25064         this.fireEvent('show', this);
25065         
25066     },
25067     
25068     hide : function()
25069     {
25070         if (!this.rendered) {
25071             this.render();
25072         }
25073         
25074         this.el.hide();
25075         
25076         this.fireEvent('hide', this);
25077     },
25078     
25079     update : function()
25080     {
25081 //        var e = this.el.dom.firstChild;
25082 //        
25083 //        if(this.closable){
25084 //            e = e.nextSibling;
25085 //        }
25086 //        
25087 //        e.data = this.html || '';
25088
25089         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25090     }
25091    
25092 });
25093
25094  
25095
25096      /*
25097  * - LGPL
25098  *
25099  * Graph
25100  * 
25101  */
25102
25103
25104 /**
25105  * @class Roo.bootstrap.Graph
25106  * @extends Roo.bootstrap.Component
25107  * Bootstrap Graph class
25108 > Prameters
25109  -sm {number} sm 4
25110  -md {number} md 5
25111  @cfg {String} graphtype  bar | vbar | pie
25112  @cfg {number} g_x coodinator | centre x (pie)
25113  @cfg {number} g_y coodinator | centre y (pie)
25114  @cfg {number} g_r radius (pie)
25115  @cfg {number} g_height height of the chart (respected by all elements in the set)
25116  @cfg {number} g_width width of the chart (respected by all elements in the set)
25117  @cfg {Object} title The title of the chart
25118     
25119  -{Array}  values
25120  -opts (object) options for the chart 
25121      o {
25122      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25123      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25124      o vgutter (number)
25125      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.
25126      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25127      o to
25128      o stretch (boolean)
25129      o }
25130  -opts (object) options for the pie
25131      o{
25132      o cut
25133      o startAngle (number)
25134      o endAngle (number)
25135      } 
25136  *
25137  * @constructor
25138  * Create a new Input
25139  * @param {Object} config The config object
25140  */
25141
25142 Roo.bootstrap.Graph = function(config){
25143     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25144     
25145     this.addEvents({
25146         // img events
25147         /**
25148          * @event click
25149          * The img click event for the img.
25150          * @param {Roo.EventObject} e
25151          */
25152         "click" : true
25153     });
25154 };
25155
25156 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25157     
25158     sm: 4,
25159     md: 5,
25160     graphtype: 'bar',
25161     g_height: 250,
25162     g_width: 400,
25163     g_x: 50,
25164     g_y: 50,
25165     g_r: 30,
25166     opts:{
25167         //g_colors: this.colors,
25168         g_type: 'soft',
25169         g_gutter: '20%'
25170
25171     },
25172     title : false,
25173
25174     getAutoCreate : function(){
25175         
25176         var cfg = {
25177             tag: 'div',
25178             html : null
25179         };
25180         
25181         
25182         return  cfg;
25183     },
25184
25185     onRender : function(ct,position){
25186         
25187         
25188         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25189         
25190         if (typeof(Raphael) == 'undefined') {
25191             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25192             return;
25193         }
25194         
25195         this.raphael = Raphael(this.el.dom);
25196         
25197                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25198                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25199                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25200                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25201                 /*
25202                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25203                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25204                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25205                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25206                 
25207                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25208                 r.barchart(330, 10, 300, 220, data1);
25209                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25210                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25211                 */
25212                 
25213                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25214                 // r.barchart(30, 30, 560, 250,  xdata, {
25215                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25216                 //     axis : "0 0 1 1",
25217                 //     axisxlabels :  xdata
25218                 //     //yvalues : cols,
25219                    
25220                 // });
25221 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25222 //        
25223 //        this.load(null,xdata,{
25224 //                axis : "0 0 1 1",
25225 //                axisxlabels :  xdata
25226 //                });
25227
25228     },
25229
25230     load : function(graphtype,xdata,opts)
25231     {
25232         this.raphael.clear();
25233         if(!graphtype) {
25234             graphtype = this.graphtype;
25235         }
25236         if(!opts){
25237             opts = this.opts;
25238         }
25239         var r = this.raphael,
25240             fin = function () {
25241                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25242             },
25243             fout = function () {
25244                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25245             },
25246             pfin = function() {
25247                 this.sector.stop();
25248                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25249
25250                 if (this.label) {
25251                     this.label[0].stop();
25252                     this.label[0].attr({ r: 7.5 });
25253                     this.label[1].attr({ "font-weight": 800 });
25254                 }
25255             },
25256             pfout = function() {
25257                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25258
25259                 if (this.label) {
25260                     this.label[0].animate({ r: 5 }, 500, "bounce");
25261                     this.label[1].attr({ "font-weight": 400 });
25262                 }
25263             };
25264
25265         switch(graphtype){
25266             case 'bar':
25267                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25268                 break;
25269             case 'hbar':
25270                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25271                 break;
25272             case 'pie':
25273 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25274 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25275 //            
25276                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25277                 
25278                 break;
25279
25280         }
25281         
25282         if(this.title){
25283             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25284         }
25285         
25286     },
25287     
25288     setTitle: function(o)
25289     {
25290         this.title = o;
25291     },
25292     
25293     initEvents: function() {
25294         
25295         if(!this.href){
25296             this.el.on('click', this.onClick, this);
25297         }
25298     },
25299     
25300     onClick : function(e)
25301     {
25302         Roo.log('img onclick');
25303         this.fireEvent('click', this, e);
25304     }
25305    
25306 });
25307
25308  
25309 /*
25310  * - LGPL
25311  *
25312  * numberBox
25313  * 
25314  */
25315 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25316
25317 /**
25318  * @class Roo.bootstrap.dash.NumberBox
25319  * @extends Roo.bootstrap.Component
25320  * Bootstrap NumberBox class
25321  * @cfg {String} headline Box headline
25322  * @cfg {String} content Box content
25323  * @cfg {String} icon Box icon
25324  * @cfg {String} footer Footer text
25325  * @cfg {String} fhref Footer href
25326  * 
25327  * @constructor
25328  * Create a new NumberBox
25329  * @param {Object} config The config object
25330  */
25331
25332
25333 Roo.bootstrap.dash.NumberBox = function(config){
25334     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25335     
25336 };
25337
25338 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25339     
25340     headline : '',
25341     content : '',
25342     icon : '',
25343     footer : '',
25344     fhref : '',
25345     ficon : '',
25346     
25347     getAutoCreate : function(){
25348         
25349         var cfg = {
25350             tag : 'div',
25351             cls : 'small-box ',
25352             cn : [
25353                 {
25354                     tag : 'div',
25355                     cls : 'inner',
25356                     cn :[
25357                         {
25358                             tag : 'h3',
25359                             cls : 'roo-headline',
25360                             html : this.headline
25361                         },
25362                         {
25363                             tag : 'p',
25364                             cls : 'roo-content',
25365                             html : this.content
25366                         }
25367                     ]
25368                 }
25369             ]
25370         };
25371         
25372         if(this.icon){
25373             cfg.cn.push({
25374                 tag : 'div',
25375                 cls : 'icon',
25376                 cn :[
25377                     {
25378                         tag : 'i',
25379                         cls : 'ion ' + this.icon
25380                     }
25381                 ]
25382             });
25383         }
25384         
25385         if(this.footer){
25386             var footer = {
25387                 tag : 'a',
25388                 cls : 'small-box-footer',
25389                 href : this.fhref || '#',
25390                 html : this.footer
25391             };
25392             
25393             cfg.cn.push(footer);
25394             
25395         }
25396         
25397         return  cfg;
25398     },
25399
25400     onRender : function(ct,position){
25401         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25402
25403
25404        
25405                 
25406     },
25407
25408     setHeadline: function (value)
25409     {
25410         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25411     },
25412     
25413     setFooter: function (value, href)
25414     {
25415         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25416         
25417         if(href){
25418             this.el.select('a.small-box-footer',true).first().attr('href', href);
25419         }
25420         
25421     },
25422
25423     setContent: function (value)
25424     {
25425         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25426     },
25427
25428     initEvents: function() 
25429     {   
25430         
25431     }
25432     
25433 });
25434
25435  
25436 /*
25437  * - LGPL
25438  *
25439  * TabBox
25440  * 
25441  */
25442 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25443
25444 /**
25445  * @class Roo.bootstrap.dash.TabBox
25446  * @extends Roo.bootstrap.Component
25447  * Bootstrap TabBox class
25448  * @cfg {String} title Title of the TabBox
25449  * @cfg {String} icon Icon of the TabBox
25450  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25451  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25452  * 
25453  * @constructor
25454  * Create a new TabBox
25455  * @param {Object} config The config object
25456  */
25457
25458
25459 Roo.bootstrap.dash.TabBox = function(config){
25460     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25461     this.addEvents({
25462         // raw events
25463         /**
25464          * @event addpane
25465          * When a pane is added
25466          * @param {Roo.bootstrap.dash.TabPane} pane
25467          */
25468         "addpane" : true,
25469         /**
25470          * @event activatepane
25471          * When a pane is activated
25472          * @param {Roo.bootstrap.dash.TabPane} pane
25473          */
25474         "activatepane" : true
25475         
25476          
25477     });
25478     
25479     this.panes = [];
25480 };
25481
25482 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25483
25484     title : '',
25485     icon : false,
25486     showtabs : true,
25487     tabScrollable : false,
25488     
25489     getChildContainer : function()
25490     {
25491         return this.el.select('.tab-content', true).first();
25492     },
25493     
25494     getAutoCreate : function(){
25495         
25496         var header = {
25497             tag: 'li',
25498             cls: 'pull-left header',
25499             html: this.title,
25500             cn : []
25501         };
25502         
25503         if(this.icon){
25504             header.cn.push({
25505                 tag: 'i',
25506                 cls: 'fa ' + this.icon
25507             });
25508         }
25509         
25510         var h = {
25511             tag: 'ul',
25512             cls: 'nav nav-tabs pull-right',
25513             cn: [
25514                 header
25515             ]
25516         };
25517         
25518         if(this.tabScrollable){
25519             h = {
25520                 tag: 'div',
25521                 cls: 'tab-header',
25522                 cn: [
25523                     {
25524                         tag: 'ul',
25525                         cls: 'nav nav-tabs pull-right',
25526                         cn: [
25527                             header
25528                         ]
25529                     }
25530                 ]
25531             };
25532         }
25533         
25534         var cfg = {
25535             tag: 'div',
25536             cls: 'nav-tabs-custom',
25537             cn: [
25538                 h,
25539                 {
25540                     tag: 'div',
25541                     cls: 'tab-content no-padding',
25542                     cn: []
25543                 }
25544             ]
25545         };
25546
25547         return  cfg;
25548     },
25549     initEvents : function()
25550     {
25551         //Roo.log('add add pane handler');
25552         this.on('addpane', this.onAddPane, this);
25553     },
25554      /**
25555      * Updates the box title
25556      * @param {String} html to set the title to.
25557      */
25558     setTitle : function(value)
25559     {
25560         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25561     },
25562     onAddPane : function(pane)
25563     {
25564         this.panes.push(pane);
25565         //Roo.log('addpane');
25566         //Roo.log(pane);
25567         // tabs are rendere left to right..
25568         if(!this.showtabs){
25569             return;
25570         }
25571         
25572         var ctr = this.el.select('.nav-tabs', true).first();
25573          
25574          
25575         var existing = ctr.select('.nav-tab',true);
25576         var qty = existing.getCount();;
25577         
25578         
25579         var tab = ctr.createChild({
25580             tag : 'li',
25581             cls : 'nav-tab' + (qty ? '' : ' active'),
25582             cn : [
25583                 {
25584                     tag : 'a',
25585                     href:'#',
25586                     html : pane.title
25587                 }
25588             ]
25589         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25590         pane.tab = tab;
25591         
25592         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25593         if (!qty) {
25594             pane.el.addClass('active');
25595         }
25596         
25597                 
25598     },
25599     onTabClick : function(ev,un,ob,pane)
25600     {
25601         //Roo.log('tab - prev default');
25602         ev.preventDefault();
25603         
25604         
25605         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25606         pane.tab.addClass('active');
25607         //Roo.log(pane.title);
25608         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25609         // technically we should have a deactivate event.. but maybe add later.
25610         // and it should not de-activate the selected tab...
25611         this.fireEvent('activatepane', pane);
25612         pane.el.addClass('active');
25613         pane.fireEvent('activate');
25614         
25615         
25616     },
25617     
25618     getActivePane : function()
25619     {
25620         var r = false;
25621         Roo.each(this.panes, function(p) {
25622             if(p.el.hasClass('active')){
25623                 r = p;
25624                 return false;
25625             }
25626             
25627             return;
25628         });
25629         
25630         return r;
25631     }
25632     
25633     
25634 });
25635
25636  
25637 /*
25638  * - LGPL
25639  *
25640  * Tab pane
25641  * 
25642  */
25643 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25644 /**
25645  * @class Roo.bootstrap.TabPane
25646  * @extends Roo.bootstrap.Component
25647  * Bootstrap TabPane class
25648  * @cfg {Boolean} active (false | true) Default false
25649  * @cfg {String} title title of panel
25650
25651  * 
25652  * @constructor
25653  * Create a new TabPane
25654  * @param {Object} config The config object
25655  */
25656
25657 Roo.bootstrap.dash.TabPane = function(config){
25658     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25659     
25660     this.addEvents({
25661         // raw events
25662         /**
25663          * @event activate
25664          * When a pane is activated
25665          * @param {Roo.bootstrap.dash.TabPane} pane
25666          */
25667         "activate" : true
25668          
25669     });
25670 };
25671
25672 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25673     
25674     active : false,
25675     title : '',
25676     
25677     // the tabBox that this is attached to.
25678     tab : false,
25679      
25680     getAutoCreate : function() 
25681     {
25682         var cfg = {
25683             tag: 'div',
25684             cls: 'tab-pane'
25685         };
25686         
25687         if(this.active){
25688             cfg.cls += ' active';
25689         }
25690         
25691         return cfg;
25692     },
25693     initEvents  : function()
25694     {
25695         //Roo.log('trigger add pane handler');
25696         this.parent().fireEvent('addpane', this)
25697     },
25698     
25699      /**
25700      * Updates the tab title 
25701      * @param {String} html to set the title to.
25702      */
25703     setTitle: function(str)
25704     {
25705         if (!this.tab) {
25706             return;
25707         }
25708         this.title = str;
25709         this.tab.select('a', true).first().dom.innerHTML = str;
25710         
25711     }
25712     
25713     
25714     
25715 });
25716
25717  
25718
25719
25720  /*
25721  * - LGPL
25722  *
25723  * menu
25724  * 
25725  */
25726 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25727
25728 /**
25729  * @class Roo.bootstrap.menu.Menu
25730  * @extends Roo.bootstrap.Component
25731  * Bootstrap Menu class - container for Menu
25732  * @cfg {String} html Text of the menu
25733  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25734  * @cfg {String} icon Font awesome icon
25735  * @cfg {String} pos Menu align to (top | bottom) default bottom
25736  * 
25737  * 
25738  * @constructor
25739  * Create a new Menu
25740  * @param {Object} config The config object
25741  */
25742
25743
25744 Roo.bootstrap.menu.Menu = function(config){
25745     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25746     
25747     this.addEvents({
25748         /**
25749          * @event beforeshow
25750          * Fires before this menu is displayed
25751          * @param {Roo.bootstrap.menu.Menu} this
25752          */
25753         beforeshow : true,
25754         /**
25755          * @event beforehide
25756          * Fires before this menu is hidden
25757          * @param {Roo.bootstrap.menu.Menu} this
25758          */
25759         beforehide : true,
25760         /**
25761          * @event show
25762          * Fires after this menu is displayed
25763          * @param {Roo.bootstrap.menu.Menu} this
25764          */
25765         show : true,
25766         /**
25767          * @event hide
25768          * Fires after this menu is hidden
25769          * @param {Roo.bootstrap.menu.Menu} this
25770          */
25771         hide : true,
25772         /**
25773          * @event click
25774          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25775          * @param {Roo.bootstrap.menu.Menu} this
25776          * @param {Roo.EventObject} e
25777          */
25778         click : true
25779     });
25780     
25781 };
25782
25783 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25784     
25785     submenu : false,
25786     html : '',
25787     weight : 'default',
25788     icon : false,
25789     pos : 'bottom',
25790     
25791     
25792     getChildContainer : function() {
25793         if(this.isSubMenu){
25794             return this.el;
25795         }
25796         
25797         return this.el.select('ul.dropdown-menu', true).first();  
25798     },
25799     
25800     getAutoCreate : function()
25801     {
25802         var text = [
25803             {
25804                 tag : 'span',
25805                 cls : 'roo-menu-text',
25806                 html : this.html
25807             }
25808         ];
25809         
25810         if(this.icon){
25811             text.unshift({
25812                 tag : 'i',
25813                 cls : 'fa ' + this.icon
25814             })
25815         }
25816         
25817         
25818         var cfg = {
25819             tag : 'div',
25820             cls : 'btn-group',
25821             cn : [
25822                 {
25823                     tag : 'button',
25824                     cls : 'dropdown-button btn btn-' + this.weight,
25825                     cn : text
25826                 },
25827                 {
25828                     tag : 'button',
25829                     cls : 'dropdown-toggle btn btn-' + this.weight,
25830                     cn : [
25831                         {
25832                             tag : 'span',
25833                             cls : 'caret'
25834                         }
25835                     ]
25836                 },
25837                 {
25838                     tag : 'ul',
25839                     cls : 'dropdown-menu'
25840                 }
25841             ]
25842             
25843         };
25844         
25845         if(this.pos == 'top'){
25846             cfg.cls += ' dropup';
25847         }
25848         
25849         if(this.isSubMenu){
25850             cfg = {
25851                 tag : 'ul',
25852                 cls : 'dropdown-menu'
25853             }
25854         }
25855         
25856         return cfg;
25857     },
25858     
25859     onRender : function(ct, position)
25860     {
25861         this.isSubMenu = ct.hasClass('dropdown-submenu');
25862         
25863         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25864     },
25865     
25866     initEvents : function() 
25867     {
25868         if(this.isSubMenu){
25869             return;
25870         }
25871         
25872         this.hidden = true;
25873         
25874         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25875         this.triggerEl.on('click', this.onTriggerPress, this);
25876         
25877         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25878         this.buttonEl.on('click', this.onClick, this);
25879         
25880     },
25881     
25882     list : function()
25883     {
25884         if(this.isSubMenu){
25885             return this.el;
25886         }
25887         
25888         return this.el.select('ul.dropdown-menu', true).first();
25889     },
25890     
25891     onClick : function(e)
25892     {
25893         this.fireEvent("click", this, e);
25894     },
25895     
25896     onTriggerPress  : function(e)
25897     {   
25898         if (this.isVisible()) {
25899             this.hide();
25900         } else {
25901             this.show();
25902         }
25903     },
25904     
25905     isVisible : function(){
25906         return !this.hidden;
25907     },
25908     
25909     show : function()
25910     {
25911         this.fireEvent("beforeshow", this);
25912         
25913         this.hidden = false;
25914         this.el.addClass('open');
25915         
25916         Roo.get(document).on("mouseup", this.onMouseUp, this);
25917         
25918         this.fireEvent("show", this);
25919         
25920         
25921     },
25922     
25923     hide : function()
25924     {
25925         this.fireEvent("beforehide", this);
25926         
25927         this.hidden = true;
25928         this.el.removeClass('open');
25929         
25930         Roo.get(document).un("mouseup", this.onMouseUp);
25931         
25932         this.fireEvent("hide", this);
25933     },
25934     
25935     onMouseUp : function()
25936     {
25937         this.hide();
25938     }
25939     
25940 });
25941
25942  
25943  /*
25944  * - LGPL
25945  *
25946  * menu item
25947  * 
25948  */
25949 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25950
25951 /**
25952  * @class Roo.bootstrap.menu.Item
25953  * @extends Roo.bootstrap.Component
25954  * Bootstrap MenuItem class
25955  * @cfg {Boolean} submenu (true | false) default false
25956  * @cfg {String} html text of the item
25957  * @cfg {String} href the link
25958  * @cfg {Boolean} disable (true | false) default false
25959  * @cfg {Boolean} preventDefault (true | false) default true
25960  * @cfg {String} icon Font awesome icon
25961  * @cfg {String} pos Submenu align to (left | right) default right 
25962  * 
25963  * 
25964  * @constructor
25965  * Create a new Item
25966  * @param {Object} config The config object
25967  */
25968
25969
25970 Roo.bootstrap.menu.Item = function(config){
25971     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25972     this.addEvents({
25973         /**
25974          * @event mouseover
25975          * Fires when the mouse is hovering over this menu
25976          * @param {Roo.bootstrap.menu.Item} this
25977          * @param {Roo.EventObject} e
25978          */
25979         mouseover : true,
25980         /**
25981          * @event mouseout
25982          * Fires when the mouse exits this menu
25983          * @param {Roo.bootstrap.menu.Item} this
25984          * @param {Roo.EventObject} e
25985          */
25986         mouseout : true,
25987         // raw events
25988         /**
25989          * @event click
25990          * The raw click event for the entire grid.
25991          * @param {Roo.EventObject} e
25992          */
25993         click : true
25994     });
25995 };
25996
25997 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25998     
25999     submenu : false,
26000     href : '',
26001     html : '',
26002     preventDefault: true,
26003     disable : false,
26004     icon : false,
26005     pos : 'right',
26006     
26007     getAutoCreate : function()
26008     {
26009         var text = [
26010             {
26011                 tag : 'span',
26012                 cls : 'roo-menu-item-text',
26013                 html : this.html
26014             }
26015         ];
26016         
26017         if(this.icon){
26018             text.unshift({
26019                 tag : 'i',
26020                 cls : 'fa ' + this.icon
26021             })
26022         }
26023         
26024         var cfg = {
26025             tag : 'li',
26026             cn : [
26027                 {
26028                     tag : 'a',
26029                     href : this.href || '#',
26030                     cn : text
26031                 }
26032             ]
26033         };
26034         
26035         if(this.disable){
26036             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26037         }
26038         
26039         if(this.submenu){
26040             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26041             
26042             if(this.pos == 'left'){
26043                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26044             }
26045         }
26046         
26047         return cfg;
26048     },
26049     
26050     initEvents : function() 
26051     {
26052         this.el.on('mouseover', this.onMouseOver, this);
26053         this.el.on('mouseout', this.onMouseOut, this);
26054         
26055         this.el.select('a', true).first().on('click', this.onClick, this);
26056         
26057     },
26058     
26059     onClick : function(e)
26060     {
26061         if(this.preventDefault){
26062             e.preventDefault();
26063         }
26064         
26065         this.fireEvent("click", this, e);
26066     },
26067     
26068     onMouseOver : function(e)
26069     {
26070         if(this.submenu && this.pos == 'left'){
26071             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26072         }
26073         
26074         this.fireEvent("mouseover", this, e);
26075     },
26076     
26077     onMouseOut : function(e)
26078     {
26079         this.fireEvent("mouseout", this, e);
26080     }
26081 });
26082
26083  
26084
26085  /*
26086  * - LGPL
26087  *
26088  * menu separator
26089  * 
26090  */
26091 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26092
26093 /**
26094  * @class Roo.bootstrap.menu.Separator
26095  * @extends Roo.bootstrap.Component
26096  * Bootstrap Separator class
26097  * 
26098  * @constructor
26099  * Create a new Separator
26100  * @param {Object} config The config object
26101  */
26102
26103
26104 Roo.bootstrap.menu.Separator = function(config){
26105     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26106 };
26107
26108 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26109     
26110     getAutoCreate : function(){
26111         var cfg = {
26112             tag : 'li',
26113             cls: 'divider'
26114         };
26115         
26116         return cfg;
26117     }
26118    
26119 });
26120
26121  
26122
26123  /*
26124  * - LGPL
26125  *
26126  * Tooltip
26127  * 
26128  */
26129
26130 /**
26131  * @class Roo.bootstrap.Tooltip
26132  * Bootstrap Tooltip class
26133  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26134  * to determine which dom element triggers the tooltip.
26135  * 
26136  * It needs to add support for additional attributes like tooltip-position
26137  * 
26138  * @constructor
26139  * Create a new Toolti
26140  * @param {Object} config The config object
26141  */
26142
26143 Roo.bootstrap.Tooltip = function(config){
26144     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26145     
26146     this.alignment = Roo.bootstrap.Tooltip.alignment;
26147     
26148     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26149         this.alignment = config.alignment;
26150     }
26151     
26152 };
26153
26154 Roo.apply(Roo.bootstrap.Tooltip, {
26155     /**
26156      * @function init initialize tooltip monitoring.
26157      * @static
26158      */
26159     currentEl : false,
26160     currentTip : false,
26161     currentRegion : false,
26162     
26163     //  init : delay?
26164     
26165     init : function()
26166     {
26167         Roo.get(document).on('mouseover', this.enter ,this);
26168         Roo.get(document).on('mouseout', this.leave, this);
26169          
26170         
26171         this.currentTip = new Roo.bootstrap.Tooltip();
26172     },
26173     
26174     enter : function(ev)
26175     {
26176         var dom = ev.getTarget();
26177         
26178         //Roo.log(['enter',dom]);
26179         var el = Roo.fly(dom);
26180         if (this.currentEl) {
26181             //Roo.log(dom);
26182             //Roo.log(this.currentEl);
26183             //Roo.log(this.currentEl.contains(dom));
26184             if (this.currentEl == el) {
26185                 return;
26186             }
26187             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26188                 return;
26189             }
26190
26191         }
26192         
26193         if (this.currentTip.el) {
26194             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26195         }    
26196         //Roo.log(ev);
26197         
26198         if(!el || el.dom == document){
26199             return;
26200         }
26201         
26202         var bindEl = el;
26203         
26204         // you can not look for children, as if el is the body.. then everythign is the child..
26205         if (!el.attr('tooltip')) { //
26206             if (!el.select("[tooltip]").elements.length) {
26207                 return;
26208             }
26209             // is the mouse over this child...?
26210             bindEl = el.select("[tooltip]").first();
26211             var xy = ev.getXY();
26212             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26213                 //Roo.log("not in region.");
26214                 return;
26215             }
26216             //Roo.log("child element over..");
26217             
26218         }
26219         this.currentEl = bindEl;
26220         this.currentTip.bind(bindEl);
26221         this.currentRegion = Roo.lib.Region.getRegion(dom);
26222         this.currentTip.enter();
26223         
26224     },
26225     leave : function(ev)
26226     {
26227         var dom = ev.getTarget();
26228         //Roo.log(['leave',dom]);
26229         if (!this.currentEl) {
26230             return;
26231         }
26232         
26233         
26234         if (dom != this.currentEl.dom) {
26235             return;
26236         }
26237         var xy = ev.getXY();
26238         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26239             return;
26240         }
26241         // only activate leave if mouse cursor is outside... bounding box..
26242         
26243         
26244         
26245         
26246         if (this.currentTip) {
26247             this.currentTip.leave();
26248         }
26249         //Roo.log('clear currentEl');
26250         this.currentEl = false;
26251         
26252         
26253     },
26254     alignment : {
26255         'left' : ['r-l', [-2,0], 'right'],
26256         'right' : ['l-r', [2,0], 'left'],
26257         'bottom' : ['t-b', [0,2], 'top'],
26258         'top' : [ 'b-t', [0,-2], 'bottom']
26259     }
26260     
26261 });
26262
26263
26264 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26265     
26266     
26267     bindEl : false,
26268     
26269     delay : null, // can be { show : 300 , hide: 500}
26270     
26271     timeout : null,
26272     
26273     hoverState : null, //???
26274     
26275     placement : 'bottom', 
26276     
26277     alignment : false,
26278     
26279     getAutoCreate : function(){
26280     
26281         var cfg = {
26282            cls : 'tooltip',
26283            role : 'tooltip',
26284            cn : [
26285                 {
26286                     cls : 'tooltip-arrow'
26287                 },
26288                 {
26289                     cls : 'tooltip-inner'
26290                 }
26291            ]
26292         };
26293         
26294         return cfg;
26295     },
26296     bind : function(el)
26297     {
26298         this.bindEl = el;
26299     },
26300       
26301     
26302     enter : function () {
26303        
26304         if (this.timeout != null) {
26305             clearTimeout(this.timeout);
26306         }
26307         
26308         this.hoverState = 'in';
26309          //Roo.log("enter - show");
26310         if (!this.delay || !this.delay.show) {
26311             this.show();
26312             return;
26313         }
26314         var _t = this;
26315         this.timeout = setTimeout(function () {
26316             if (_t.hoverState == 'in') {
26317                 _t.show();
26318             }
26319         }, this.delay.show);
26320     },
26321     leave : function()
26322     {
26323         clearTimeout(this.timeout);
26324     
26325         this.hoverState = 'out';
26326          if (!this.delay || !this.delay.hide) {
26327             this.hide();
26328             return;
26329         }
26330        
26331         var _t = this;
26332         this.timeout = setTimeout(function () {
26333             //Roo.log("leave - timeout");
26334             
26335             if (_t.hoverState == 'out') {
26336                 _t.hide();
26337                 Roo.bootstrap.Tooltip.currentEl = false;
26338             }
26339         }, delay);
26340     },
26341     
26342     show : function (msg)
26343     {
26344         if (!this.el) {
26345             this.render(document.body);
26346         }
26347         // set content.
26348         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26349         
26350         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26351         
26352         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26353         
26354         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26355         
26356         var placement = typeof this.placement == 'function' ?
26357             this.placement.call(this, this.el, on_el) :
26358             this.placement;
26359             
26360         var autoToken = /\s?auto?\s?/i;
26361         var autoPlace = autoToken.test(placement);
26362         if (autoPlace) {
26363             placement = placement.replace(autoToken, '') || 'top';
26364         }
26365         
26366         //this.el.detach()
26367         //this.el.setXY([0,0]);
26368         this.el.show();
26369         //this.el.dom.style.display='block';
26370         
26371         //this.el.appendTo(on_el);
26372         
26373         var p = this.getPosition();
26374         var box = this.el.getBox();
26375         
26376         if (autoPlace) {
26377             // fixme..
26378         }
26379         
26380         var align = this.alignment[placement];
26381         
26382         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26383         
26384         if(placement == 'top' || placement == 'bottom'){
26385             if(xy[0] < 0){
26386                 placement = 'right';
26387             }
26388             
26389             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26390                 placement = 'left';
26391             }
26392             
26393             var scroll = Roo.select('body', true).first().getScroll();
26394             
26395             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26396                 placement = 'top';
26397             }
26398             
26399             align = this.alignment[placement];
26400         }
26401         
26402         this.el.alignTo(this.bindEl, align[0],align[1]);
26403         //var arrow = this.el.select('.arrow',true).first();
26404         //arrow.set(align[2], 
26405         
26406         this.el.addClass(placement);
26407         
26408         this.el.addClass('in fade');
26409         
26410         this.hoverState = null;
26411         
26412         if (this.el.hasClass('fade')) {
26413             // fade it?
26414         }
26415         
26416     },
26417     hide : function()
26418     {
26419          
26420         if (!this.el) {
26421             return;
26422         }
26423         //this.el.setXY([0,0]);
26424         this.el.removeClass('in');
26425         //this.el.hide();
26426         
26427     }
26428     
26429 });
26430  
26431
26432  /*
26433  * - LGPL
26434  *
26435  * Location Picker
26436  * 
26437  */
26438
26439 /**
26440  * @class Roo.bootstrap.LocationPicker
26441  * @extends Roo.bootstrap.Component
26442  * Bootstrap LocationPicker class
26443  * @cfg {Number} latitude Position when init default 0
26444  * @cfg {Number} longitude Position when init default 0
26445  * @cfg {Number} zoom default 15
26446  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26447  * @cfg {Boolean} mapTypeControl default false
26448  * @cfg {Boolean} disableDoubleClickZoom default false
26449  * @cfg {Boolean} scrollwheel default true
26450  * @cfg {Boolean} streetViewControl default false
26451  * @cfg {Number} radius default 0
26452  * @cfg {String} locationName
26453  * @cfg {Boolean} draggable default true
26454  * @cfg {Boolean} enableAutocomplete default false
26455  * @cfg {Boolean} enableReverseGeocode default true
26456  * @cfg {String} markerTitle
26457  * 
26458  * @constructor
26459  * Create a new LocationPicker
26460  * @param {Object} config The config object
26461  */
26462
26463
26464 Roo.bootstrap.LocationPicker = function(config){
26465     
26466     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26467     
26468     this.addEvents({
26469         /**
26470          * @event initial
26471          * Fires when the picker initialized.
26472          * @param {Roo.bootstrap.LocationPicker} this
26473          * @param {Google Location} location
26474          */
26475         initial : true,
26476         /**
26477          * @event positionchanged
26478          * Fires when the picker position changed.
26479          * @param {Roo.bootstrap.LocationPicker} this
26480          * @param {Google Location} location
26481          */
26482         positionchanged : true,
26483         /**
26484          * @event resize
26485          * Fires when the map resize.
26486          * @param {Roo.bootstrap.LocationPicker} this
26487          */
26488         resize : true,
26489         /**
26490          * @event show
26491          * Fires when the map show.
26492          * @param {Roo.bootstrap.LocationPicker} this
26493          */
26494         show : true,
26495         /**
26496          * @event hide
26497          * Fires when the map hide.
26498          * @param {Roo.bootstrap.LocationPicker} this
26499          */
26500         hide : true,
26501         /**
26502          * @event mapClick
26503          * Fires when click the map.
26504          * @param {Roo.bootstrap.LocationPicker} this
26505          * @param {Map event} e
26506          */
26507         mapClick : true,
26508         /**
26509          * @event mapRightClick
26510          * Fires when right click the map.
26511          * @param {Roo.bootstrap.LocationPicker} this
26512          * @param {Map event} e
26513          */
26514         mapRightClick : true,
26515         /**
26516          * @event markerClick
26517          * Fires when click the marker.
26518          * @param {Roo.bootstrap.LocationPicker} this
26519          * @param {Map event} e
26520          */
26521         markerClick : true,
26522         /**
26523          * @event markerRightClick
26524          * Fires when right click the marker.
26525          * @param {Roo.bootstrap.LocationPicker} this
26526          * @param {Map event} e
26527          */
26528         markerRightClick : true,
26529         /**
26530          * @event OverlayViewDraw
26531          * Fires when OverlayView Draw
26532          * @param {Roo.bootstrap.LocationPicker} this
26533          */
26534         OverlayViewDraw : true,
26535         /**
26536          * @event OverlayViewOnAdd
26537          * Fires when OverlayView Draw
26538          * @param {Roo.bootstrap.LocationPicker} this
26539          */
26540         OverlayViewOnAdd : true,
26541         /**
26542          * @event OverlayViewOnRemove
26543          * Fires when OverlayView Draw
26544          * @param {Roo.bootstrap.LocationPicker} this
26545          */
26546         OverlayViewOnRemove : true,
26547         /**
26548          * @event OverlayViewShow
26549          * Fires when OverlayView Draw
26550          * @param {Roo.bootstrap.LocationPicker} this
26551          * @param {Pixel} cpx
26552          */
26553         OverlayViewShow : true,
26554         /**
26555          * @event OverlayViewHide
26556          * Fires when OverlayView Draw
26557          * @param {Roo.bootstrap.LocationPicker} this
26558          */
26559         OverlayViewHide : true,
26560         /**
26561          * @event loadexception
26562          * Fires when load google lib failed.
26563          * @param {Roo.bootstrap.LocationPicker} this
26564          */
26565         loadexception : true
26566     });
26567         
26568 };
26569
26570 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26571     
26572     gMapContext: false,
26573     
26574     latitude: 0,
26575     longitude: 0,
26576     zoom: 15,
26577     mapTypeId: false,
26578     mapTypeControl: false,
26579     disableDoubleClickZoom: false,
26580     scrollwheel: true,
26581     streetViewControl: false,
26582     radius: 0,
26583     locationName: '',
26584     draggable: true,
26585     enableAutocomplete: false,
26586     enableReverseGeocode: true,
26587     markerTitle: '',
26588     
26589     getAutoCreate: function()
26590     {
26591
26592         var cfg = {
26593             tag: 'div',
26594             cls: 'roo-location-picker'
26595         };
26596         
26597         return cfg
26598     },
26599     
26600     initEvents: function(ct, position)
26601     {       
26602         if(!this.el.getWidth() || this.isApplied()){
26603             return;
26604         }
26605         
26606         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26607         
26608         this.initial();
26609     },
26610     
26611     initial: function()
26612     {
26613         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26614             this.fireEvent('loadexception', this);
26615             return;
26616         }
26617         
26618         if(!this.mapTypeId){
26619             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26620         }
26621         
26622         this.gMapContext = this.GMapContext();
26623         
26624         this.initOverlayView();
26625         
26626         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26627         
26628         var _this = this;
26629                 
26630         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26631             _this.setPosition(_this.gMapContext.marker.position);
26632         });
26633         
26634         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26635             _this.fireEvent('mapClick', this, event);
26636             
26637         });
26638
26639         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26640             _this.fireEvent('mapRightClick', this, event);
26641             
26642         });
26643         
26644         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26645             _this.fireEvent('markerClick', this, event);
26646             
26647         });
26648
26649         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26650             _this.fireEvent('markerRightClick', this, event);
26651             
26652         });
26653         
26654         this.setPosition(this.gMapContext.location);
26655         
26656         this.fireEvent('initial', this, this.gMapContext.location);
26657     },
26658     
26659     initOverlayView: function()
26660     {
26661         var _this = this;
26662         
26663         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26664             
26665             draw: function()
26666             {
26667                 _this.fireEvent('OverlayViewDraw', _this);
26668             },
26669             
26670             onAdd: function()
26671             {
26672                 _this.fireEvent('OverlayViewOnAdd', _this);
26673             },
26674             
26675             onRemove: function()
26676             {
26677                 _this.fireEvent('OverlayViewOnRemove', _this);
26678             },
26679             
26680             show: function(cpx)
26681             {
26682                 _this.fireEvent('OverlayViewShow', _this, cpx);
26683             },
26684             
26685             hide: function()
26686             {
26687                 _this.fireEvent('OverlayViewHide', _this);
26688             }
26689             
26690         });
26691     },
26692     
26693     fromLatLngToContainerPixel: function(event)
26694     {
26695         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26696     },
26697     
26698     isApplied: function() 
26699     {
26700         return this.getGmapContext() == false ? false : true;
26701     },
26702     
26703     getGmapContext: function() 
26704     {
26705         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26706     },
26707     
26708     GMapContext: function() 
26709     {
26710         var position = new google.maps.LatLng(this.latitude, this.longitude);
26711         
26712         var _map = new google.maps.Map(this.el.dom, {
26713             center: position,
26714             zoom: this.zoom,
26715             mapTypeId: this.mapTypeId,
26716             mapTypeControl: this.mapTypeControl,
26717             disableDoubleClickZoom: this.disableDoubleClickZoom,
26718             scrollwheel: this.scrollwheel,
26719             streetViewControl: this.streetViewControl,
26720             locationName: this.locationName,
26721             draggable: this.draggable,
26722             enableAutocomplete: this.enableAutocomplete,
26723             enableReverseGeocode: this.enableReverseGeocode
26724         });
26725         
26726         var _marker = new google.maps.Marker({
26727             position: position,
26728             map: _map,
26729             title: this.markerTitle,
26730             draggable: this.draggable
26731         });
26732         
26733         return {
26734             map: _map,
26735             marker: _marker,
26736             circle: null,
26737             location: position,
26738             radius: this.radius,
26739             locationName: this.locationName,
26740             addressComponents: {
26741                 formatted_address: null,
26742                 addressLine1: null,
26743                 addressLine2: null,
26744                 streetName: null,
26745                 streetNumber: null,
26746                 city: null,
26747                 district: null,
26748                 state: null,
26749                 stateOrProvince: null
26750             },
26751             settings: this,
26752             domContainer: this.el.dom,
26753             geodecoder: new google.maps.Geocoder()
26754         };
26755     },
26756     
26757     drawCircle: function(center, radius, options) 
26758     {
26759         if (this.gMapContext.circle != null) {
26760             this.gMapContext.circle.setMap(null);
26761         }
26762         if (radius > 0) {
26763             radius *= 1;
26764             options = Roo.apply({}, options, {
26765                 strokeColor: "#0000FF",
26766                 strokeOpacity: .35,
26767                 strokeWeight: 2,
26768                 fillColor: "#0000FF",
26769                 fillOpacity: .2
26770             });
26771             
26772             options.map = this.gMapContext.map;
26773             options.radius = radius;
26774             options.center = center;
26775             this.gMapContext.circle = new google.maps.Circle(options);
26776             return this.gMapContext.circle;
26777         }
26778         
26779         return null;
26780     },
26781     
26782     setPosition: function(location) 
26783     {
26784         this.gMapContext.location = location;
26785         this.gMapContext.marker.setPosition(location);
26786         this.gMapContext.map.panTo(location);
26787         this.drawCircle(location, this.gMapContext.radius, {});
26788         
26789         var _this = this;
26790         
26791         if (this.gMapContext.settings.enableReverseGeocode) {
26792             this.gMapContext.geodecoder.geocode({
26793                 latLng: this.gMapContext.location
26794             }, function(results, status) {
26795                 
26796                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26797                     _this.gMapContext.locationName = results[0].formatted_address;
26798                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26799                     
26800                     _this.fireEvent('positionchanged', this, location);
26801                 }
26802             });
26803             
26804             return;
26805         }
26806         
26807         this.fireEvent('positionchanged', this, location);
26808     },
26809     
26810     resize: function()
26811     {
26812         google.maps.event.trigger(this.gMapContext.map, "resize");
26813         
26814         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26815         
26816         this.fireEvent('resize', this);
26817     },
26818     
26819     setPositionByLatLng: function(latitude, longitude)
26820     {
26821         this.setPosition(new google.maps.LatLng(latitude, longitude));
26822     },
26823     
26824     getCurrentPosition: function() 
26825     {
26826         return {
26827             latitude: this.gMapContext.location.lat(),
26828             longitude: this.gMapContext.location.lng()
26829         };
26830     },
26831     
26832     getAddressName: function() 
26833     {
26834         return this.gMapContext.locationName;
26835     },
26836     
26837     getAddressComponents: function() 
26838     {
26839         return this.gMapContext.addressComponents;
26840     },
26841     
26842     address_component_from_google_geocode: function(address_components) 
26843     {
26844         var result = {};
26845         
26846         for (var i = 0; i < address_components.length; i++) {
26847             var component = address_components[i];
26848             if (component.types.indexOf("postal_code") >= 0) {
26849                 result.postalCode = component.short_name;
26850             } else if (component.types.indexOf("street_number") >= 0) {
26851                 result.streetNumber = component.short_name;
26852             } else if (component.types.indexOf("route") >= 0) {
26853                 result.streetName = component.short_name;
26854             } else if (component.types.indexOf("neighborhood") >= 0) {
26855                 result.city = component.short_name;
26856             } else if (component.types.indexOf("locality") >= 0) {
26857                 result.city = component.short_name;
26858             } else if (component.types.indexOf("sublocality") >= 0) {
26859                 result.district = component.short_name;
26860             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26861                 result.stateOrProvince = component.short_name;
26862             } else if (component.types.indexOf("country") >= 0) {
26863                 result.country = component.short_name;
26864             }
26865         }
26866         
26867         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26868         result.addressLine2 = "";
26869         return result;
26870     },
26871     
26872     setZoomLevel: function(zoom)
26873     {
26874         this.gMapContext.map.setZoom(zoom);
26875     },
26876     
26877     show: function()
26878     {
26879         if(!this.el){
26880             return;
26881         }
26882         
26883         this.el.show();
26884         
26885         this.resize();
26886         
26887         this.fireEvent('show', this);
26888     },
26889     
26890     hide: function()
26891     {
26892         if(!this.el){
26893             return;
26894         }
26895         
26896         this.el.hide();
26897         
26898         this.fireEvent('hide', this);
26899     }
26900     
26901 });
26902
26903 Roo.apply(Roo.bootstrap.LocationPicker, {
26904     
26905     OverlayView : function(map, options)
26906     {
26907         options = options || {};
26908         
26909         this.setMap(map);
26910     }
26911     
26912     
26913 });/*
26914  * - LGPL
26915  *
26916  * Alert
26917  * 
26918  */
26919
26920 /**
26921  * @class Roo.bootstrap.Alert
26922  * @extends Roo.bootstrap.Component
26923  * Bootstrap Alert class
26924  * @cfg {String} title The title of alert
26925  * @cfg {String} html The content of alert
26926  * @cfg {String} weight (  success | info | warning | danger )
26927  * @cfg {String} faicon font-awesomeicon
26928  * 
26929  * @constructor
26930  * Create a new alert
26931  * @param {Object} config The config object
26932  */
26933
26934
26935 Roo.bootstrap.Alert = function(config){
26936     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26937     
26938 };
26939
26940 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26941     
26942     title: '',
26943     html: '',
26944     weight: false,
26945     faicon: false,
26946     
26947     getAutoCreate : function()
26948     {
26949         
26950         var cfg = {
26951             tag : 'div',
26952             cls : 'alert',
26953             cn : [
26954                 {
26955                     tag : 'i',
26956                     cls : 'roo-alert-icon'
26957                     
26958                 },
26959                 {
26960                     tag : 'b',
26961                     cls : 'roo-alert-title',
26962                     html : this.title
26963                 },
26964                 {
26965                     tag : 'span',
26966                     cls : 'roo-alert-text',
26967                     html : this.html
26968                 }
26969             ]
26970         };
26971         
26972         if(this.faicon){
26973             cfg.cn[0].cls += ' fa ' + this.faicon;
26974         }
26975         
26976         if(this.weight){
26977             cfg.cls += ' alert-' + this.weight;
26978         }
26979         
26980         return cfg;
26981     },
26982     
26983     initEvents: function() 
26984     {
26985         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26986     },
26987     
26988     setTitle : function(str)
26989     {
26990         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26991     },
26992     
26993     setText : function(str)
26994     {
26995         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26996     },
26997     
26998     setWeight : function(weight)
26999     {
27000         if(this.weight){
27001             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27002         }
27003         
27004         this.weight = weight;
27005         
27006         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27007     },
27008     
27009     setIcon : function(icon)
27010     {
27011         if(this.faicon){
27012             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27013         }
27014         
27015         this.faicon = icon;
27016         
27017         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27018     },
27019     
27020     hide: function() 
27021     {
27022         this.el.hide();   
27023     },
27024     
27025     show: function() 
27026     {  
27027         this.el.show();   
27028     }
27029     
27030 });
27031
27032  
27033 /*
27034 * Licence: LGPL
27035 */
27036
27037 /**
27038  * @class Roo.bootstrap.UploadCropbox
27039  * @extends Roo.bootstrap.Component
27040  * Bootstrap UploadCropbox class
27041  * @cfg {String} emptyText show when image has been loaded
27042  * @cfg {String} rotateNotify show when image too small to rotate
27043  * @cfg {Number} errorTimeout default 3000
27044  * @cfg {Number} minWidth default 300
27045  * @cfg {Number} minHeight default 300
27046  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27047  * @cfg {Boolean} isDocument (true|false) default false
27048  * @cfg {String} url action url
27049  * @cfg {String} paramName default 'imageUpload'
27050  * @cfg {String} method default POST
27051  * @cfg {Boolean} loadMask (true|false) default true
27052  * @cfg {Boolean} loadingText default 'Loading...'
27053  * 
27054  * @constructor
27055  * Create a new UploadCropbox
27056  * @param {Object} config The config object
27057  */
27058
27059 Roo.bootstrap.UploadCropbox = function(config){
27060     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27061     
27062     this.addEvents({
27063         /**
27064          * @event beforeselectfile
27065          * Fire before select file
27066          * @param {Roo.bootstrap.UploadCropbox} this
27067          */
27068         "beforeselectfile" : true,
27069         /**
27070          * @event initial
27071          * Fire after initEvent
27072          * @param {Roo.bootstrap.UploadCropbox} this
27073          */
27074         "initial" : true,
27075         /**
27076          * @event crop
27077          * Fire after initEvent
27078          * @param {Roo.bootstrap.UploadCropbox} this
27079          * @param {String} data
27080          */
27081         "crop" : true,
27082         /**
27083          * @event prepare
27084          * Fire when preparing the file data
27085          * @param {Roo.bootstrap.UploadCropbox} this
27086          * @param {Object} file
27087          */
27088         "prepare" : true,
27089         /**
27090          * @event exception
27091          * Fire when get exception
27092          * @param {Roo.bootstrap.UploadCropbox} this
27093          * @param {XMLHttpRequest} xhr
27094          */
27095         "exception" : true,
27096         /**
27097          * @event beforeloadcanvas
27098          * Fire before load the canvas
27099          * @param {Roo.bootstrap.UploadCropbox} this
27100          * @param {String} src
27101          */
27102         "beforeloadcanvas" : true,
27103         /**
27104          * @event trash
27105          * Fire when trash image
27106          * @param {Roo.bootstrap.UploadCropbox} this
27107          */
27108         "trash" : true,
27109         /**
27110          * @event download
27111          * Fire when download the image
27112          * @param {Roo.bootstrap.UploadCropbox} this
27113          */
27114         "download" : true,
27115         /**
27116          * @event footerbuttonclick
27117          * Fire when footerbuttonclick
27118          * @param {Roo.bootstrap.UploadCropbox} this
27119          * @param {String} type
27120          */
27121         "footerbuttonclick" : true,
27122         /**
27123          * @event resize
27124          * Fire when resize
27125          * @param {Roo.bootstrap.UploadCropbox} this
27126          */
27127         "resize" : true,
27128         /**
27129          * @event rotate
27130          * Fire when rotate the image
27131          * @param {Roo.bootstrap.UploadCropbox} this
27132          * @param {String} pos
27133          */
27134         "rotate" : true,
27135         /**
27136          * @event inspect
27137          * Fire when inspect the file
27138          * @param {Roo.bootstrap.UploadCropbox} this
27139          * @param {Object} file
27140          */
27141         "inspect" : true,
27142         /**
27143          * @event upload
27144          * Fire when xhr upload the file
27145          * @param {Roo.bootstrap.UploadCropbox} this
27146          * @param {Object} data
27147          */
27148         "upload" : true,
27149         /**
27150          * @event arrange
27151          * Fire when arrange the file data
27152          * @param {Roo.bootstrap.UploadCropbox} this
27153          * @param {Object} formData
27154          */
27155         "arrange" : true
27156     });
27157     
27158     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27159 };
27160
27161 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27162     
27163     emptyText : 'Click to upload image',
27164     rotateNotify : 'Image is too small to rotate',
27165     errorTimeout : 3000,
27166     scale : 0,
27167     baseScale : 1,
27168     rotate : 0,
27169     dragable : false,
27170     pinching : false,
27171     mouseX : 0,
27172     mouseY : 0,
27173     cropData : false,
27174     minWidth : 300,
27175     minHeight : 300,
27176     file : false,
27177     exif : {},
27178     baseRotate : 1,
27179     cropType : 'image/jpeg',
27180     buttons : false,
27181     canvasLoaded : false,
27182     isDocument : false,
27183     method : 'POST',
27184     paramName : 'imageUpload',
27185     loadMask : true,
27186     loadingText : 'Loading...',
27187     maskEl : false,
27188     
27189     getAutoCreate : function()
27190     {
27191         var cfg = {
27192             tag : 'div',
27193             cls : 'roo-upload-cropbox',
27194             cn : [
27195                 {
27196                     tag : 'input',
27197                     cls : 'roo-upload-cropbox-selector',
27198                     type : 'file'
27199                 },
27200                 {
27201                     tag : 'div',
27202                     cls : 'roo-upload-cropbox-body',
27203                     style : 'cursor:pointer',
27204                     cn : [
27205                         {
27206                             tag : 'div',
27207                             cls : 'roo-upload-cropbox-preview'
27208                         },
27209                         {
27210                             tag : 'div',
27211                             cls : 'roo-upload-cropbox-thumb'
27212                         },
27213                         {
27214                             tag : 'div',
27215                             cls : 'roo-upload-cropbox-empty-notify',
27216                             html : this.emptyText
27217                         },
27218                         {
27219                             tag : 'div',
27220                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27221                             html : this.rotateNotify
27222                         }
27223                     ]
27224                 },
27225                 {
27226                     tag : 'div',
27227                     cls : 'roo-upload-cropbox-footer',
27228                     cn : {
27229                         tag : 'div',
27230                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27231                         cn : []
27232                     }
27233                 }
27234             ]
27235         };
27236         
27237         return cfg;
27238     },
27239     
27240     onRender : function(ct, position)
27241     {
27242         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27243         
27244         if (this.buttons.length) {
27245             
27246             Roo.each(this.buttons, function(bb) {
27247                 
27248                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27249                 
27250                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27251                 
27252             }, this);
27253         }
27254         
27255         if(this.loadMask){
27256             this.maskEl = this.el;
27257         }
27258     },
27259     
27260     initEvents : function()
27261     {
27262         this.urlAPI = (window.createObjectURL && window) || 
27263                                 (window.URL && URL.revokeObjectURL && URL) || 
27264                                 (window.webkitURL && webkitURL);
27265                         
27266         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27267         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27268         
27269         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27270         this.selectorEl.hide();
27271         
27272         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27273         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27274         
27275         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27276         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27277         this.thumbEl.hide();
27278         
27279         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27280         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27281         
27282         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27283         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27284         this.errorEl.hide();
27285         
27286         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27287         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27288         this.footerEl.hide();
27289         
27290         this.setThumbBoxSize();
27291         
27292         this.bind();
27293         
27294         this.resize();
27295         
27296         this.fireEvent('initial', this);
27297     },
27298
27299     bind : function()
27300     {
27301         var _this = this;
27302         
27303         window.addEventListener("resize", function() { _this.resize(); } );
27304         
27305         this.bodyEl.on('click', this.beforeSelectFile, this);
27306         
27307         if(Roo.isTouch){
27308             this.bodyEl.on('touchstart', this.onTouchStart, this);
27309             this.bodyEl.on('touchmove', this.onTouchMove, this);
27310             this.bodyEl.on('touchend', this.onTouchEnd, this);
27311         }
27312         
27313         if(!Roo.isTouch){
27314             this.bodyEl.on('mousedown', this.onMouseDown, this);
27315             this.bodyEl.on('mousemove', this.onMouseMove, this);
27316             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27317             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27318             Roo.get(document).on('mouseup', this.onMouseUp, this);
27319         }
27320         
27321         this.selectorEl.on('change', this.onFileSelected, this);
27322     },
27323     
27324     reset : function()
27325     {    
27326         this.scale = 0;
27327         this.baseScale = 1;
27328         this.rotate = 0;
27329         this.baseRotate = 1;
27330         this.dragable = false;
27331         this.pinching = false;
27332         this.mouseX = 0;
27333         this.mouseY = 0;
27334         this.cropData = false;
27335         this.notifyEl.dom.innerHTML = this.emptyText;
27336         
27337         this.selectorEl.dom.value = '';
27338         
27339     },
27340     
27341     resize : function()
27342     {
27343         if(this.fireEvent('resize', this) != false){
27344             this.setThumbBoxPosition();
27345             this.setCanvasPosition();
27346         }
27347     },
27348     
27349     onFooterButtonClick : function(e, el, o, type)
27350     {
27351         switch (type) {
27352             case 'rotate-left' :
27353                 this.onRotateLeft(e);
27354                 break;
27355             case 'rotate-right' :
27356                 this.onRotateRight(e);
27357                 break;
27358             case 'picture' :
27359                 this.beforeSelectFile(e);
27360                 break;
27361             case 'trash' :
27362                 this.trash(e);
27363                 break;
27364             case 'crop' :
27365                 this.crop(e);
27366                 break;
27367             case 'download' :
27368                 this.download(e);
27369                 break;
27370             default :
27371                 break;
27372         }
27373         
27374         this.fireEvent('footerbuttonclick', this, type);
27375     },
27376     
27377     beforeSelectFile : function(e)
27378     {
27379         e.preventDefault();
27380         
27381         if(this.fireEvent('beforeselectfile', this) != false){
27382             this.selectorEl.dom.click();
27383         }
27384     },
27385     
27386     onFileSelected : function(e)
27387     {
27388         e.preventDefault();
27389         
27390         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27391             return;
27392         }
27393         
27394         var file = this.selectorEl.dom.files[0];
27395         
27396         if(this.fireEvent('inspect', this, file) != false){
27397             this.prepare(file);
27398         }
27399         
27400     },
27401     
27402     trash : function(e)
27403     {
27404         this.fireEvent('trash', this);
27405     },
27406     
27407     download : function(e)
27408     {
27409         this.fireEvent('download', this);
27410     },
27411     
27412     loadCanvas : function(src)
27413     {   
27414         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27415             
27416             this.reset();
27417             
27418             this.imageEl = document.createElement('img');
27419             
27420             var _this = this;
27421             
27422             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27423             
27424             this.imageEl.src = src;
27425         }
27426     },
27427     
27428     onLoadCanvas : function()
27429     {   
27430         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27431         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27432         
27433         this.bodyEl.un('click', this.beforeSelectFile, this);
27434         
27435         this.notifyEl.hide();
27436         this.thumbEl.show();
27437         this.footerEl.show();
27438         
27439         this.baseRotateLevel();
27440         
27441         if(this.isDocument){
27442             this.setThumbBoxSize();
27443         }
27444         
27445         this.setThumbBoxPosition();
27446         
27447         this.baseScaleLevel();
27448         
27449         this.draw();
27450         
27451         this.resize();
27452         
27453         this.canvasLoaded = true;
27454         
27455         if(this.loadMask){
27456             this.maskEl.unmask();
27457         }
27458         
27459     },
27460     
27461     setCanvasPosition : function()
27462     {   
27463         if(!this.canvasEl){
27464             return;
27465         }
27466         
27467         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27468         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27469         
27470         this.previewEl.setLeft(pw);
27471         this.previewEl.setTop(ph);
27472         
27473     },
27474     
27475     onMouseDown : function(e)
27476     {   
27477         e.stopEvent();
27478         
27479         this.dragable = true;
27480         this.pinching = false;
27481         
27482         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27483             this.dragable = false;
27484             return;
27485         }
27486         
27487         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27488         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27489         
27490     },
27491     
27492     onMouseMove : function(e)
27493     {   
27494         e.stopEvent();
27495         
27496         if(!this.canvasLoaded){
27497             return;
27498         }
27499         
27500         if (!this.dragable){
27501             return;
27502         }
27503         
27504         var minX = Math.ceil(this.thumbEl.getLeft(true));
27505         var minY = Math.ceil(this.thumbEl.getTop(true));
27506         
27507         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27508         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27509         
27510         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27511         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27512         
27513         x = x - this.mouseX;
27514         y = y - this.mouseY;
27515         
27516         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27517         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27518         
27519         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27520         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27521         
27522         this.previewEl.setLeft(bgX);
27523         this.previewEl.setTop(bgY);
27524         
27525         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27526         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27527     },
27528     
27529     onMouseUp : function(e)
27530     {   
27531         e.stopEvent();
27532         
27533         this.dragable = false;
27534     },
27535     
27536     onMouseWheel : function(e)
27537     {   
27538         e.stopEvent();
27539         
27540         this.startScale = this.scale;
27541         
27542         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27543         
27544         if(!this.zoomable()){
27545             this.scale = this.startScale;
27546             return;
27547         }
27548         
27549         this.draw();
27550         
27551         return;
27552     },
27553     
27554     zoomable : function()
27555     {
27556         var minScale = this.thumbEl.getWidth() / this.minWidth;
27557         
27558         if(this.minWidth < this.minHeight){
27559             minScale = this.thumbEl.getHeight() / this.minHeight;
27560         }
27561         
27562         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27563         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27564         
27565         if(
27566                 this.isDocument &&
27567                 (this.rotate == 0 || this.rotate == 180) && 
27568                 (
27569                     width > this.imageEl.OriginWidth || 
27570                     height > this.imageEl.OriginHeight ||
27571                     (width < this.minWidth && height < this.minHeight)
27572                 )
27573         ){
27574             return false;
27575         }
27576         
27577         if(
27578                 this.isDocument &&
27579                 (this.rotate == 90 || this.rotate == 270) && 
27580                 (
27581                     width > this.imageEl.OriginWidth || 
27582                     height > this.imageEl.OriginHeight ||
27583                     (width < this.minHeight && height < this.minWidth)
27584                 )
27585         ){
27586             return false;
27587         }
27588         
27589         if(
27590                 !this.isDocument &&
27591                 (this.rotate == 0 || this.rotate == 180) && 
27592                 (
27593                     width < this.minWidth || 
27594                     width > this.imageEl.OriginWidth || 
27595                     height < this.minHeight || 
27596                     height > this.imageEl.OriginHeight
27597                 )
27598         ){
27599             return false;
27600         }
27601         
27602         if(
27603                 !this.isDocument &&
27604                 (this.rotate == 90 || this.rotate == 270) && 
27605                 (
27606                     width < this.minHeight || 
27607                     width > this.imageEl.OriginWidth || 
27608                     height < this.minWidth || 
27609                     height > this.imageEl.OriginHeight
27610                 )
27611         ){
27612             return false;
27613         }
27614         
27615         return true;
27616         
27617     },
27618     
27619     onRotateLeft : function(e)
27620     {   
27621         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27622             
27623             var minScale = this.thumbEl.getWidth() / this.minWidth;
27624             
27625             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27626             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27627             
27628             this.startScale = this.scale;
27629             
27630             while (this.getScaleLevel() < minScale){
27631             
27632                 this.scale = this.scale + 1;
27633                 
27634                 if(!this.zoomable()){
27635                     break;
27636                 }
27637                 
27638                 if(
27639                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27640                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27641                 ){
27642                     continue;
27643                 }
27644                 
27645                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27646
27647                 this.draw();
27648                 
27649                 return;
27650             }
27651             
27652             this.scale = this.startScale;
27653             
27654             this.onRotateFail();
27655             
27656             return false;
27657         }
27658         
27659         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27660
27661         if(this.isDocument){
27662             this.setThumbBoxSize();
27663             this.setThumbBoxPosition();
27664             this.setCanvasPosition();
27665         }
27666         
27667         this.draw();
27668         
27669         this.fireEvent('rotate', this, 'left');
27670         
27671     },
27672     
27673     onRotateRight : function(e)
27674     {
27675         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27676             
27677             var minScale = this.thumbEl.getWidth() / this.minWidth;
27678         
27679             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27680             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27681             
27682             this.startScale = this.scale;
27683             
27684             while (this.getScaleLevel() < minScale){
27685             
27686                 this.scale = this.scale + 1;
27687                 
27688                 if(!this.zoomable()){
27689                     break;
27690                 }
27691                 
27692                 if(
27693                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27694                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27695                 ){
27696                     continue;
27697                 }
27698                 
27699                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27700
27701                 this.draw();
27702                 
27703                 return;
27704             }
27705             
27706             this.scale = this.startScale;
27707             
27708             this.onRotateFail();
27709             
27710             return false;
27711         }
27712         
27713         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27714
27715         if(this.isDocument){
27716             this.setThumbBoxSize();
27717             this.setThumbBoxPosition();
27718             this.setCanvasPosition();
27719         }
27720         
27721         this.draw();
27722         
27723         this.fireEvent('rotate', this, 'right');
27724     },
27725     
27726     onRotateFail : function()
27727     {
27728         this.errorEl.show(true);
27729         
27730         var _this = this;
27731         
27732         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27733     },
27734     
27735     draw : function()
27736     {
27737         this.previewEl.dom.innerHTML = '';
27738         
27739         var canvasEl = document.createElement("canvas");
27740         
27741         var contextEl = canvasEl.getContext("2d");
27742         
27743         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27744         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27745         var center = this.imageEl.OriginWidth / 2;
27746         
27747         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27748             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27749             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27750             center = this.imageEl.OriginHeight / 2;
27751         }
27752         
27753         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27754         
27755         contextEl.translate(center, center);
27756         contextEl.rotate(this.rotate * Math.PI / 180);
27757
27758         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27759         
27760         this.canvasEl = document.createElement("canvas");
27761         
27762         this.contextEl = this.canvasEl.getContext("2d");
27763         
27764         switch (this.rotate) {
27765             case 0 :
27766                 
27767                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27768                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27769                 
27770                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27771                 
27772                 break;
27773             case 90 : 
27774                 
27775                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27776                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27777                 
27778                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27779                     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);
27780                     break;
27781                 }
27782                 
27783                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27784                 
27785                 break;
27786             case 180 :
27787                 
27788                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27789                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27790                 
27791                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27792                     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);
27793                     break;
27794                 }
27795                 
27796                 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);
27797                 
27798                 break;
27799             case 270 :
27800                 
27801                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27802                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27803         
27804                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27805                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27806                     break;
27807                 }
27808                 
27809                 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);
27810                 
27811                 break;
27812             default : 
27813                 break;
27814         }
27815         
27816         this.previewEl.appendChild(this.canvasEl);
27817         
27818         this.setCanvasPosition();
27819     },
27820     
27821     crop : function()
27822     {
27823         if(!this.canvasLoaded){
27824             return;
27825         }
27826         
27827         var imageCanvas = document.createElement("canvas");
27828         
27829         var imageContext = imageCanvas.getContext("2d");
27830         
27831         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27832         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27833         
27834         var center = imageCanvas.width / 2;
27835         
27836         imageContext.translate(center, center);
27837         
27838         imageContext.rotate(this.rotate * Math.PI / 180);
27839         
27840         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27841         
27842         var canvas = document.createElement("canvas");
27843         
27844         var context = canvas.getContext("2d");
27845                 
27846         canvas.width = this.minWidth;
27847         canvas.height = this.minHeight;
27848
27849         switch (this.rotate) {
27850             case 0 :
27851                 
27852                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27853                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27854                 
27855                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27856                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27857                 
27858                 var targetWidth = this.minWidth - 2 * x;
27859                 var targetHeight = this.minHeight - 2 * y;
27860                 
27861                 var scale = 1;
27862                 
27863                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27864                     scale = targetWidth / width;
27865                 }
27866                 
27867                 if(x > 0 && y == 0){
27868                     scale = targetHeight / height;
27869                 }
27870                 
27871                 if(x > 0 && y > 0){
27872                     scale = targetWidth / width;
27873                     
27874                     if(width < height){
27875                         scale = targetHeight / height;
27876                     }
27877                 }
27878                 
27879                 context.scale(scale, scale);
27880                 
27881                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27882                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27883
27884                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27885                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27886
27887                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27888                 
27889                 break;
27890             case 90 : 
27891                 
27892                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27893                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27894                 
27895                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27896                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27897                 
27898                 var targetWidth = this.minWidth - 2 * x;
27899                 var targetHeight = this.minHeight - 2 * y;
27900                 
27901                 var scale = 1;
27902                 
27903                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27904                     scale = targetWidth / width;
27905                 }
27906                 
27907                 if(x > 0 && y == 0){
27908                     scale = targetHeight / height;
27909                 }
27910                 
27911                 if(x > 0 && y > 0){
27912                     scale = targetWidth / width;
27913                     
27914                     if(width < height){
27915                         scale = targetHeight / height;
27916                     }
27917                 }
27918                 
27919                 context.scale(scale, scale);
27920                 
27921                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27922                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27923
27924                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27925                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27926                 
27927                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27928                 
27929                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27930                 
27931                 break;
27932             case 180 :
27933                 
27934                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27935                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27936                 
27937                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27938                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27939                 
27940                 var targetWidth = this.minWidth - 2 * x;
27941                 var targetHeight = this.minHeight - 2 * y;
27942                 
27943                 var scale = 1;
27944                 
27945                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27946                     scale = targetWidth / width;
27947                 }
27948                 
27949                 if(x > 0 && y == 0){
27950                     scale = targetHeight / height;
27951                 }
27952                 
27953                 if(x > 0 && y > 0){
27954                     scale = targetWidth / width;
27955                     
27956                     if(width < height){
27957                         scale = targetHeight / height;
27958                     }
27959                 }
27960                 
27961                 context.scale(scale, scale);
27962                 
27963                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27964                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27965
27966                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27967                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27968
27969                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27970                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27971                 
27972                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27973                 
27974                 break;
27975             case 270 :
27976                 
27977                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27978                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27979                 
27980                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27981                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27982                 
27983                 var targetWidth = this.minWidth - 2 * x;
27984                 var targetHeight = this.minHeight - 2 * y;
27985                 
27986                 var scale = 1;
27987                 
27988                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27989                     scale = targetWidth / width;
27990                 }
27991                 
27992                 if(x > 0 && y == 0){
27993                     scale = targetHeight / height;
27994                 }
27995                 
27996                 if(x > 0 && y > 0){
27997                     scale = targetWidth / width;
27998                     
27999                     if(width < height){
28000                         scale = targetHeight / height;
28001                     }
28002                 }
28003                 
28004                 context.scale(scale, scale);
28005                 
28006                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28007                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28008
28009                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28010                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28011                 
28012                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28013                 
28014                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28015                 
28016                 break;
28017             default : 
28018                 break;
28019         }
28020         
28021         this.cropData = canvas.toDataURL(this.cropType);
28022         
28023         if(this.fireEvent('crop', this, this.cropData) !== false){
28024             this.process(this.file, this.cropData);
28025         }
28026         
28027         return;
28028         
28029     },
28030     
28031     setThumbBoxSize : function()
28032     {
28033         var width, height;
28034         
28035         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28036             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28037             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28038             
28039             this.minWidth = width;
28040             this.minHeight = height;
28041             
28042             if(this.rotate == 90 || this.rotate == 270){
28043                 this.minWidth = height;
28044                 this.minHeight = width;
28045             }
28046         }
28047         
28048         height = 300;
28049         width = Math.ceil(this.minWidth * height / this.minHeight);
28050         
28051         if(this.minWidth > this.minHeight){
28052             width = 300;
28053             height = Math.ceil(this.minHeight * width / this.minWidth);
28054         }
28055         
28056         this.thumbEl.setStyle({
28057             width : width + 'px',
28058             height : height + 'px'
28059         });
28060
28061         return;
28062             
28063     },
28064     
28065     setThumbBoxPosition : function()
28066     {
28067         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28068         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28069         
28070         this.thumbEl.setLeft(x);
28071         this.thumbEl.setTop(y);
28072         
28073     },
28074     
28075     baseRotateLevel : function()
28076     {
28077         this.baseRotate = 1;
28078         
28079         if(
28080                 typeof(this.exif) != 'undefined' &&
28081                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28082                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28083         ){
28084             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28085         }
28086         
28087         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28088         
28089     },
28090     
28091     baseScaleLevel : function()
28092     {
28093         var width, height;
28094         
28095         if(this.isDocument){
28096             
28097             if(this.baseRotate == 6 || this.baseRotate == 8){
28098             
28099                 height = this.thumbEl.getHeight();
28100                 this.baseScale = height / this.imageEl.OriginWidth;
28101
28102                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28103                     width = this.thumbEl.getWidth();
28104                     this.baseScale = width / this.imageEl.OriginHeight;
28105                 }
28106
28107                 return;
28108             }
28109
28110             height = this.thumbEl.getHeight();
28111             this.baseScale = height / this.imageEl.OriginHeight;
28112
28113             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28114                 width = this.thumbEl.getWidth();
28115                 this.baseScale = width / this.imageEl.OriginWidth;
28116             }
28117
28118             return;
28119         }
28120         
28121         if(this.baseRotate == 6 || this.baseRotate == 8){
28122             
28123             width = this.thumbEl.getHeight();
28124             this.baseScale = width / this.imageEl.OriginHeight;
28125             
28126             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28127                 height = this.thumbEl.getWidth();
28128                 this.baseScale = height / this.imageEl.OriginHeight;
28129             }
28130             
28131             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28132                 height = this.thumbEl.getWidth();
28133                 this.baseScale = height / this.imageEl.OriginHeight;
28134                 
28135                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28136                     width = this.thumbEl.getHeight();
28137                     this.baseScale = width / this.imageEl.OriginWidth;
28138                 }
28139             }
28140             
28141             return;
28142         }
28143         
28144         width = this.thumbEl.getWidth();
28145         this.baseScale = width / this.imageEl.OriginWidth;
28146         
28147         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28148             height = this.thumbEl.getHeight();
28149             this.baseScale = height / this.imageEl.OriginHeight;
28150         }
28151         
28152         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28153             
28154             height = this.thumbEl.getHeight();
28155             this.baseScale = height / this.imageEl.OriginHeight;
28156             
28157             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28158                 width = this.thumbEl.getWidth();
28159                 this.baseScale = width / this.imageEl.OriginWidth;
28160             }
28161             
28162         }
28163         
28164         return;
28165     },
28166     
28167     getScaleLevel : function()
28168     {
28169         return this.baseScale * Math.pow(1.1, this.scale);
28170     },
28171     
28172     onTouchStart : function(e)
28173     {
28174         if(!this.canvasLoaded){
28175             this.beforeSelectFile(e);
28176             return;
28177         }
28178         
28179         var touches = e.browserEvent.touches;
28180         
28181         if(!touches){
28182             return;
28183         }
28184         
28185         if(touches.length == 1){
28186             this.onMouseDown(e);
28187             return;
28188         }
28189         
28190         if(touches.length != 2){
28191             return;
28192         }
28193         
28194         var coords = [];
28195         
28196         for(var i = 0, finger; finger = touches[i]; i++){
28197             coords.push(finger.pageX, finger.pageY);
28198         }
28199         
28200         var x = Math.pow(coords[0] - coords[2], 2);
28201         var y = Math.pow(coords[1] - coords[3], 2);
28202         
28203         this.startDistance = Math.sqrt(x + y);
28204         
28205         this.startScale = this.scale;
28206         
28207         this.pinching = true;
28208         this.dragable = false;
28209         
28210     },
28211     
28212     onTouchMove : function(e)
28213     {
28214         if(!this.pinching && !this.dragable){
28215             return;
28216         }
28217         
28218         var touches = e.browserEvent.touches;
28219         
28220         if(!touches){
28221             return;
28222         }
28223         
28224         if(this.dragable){
28225             this.onMouseMove(e);
28226             return;
28227         }
28228         
28229         var coords = [];
28230         
28231         for(var i = 0, finger; finger = touches[i]; i++){
28232             coords.push(finger.pageX, finger.pageY);
28233         }
28234         
28235         var x = Math.pow(coords[0] - coords[2], 2);
28236         var y = Math.pow(coords[1] - coords[3], 2);
28237         
28238         this.endDistance = Math.sqrt(x + y);
28239         
28240         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28241         
28242         if(!this.zoomable()){
28243             this.scale = this.startScale;
28244             return;
28245         }
28246         
28247         this.draw();
28248         
28249     },
28250     
28251     onTouchEnd : function(e)
28252     {
28253         this.pinching = false;
28254         this.dragable = false;
28255         
28256     },
28257     
28258     process : function(file, crop)
28259     {
28260         if(this.loadMask){
28261             this.maskEl.mask(this.loadingText);
28262         }
28263         
28264         this.xhr = new XMLHttpRequest();
28265         
28266         file.xhr = this.xhr;
28267
28268         this.xhr.open(this.method, this.url, true);
28269         
28270         var headers = {
28271             "Accept": "application/json",
28272             "Cache-Control": "no-cache",
28273             "X-Requested-With": "XMLHttpRequest"
28274         };
28275         
28276         for (var headerName in headers) {
28277             var headerValue = headers[headerName];
28278             if (headerValue) {
28279                 this.xhr.setRequestHeader(headerName, headerValue);
28280             }
28281         }
28282         
28283         var _this = this;
28284         
28285         this.xhr.onload = function()
28286         {
28287             _this.xhrOnLoad(_this.xhr);
28288         }
28289         
28290         this.xhr.onerror = function()
28291         {
28292             _this.xhrOnError(_this.xhr);
28293         }
28294         
28295         var formData = new FormData();
28296
28297         formData.append('returnHTML', 'NO');
28298         
28299         if(crop){
28300             formData.append('crop', crop);
28301         }
28302         
28303         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28304             formData.append(this.paramName, file, file.name);
28305         }
28306         
28307         if(typeof(file.filename) != 'undefined'){
28308             formData.append('filename', file.filename);
28309         }
28310         
28311         if(typeof(file.mimetype) != 'undefined'){
28312             formData.append('mimetype', file.mimetype);
28313         }
28314         
28315         if(this.fireEvent('arrange', this, formData) != false){
28316             this.xhr.send(formData);
28317         };
28318     },
28319     
28320     xhrOnLoad : function(xhr)
28321     {
28322         if(this.loadMask){
28323             this.maskEl.unmask();
28324         }
28325         
28326         if (xhr.readyState !== 4) {
28327             this.fireEvent('exception', this, xhr);
28328             return;
28329         }
28330
28331         var response = Roo.decode(xhr.responseText);
28332         
28333         if(!response.success){
28334             this.fireEvent('exception', this, xhr);
28335             return;
28336         }
28337         
28338         var response = Roo.decode(xhr.responseText);
28339         
28340         this.fireEvent('upload', this, response);
28341         
28342     },
28343     
28344     xhrOnError : function()
28345     {
28346         if(this.loadMask){
28347             this.maskEl.unmask();
28348         }
28349         
28350         Roo.log('xhr on error');
28351         
28352         var response = Roo.decode(xhr.responseText);
28353           
28354         Roo.log(response);
28355         
28356     },
28357     
28358     prepare : function(file)
28359     {   
28360         if(this.loadMask){
28361             this.maskEl.mask(this.loadingText);
28362         }
28363         
28364         this.file = false;
28365         this.exif = {};
28366         
28367         if(typeof(file) === 'string'){
28368             this.loadCanvas(file);
28369             return;
28370         }
28371         
28372         if(!file || !this.urlAPI){
28373             return;
28374         }
28375         
28376         this.file = file;
28377         this.cropType = file.type;
28378         
28379         var _this = this;
28380         
28381         if(this.fireEvent('prepare', this, this.file) != false){
28382             
28383             var reader = new FileReader();
28384             
28385             reader.onload = function (e) {
28386                 if (e.target.error) {
28387                     Roo.log(e.target.error);
28388                     return;
28389                 }
28390                 
28391                 var buffer = e.target.result,
28392                     dataView = new DataView(buffer),
28393                     offset = 2,
28394                     maxOffset = dataView.byteLength - 4,
28395                     markerBytes,
28396                     markerLength;
28397                 
28398                 if (dataView.getUint16(0) === 0xffd8) {
28399                     while (offset < maxOffset) {
28400                         markerBytes = dataView.getUint16(offset);
28401                         
28402                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28403                             markerLength = dataView.getUint16(offset + 2) + 2;
28404                             if (offset + markerLength > dataView.byteLength) {
28405                                 Roo.log('Invalid meta data: Invalid segment size.');
28406                                 break;
28407                             }
28408                             
28409                             if(markerBytes == 0xffe1){
28410                                 _this.parseExifData(
28411                                     dataView,
28412                                     offset,
28413                                     markerLength
28414                                 );
28415                             }
28416                             
28417                             offset += markerLength;
28418                             
28419                             continue;
28420                         }
28421                         
28422                         break;
28423                     }
28424                     
28425                 }
28426                 
28427                 var url = _this.urlAPI.createObjectURL(_this.file);
28428                 
28429                 _this.loadCanvas(url);
28430                 
28431                 return;
28432             }
28433             
28434             reader.readAsArrayBuffer(this.file);
28435             
28436         }
28437         
28438     },
28439     
28440     parseExifData : function(dataView, offset, length)
28441     {
28442         var tiffOffset = offset + 10,
28443             littleEndian,
28444             dirOffset;
28445     
28446         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28447             // No Exif data, might be XMP data instead
28448             return;
28449         }
28450         
28451         // Check for the ASCII code for "Exif" (0x45786966):
28452         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28453             // No Exif data, might be XMP data instead
28454             return;
28455         }
28456         if (tiffOffset + 8 > dataView.byteLength) {
28457             Roo.log('Invalid Exif data: Invalid segment size.');
28458             return;
28459         }
28460         // Check for the two null bytes:
28461         if (dataView.getUint16(offset + 8) !== 0x0000) {
28462             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28463             return;
28464         }
28465         // Check the byte alignment:
28466         switch (dataView.getUint16(tiffOffset)) {
28467         case 0x4949:
28468             littleEndian = true;
28469             break;
28470         case 0x4D4D:
28471             littleEndian = false;
28472             break;
28473         default:
28474             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28475             return;
28476         }
28477         // Check for the TIFF tag marker (0x002A):
28478         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28479             Roo.log('Invalid Exif data: Missing TIFF marker.');
28480             return;
28481         }
28482         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28483         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28484         
28485         this.parseExifTags(
28486             dataView,
28487             tiffOffset,
28488             tiffOffset + dirOffset,
28489             littleEndian
28490         );
28491     },
28492     
28493     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28494     {
28495         var tagsNumber,
28496             dirEndOffset,
28497             i;
28498         if (dirOffset + 6 > dataView.byteLength) {
28499             Roo.log('Invalid Exif data: Invalid directory offset.');
28500             return;
28501         }
28502         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28503         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28504         if (dirEndOffset + 4 > dataView.byteLength) {
28505             Roo.log('Invalid Exif data: Invalid directory size.');
28506             return;
28507         }
28508         for (i = 0; i < tagsNumber; i += 1) {
28509             this.parseExifTag(
28510                 dataView,
28511                 tiffOffset,
28512                 dirOffset + 2 + 12 * i, // tag offset
28513                 littleEndian
28514             );
28515         }
28516         // Return the offset to the next directory:
28517         return dataView.getUint32(dirEndOffset, littleEndian);
28518     },
28519     
28520     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28521     {
28522         var tag = dataView.getUint16(offset, littleEndian);
28523         
28524         this.exif[tag] = this.getExifValue(
28525             dataView,
28526             tiffOffset,
28527             offset,
28528             dataView.getUint16(offset + 2, littleEndian), // tag type
28529             dataView.getUint32(offset + 4, littleEndian), // tag length
28530             littleEndian
28531         );
28532     },
28533     
28534     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28535     {
28536         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28537             tagSize,
28538             dataOffset,
28539             values,
28540             i,
28541             str,
28542             c;
28543     
28544         if (!tagType) {
28545             Roo.log('Invalid Exif data: Invalid tag type.');
28546             return;
28547         }
28548         
28549         tagSize = tagType.size * length;
28550         // Determine if the value is contained in the dataOffset bytes,
28551         // or if the value at the dataOffset is a pointer to the actual data:
28552         dataOffset = tagSize > 4 ?
28553                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28554         if (dataOffset + tagSize > dataView.byteLength) {
28555             Roo.log('Invalid Exif data: Invalid data offset.');
28556             return;
28557         }
28558         if (length === 1) {
28559             return tagType.getValue(dataView, dataOffset, littleEndian);
28560         }
28561         values = [];
28562         for (i = 0; i < length; i += 1) {
28563             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28564         }
28565         
28566         if (tagType.ascii) {
28567             str = '';
28568             // Concatenate the chars:
28569             for (i = 0; i < values.length; i += 1) {
28570                 c = values[i];
28571                 // Ignore the terminating NULL byte(s):
28572                 if (c === '\u0000') {
28573                     break;
28574                 }
28575                 str += c;
28576             }
28577             return str;
28578         }
28579         return values;
28580     }
28581     
28582 });
28583
28584 Roo.apply(Roo.bootstrap.UploadCropbox, {
28585     tags : {
28586         'Orientation': 0x0112
28587     },
28588     
28589     Orientation: {
28590             1: 0, //'top-left',
28591 //            2: 'top-right',
28592             3: 180, //'bottom-right',
28593 //            4: 'bottom-left',
28594 //            5: 'left-top',
28595             6: 90, //'right-top',
28596 //            7: 'right-bottom',
28597             8: 270 //'left-bottom'
28598     },
28599     
28600     exifTagTypes : {
28601         // byte, 8-bit unsigned int:
28602         1: {
28603             getValue: function (dataView, dataOffset) {
28604                 return dataView.getUint8(dataOffset);
28605             },
28606             size: 1
28607         },
28608         // ascii, 8-bit byte:
28609         2: {
28610             getValue: function (dataView, dataOffset) {
28611                 return String.fromCharCode(dataView.getUint8(dataOffset));
28612             },
28613             size: 1,
28614             ascii: true
28615         },
28616         // short, 16 bit int:
28617         3: {
28618             getValue: function (dataView, dataOffset, littleEndian) {
28619                 return dataView.getUint16(dataOffset, littleEndian);
28620             },
28621             size: 2
28622         },
28623         // long, 32 bit int:
28624         4: {
28625             getValue: function (dataView, dataOffset, littleEndian) {
28626                 return dataView.getUint32(dataOffset, littleEndian);
28627             },
28628             size: 4
28629         },
28630         // rational = two long values, first is numerator, second is denominator:
28631         5: {
28632             getValue: function (dataView, dataOffset, littleEndian) {
28633                 return dataView.getUint32(dataOffset, littleEndian) /
28634                     dataView.getUint32(dataOffset + 4, littleEndian);
28635             },
28636             size: 8
28637         },
28638         // slong, 32 bit signed int:
28639         9: {
28640             getValue: function (dataView, dataOffset, littleEndian) {
28641                 return dataView.getInt32(dataOffset, littleEndian);
28642             },
28643             size: 4
28644         },
28645         // srational, two slongs, first is numerator, second is denominator:
28646         10: {
28647             getValue: function (dataView, dataOffset, littleEndian) {
28648                 return dataView.getInt32(dataOffset, littleEndian) /
28649                     dataView.getInt32(dataOffset + 4, littleEndian);
28650             },
28651             size: 8
28652         }
28653     },
28654     
28655     footer : {
28656         STANDARD : [
28657             {
28658                 tag : 'div',
28659                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28660                 action : 'rotate-left',
28661                 cn : [
28662                     {
28663                         tag : 'button',
28664                         cls : 'btn btn-default',
28665                         html : '<i class="fa fa-undo"></i>'
28666                     }
28667                 ]
28668             },
28669             {
28670                 tag : 'div',
28671                 cls : 'btn-group roo-upload-cropbox-picture',
28672                 action : 'picture',
28673                 cn : [
28674                     {
28675                         tag : 'button',
28676                         cls : 'btn btn-default',
28677                         html : '<i class="fa fa-picture-o"></i>'
28678                     }
28679                 ]
28680             },
28681             {
28682                 tag : 'div',
28683                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28684                 action : 'rotate-right',
28685                 cn : [
28686                     {
28687                         tag : 'button',
28688                         cls : 'btn btn-default',
28689                         html : '<i class="fa fa-repeat"></i>'
28690                     }
28691                 ]
28692             }
28693         ],
28694         DOCUMENT : [
28695             {
28696                 tag : 'div',
28697                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28698                 action : 'rotate-left',
28699                 cn : [
28700                     {
28701                         tag : 'button',
28702                         cls : 'btn btn-default',
28703                         html : '<i class="fa fa-undo"></i>'
28704                     }
28705                 ]
28706             },
28707             {
28708                 tag : 'div',
28709                 cls : 'btn-group roo-upload-cropbox-download',
28710                 action : 'download',
28711                 cn : [
28712                     {
28713                         tag : 'button',
28714                         cls : 'btn btn-default',
28715                         html : '<i class="fa fa-download"></i>'
28716                     }
28717                 ]
28718             },
28719             {
28720                 tag : 'div',
28721                 cls : 'btn-group roo-upload-cropbox-crop',
28722                 action : 'crop',
28723                 cn : [
28724                     {
28725                         tag : 'button',
28726                         cls : 'btn btn-default',
28727                         html : '<i class="fa fa-crop"></i>'
28728                     }
28729                 ]
28730             },
28731             {
28732                 tag : 'div',
28733                 cls : 'btn-group roo-upload-cropbox-trash',
28734                 action : 'trash',
28735                 cn : [
28736                     {
28737                         tag : 'button',
28738                         cls : 'btn btn-default',
28739                         html : '<i class="fa fa-trash"></i>'
28740                     }
28741                 ]
28742             },
28743             {
28744                 tag : 'div',
28745                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28746                 action : 'rotate-right',
28747                 cn : [
28748                     {
28749                         tag : 'button',
28750                         cls : 'btn btn-default',
28751                         html : '<i class="fa fa-repeat"></i>'
28752                     }
28753                 ]
28754             }
28755         ],
28756         ROTATOR : [
28757             {
28758                 tag : 'div',
28759                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28760                 action : 'rotate-left',
28761                 cn : [
28762                     {
28763                         tag : 'button',
28764                         cls : 'btn btn-default',
28765                         html : '<i class="fa fa-undo"></i>'
28766                     }
28767                 ]
28768             },
28769             {
28770                 tag : 'div',
28771                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28772                 action : 'rotate-right',
28773                 cn : [
28774                     {
28775                         tag : 'button',
28776                         cls : 'btn btn-default',
28777                         html : '<i class="fa fa-repeat"></i>'
28778                     }
28779                 ]
28780             }
28781         ]
28782     }
28783 });
28784
28785 /*
28786 * Licence: LGPL
28787 */
28788
28789 /**
28790  * @class Roo.bootstrap.DocumentManager
28791  * @extends Roo.bootstrap.Component
28792  * Bootstrap DocumentManager class
28793  * @cfg {String} paramName default 'imageUpload'
28794  * @cfg {String} toolTipName default 'filename'
28795  * @cfg {String} method default POST
28796  * @cfg {String} url action url
28797  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28798  * @cfg {Boolean} multiple multiple upload default true
28799  * @cfg {Number} thumbSize default 300
28800  * @cfg {String} fieldLabel
28801  * @cfg {Number} labelWidth default 4
28802  * @cfg {String} labelAlign (left|top) default left
28803  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28804 * @cfg {Number} labellg set the width of label (1-12)
28805  * @cfg {Number} labelmd set the width of label (1-12)
28806  * @cfg {Number} labelsm set the width of label (1-12)
28807  * @cfg {Number} labelxs set the width of label (1-12)
28808  * 
28809  * @constructor
28810  * Create a new DocumentManager
28811  * @param {Object} config The config object
28812  */
28813
28814 Roo.bootstrap.DocumentManager = function(config){
28815     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28816     
28817     this.files = [];
28818     this.delegates = [];
28819     
28820     this.addEvents({
28821         /**
28822          * @event initial
28823          * Fire when initial the DocumentManager
28824          * @param {Roo.bootstrap.DocumentManager} this
28825          */
28826         "initial" : true,
28827         /**
28828          * @event inspect
28829          * inspect selected file
28830          * @param {Roo.bootstrap.DocumentManager} this
28831          * @param {File} file
28832          */
28833         "inspect" : true,
28834         /**
28835          * @event exception
28836          * Fire when xhr load exception
28837          * @param {Roo.bootstrap.DocumentManager} this
28838          * @param {XMLHttpRequest} xhr
28839          */
28840         "exception" : true,
28841         /**
28842          * @event afterupload
28843          * Fire when xhr load exception
28844          * @param {Roo.bootstrap.DocumentManager} this
28845          * @param {XMLHttpRequest} xhr
28846          */
28847         "afterupload" : true,
28848         /**
28849          * @event prepare
28850          * prepare the form data
28851          * @param {Roo.bootstrap.DocumentManager} this
28852          * @param {Object} formData
28853          */
28854         "prepare" : true,
28855         /**
28856          * @event remove
28857          * Fire when remove the file
28858          * @param {Roo.bootstrap.DocumentManager} this
28859          * @param {Object} file
28860          */
28861         "remove" : true,
28862         /**
28863          * @event refresh
28864          * Fire after refresh the file
28865          * @param {Roo.bootstrap.DocumentManager} this
28866          */
28867         "refresh" : true,
28868         /**
28869          * @event click
28870          * Fire after click the image
28871          * @param {Roo.bootstrap.DocumentManager} this
28872          * @param {Object} file
28873          */
28874         "click" : true,
28875         /**
28876          * @event edit
28877          * Fire when upload a image and editable set to true
28878          * @param {Roo.bootstrap.DocumentManager} this
28879          * @param {Object} file
28880          */
28881         "edit" : true,
28882         /**
28883          * @event beforeselectfile
28884          * Fire before select file
28885          * @param {Roo.bootstrap.DocumentManager} this
28886          */
28887         "beforeselectfile" : true,
28888         /**
28889          * @event process
28890          * Fire before process file
28891          * @param {Roo.bootstrap.DocumentManager} this
28892          * @param {Object} file
28893          */
28894         "process" : true,
28895         /**
28896          * @event previewrendered
28897          * Fire when preview rendered
28898          * @param {Roo.bootstrap.DocumentManager} this
28899          * @param {Object} file
28900          */
28901         "previewrendered" : true,
28902         /**
28903          */
28904         "previewResize" : true
28905         
28906     });
28907 };
28908
28909 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28910     
28911     boxes : 0,
28912     inputName : '',
28913     thumbSize : 300,
28914     multiple : true,
28915     files : false,
28916     method : 'POST',
28917     url : '',
28918     paramName : 'imageUpload',
28919     toolTipName : 'filename',
28920     fieldLabel : '',
28921     labelWidth : 4,
28922     labelAlign : 'left',
28923     editable : true,
28924     delegates : false,
28925     xhr : false, 
28926     
28927     labellg : 0,
28928     labelmd : 0,
28929     labelsm : 0,
28930     labelxs : 0,
28931     
28932     getAutoCreate : function()
28933     {   
28934         var managerWidget = {
28935             tag : 'div',
28936             cls : 'roo-document-manager',
28937             cn : [
28938                 {
28939                     tag : 'input',
28940                     cls : 'roo-document-manager-selector',
28941                     type : 'file'
28942                 },
28943                 {
28944                     tag : 'div',
28945                     cls : 'roo-document-manager-uploader',
28946                     cn : [
28947                         {
28948                             tag : 'div',
28949                             cls : 'roo-document-manager-upload-btn',
28950                             html : '<i class="fa fa-plus"></i>'
28951                         }
28952                     ]
28953                     
28954                 }
28955             ]
28956         };
28957         
28958         var content = [
28959             {
28960                 tag : 'div',
28961                 cls : 'column col-md-12',
28962                 cn : managerWidget
28963             }
28964         ];
28965         
28966         if(this.fieldLabel.length){
28967             
28968             content = [
28969                 {
28970                     tag : 'div',
28971                     cls : 'column col-md-12',
28972                     html : this.fieldLabel
28973                 },
28974                 {
28975                     tag : 'div',
28976                     cls : 'column col-md-12',
28977                     cn : managerWidget
28978                 }
28979             ];
28980
28981             if(this.labelAlign == 'left'){
28982                 content = [
28983                     {
28984                         tag : 'div',
28985                         cls : 'column',
28986                         html : this.fieldLabel
28987                     },
28988                     {
28989                         tag : 'div',
28990                         cls : 'column',
28991                         cn : managerWidget
28992                     }
28993                 ];
28994                 
28995                 if(this.labelWidth > 12){
28996                     content[0].style = "width: " + this.labelWidth + 'px';
28997                 }
28998
28999                 if(this.labelWidth < 13 && this.labelmd == 0){
29000                     this.labelmd = this.labelWidth;
29001                 }
29002
29003                 if(this.labellg > 0){
29004                     content[0].cls += ' col-lg-' + this.labellg;
29005                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29006                 }
29007
29008                 if(this.labelmd > 0){
29009                     content[0].cls += ' col-md-' + this.labelmd;
29010                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29011                 }
29012
29013                 if(this.labelsm > 0){
29014                     content[0].cls += ' col-sm-' + this.labelsm;
29015                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29016                 }
29017
29018                 if(this.labelxs > 0){
29019                     content[0].cls += ' col-xs-' + this.labelxs;
29020                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29021                 }
29022                 
29023             }
29024         }
29025         
29026         var cfg = {
29027             tag : 'div',
29028             cls : 'row clearfix',
29029             cn : content
29030         };
29031         
29032         return cfg;
29033         
29034     },
29035     
29036     initEvents : function()
29037     {
29038         this.managerEl = this.el.select('.roo-document-manager', true).first();
29039         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29040         
29041         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29042         this.selectorEl.hide();
29043         
29044         if(this.multiple){
29045             this.selectorEl.attr('multiple', 'multiple');
29046         }
29047         
29048         this.selectorEl.on('change', this.onFileSelected, this);
29049         
29050         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29051         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29052         
29053         this.uploader.on('click', this.onUploaderClick, this);
29054         
29055         this.renderProgressDialog();
29056         
29057         var _this = this;
29058         
29059         window.addEventListener("resize", function() { _this.refresh(); } );
29060         
29061         this.fireEvent('initial', this);
29062     },
29063     
29064     renderProgressDialog : function()
29065     {
29066         var _this = this;
29067         
29068         this.progressDialog = new Roo.bootstrap.Modal({
29069             cls : 'roo-document-manager-progress-dialog',
29070             allow_close : false,
29071             title : '',
29072             buttons : [
29073                 {
29074                     name  :'cancel',
29075                     weight : 'danger',
29076                     html : 'Cancel'
29077                 }
29078             ], 
29079             listeners : { 
29080                 btnclick : function() {
29081                     _this.uploadCancel();
29082                     this.hide();
29083                 }
29084             }
29085         });
29086          
29087         this.progressDialog.render(Roo.get(document.body));
29088          
29089         this.progress = new Roo.bootstrap.Progress({
29090             cls : 'roo-document-manager-progress',
29091             active : true,
29092             striped : true
29093         });
29094         
29095         this.progress.render(this.progressDialog.getChildContainer());
29096         
29097         this.progressBar = new Roo.bootstrap.ProgressBar({
29098             cls : 'roo-document-manager-progress-bar',
29099             aria_valuenow : 0,
29100             aria_valuemin : 0,
29101             aria_valuemax : 12,
29102             panel : 'success'
29103         });
29104         
29105         this.progressBar.render(this.progress.getChildContainer());
29106     },
29107     
29108     onUploaderClick : function(e)
29109     {
29110         e.preventDefault();
29111      
29112         if(this.fireEvent('beforeselectfile', this) != false){
29113             this.selectorEl.dom.click();
29114         }
29115         
29116     },
29117     
29118     onFileSelected : function(e)
29119     {
29120         e.preventDefault();
29121         
29122         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29123             return;
29124         }
29125         
29126         Roo.each(this.selectorEl.dom.files, function(file){
29127             if(this.fireEvent('inspect', this, file) != false){
29128                 this.files.push(file);
29129             }
29130         }, this);
29131         
29132         this.queue();
29133         
29134     },
29135     
29136     queue : function()
29137     {
29138         this.selectorEl.dom.value = '';
29139         
29140         if(!this.files || !this.files.length){
29141             return;
29142         }
29143         
29144         if(this.boxes > 0 && this.files.length > this.boxes){
29145             this.files = this.files.slice(0, this.boxes);
29146         }
29147         
29148         this.uploader.show();
29149         
29150         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29151             this.uploader.hide();
29152         }
29153         
29154         var _this = this;
29155         
29156         var files = [];
29157         
29158         var docs = [];
29159         
29160         Roo.each(this.files, function(file){
29161             
29162             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29163                 var f = this.renderPreview(file);
29164                 files.push(f);
29165                 return;
29166             }
29167             
29168             if(file.type.indexOf('image') != -1){
29169                 this.delegates.push(
29170                     (function(){
29171                         _this.process(file);
29172                     }).createDelegate(this)
29173                 );
29174         
29175                 return;
29176             }
29177             
29178             docs.push(
29179                 (function(){
29180                     _this.process(file);
29181                 }).createDelegate(this)
29182             );
29183             
29184         }, this);
29185         
29186         this.files = files;
29187         
29188         this.delegates = this.delegates.concat(docs);
29189         
29190         if(!this.delegates.length){
29191             this.refresh();
29192             return;
29193         }
29194         
29195         this.progressBar.aria_valuemax = this.delegates.length;
29196         
29197         this.arrange();
29198         
29199         return;
29200     },
29201     
29202     arrange : function()
29203     {
29204         if(!this.delegates.length){
29205             this.progressDialog.hide();
29206             this.refresh();
29207             return;
29208         }
29209         
29210         var delegate = this.delegates.shift();
29211         
29212         this.progressDialog.show();
29213         
29214         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29215         
29216         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29217         
29218         delegate();
29219     },
29220     
29221     refresh : function()
29222     {
29223         this.uploader.show();
29224         
29225         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29226             this.uploader.hide();
29227         }
29228         
29229         Roo.isTouch ? this.closable(false) : this.closable(true);
29230         
29231         this.fireEvent('refresh', this);
29232     },
29233     
29234     onRemove : function(e, el, o)
29235     {
29236         e.preventDefault();
29237         
29238         this.fireEvent('remove', this, o);
29239         
29240     },
29241     
29242     remove : function(o)
29243     {
29244         var files = [];
29245         
29246         Roo.each(this.files, function(file){
29247             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29248                 files.push(file);
29249                 return;
29250             }
29251
29252             o.target.remove();
29253
29254         }, this);
29255         
29256         this.files = files;
29257         
29258         this.refresh();
29259     },
29260     
29261     clear : function()
29262     {
29263         Roo.each(this.files, function(file){
29264             if(!file.target){
29265                 return;
29266             }
29267             
29268             file.target.remove();
29269
29270         }, this);
29271         
29272         this.files = [];
29273         
29274         this.refresh();
29275     },
29276     
29277     onClick : function(e, el, o)
29278     {
29279         e.preventDefault();
29280         
29281         this.fireEvent('click', this, o);
29282         
29283     },
29284     
29285     closable : function(closable)
29286     {
29287         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29288             
29289             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29290             
29291             if(closable){
29292                 el.show();
29293                 return;
29294             }
29295             
29296             el.hide();
29297             
29298         }, this);
29299     },
29300     
29301     xhrOnLoad : function(xhr)
29302     {
29303         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29304             el.remove();
29305         }, this);
29306         
29307         if (xhr.readyState !== 4) {
29308             this.arrange();
29309             this.fireEvent('exception', this, xhr);
29310             return;
29311         }
29312
29313         var response = Roo.decode(xhr.responseText);
29314         
29315         if(!response.success){
29316             this.arrange();
29317             this.fireEvent('exception', this, xhr);
29318             return;
29319         }
29320         
29321         var file = this.renderPreview(response.data);
29322         
29323         this.files.push(file);
29324         
29325         this.arrange();
29326         
29327         this.fireEvent('afterupload', this, xhr);
29328         
29329     },
29330     
29331     xhrOnError : function(xhr)
29332     {
29333         Roo.log('xhr on error');
29334         
29335         var response = Roo.decode(xhr.responseText);
29336           
29337         Roo.log(response);
29338         
29339         this.arrange();
29340     },
29341     
29342     process : function(file)
29343     {
29344         if(this.fireEvent('process', this, file) !== false){
29345             if(this.editable && file.type.indexOf('image') != -1){
29346                 this.fireEvent('edit', this, file);
29347                 return;
29348             }
29349
29350             this.uploadStart(file, false);
29351
29352             return;
29353         }
29354         
29355     },
29356     
29357     uploadStart : function(file, crop)
29358     {
29359         this.xhr = new XMLHttpRequest();
29360         
29361         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29362             this.arrange();
29363             return;
29364         }
29365         
29366         file.xhr = this.xhr;
29367             
29368         this.managerEl.createChild({
29369             tag : 'div',
29370             cls : 'roo-document-manager-loading',
29371             cn : [
29372                 {
29373                     tag : 'div',
29374                     tooltip : file.name,
29375                     cls : 'roo-document-manager-thumb',
29376                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29377                 }
29378             ]
29379
29380         });
29381
29382         this.xhr.open(this.method, this.url, true);
29383         
29384         var headers = {
29385             "Accept": "application/json",
29386             "Cache-Control": "no-cache",
29387             "X-Requested-With": "XMLHttpRequest"
29388         };
29389         
29390         for (var headerName in headers) {
29391             var headerValue = headers[headerName];
29392             if (headerValue) {
29393                 this.xhr.setRequestHeader(headerName, headerValue);
29394             }
29395         }
29396         
29397         var _this = this;
29398         
29399         this.xhr.onload = function()
29400         {
29401             _this.xhrOnLoad(_this.xhr);
29402         }
29403         
29404         this.xhr.onerror = function()
29405         {
29406             _this.xhrOnError(_this.xhr);
29407         }
29408         
29409         var formData = new FormData();
29410
29411         formData.append('returnHTML', 'NO');
29412         
29413         if(crop){
29414             formData.append('crop', crop);
29415         }
29416         
29417         formData.append(this.paramName, file, file.name);
29418         
29419         var options = {
29420             file : file, 
29421             manually : false
29422         };
29423         
29424         if(this.fireEvent('prepare', this, formData, options) != false){
29425             
29426             if(options.manually){
29427                 return;
29428             }
29429             
29430             this.xhr.send(formData);
29431             return;
29432         };
29433         
29434         this.uploadCancel();
29435     },
29436     
29437     uploadCancel : function()
29438     {
29439         if (this.xhr) {
29440             this.xhr.abort();
29441         }
29442         
29443         this.delegates = [];
29444         
29445         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29446             el.remove();
29447         }, this);
29448         
29449         this.arrange();
29450     },
29451     
29452     renderPreview : function(file)
29453     {
29454         if(typeof(file.target) != 'undefined' && file.target){
29455             return file;
29456         }
29457         
29458         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29459         
29460         var previewEl = this.managerEl.createChild({
29461             tag : 'div',
29462             cls : 'roo-document-manager-preview',
29463             cn : [
29464                 {
29465                     tag : 'div',
29466                     tooltip : file[this.toolTipName],
29467                     cls : 'roo-document-manager-thumb',
29468                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29469                 },
29470                 {
29471                     tag : 'button',
29472                     cls : 'close',
29473                     html : '<i class="fa fa-times-circle"></i>'
29474                 }
29475             ]
29476         });
29477
29478         var close = previewEl.select('button.close', true).first();
29479
29480         close.on('click', this.onRemove, this, file);
29481
29482         file.target = previewEl;
29483
29484         var image = previewEl.select('img', true).first();
29485         
29486         var _this = this;
29487         
29488         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29489         
29490         image.on('click', this.onClick, this, file);
29491         
29492         this.fireEvent('previewrendered', this, file);
29493         
29494         return file;
29495         
29496     },
29497     
29498     onPreviewLoad : function(file, image)
29499     {
29500         if(typeof(file.target) == 'undefined' || !file.target){
29501             return;
29502         }
29503         
29504         var width = image.dom.naturalWidth || image.dom.width;
29505         var height = image.dom.naturalHeight || image.dom.height;
29506         
29507         if(!this.previewResize) {
29508             return;
29509         }
29510         
29511         if(width > height){
29512             file.target.addClass('wide');
29513             return;
29514         }
29515         
29516         file.target.addClass('tall');
29517         return;
29518         
29519     },
29520     
29521     uploadFromSource : function(file, crop)
29522     {
29523         this.xhr = new XMLHttpRequest();
29524         
29525         this.managerEl.createChild({
29526             tag : 'div',
29527             cls : 'roo-document-manager-loading',
29528             cn : [
29529                 {
29530                     tag : 'div',
29531                     tooltip : file.name,
29532                     cls : 'roo-document-manager-thumb',
29533                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29534                 }
29535             ]
29536
29537         });
29538
29539         this.xhr.open(this.method, this.url, true);
29540         
29541         var headers = {
29542             "Accept": "application/json",
29543             "Cache-Control": "no-cache",
29544             "X-Requested-With": "XMLHttpRequest"
29545         };
29546         
29547         for (var headerName in headers) {
29548             var headerValue = headers[headerName];
29549             if (headerValue) {
29550                 this.xhr.setRequestHeader(headerName, headerValue);
29551             }
29552         }
29553         
29554         var _this = this;
29555         
29556         this.xhr.onload = function()
29557         {
29558             _this.xhrOnLoad(_this.xhr);
29559         }
29560         
29561         this.xhr.onerror = function()
29562         {
29563             _this.xhrOnError(_this.xhr);
29564         }
29565         
29566         var formData = new FormData();
29567
29568         formData.append('returnHTML', 'NO');
29569         
29570         formData.append('crop', crop);
29571         
29572         if(typeof(file.filename) != 'undefined'){
29573             formData.append('filename', file.filename);
29574         }
29575         
29576         if(typeof(file.mimetype) != 'undefined'){
29577             formData.append('mimetype', file.mimetype);
29578         }
29579         
29580         Roo.log(formData);
29581         
29582         if(this.fireEvent('prepare', this, formData) != false){
29583             this.xhr.send(formData);
29584         };
29585     }
29586 });
29587
29588 /*
29589 * Licence: LGPL
29590 */
29591
29592 /**
29593  * @class Roo.bootstrap.DocumentViewer
29594  * @extends Roo.bootstrap.Component
29595  * Bootstrap DocumentViewer class
29596  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29597  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29598  * 
29599  * @constructor
29600  * Create a new DocumentViewer
29601  * @param {Object} config The config object
29602  */
29603
29604 Roo.bootstrap.DocumentViewer = function(config){
29605     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29606     
29607     this.addEvents({
29608         /**
29609          * @event initial
29610          * Fire after initEvent
29611          * @param {Roo.bootstrap.DocumentViewer} this
29612          */
29613         "initial" : true,
29614         /**
29615          * @event click
29616          * Fire after click
29617          * @param {Roo.bootstrap.DocumentViewer} this
29618          */
29619         "click" : true,
29620         /**
29621          * @event download
29622          * Fire after download button
29623          * @param {Roo.bootstrap.DocumentViewer} this
29624          */
29625         "download" : true,
29626         /**
29627          * @event trash
29628          * Fire after trash button
29629          * @param {Roo.bootstrap.DocumentViewer} this
29630          */
29631         "trash" : true
29632         
29633     });
29634 };
29635
29636 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29637     
29638     showDownload : true,
29639     
29640     showTrash : true,
29641     
29642     getAutoCreate : function()
29643     {
29644         var cfg = {
29645             tag : 'div',
29646             cls : 'roo-document-viewer',
29647             cn : [
29648                 {
29649                     tag : 'div',
29650                     cls : 'roo-document-viewer-body',
29651                     cn : [
29652                         {
29653                             tag : 'div',
29654                             cls : 'roo-document-viewer-thumb',
29655                             cn : [
29656                                 {
29657                                     tag : 'img',
29658                                     cls : 'roo-document-viewer-image'
29659                                 }
29660                             ]
29661                         }
29662                     ]
29663                 },
29664                 {
29665                     tag : 'div',
29666                     cls : 'roo-document-viewer-footer',
29667                     cn : {
29668                         tag : 'div',
29669                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29670                         cn : [
29671                             {
29672                                 tag : 'div',
29673                                 cls : 'btn-group roo-document-viewer-download',
29674                                 cn : [
29675                                     {
29676                                         tag : 'button',
29677                                         cls : 'btn btn-default',
29678                                         html : '<i class="fa fa-download"></i>'
29679                                     }
29680                                 ]
29681                             },
29682                             {
29683                                 tag : 'div',
29684                                 cls : 'btn-group roo-document-viewer-trash',
29685                                 cn : [
29686                                     {
29687                                         tag : 'button',
29688                                         cls : 'btn btn-default',
29689                                         html : '<i class="fa fa-trash"></i>'
29690                                     }
29691                                 ]
29692                             }
29693                         ]
29694                     }
29695                 }
29696             ]
29697         };
29698         
29699         return cfg;
29700     },
29701     
29702     initEvents : function()
29703     {
29704         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29705         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29706         
29707         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29708         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29709         
29710         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29711         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29712         
29713         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29714         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29715         
29716         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29717         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29718         
29719         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29720         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29721         
29722         this.bodyEl.on('click', this.onClick, this);
29723         this.downloadBtn.on('click', this.onDownload, this);
29724         this.trashBtn.on('click', this.onTrash, this);
29725         
29726         this.downloadBtn.hide();
29727         this.trashBtn.hide();
29728         
29729         if(this.showDownload){
29730             this.downloadBtn.show();
29731         }
29732         
29733         if(this.showTrash){
29734             this.trashBtn.show();
29735         }
29736         
29737         if(!this.showDownload && !this.showTrash) {
29738             this.footerEl.hide();
29739         }
29740         
29741     },
29742     
29743     initial : function()
29744     {
29745         this.fireEvent('initial', this);
29746         
29747     },
29748     
29749     onClick : function(e)
29750     {
29751         e.preventDefault();
29752         
29753         this.fireEvent('click', this);
29754     },
29755     
29756     onDownload : function(e)
29757     {
29758         e.preventDefault();
29759         
29760         this.fireEvent('download', this);
29761     },
29762     
29763     onTrash : function(e)
29764     {
29765         e.preventDefault();
29766         
29767         this.fireEvent('trash', this);
29768     }
29769     
29770 });
29771 /*
29772  * - LGPL
29773  *
29774  * nav progress bar
29775  * 
29776  */
29777
29778 /**
29779  * @class Roo.bootstrap.NavProgressBar
29780  * @extends Roo.bootstrap.Component
29781  * Bootstrap NavProgressBar class
29782  * 
29783  * @constructor
29784  * Create a new nav progress bar
29785  * @param {Object} config The config object
29786  */
29787
29788 Roo.bootstrap.NavProgressBar = function(config){
29789     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29790
29791     this.bullets = this.bullets || [];
29792    
29793 //    Roo.bootstrap.NavProgressBar.register(this);
29794      this.addEvents({
29795         /**
29796              * @event changed
29797              * Fires when the active item changes
29798              * @param {Roo.bootstrap.NavProgressBar} this
29799              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29800              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29801          */
29802         'changed': true
29803      });
29804     
29805 };
29806
29807 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29808     
29809     bullets : [],
29810     barItems : [],
29811     
29812     getAutoCreate : function()
29813     {
29814         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29815         
29816         cfg = {
29817             tag : 'div',
29818             cls : 'roo-navigation-bar-group',
29819             cn : [
29820                 {
29821                     tag : 'div',
29822                     cls : 'roo-navigation-top-bar'
29823                 },
29824                 {
29825                     tag : 'div',
29826                     cls : 'roo-navigation-bullets-bar',
29827                     cn : [
29828                         {
29829                             tag : 'ul',
29830                             cls : 'roo-navigation-bar'
29831                         }
29832                     ]
29833                 },
29834                 
29835                 {
29836                     tag : 'div',
29837                     cls : 'roo-navigation-bottom-bar'
29838                 }
29839             ]
29840             
29841         };
29842         
29843         return cfg;
29844         
29845     },
29846     
29847     initEvents: function() 
29848     {
29849         
29850     },
29851     
29852     onRender : function(ct, position) 
29853     {
29854         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29855         
29856         if(this.bullets.length){
29857             Roo.each(this.bullets, function(b){
29858                this.addItem(b);
29859             }, this);
29860         }
29861         
29862         this.format();
29863         
29864     },
29865     
29866     addItem : function(cfg)
29867     {
29868         var item = new Roo.bootstrap.NavProgressItem(cfg);
29869         
29870         item.parentId = this.id;
29871         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29872         
29873         if(cfg.html){
29874             var top = new Roo.bootstrap.Element({
29875                 tag : 'div',
29876                 cls : 'roo-navigation-bar-text'
29877             });
29878             
29879             var bottom = new Roo.bootstrap.Element({
29880                 tag : 'div',
29881                 cls : 'roo-navigation-bar-text'
29882             });
29883             
29884             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29885             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29886             
29887             var topText = new Roo.bootstrap.Element({
29888                 tag : 'span',
29889                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29890             });
29891             
29892             var bottomText = new Roo.bootstrap.Element({
29893                 tag : 'span',
29894                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29895             });
29896             
29897             topText.onRender(top.el, null);
29898             bottomText.onRender(bottom.el, null);
29899             
29900             item.topEl = top;
29901             item.bottomEl = bottom;
29902         }
29903         
29904         this.barItems.push(item);
29905         
29906         return item;
29907     },
29908     
29909     getActive : function()
29910     {
29911         var active = false;
29912         
29913         Roo.each(this.barItems, function(v){
29914             
29915             if (!v.isActive()) {
29916                 return;
29917             }
29918             
29919             active = v;
29920             return false;
29921             
29922         });
29923         
29924         return active;
29925     },
29926     
29927     setActiveItem : function(item)
29928     {
29929         var prev = false;
29930         
29931         Roo.each(this.barItems, function(v){
29932             if (v.rid == item.rid) {
29933                 return ;
29934             }
29935             
29936             if (v.isActive()) {
29937                 v.setActive(false);
29938                 prev = v;
29939             }
29940         });
29941
29942         item.setActive(true);
29943         
29944         this.fireEvent('changed', this, item, prev);
29945     },
29946     
29947     getBarItem: function(rid)
29948     {
29949         var ret = false;
29950         
29951         Roo.each(this.barItems, function(e) {
29952             if (e.rid != rid) {
29953                 return;
29954             }
29955             
29956             ret =  e;
29957             return false;
29958         });
29959         
29960         return ret;
29961     },
29962     
29963     indexOfItem : function(item)
29964     {
29965         var index = false;
29966         
29967         Roo.each(this.barItems, function(v, i){
29968             
29969             if (v.rid != item.rid) {
29970                 return;
29971             }
29972             
29973             index = i;
29974             return false
29975         });
29976         
29977         return index;
29978     },
29979     
29980     setActiveNext : function()
29981     {
29982         var i = this.indexOfItem(this.getActive());
29983         
29984         if (i > this.barItems.length) {
29985             return;
29986         }
29987         
29988         this.setActiveItem(this.barItems[i+1]);
29989     },
29990     
29991     setActivePrev : function()
29992     {
29993         var i = this.indexOfItem(this.getActive());
29994         
29995         if (i  < 1) {
29996             return;
29997         }
29998         
29999         this.setActiveItem(this.barItems[i-1]);
30000     },
30001     
30002     format : function()
30003     {
30004         if(!this.barItems.length){
30005             return;
30006         }
30007      
30008         var width = 100 / this.barItems.length;
30009         
30010         Roo.each(this.barItems, function(i){
30011             i.el.setStyle('width', width + '%');
30012             i.topEl.el.setStyle('width', width + '%');
30013             i.bottomEl.el.setStyle('width', width + '%');
30014         }, this);
30015         
30016     }
30017     
30018 });
30019 /*
30020  * - LGPL
30021  *
30022  * Nav Progress Item
30023  * 
30024  */
30025
30026 /**
30027  * @class Roo.bootstrap.NavProgressItem
30028  * @extends Roo.bootstrap.Component
30029  * Bootstrap NavProgressItem class
30030  * @cfg {String} rid the reference id
30031  * @cfg {Boolean} active (true|false) Is item active default false
30032  * @cfg {Boolean} disabled (true|false) Is item active default false
30033  * @cfg {String} html
30034  * @cfg {String} position (top|bottom) text position default bottom
30035  * @cfg {String} icon show icon instead of number
30036  * 
30037  * @constructor
30038  * Create a new NavProgressItem
30039  * @param {Object} config The config object
30040  */
30041 Roo.bootstrap.NavProgressItem = function(config){
30042     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30043     this.addEvents({
30044         // raw events
30045         /**
30046          * @event click
30047          * The raw click event for the entire grid.
30048          * @param {Roo.bootstrap.NavProgressItem} this
30049          * @param {Roo.EventObject} e
30050          */
30051         "click" : true
30052     });
30053    
30054 };
30055
30056 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30057     
30058     rid : '',
30059     active : false,
30060     disabled : false,
30061     html : '',
30062     position : 'bottom',
30063     icon : false,
30064     
30065     getAutoCreate : function()
30066     {
30067         var iconCls = 'roo-navigation-bar-item-icon';
30068         
30069         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30070         
30071         var cfg = {
30072             tag: 'li',
30073             cls: 'roo-navigation-bar-item',
30074             cn : [
30075                 {
30076                     tag : 'i',
30077                     cls : iconCls
30078                 }
30079             ]
30080         };
30081         
30082         if(this.active){
30083             cfg.cls += ' active';
30084         }
30085         if(this.disabled){
30086             cfg.cls += ' disabled';
30087         }
30088         
30089         return cfg;
30090     },
30091     
30092     disable : function()
30093     {
30094         this.setDisabled(true);
30095     },
30096     
30097     enable : function()
30098     {
30099         this.setDisabled(false);
30100     },
30101     
30102     initEvents: function() 
30103     {
30104         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30105         
30106         this.iconEl.on('click', this.onClick, this);
30107     },
30108     
30109     onClick : function(e)
30110     {
30111         e.preventDefault();
30112         
30113         if(this.disabled){
30114             return;
30115         }
30116         
30117         if(this.fireEvent('click', this, e) === false){
30118             return;
30119         };
30120         
30121         this.parent().setActiveItem(this);
30122     },
30123     
30124     isActive: function () 
30125     {
30126         return this.active;
30127     },
30128     
30129     setActive : function(state)
30130     {
30131         if(this.active == state){
30132             return;
30133         }
30134         
30135         this.active = state;
30136         
30137         if (state) {
30138             this.el.addClass('active');
30139             return;
30140         }
30141         
30142         this.el.removeClass('active');
30143         
30144         return;
30145     },
30146     
30147     setDisabled : function(state)
30148     {
30149         if(this.disabled == state){
30150             return;
30151         }
30152         
30153         this.disabled = state;
30154         
30155         if (state) {
30156             this.el.addClass('disabled');
30157             return;
30158         }
30159         
30160         this.el.removeClass('disabled');
30161     },
30162     
30163     tooltipEl : function()
30164     {
30165         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30166     }
30167 });
30168  
30169
30170  /*
30171  * - LGPL
30172  *
30173  * FieldLabel
30174  * 
30175  */
30176
30177 /**
30178  * @class Roo.bootstrap.FieldLabel
30179  * @extends Roo.bootstrap.Component
30180  * Bootstrap FieldLabel class
30181  * @cfg {String} html contents of the element
30182  * @cfg {String} tag tag of the element default label
30183  * @cfg {String} cls class of the element
30184  * @cfg {String} target label target 
30185  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30186  * @cfg {String} invalidClass default "text-warning"
30187  * @cfg {String} validClass default "text-success"
30188  * @cfg {String} iconTooltip default "This field is required"
30189  * @cfg {String} indicatorpos (left|right) default left
30190  * 
30191  * @constructor
30192  * Create a new FieldLabel
30193  * @param {Object} config The config object
30194  */
30195
30196 Roo.bootstrap.FieldLabel = function(config){
30197     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30198     
30199     this.addEvents({
30200             /**
30201              * @event invalid
30202              * Fires after the field has been marked as invalid.
30203              * @param {Roo.form.FieldLabel} this
30204              * @param {String} msg The validation message
30205              */
30206             invalid : true,
30207             /**
30208              * @event valid
30209              * Fires after the field has been validated with no errors.
30210              * @param {Roo.form.FieldLabel} this
30211              */
30212             valid : true
30213         });
30214 };
30215
30216 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30217     
30218     tag: 'label',
30219     cls: '',
30220     html: '',
30221     target: '',
30222     allowBlank : true,
30223     invalidClass : 'has-warning',
30224     validClass : 'has-success',
30225     iconTooltip : 'This field is required',
30226     indicatorpos : 'left',
30227     
30228     getAutoCreate : function(){
30229         
30230         var cls = "";
30231         if (!this.allowBlank) {
30232             cls  = "visible";
30233         }
30234         
30235         var cfg = {
30236             tag : this.tag,
30237             cls : 'roo-bootstrap-field-label ' + this.cls,
30238             for : this.target,
30239             cn : [
30240                 {
30241                     tag : 'i',
30242                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30243                     tooltip : this.iconTooltip
30244                 },
30245                 {
30246                     tag : 'span',
30247                     html : this.html
30248                 }
30249             ] 
30250         };
30251         
30252         if(this.indicatorpos == 'right'){
30253             var cfg = {
30254                 tag : this.tag,
30255                 cls : 'roo-bootstrap-field-label ' + this.cls,
30256                 for : this.target,
30257                 cn : [
30258                     {
30259                         tag : 'span',
30260                         html : this.html
30261                     },
30262                     {
30263                         tag : 'i',
30264                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30265                         tooltip : this.iconTooltip
30266                     }
30267                 ] 
30268             };
30269         }
30270         
30271         return cfg;
30272     },
30273     
30274     initEvents: function() 
30275     {
30276         Roo.bootstrap.Element.superclass.initEvents.call(this);
30277         
30278         this.indicator = this.indicatorEl();
30279         
30280         if(this.indicator){
30281             this.indicator.removeClass('visible');
30282             this.indicator.addClass('invisible');
30283         }
30284         
30285         Roo.bootstrap.FieldLabel.register(this);
30286     },
30287     
30288     indicatorEl : function()
30289     {
30290         var indicator = this.el.select('i.roo-required-indicator',true).first();
30291         
30292         if(!indicator){
30293             return false;
30294         }
30295         
30296         return indicator;
30297         
30298     },
30299     
30300     /**
30301      * Mark this field as valid
30302      */
30303     markValid : function()
30304     {
30305         if(this.indicator){
30306             this.indicator.removeClass('visible');
30307             this.indicator.addClass('invisible');
30308         }
30309         
30310         this.el.removeClass(this.invalidClass);
30311         
30312         this.el.addClass(this.validClass);
30313         
30314         this.fireEvent('valid', this);
30315     },
30316     
30317     /**
30318      * Mark this field as invalid
30319      * @param {String} msg The validation message
30320      */
30321     markInvalid : function(msg)
30322     {
30323         if(this.indicator){
30324             this.indicator.removeClass('invisible');
30325             this.indicator.addClass('visible');
30326         }
30327         
30328         this.el.removeClass(this.validClass);
30329         
30330         this.el.addClass(this.invalidClass);
30331         
30332         this.fireEvent('invalid', this, msg);
30333     }
30334     
30335    
30336 });
30337
30338 Roo.apply(Roo.bootstrap.FieldLabel, {
30339     
30340     groups: {},
30341     
30342      /**
30343     * register a FieldLabel Group
30344     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30345     */
30346     register : function(label)
30347     {
30348         if(this.groups.hasOwnProperty(label.target)){
30349             return;
30350         }
30351      
30352         this.groups[label.target] = label;
30353         
30354     },
30355     /**
30356     * fetch a FieldLabel Group based on the target
30357     * @param {string} target
30358     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30359     */
30360     get: function(target) {
30361         if (typeof(this.groups[target]) == 'undefined') {
30362             return false;
30363         }
30364         
30365         return this.groups[target] ;
30366     }
30367 });
30368
30369  
30370
30371  /*
30372  * - LGPL
30373  *
30374  * page DateSplitField.
30375  * 
30376  */
30377
30378
30379 /**
30380  * @class Roo.bootstrap.DateSplitField
30381  * @extends Roo.bootstrap.Component
30382  * Bootstrap DateSplitField class
30383  * @cfg {string} fieldLabel - the label associated
30384  * @cfg {Number} labelWidth set the width of label (0-12)
30385  * @cfg {String} labelAlign (top|left)
30386  * @cfg {Boolean} dayAllowBlank (true|false) default false
30387  * @cfg {Boolean} monthAllowBlank (true|false) default false
30388  * @cfg {Boolean} yearAllowBlank (true|false) default false
30389  * @cfg {string} dayPlaceholder 
30390  * @cfg {string} monthPlaceholder
30391  * @cfg {string} yearPlaceholder
30392  * @cfg {string} dayFormat default 'd'
30393  * @cfg {string} monthFormat default 'm'
30394  * @cfg {string} yearFormat default 'Y'
30395  * @cfg {Number} labellg set the width of label (1-12)
30396  * @cfg {Number} labelmd set the width of label (1-12)
30397  * @cfg {Number} labelsm set the width of label (1-12)
30398  * @cfg {Number} labelxs set the width of label (1-12)
30399
30400  *     
30401  * @constructor
30402  * Create a new DateSplitField
30403  * @param {Object} config The config object
30404  */
30405
30406 Roo.bootstrap.DateSplitField = function(config){
30407     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30408     
30409     this.addEvents({
30410         // raw events
30411          /**
30412          * @event years
30413          * getting the data of years
30414          * @param {Roo.bootstrap.DateSplitField} this
30415          * @param {Object} years
30416          */
30417         "years" : true,
30418         /**
30419          * @event days
30420          * getting the data of days
30421          * @param {Roo.bootstrap.DateSplitField} this
30422          * @param {Object} days
30423          */
30424         "days" : true,
30425         /**
30426          * @event invalid
30427          * Fires after the field has been marked as invalid.
30428          * @param {Roo.form.Field} this
30429          * @param {String} msg The validation message
30430          */
30431         invalid : true,
30432        /**
30433          * @event valid
30434          * Fires after the field has been validated with no errors.
30435          * @param {Roo.form.Field} this
30436          */
30437         valid : true
30438     });
30439 };
30440
30441 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30442     
30443     fieldLabel : '',
30444     labelAlign : 'top',
30445     labelWidth : 3,
30446     dayAllowBlank : false,
30447     monthAllowBlank : false,
30448     yearAllowBlank : false,
30449     dayPlaceholder : '',
30450     monthPlaceholder : '',
30451     yearPlaceholder : '',
30452     dayFormat : 'd',
30453     monthFormat : 'm',
30454     yearFormat : 'Y',
30455     isFormField : true,
30456     labellg : 0,
30457     labelmd : 0,
30458     labelsm : 0,
30459     labelxs : 0,
30460     
30461     getAutoCreate : function()
30462     {
30463         var cfg = {
30464             tag : 'div',
30465             cls : 'row roo-date-split-field-group',
30466             cn : [
30467                 {
30468                     tag : 'input',
30469                     type : 'hidden',
30470                     cls : 'form-hidden-field roo-date-split-field-group-value',
30471                     name : this.name
30472                 }
30473             ]
30474         };
30475         
30476         var labelCls = 'col-md-12';
30477         var contentCls = 'col-md-4';
30478         
30479         if(this.fieldLabel){
30480             
30481             var label = {
30482                 tag : 'div',
30483                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30484                 cn : [
30485                     {
30486                         tag : 'label',
30487                         html : this.fieldLabel
30488                     }
30489                 ]
30490             };
30491             
30492             if(this.labelAlign == 'left'){
30493             
30494                 if(this.labelWidth > 12){
30495                     label.style = "width: " + this.labelWidth + 'px';
30496                 }
30497
30498                 if(this.labelWidth < 13 && this.labelmd == 0){
30499                     this.labelmd = this.labelWidth;
30500                 }
30501
30502                 if(this.labellg > 0){
30503                     labelCls = ' col-lg-' + this.labellg;
30504                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30505                 }
30506
30507                 if(this.labelmd > 0){
30508                     labelCls = ' col-md-' + this.labelmd;
30509                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30510                 }
30511
30512                 if(this.labelsm > 0){
30513                     labelCls = ' col-sm-' + this.labelsm;
30514                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30515                 }
30516
30517                 if(this.labelxs > 0){
30518                     labelCls = ' col-xs-' + this.labelxs;
30519                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30520                 }
30521             }
30522             
30523             label.cls += ' ' + labelCls;
30524             
30525             cfg.cn.push(label);
30526         }
30527         
30528         Roo.each(['day', 'month', 'year'], function(t){
30529             cfg.cn.push({
30530                 tag : 'div',
30531                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30532             });
30533         }, this);
30534         
30535         return cfg;
30536     },
30537     
30538     inputEl: function ()
30539     {
30540         return this.el.select('.roo-date-split-field-group-value', true).first();
30541     },
30542     
30543     onRender : function(ct, position) 
30544     {
30545         var _this = this;
30546         
30547         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30548         
30549         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30550         
30551         this.dayField = new Roo.bootstrap.ComboBox({
30552             allowBlank : this.dayAllowBlank,
30553             alwaysQuery : true,
30554             displayField : 'value',
30555             editable : false,
30556             fieldLabel : '',
30557             forceSelection : true,
30558             mode : 'local',
30559             placeholder : this.dayPlaceholder,
30560             selectOnFocus : true,
30561             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30562             triggerAction : 'all',
30563             typeAhead : true,
30564             valueField : 'value',
30565             store : new Roo.data.SimpleStore({
30566                 data : (function() {    
30567                     var days = [];
30568                     _this.fireEvent('days', _this, days);
30569                     return days;
30570                 })(),
30571                 fields : [ 'value' ]
30572             }),
30573             listeners : {
30574                 select : function (_self, record, index)
30575                 {
30576                     _this.setValue(_this.getValue());
30577                 }
30578             }
30579         });
30580
30581         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30582         
30583         this.monthField = new Roo.bootstrap.MonthField({
30584             after : '<i class=\"fa fa-calendar\"></i>',
30585             allowBlank : this.monthAllowBlank,
30586             placeholder : this.monthPlaceholder,
30587             readOnly : true,
30588             listeners : {
30589                 render : function (_self)
30590                 {
30591                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30592                         e.preventDefault();
30593                         _self.focus();
30594                     });
30595                 },
30596                 select : function (_self, oldvalue, newvalue)
30597                 {
30598                     _this.setValue(_this.getValue());
30599                 }
30600             }
30601         });
30602         
30603         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30604         
30605         this.yearField = new Roo.bootstrap.ComboBox({
30606             allowBlank : this.yearAllowBlank,
30607             alwaysQuery : true,
30608             displayField : 'value',
30609             editable : false,
30610             fieldLabel : '',
30611             forceSelection : true,
30612             mode : 'local',
30613             placeholder : this.yearPlaceholder,
30614             selectOnFocus : true,
30615             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30616             triggerAction : 'all',
30617             typeAhead : true,
30618             valueField : 'value',
30619             store : new Roo.data.SimpleStore({
30620                 data : (function() {
30621                     var years = [];
30622                     _this.fireEvent('years', _this, years);
30623                     return years;
30624                 })(),
30625                 fields : [ 'value' ]
30626             }),
30627             listeners : {
30628                 select : function (_self, record, index)
30629                 {
30630                     _this.setValue(_this.getValue());
30631                 }
30632             }
30633         });
30634
30635         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30636     },
30637     
30638     setValue : function(v, format)
30639     {
30640         this.inputEl.dom.value = v;
30641         
30642         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30643         
30644         var d = Date.parseDate(v, f);
30645         
30646         if(!d){
30647             this.validate();
30648             return;
30649         }
30650         
30651         this.setDay(d.format(this.dayFormat));
30652         this.setMonth(d.format(this.monthFormat));
30653         this.setYear(d.format(this.yearFormat));
30654         
30655         this.validate();
30656         
30657         return;
30658     },
30659     
30660     setDay : function(v)
30661     {
30662         this.dayField.setValue(v);
30663         this.inputEl.dom.value = this.getValue();
30664         this.validate();
30665         return;
30666     },
30667     
30668     setMonth : function(v)
30669     {
30670         this.monthField.setValue(v, true);
30671         this.inputEl.dom.value = this.getValue();
30672         this.validate();
30673         return;
30674     },
30675     
30676     setYear : function(v)
30677     {
30678         this.yearField.setValue(v);
30679         this.inputEl.dom.value = this.getValue();
30680         this.validate();
30681         return;
30682     },
30683     
30684     getDay : function()
30685     {
30686         return this.dayField.getValue();
30687     },
30688     
30689     getMonth : function()
30690     {
30691         return this.monthField.getValue();
30692     },
30693     
30694     getYear : function()
30695     {
30696         return this.yearField.getValue();
30697     },
30698     
30699     getValue : function()
30700     {
30701         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30702         
30703         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30704         
30705         return date;
30706     },
30707     
30708     reset : function()
30709     {
30710         this.setDay('');
30711         this.setMonth('');
30712         this.setYear('');
30713         this.inputEl.dom.value = '';
30714         this.validate();
30715         return;
30716     },
30717     
30718     validate : function()
30719     {
30720         var d = this.dayField.validate();
30721         var m = this.monthField.validate();
30722         var y = this.yearField.validate();
30723         
30724         var valid = true;
30725         
30726         if(
30727                 (!this.dayAllowBlank && !d) ||
30728                 (!this.monthAllowBlank && !m) ||
30729                 (!this.yearAllowBlank && !y)
30730         ){
30731             valid = false;
30732         }
30733         
30734         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30735             return valid;
30736         }
30737         
30738         if(valid){
30739             this.markValid();
30740             return valid;
30741         }
30742         
30743         this.markInvalid();
30744         
30745         return valid;
30746     },
30747     
30748     markValid : function()
30749     {
30750         
30751         var label = this.el.select('label', true).first();
30752         var icon = this.el.select('i.fa-star', true).first();
30753
30754         if(label && icon){
30755             icon.remove();
30756         }
30757         
30758         this.fireEvent('valid', this);
30759     },
30760     
30761      /**
30762      * Mark this field as invalid
30763      * @param {String} msg The validation message
30764      */
30765     markInvalid : function(msg)
30766     {
30767         
30768         var label = this.el.select('label', true).first();
30769         var icon = this.el.select('i.fa-star', true).first();
30770
30771         if(label && !icon){
30772             this.el.select('.roo-date-split-field-label', true).createChild({
30773                 tag : 'i',
30774                 cls : 'text-danger fa fa-lg fa-star',
30775                 tooltip : 'This field is required',
30776                 style : 'margin-right:5px;'
30777             }, label, true);
30778         }
30779         
30780         this.fireEvent('invalid', this, msg);
30781     },
30782     
30783     clearInvalid : function()
30784     {
30785         var label = this.el.select('label', true).first();
30786         var icon = this.el.select('i.fa-star', true).first();
30787
30788         if(label && icon){
30789             icon.remove();
30790         }
30791         
30792         this.fireEvent('valid', this);
30793     },
30794     
30795     getName: function()
30796     {
30797         return this.name;
30798     }
30799     
30800 });
30801
30802  /**
30803  *
30804  * This is based on 
30805  * http://masonry.desandro.com
30806  *
30807  * The idea is to render all the bricks based on vertical width...
30808  *
30809  * The original code extends 'outlayer' - we might need to use that....
30810  * 
30811  */
30812
30813
30814 /**
30815  * @class Roo.bootstrap.LayoutMasonry
30816  * @extends Roo.bootstrap.Component
30817  * Bootstrap Layout Masonry class
30818  * 
30819  * @constructor
30820  * Create a new Element
30821  * @param {Object} config The config object
30822  */
30823
30824 Roo.bootstrap.LayoutMasonry = function(config){
30825     
30826     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30827     
30828     this.bricks = [];
30829     
30830     Roo.bootstrap.LayoutMasonry.register(this);
30831     
30832     this.addEvents({
30833         // raw events
30834         /**
30835          * @event layout
30836          * Fire after layout the items
30837          * @param {Roo.bootstrap.LayoutMasonry} this
30838          * @param {Roo.EventObject} e
30839          */
30840         "layout" : true
30841     });
30842     
30843 };
30844
30845 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30846     
30847     /**
30848      * @cfg {Boolean} isLayoutInstant = no animation?
30849      */   
30850     isLayoutInstant : false, // needed?
30851    
30852     /**
30853      * @cfg {Number} boxWidth  width of the columns
30854      */   
30855     boxWidth : 450,
30856     
30857       /**
30858      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30859      */   
30860     boxHeight : 0,
30861     
30862     /**
30863      * @cfg {Number} padWidth padding below box..
30864      */   
30865     padWidth : 10, 
30866     
30867     /**
30868      * @cfg {Number} gutter gutter width..
30869      */   
30870     gutter : 10,
30871     
30872      /**
30873      * @cfg {Number} maxCols maximum number of columns
30874      */   
30875     
30876     maxCols: 0,
30877     
30878     /**
30879      * @cfg {Boolean} isAutoInitial defalut true
30880      */   
30881     isAutoInitial : true, 
30882     
30883     containerWidth: 0,
30884     
30885     /**
30886      * @cfg {Boolean} isHorizontal defalut false
30887      */   
30888     isHorizontal : false, 
30889
30890     currentSize : null,
30891     
30892     tag: 'div',
30893     
30894     cls: '',
30895     
30896     bricks: null, //CompositeElement
30897     
30898     cols : 1,
30899     
30900     _isLayoutInited : false,
30901     
30902 //    isAlternative : false, // only use for vertical layout...
30903     
30904     /**
30905      * @cfg {Number} alternativePadWidth padding below box..
30906      */   
30907     alternativePadWidth : 50,
30908     
30909     selectedBrick : [],
30910     
30911     getAutoCreate : function(){
30912         
30913         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30914         
30915         var cfg = {
30916             tag: this.tag,
30917             cls: 'blog-masonary-wrapper ' + this.cls,
30918             cn : {
30919                 cls : 'mas-boxes masonary'
30920             }
30921         };
30922         
30923         return cfg;
30924     },
30925     
30926     getChildContainer: function( )
30927     {
30928         if (this.boxesEl) {
30929             return this.boxesEl;
30930         }
30931         
30932         this.boxesEl = this.el.select('.mas-boxes').first();
30933         
30934         return this.boxesEl;
30935     },
30936     
30937     
30938     initEvents : function()
30939     {
30940         var _this = this;
30941         
30942         if(this.isAutoInitial){
30943             Roo.log('hook children rendered');
30944             this.on('childrenrendered', function() {
30945                 Roo.log('children rendered');
30946                 _this.initial();
30947             } ,this);
30948         }
30949     },
30950     
30951     initial : function()
30952     {
30953         this.selectedBrick = [];
30954         
30955         this.currentSize = this.el.getBox(true);
30956         
30957         Roo.EventManager.onWindowResize(this.resize, this); 
30958
30959         if(!this.isAutoInitial){
30960             this.layout();
30961             return;
30962         }
30963         
30964         this.layout();
30965         
30966         return;
30967         //this.layout.defer(500,this);
30968         
30969     },
30970     
30971     resize : function()
30972     {
30973         var cs = this.el.getBox(true);
30974         
30975         if (
30976                 this.currentSize.width == cs.width && 
30977                 this.currentSize.x == cs.x && 
30978                 this.currentSize.height == cs.height && 
30979                 this.currentSize.y == cs.y 
30980         ) {
30981             Roo.log("no change in with or X or Y");
30982             return;
30983         }
30984         
30985         this.currentSize = cs;
30986         
30987         this.layout();
30988         
30989     },
30990     
30991     layout : function()
30992     {   
30993         this._resetLayout();
30994         
30995         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30996         
30997         this.layoutItems( isInstant );
30998       
30999         this._isLayoutInited = true;
31000         
31001         this.fireEvent('layout', this);
31002         
31003     },
31004     
31005     _resetLayout : function()
31006     {
31007         if(this.isHorizontal){
31008             this.horizontalMeasureColumns();
31009             return;
31010         }
31011         
31012         this.verticalMeasureColumns();
31013         
31014     },
31015     
31016     verticalMeasureColumns : function()
31017     {
31018         this.getContainerWidth();
31019         
31020 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31021 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31022 //            return;
31023 //        }
31024         
31025         var boxWidth = this.boxWidth + this.padWidth;
31026         
31027         if(this.containerWidth < this.boxWidth){
31028             boxWidth = this.containerWidth
31029         }
31030         
31031         var containerWidth = this.containerWidth;
31032         
31033         var cols = Math.floor(containerWidth / boxWidth);
31034         
31035         this.cols = Math.max( cols, 1 );
31036         
31037         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31038         
31039         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31040         
31041         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31042         
31043         this.colWidth = boxWidth + avail - this.padWidth;
31044         
31045         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31046         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31047     },
31048     
31049     horizontalMeasureColumns : function()
31050     {
31051         this.getContainerWidth();
31052         
31053         var boxWidth = this.boxWidth;
31054         
31055         if(this.containerWidth < boxWidth){
31056             boxWidth = this.containerWidth;
31057         }
31058         
31059         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31060         
31061         this.el.setHeight(boxWidth);
31062         
31063     },
31064     
31065     getContainerWidth : function()
31066     {
31067         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31068     },
31069     
31070     layoutItems : function( isInstant )
31071     {
31072         Roo.log(this.bricks);
31073         
31074         var items = Roo.apply([], this.bricks);
31075         
31076         if(this.isHorizontal){
31077             this._horizontalLayoutItems( items , isInstant );
31078             return;
31079         }
31080         
31081 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31082 //            this._verticalAlternativeLayoutItems( items , isInstant );
31083 //            return;
31084 //        }
31085         
31086         this._verticalLayoutItems( items , isInstant );
31087         
31088     },
31089     
31090     _verticalLayoutItems : function ( items , isInstant)
31091     {
31092         if ( !items || !items.length ) {
31093             return;
31094         }
31095         
31096         var standard = [
31097             ['xs', 'xs', 'xs', 'tall'],
31098             ['xs', 'xs', 'tall'],
31099             ['xs', 'xs', 'sm'],
31100             ['xs', 'xs', 'xs'],
31101             ['xs', 'tall'],
31102             ['xs', 'sm'],
31103             ['xs', 'xs'],
31104             ['xs'],
31105             
31106             ['sm', 'xs', 'xs'],
31107             ['sm', 'xs'],
31108             ['sm'],
31109             
31110             ['tall', 'xs', 'xs', 'xs'],
31111             ['tall', 'xs', 'xs'],
31112             ['tall', 'xs'],
31113             ['tall']
31114             
31115         ];
31116         
31117         var queue = [];
31118         
31119         var boxes = [];
31120         
31121         var box = [];
31122         
31123         Roo.each(items, function(item, k){
31124             
31125             switch (item.size) {
31126                 // these layouts take up a full box,
31127                 case 'md' :
31128                 case 'md-left' :
31129                 case 'md-right' :
31130                 case 'wide' :
31131                     
31132                     if(box.length){
31133                         boxes.push(box);
31134                         box = [];
31135                     }
31136                     
31137                     boxes.push([item]);
31138                     
31139                     break;
31140                     
31141                 case 'xs' :
31142                 case 'sm' :
31143                 case 'tall' :
31144                     
31145                     box.push(item);
31146                     
31147                     break;
31148                 default :
31149                     break;
31150                     
31151             }
31152             
31153         }, this);
31154         
31155         if(box.length){
31156             boxes.push(box);
31157             box = [];
31158         }
31159         
31160         var filterPattern = function(box, length)
31161         {
31162             if(!box.length){
31163                 return;
31164             }
31165             
31166             var match = false;
31167             
31168             var pattern = box.slice(0, length);
31169             
31170             var format = [];
31171             
31172             Roo.each(pattern, function(i){
31173                 format.push(i.size);
31174             }, this);
31175             
31176             Roo.each(standard, function(s){
31177                 
31178                 if(String(s) != String(format)){
31179                     return;
31180                 }
31181                 
31182                 match = true;
31183                 return false;
31184                 
31185             }, this);
31186             
31187             if(!match && length == 1){
31188                 return;
31189             }
31190             
31191             if(!match){
31192                 filterPattern(box, length - 1);
31193                 return;
31194             }
31195                 
31196             queue.push(pattern);
31197
31198             box = box.slice(length, box.length);
31199
31200             filterPattern(box, 4);
31201
31202             return;
31203             
31204         }
31205         
31206         Roo.each(boxes, function(box, k){
31207             
31208             if(!box.length){
31209                 return;
31210             }
31211             
31212             if(box.length == 1){
31213                 queue.push(box);
31214                 return;
31215             }
31216             
31217             filterPattern(box, 4);
31218             
31219         }, this);
31220         
31221         this._processVerticalLayoutQueue( queue, isInstant );
31222         
31223     },
31224     
31225 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31226 //    {
31227 //        if ( !items || !items.length ) {
31228 //            return;
31229 //        }
31230 //
31231 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31232 //        
31233 //    },
31234     
31235     _horizontalLayoutItems : function ( items , isInstant)
31236     {
31237         if ( !items || !items.length || items.length < 3) {
31238             return;
31239         }
31240         
31241         items.reverse();
31242         
31243         var eItems = items.slice(0, 3);
31244         
31245         items = items.slice(3, items.length);
31246         
31247         var standard = [
31248             ['xs', 'xs', 'xs', 'wide'],
31249             ['xs', 'xs', 'wide'],
31250             ['xs', 'xs', 'sm'],
31251             ['xs', 'xs', 'xs'],
31252             ['xs', 'wide'],
31253             ['xs', 'sm'],
31254             ['xs', 'xs'],
31255             ['xs'],
31256             
31257             ['sm', 'xs', 'xs'],
31258             ['sm', 'xs'],
31259             ['sm'],
31260             
31261             ['wide', 'xs', 'xs', 'xs'],
31262             ['wide', 'xs', 'xs'],
31263             ['wide', 'xs'],
31264             ['wide'],
31265             
31266             ['wide-thin']
31267         ];
31268         
31269         var queue = [];
31270         
31271         var boxes = [];
31272         
31273         var box = [];
31274         
31275         Roo.each(items, function(item, k){
31276             
31277             switch (item.size) {
31278                 case 'md' :
31279                 case 'md-left' :
31280                 case 'md-right' :
31281                 case 'tall' :
31282                     
31283                     if(box.length){
31284                         boxes.push(box);
31285                         box = [];
31286                     }
31287                     
31288                     boxes.push([item]);
31289                     
31290                     break;
31291                     
31292                 case 'xs' :
31293                 case 'sm' :
31294                 case 'wide' :
31295                 case 'wide-thin' :
31296                     
31297                     box.push(item);
31298                     
31299                     break;
31300                 default :
31301                     break;
31302                     
31303             }
31304             
31305         }, this);
31306         
31307         if(box.length){
31308             boxes.push(box);
31309             box = [];
31310         }
31311         
31312         var filterPattern = function(box, length)
31313         {
31314             if(!box.length){
31315                 return;
31316             }
31317             
31318             var match = false;
31319             
31320             var pattern = box.slice(0, length);
31321             
31322             var format = [];
31323             
31324             Roo.each(pattern, function(i){
31325                 format.push(i.size);
31326             }, this);
31327             
31328             Roo.each(standard, function(s){
31329                 
31330                 if(String(s) != String(format)){
31331                     return;
31332                 }
31333                 
31334                 match = true;
31335                 return false;
31336                 
31337             }, this);
31338             
31339             if(!match && length == 1){
31340                 return;
31341             }
31342             
31343             if(!match){
31344                 filterPattern(box, length - 1);
31345                 return;
31346             }
31347                 
31348             queue.push(pattern);
31349
31350             box = box.slice(length, box.length);
31351
31352             filterPattern(box, 4);
31353
31354             return;
31355             
31356         }
31357         
31358         Roo.each(boxes, function(box, k){
31359             
31360             if(!box.length){
31361                 return;
31362             }
31363             
31364             if(box.length == 1){
31365                 queue.push(box);
31366                 return;
31367             }
31368             
31369             filterPattern(box, 4);
31370             
31371         }, this);
31372         
31373         
31374         var prune = [];
31375         
31376         var pos = this.el.getBox(true);
31377         
31378         var minX = pos.x;
31379         
31380         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31381         
31382         var hit_end = false;
31383         
31384         Roo.each(queue, function(box){
31385             
31386             if(hit_end){
31387                 
31388                 Roo.each(box, function(b){
31389                 
31390                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31391                     b.el.hide();
31392
31393                 }, this);
31394
31395                 return;
31396             }
31397             
31398             var mx = 0;
31399             
31400             Roo.each(box, function(b){
31401                 
31402                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31403                 b.el.show();
31404
31405                 mx = Math.max(mx, b.x);
31406                 
31407             }, this);
31408             
31409             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31410             
31411             if(maxX < minX){
31412                 
31413                 Roo.each(box, function(b){
31414                 
31415                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31416                     b.el.hide();
31417                     
31418                 }, this);
31419                 
31420                 hit_end = true;
31421                 
31422                 return;
31423             }
31424             
31425             prune.push(box);
31426             
31427         }, this);
31428         
31429         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31430     },
31431     
31432     /** Sets position of item in DOM
31433     * @param {Element} item
31434     * @param {Number} x - horizontal position
31435     * @param {Number} y - vertical position
31436     * @param {Boolean} isInstant - disables transitions
31437     */
31438     _processVerticalLayoutQueue : function( queue, isInstant )
31439     {
31440         var pos = this.el.getBox(true);
31441         var x = pos.x;
31442         var y = pos.y;
31443         var maxY = [];
31444         
31445         for (var i = 0; i < this.cols; i++){
31446             maxY[i] = pos.y;
31447         }
31448         
31449         Roo.each(queue, function(box, k){
31450             
31451             var col = k % this.cols;
31452             
31453             Roo.each(box, function(b,kk){
31454                 
31455                 b.el.position('absolute');
31456                 
31457                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31458                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31459                 
31460                 if(b.size == 'md-left' || b.size == 'md-right'){
31461                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31462                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31463                 }
31464                 
31465                 b.el.setWidth(width);
31466                 b.el.setHeight(height);
31467                 // iframe?
31468                 b.el.select('iframe',true).setSize(width,height);
31469                 
31470             }, this);
31471             
31472             for (var i = 0; i < this.cols; i++){
31473                 
31474                 if(maxY[i] < maxY[col]){
31475                     col = i;
31476                     continue;
31477                 }
31478                 
31479                 col = Math.min(col, i);
31480                 
31481             }
31482             
31483             x = pos.x + col * (this.colWidth + this.padWidth);
31484             
31485             y = maxY[col];
31486             
31487             var positions = [];
31488             
31489             switch (box.length){
31490                 case 1 :
31491                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31492                     break;
31493                 case 2 :
31494                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31495                     break;
31496                 case 3 :
31497                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31498                     break;
31499                 case 4 :
31500                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31501                     break;
31502                 default :
31503                     break;
31504             }
31505             
31506             Roo.each(box, function(b,kk){
31507                 
31508                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31509                 
31510                 var sz = b.el.getSize();
31511                 
31512                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31513                 
31514             }, this);
31515             
31516         }, this);
31517         
31518         var mY = 0;
31519         
31520         for (var i = 0; i < this.cols; i++){
31521             mY = Math.max(mY, maxY[i]);
31522         }
31523         
31524         this.el.setHeight(mY - pos.y);
31525         
31526     },
31527     
31528 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31529 //    {
31530 //        var pos = this.el.getBox(true);
31531 //        var x = pos.x;
31532 //        var y = pos.y;
31533 //        var maxX = pos.right;
31534 //        
31535 //        var maxHeight = 0;
31536 //        
31537 //        Roo.each(items, function(item, k){
31538 //            
31539 //            var c = k % 2;
31540 //            
31541 //            item.el.position('absolute');
31542 //                
31543 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31544 //
31545 //            item.el.setWidth(width);
31546 //
31547 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31548 //
31549 //            item.el.setHeight(height);
31550 //            
31551 //            if(c == 0){
31552 //                item.el.setXY([x, y], isInstant ? false : true);
31553 //            } else {
31554 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31555 //            }
31556 //            
31557 //            y = y + height + this.alternativePadWidth;
31558 //            
31559 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31560 //            
31561 //        }, this);
31562 //        
31563 //        this.el.setHeight(maxHeight);
31564 //        
31565 //    },
31566     
31567     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31568     {
31569         var pos = this.el.getBox(true);
31570         
31571         var minX = pos.x;
31572         var minY = pos.y;
31573         
31574         var maxX = pos.right;
31575         
31576         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31577         
31578         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31579         
31580         Roo.each(queue, function(box, k){
31581             
31582             Roo.each(box, function(b, kk){
31583                 
31584                 b.el.position('absolute');
31585                 
31586                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31587                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31588                 
31589                 if(b.size == 'md-left' || b.size == 'md-right'){
31590                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31591                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31592                 }
31593                 
31594                 b.el.setWidth(width);
31595                 b.el.setHeight(height);
31596                 
31597             }, this);
31598             
31599             if(!box.length){
31600                 return;
31601             }
31602             
31603             var positions = [];
31604             
31605             switch (box.length){
31606                 case 1 :
31607                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31608                     break;
31609                 case 2 :
31610                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31611                     break;
31612                 case 3 :
31613                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31614                     break;
31615                 case 4 :
31616                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31617                     break;
31618                 default :
31619                     break;
31620             }
31621             
31622             Roo.each(box, function(b,kk){
31623                 
31624                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31625                 
31626                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31627                 
31628             }, this);
31629             
31630         }, this);
31631         
31632     },
31633     
31634     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31635     {
31636         Roo.each(eItems, function(b,k){
31637             
31638             b.size = (k == 0) ? 'sm' : 'xs';
31639             b.x = (k == 0) ? 2 : 1;
31640             b.y = (k == 0) ? 2 : 1;
31641             
31642             b.el.position('absolute');
31643             
31644             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31645                 
31646             b.el.setWidth(width);
31647             
31648             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31649             
31650             b.el.setHeight(height);
31651             
31652         }, this);
31653
31654         var positions = [];
31655         
31656         positions.push({
31657             x : maxX - this.unitWidth * 2 - this.gutter,
31658             y : minY
31659         });
31660         
31661         positions.push({
31662             x : maxX - this.unitWidth,
31663             y : minY + (this.unitWidth + this.gutter) * 2
31664         });
31665         
31666         positions.push({
31667             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31668             y : minY
31669         });
31670         
31671         Roo.each(eItems, function(b,k){
31672             
31673             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31674
31675         }, this);
31676         
31677     },
31678     
31679     getVerticalOneBoxColPositions : function(x, y, box)
31680     {
31681         var pos = [];
31682         
31683         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31684         
31685         if(box[0].size == 'md-left'){
31686             rand = 0;
31687         }
31688         
31689         if(box[0].size == 'md-right'){
31690             rand = 1;
31691         }
31692         
31693         pos.push({
31694             x : x + (this.unitWidth + this.gutter) * rand,
31695             y : y
31696         });
31697         
31698         return pos;
31699     },
31700     
31701     getVerticalTwoBoxColPositions : function(x, y, box)
31702     {
31703         var pos = [];
31704         
31705         if(box[0].size == 'xs'){
31706             
31707             pos.push({
31708                 x : x,
31709                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31710             });
31711
31712             pos.push({
31713                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31714                 y : y
31715             });
31716             
31717             return pos;
31718             
31719         }
31720         
31721         pos.push({
31722             x : x,
31723             y : y
31724         });
31725
31726         pos.push({
31727             x : x + (this.unitWidth + this.gutter) * 2,
31728             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31729         });
31730         
31731         return pos;
31732         
31733     },
31734     
31735     getVerticalThreeBoxColPositions : function(x, y, box)
31736     {
31737         var pos = [];
31738         
31739         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31740             
31741             pos.push({
31742                 x : x,
31743                 y : y
31744             });
31745
31746             pos.push({
31747                 x : x + (this.unitWidth + this.gutter) * 1,
31748                 y : y
31749             });
31750             
31751             pos.push({
31752                 x : x + (this.unitWidth + this.gutter) * 2,
31753                 y : y
31754             });
31755             
31756             return pos;
31757             
31758         }
31759         
31760         if(box[0].size == 'xs' && box[1].size == 'xs'){
31761             
31762             pos.push({
31763                 x : x,
31764                 y : y
31765             });
31766
31767             pos.push({
31768                 x : x,
31769                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31770             });
31771             
31772             pos.push({
31773                 x : x + (this.unitWidth + this.gutter) * 1,
31774                 y : y
31775             });
31776             
31777             return pos;
31778             
31779         }
31780         
31781         pos.push({
31782             x : x,
31783             y : y
31784         });
31785
31786         pos.push({
31787             x : x + (this.unitWidth + this.gutter) * 2,
31788             y : y
31789         });
31790
31791         pos.push({
31792             x : x + (this.unitWidth + this.gutter) * 2,
31793             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31794         });
31795             
31796         return pos;
31797         
31798     },
31799     
31800     getVerticalFourBoxColPositions : function(x, y, box)
31801     {
31802         var pos = [];
31803         
31804         if(box[0].size == 'xs'){
31805             
31806             pos.push({
31807                 x : x,
31808                 y : y
31809             });
31810
31811             pos.push({
31812                 x : x,
31813                 y : y + (this.unitHeight + this.gutter) * 1
31814             });
31815             
31816             pos.push({
31817                 x : x,
31818                 y : y + (this.unitHeight + this.gutter) * 2
31819             });
31820             
31821             pos.push({
31822                 x : x + (this.unitWidth + this.gutter) * 1,
31823                 y : y
31824             });
31825             
31826             return pos;
31827             
31828         }
31829         
31830         pos.push({
31831             x : x,
31832             y : y
31833         });
31834
31835         pos.push({
31836             x : x + (this.unitWidth + this.gutter) * 2,
31837             y : y
31838         });
31839
31840         pos.push({
31841             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31842             y : y + (this.unitHeight + this.gutter) * 1
31843         });
31844
31845         pos.push({
31846             x : x + (this.unitWidth + this.gutter) * 2,
31847             y : y + (this.unitWidth + this.gutter) * 2
31848         });
31849
31850         return pos;
31851         
31852     },
31853     
31854     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31855     {
31856         var pos = [];
31857         
31858         if(box[0].size == 'md-left'){
31859             pos.push({
31860                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31861                 y : minY
31862             });
31863             
31864             return pos;
31865         }
31866         
31867         if(box[0].size == 'md-right'){
31868             pos.push({
31869                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31870                 y : minY + (this.unitWidth + this.gutter) * 1
31871             });
31872             
31873             return pos;
31874         }
31875         
31876         var rand = Math.floor(Math.random() * (4 - box[0].y));
31877         
31878         pos.push({
31879             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31880             y : minY + (this.unitWidth + this.gutter) * rand
31881         });
31882         
31883         return pos;
31884         
31885     },
31886     
31887     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31888     {
31889         var pos = [];
31890         
31891         if(box[0].size == 'xs'){
31892             
31893             pos.push({
31894                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31895                 y : minY
31896             });
31897
31898             pos.push({
31899                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31900                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31901             });
31902             
31903             return pos;
31904             
31905         }
31906         
31907         pos.push({
31908             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31909             y : minY
31910         });
31911
31912         pos.push({
31913             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31914             y : minY + (this.unitWidth + this.gutter) * 2
31915         });
31916         
31917         return pos;
31918         
31919     },
31920     
31921     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31922     {
31923         var pos = [];
31924         
31925         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31926             
31927             pos.push({
31928                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31929                 y : minY
31930             });
31931
31932             pos.push({
31933                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31934                 y : minY + (this.unitWidth + this.gutter) * 1
31935             });
31936             
31937             pos.push({
31938                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31939                 y : minY + (this.unitWidth + this.gutter) * 2
31940             });
31941             
31942             return pos;
31943             
31944         }
31945         
31946         if(box[0].size == 'xs' && box[1].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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31955                 y : minY
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) * 1
31961             });
31962             
31963             return pos;
31964             
31965         }
31966         
31967         pos.push({
31968             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31969             y : minY
31970         });
31971
31972         pos.push({
31973             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31974             y : minY + (this.unitWidth + this.gutter) * 2
31975         });
31976
31977         pos.push({
31978             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31979             y : minY + (this.unitWidth + this.gutter) * 2
31980         });
31981             
31982         return pos;
31983         
31984     },
31985     
31986     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31987     {
31988         var pos = [];
31989         
31990         if(box[0].size == 'xs'){
31991             
31992             pos.push({
31993                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31994                 y : minY
31995             });
31996
31997             pos.push({
31998                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31999                 y : minY
32000             });
32001             
32002             pos.push({
32003                 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),
32004                 y : minY
32005             });
32006             
32007             pos.push({
32008                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32009                 y : minY + (this.unitWidth + this.gutter) * 1
32010             });
32011             
32012             return pos;
32013             
32014         }
32015         
32016         pos.push({
32017             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32018             y : minY
32019         });
32020         
32021         pos.push({
32022             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32023             y : minY + (this.unitWidth + this.gutter) * 2
32024         });
32025         
32026         pos.push({
32027             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32028             y : minY + (this.unitWidth + this.gutter) * 2
32029         });
32030         
32031         pos.push({
32032             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),
32033             y : minY + (this.unitWidth + this.gutter) * 2
32034         });
32035
32036         return pos;
32037         
32038     },
32039     
32040     /**
32041     * remove a Masonry Brick
32042     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32043     */
32044     removeBrick : function(brick_id)
32045     {
32046         if (!brick_id) {
32047             return;
32048         }
32049         
32050         for (var i = 0; i<this.bricks.length; i++) {
32051             if (this.bricks[i].id == brick_id) {
32052                 this.bricks.splice(i,1);
32053                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32054                 this.initial();
32055             }
32056         }
32057     },
32058     
32059     /**
32060     * adds a Masonry Brick
32061     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32062     */
32063     addBrick : function(cfg)
32064     {
32065         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32066         //this.register(cn);
32067         cn.parentId = this.id;
32068         cn.render(this.el);
32069         return cn;
32070     },
32071     
32072     /**
32073     * register a Masonry Brick
32074     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32075     */
32076     
32077     register : function(brick)
32078     {
32079         this.bricks.push(brick);
32080         brick.masonryId = this.id;
32081     },
32082     
32083     /**
32084     * clear all the Masonry Brick
32085     */
32086     clearAll : function()
32087     {
32088         this.bricks = [];
32089         //this.getChildContainer().dom.innerHTML = "";
32090         this.el.dom.innerHTML = '';
32091     },
32092     
32093     getSelected : function()
32094     {
32095         if (!this.selectedBrick) {
32096             return false;
32097         }
32098         
32099         return this.selectedBrick;
32100     }
32101 });
32102
32103 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32104     
32105     groups: {},
32106      /**
32107     * register a Masonry Layout
32108     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32109     */
32110     
32111     register : function(layout)
32112     {
32113         this.groups[layout.id] = layout;
32114     },
32115     /**
32116     * fetch a  Masonry Layout based on the masonry layout ID
32117     * @param {string} the masonry layout to add
32118     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32119     */
32120     
32121     get: function(layout_id) {
32122         if (typeof(this.groups[layout_id]) == 'undefined') {
32123             return false;
32124         }
32125         return this.groups[layout_id] ;
32126     }
32127     
32128     
32129     
32130 });
32131
32132  
32133
32134  /**
32135  *
32136  * This is based on 
32137  * http://masonry.desandro.com
32138  *
32139  * The idea is to render all the bricks based on vertical width...
32140  *
32141  * The original code extends 'outlayer' - we might need to use that....
32142  * 
32143  */
32144
32145
32146 /**
32147  * @class Roo.bootstrap.LayoutMasonryAuto
32148  * @extends Roo.bootstrap.Component
32149  * Bootstrap Layout Masonry class
32150  * 
32151  * @constructor
32152  * Create a new Element
32153  * @param {Object} config The config object
32154  */
32155
32156 Roo.bootstrap.LayoutMasonryAuto = function(config){
32157     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32158 };
32159
32160 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32161     
32162       /**
32163      * @cfg {Boolean} isFitWidth  - resize the width..
32164      */   
32165     isFitWidth : false,  // options..
32166     /**
32167      * @cfg {Boolean} isOriginLeft = left align?
32168      */   
32169     isOriginLeft : true,
32170     /**
32171      * @cfg {Boolean} isOriginTop = top align?
32172      */   
32173     isOriginTop : false,
32174     /**
32175      * @cfg {Boolean} isLayoutInstant = no animation?
32176      */   
32177     isLayoutInstant : false, // needed?
32178     /**
32179      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32180      */   
32181     isResizingContainer : true,
32182     /**
32183      * @cfg {Number} columnWidth  width of the columns 
32184      */   
32185     
32186     columnWidth : 0,
32187     
32188     /**
32189      * @cfg {Number} maxCols maximum number of columns
32190      */   
32191     
32192     maxCols: 0,
32193     /**
32194      * @cfg {Number} padHeight padding below box..
32195      */   
32196     
32197     padHeight : 10, 
32198     
32199     /**
32200      * @cfg {Boolean} isAutoInitial defalut true
32201      */   
32202     
32203     isAutoInitial : true, 
32204     
32205     // private?
32206     gutter : 0,
32207     
32208     containerWidth: 0,
32209     initialColumnWidth : 0,
32210     currentSize : null,
32211     
32212     colYs : null, // array.
32213     maxY : 0,
32214     padWidth: 10,
32215     
32216     
32217     tag: 'div',
32218     cls: '',
32219     bricks: null, //CompositeElement
32220     cols : 0, // array?
32221     // element : null, // wrapped now this.el
32222     _isLayoutInited : null, 
32223     
32224     
32225     getAutoCreate : function(){
32226         
32227         var cfg = {
32228             tag: this.tag,
32229             cls: 'blog-masonary-wrapper ' + this.cls,
32230             cn : {
32231                 cls : 'mas-boxes masonary'
32232             }
32233         };
32234         
32235         return cfg;
32236     },
32237     
32238     getChildContainer: function( )
32239     {
32240         if (this.boxesEl) {
32241             return this.boxesEl;
32242         }
32243         
32244         this.boxesEl = this.el.select('.mas-boxes').first();
32245         
32246         return this.boxesEl;
32247     },
32248     
32249     
32250     initEvents : function()
32251     {
32252         var _this = this;
32253         
32254         if(this.isAutoInitial){
32255             Roo.log('hook children rendered');
32256             this.on('childrenrendered', function() {
32257                 Roo.log('children rendered');
32258                 _this.initial();
32259             } ,this);
32260         }
32261         
32262     },
32263     
32264     initial : function()
32265     {
32266         this.reloadItems();
32267
32268         this.currentSize = this.el.getBox(true);
32269
32270         /// was window resize... - let's see if this works..
32271         Roo.EventManager.onWindowResize(this.resize, this); 
32272
32273         if(!this.isAutoInitial){
32274             this.layout();
32275             return;
32276         }
32277         
32278         this.layout.defer(500,this);
32279     },
32280     
32281     reloadItems: function()
32282     {
32283         this.bricks = this.el.select('.masonry-brick', true);
32284         
32285         this.bricks.each(function(b) {
32286             //Roo.log(b.getSize());
32287             if (!b.attr('originalwidth')) {
32288                 b.attr('originalwidth',  b.getSize().width);
32289             }
32290             
32291         });
32292         
32293         Roo.log(this.bricks.elements.length);
32294     },
32295     
32296     resize : function()
32297     {
32298         Roo.log('resize');
32299         var cs = this.el.getBox(true);
32300         
32301         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32302             Roo.log("no change in with or X");
32303             return;
32304         }
32305         this.currentSize = cs;
32306         this.layout();
32307     },
32308     
32309     layout : function()
32310     {
32311          Roo.log('layout');
32312         this._resetLayout();
32313         //this._manageStamps();
32314       
32315         // don't animate first layout
32316         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32317         this.layoutItems( isInstant );
32318       
32319         // flag for initalized
32320         this._isLayoutInited = true;
32321     },
32322     
32323     layoutItems : function( isInstant )
32324     {
32325         //var items = this._getItemsForLayout( this.items );
32326         // original code supports filtering layout items.. we just ignore it..
32327         
32328         this._layoutItems( this.bricks , isInstant );
32329       
32330         this._postLayout();
32331     },
32332     _layoutItems : function ( items , isInstant)
32333     {
32334        //this.fireEvent( 'layout', this, items );
32335     
32336
32337         if ( !items || !items.elements.length ) {
32338           // no items, emit event with empty array
32339             return;
32340         }
32341
32342         var queue = [];
32343         items.each(function(item) {
32344             Roo.log("layout item");
32345             Roo.log(item);
32346             // get x/y object from method
32347             var position = this._getItemLayoutPosition( item );
32348             // enqueue
32349             position.item = item;
32350             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32351             queue.push( position );
32352         }, this);
32353       
32354         this._processLayoutQueue( queue );
32355     },
32356     /** Sets position of item in DOM
32357     * @param {Element} item
32358     * @param {Number} x - horizontal position
32359     * @param {Number} y - vertical position
32360     * @param {Boolean} isInstant - disables transitions
32361     */
32362     _processLayoutQueue : function( queue )
32363     {
32364         for ( var i=0, len = queue.length; i < len; i++ ) {
32365             var obj = queue[i];
32366             obj.item.position('absolute');
32367             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32368         }
32369     },
32370       
32371     
32372     /**
32373     * Any logic you want to do after each layout,
32374     * i.e. size the container
32375     */
32376     _postLayout : function()
32377     {
32378         this.resizeContainer();
32379     },
32380     
32381     resizeContainer : function()
32382     {
32383         if ( !this.isResizingContainer ) {
32384             return;
32385         }
32386         var size = this._getContainerSize();
32387         if ( size ) {
32388             this.el.setSize(size.width,size.height);
32389             this.boxesEl.setSize(size.width,size.height);
32390         }
32391     },
32392     
32393     
32394     
32395     _resetLayout : function()
32396     {
32397         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32398         this.colWidth = this.el.getWidth();
32399         //this.gutter = this.el.getWidth(); 
32400         
32401         this.measureColumns();
32402
32403         // reset column Y
32404         var i = this.cols;
32405         this.colYs = [];
32406         while (i--) {
32407             this.colYs.push( 0 );
32408         }
32409     
32410         this.maxY = 0;
32411     },
32412
32413     measureColumns : function()
32414     {
32415         this.getContainerWidth();
32416       // if columnWidth is 0, default to outerWidth of first item
32417         if ( !this.columnWidth ) {
32418             var firstItem = this.bricks.first();
32419             Roo.log(firstItem);
32420             this.columnWidth  = this.containerWidth;
32421             if (firstItem && firstItem.attr('originalwidth') ) {
32422                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32423             }
32424             // columnWidth fall back to item of first element
32425             Roo.log("set column width?");
32426                         this.initialColumnWidth = this.columnWidth  ;
32427
32428             // if first elem has no width, default to size of container
32429             
32430         }
32431         
32432         
32433         if (this.initialColumnWidth) {
32434             this.columnWidth = this.initialColumnWidth;
32435         }
32436         
32437         
32438             
32439         // column width is fixed at the top - however if container width get's smaller we should
32440         // reduce it...
32441         
32442         // this bit calcs how man columns..
32443             
32444         var columnWidth = this.columnWidth += this.gutter;
32445       
32446         // calculate columns
32447         var containerWidth = this.containerWidth + this.gutter;
32448         
32449         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32450         // fix rounding errors, typically with gutters
32451         var excess = columnWidth - containerWidth % columnWidth;
32452         
32453         
32454         // if overshoot is less than a pixel, round up, otherwise floor it
32455         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32456         cols = Math[ mathMethod ]( cols );
32457         this.cols = Math.max( cols, 1 );
32458         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32459         
32460          // padding positioning..
32461         var totalColWidth = this.cols * this.columnWidth;
32462         var padavail = this.containerWidth - totalColWidth;
32463         // so for 2 columns - we need 3 'pads'
32464         
32465         var padNeeded = (1+this.cols) * this.padWidth;
32466         
32467         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32468         
32469         this.columnWidth += padExtra
32470         //this.padWidth = Math.floor(padavail /  ( this.cols));
32471         
32472         // adjust colum width so that padding is fixed??
32473         
32474         // we have 3 columns ... total = width * 3
32475         // we have X left over... that should be used by 
32476         
32477         //if (this.expandC) {
32478             
32479         //}
32480         
32481         
32482         
32483     },
32484     
32485     getContainerWidth : function()
32486     {
32487        /* // container is parent if fit width
32488         var container = this.isFitWidth ? this.element.parentNode : this.element;
32489         // check that this.size and size are there
32490         // IE8 triggers resize on body size change, so they might not be
32491         
32492         var size = getSize( container );  //FIXME
32493         this.containerWidth = size && size.innerWidth; //FIXME
32494         */
32495          
32496         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32497         
32498     },
32499     
32500     _getItemLayoutPosition : function( item )  // what is item?
32501     {
32502         // we resize the item to our columnWidth..
32503       
32504         item.setWidth(this.columnWidth);
32505         item.autoBoxAdjust  = false;
32506         
32507         var sz = item.getSize();
32508  
32509         // how many columns does this brick span
32510         var remainder = this.containerWidth % this.columnWidth;
32511         
32512         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32513         // round if off by 1 pixel, otherwise use ceil
32514         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32515         colSpan = Math.min( colSpan, this.cols );
32516         
32517         // normally this should be '1' as we dont' currently allow multi width columns..
32518         
32519         var colGroup = this._getColGroup( colSpan );
32520         // get the minimum Y value from the columns
32521         var minimumY = Math.min.apply( Math, colGroup );
32522         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32523         
32524         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32525          
32526         // position the brick
32527         var position = {
32528             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32529             y: this.currentSize.y + minimumY + this.padHeight
32530         };
32531         
32532         Roo.log(position);
32533         // apply setHeight to necessary columns
32534         var setHeight = minimumY + sz.height + this.padHeight;
32535         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32536         
32537         var setSpan = this.cols + 1 - colGroup.length;
32538         for ( var i = 0; i < setSpan; i++ ) {
32539           this.colYs[ shortColIndex + i ] = setHeight ;
32540         }
32541       
32542         return position;
32543     },
32544     
32545     /**
32546      * @param {Number} colSpan - number of columns the element spans
32547      * @returns {Array} colGroup
32548      */
32549     _getColGroup : function( colSpan )
32550     {
32551         if ( colSpan < 2 ) {
32552           // if brick spans only one column, use all the column Ys
32553           return this.colYs;
32554         }
32555       
32556         var colGroup = [];
32557         // how many different places could this brick fit horizontally
32558         var groupCount = this.cols + 1 - colSpan;
32559         // for each group potential horizontal position
32560         for ( var i = 0; i < groupCount; i++ ) {
32561           // make an array of colY values for that one group
32562           var groupColYs = this.colYs.slice( i, i + colSpan );
32563           // and get the max value of the array
32564           colGroup[i] = Math.max.apply( Math, groupColYs );
32565         }
32566         return colGroup;
32567     },
32568     /*
32569     _manageStamp : function( stamp )
32570     {
32571         var stampSize =  stamp.getSize();
32572         var offset = stamp.getBox();
32573         // get the columns that this stamp affects
32574         var firstX = this.isOriginLeft ? offset.x : offset.right;
32575         var lastX = firstX + stampSize.width;
32576         var firstCol = Math.floor( firstX / this.columnWidth );
32577         firstCol = Math.max( 0, firstCol );
32578         
32579         var lastCol = Math.floor( lastX / this.columnWidth );
32580         // lastCol should not go over if multiple of columnWidth #425
32581         lastCol -= lastX % this.columnWidth ? 0 : 1;
32582         lastCol = Math.min( this.cols - 1, lastCol );
32583         
32584         // set colYs to bottom of the stamp
32585         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32586             stampSize.height;
32587             
32588         for ( var i = firstCol; i <= lastCol; i++ ) {
32589           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32590         }
32591     },
32592     */
32593     
32594     _getContainerSize : function()
32595     {
32596         this.maxY = Math.max.apply( Math, this.colYs );
32597         var size = {
32598             height: this.maxY
32599         };
32600       
32601         if ( this.isFitWidth ) {
32602             size.width = this._getContainerFitWidth();
32603         }
32604       
32605         return size;
32606     },
32607     
32608     _getContainerFitWidth : function()
32609     {
32610         var unusedCols = 0;
32611         // count unused columns
32612         var i = this.cols;
32613         while ( --i ) {
32614           if ( this.colYs[i] !== 0 ) {
32615             break;
32616           }
32617           unusedCols++;
32618         }
32619         // fit container to columns that have been used
32620         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32621     },
32622     
32623     needsResizeLayout : function()
32624     {
32625         var previousWidth = this.containerWidth;
32626         this.getContainerWidth();
32627         return previousWidth !== this.containerWidth;
32628     }
32629  
32630 });
32631
32632  
32633
32634  /*
32635  * - LGPL
32636  *
32637  * element
32638  * 
32639  */
32640
32641 /**
32642  * @class Roo.bootstrap.MasonryBrick
32643  * @extends Roo.bootstrap.Component
32644  * Bootstrap MasonryBrick class
32645  * 
32646  * @constructor
32647  * Create a new MasonryBrick
32648  * @param {Object} config The config object
32649  */
32650
32651 Roo.bootstrap.MasonryBrick = function(config){
32652     
32653     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32654     
32655     Roo.bootstrap.MasonryBrick.register(this);
32656     
32657     this.addEvents({
32658         // raw events
32659         /**
32660          * @event click
32661          * When a MasonryBrick is clcik
32662          * @param {Roo.bootstrap.MasonryBrick} this
32663          * @param {Roo.EventObject} e
32664          */
32665         "click" : true
32666     });
32667 };
32668
32669 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32670     
32671     /**
32672      * @cfg {String} title
32673      */   
32674     title : '',
32675     /**
32676      * @cfg {String} html
32677      */   
32678     html : '',
32679     /**
32680      * @cfg {String} bgimage
32681      */   
32682     bgimage : '',
32683     /**
32684      * @cfg {String} videourl
32685      */   
32686     videourl : '',
32687     /**
32688      * @cfg {String} cls
32689      */   
32690     cls : '',
32691     /**
32692      * @cfg {String} href
32693      */   
32694     href : '',
32695     /**
32696      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32697      */   
32698     size : 'xs',
32699     
32700     /**
32701      * @cfg {String} placetitle (center|bottom)
32702      */   
32703     placetitle : '',
32704     
32705     /**
32706      * @cfg {Boolean} isFitContainer defalut true
32707      */   
32708     isFitContainer : true, 
32709     
32710     /**
32711      * @cfg {Boolean} preventDefault defalut false
32712      */   
32713     preventDefault : false, 
32714     
32715     /**
32716      * @cfg {Boolean} inverse defalut false
32717      */   
32718     maskInverse : false, 
32719     
32720     getAutoCreate : function()
32721     {
32722         if(!this.isFitContainer){
32723             return this.getSplitAutoCreate();
32724         }
32725         
32726         var cls = 'masonry-brick masonry-brick-full';
32727         
32728         if(this.href.length){
32729             cls += ' masonry-brick-link';
32730         }
32731         
32732         if(this.bgimage.length){
32733             cls += ' masonry-brick-image';
32734         }
32735         
32736         if(this.maskInverse){
32737             cls += ' mask-inverse';
32738         }
32739         
32740         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32741             cls += ' enable-mask';
32742         }
32743         
32744         if(this.size){
32745             cls += ' masonry-' + this.size + '-brick';
32746         }
32747         
32748         if(this.placetitle.length){
32749             
32750             switch (this.placetitle) {
32751                 case 'center' :
32752                     cls += ' masonry-center-title';
32753                     break;
32754                 case 'bottom' :
32755                     cls += ' masonry-bottom-title';
32756                     break;
32757                 default:
32758                     break;
32759             }
32760             
32761         } else {
32762             if(!this.html.length && !this.bgimage.length){
32763                 cls += ' masonry-center-title';
32764             }
32765
32766             if(!this.html.length && this.bgimage.length){
32767                 cls += ' masonry-bottom-title';
32768             }
32769         }
32770         
32771         if(this.cls){
32772             cls += ' ' + this.cls;
32773         }
32774         
32775         var cfg = {
32776             tag: (this.href.length) ? 'a' : 'div',
32777             cls: cls,
32778             cn: [
32779                 {
32780                     tag: 'div',
32781                     cls: 'masonry-brick-mask'
32782                 },
32783                 {
32784                     tag: 'div',
32785                     cls: 'masonry-brick-paragraph',
32786                     cn: []
32787                 }
32788             ]
32789         };
32790         
32791         if(this.href.length){
32792             cfg.href = this.href;
32793         }
32794         
32795         var cn = cfg.cn[1].cn;
32796         
32797         if(this.title.length){
32798             cn.push({
32799                 tag: 'h4',
32800                 cls: 'masonry-brick-title',
32801                 html: this.title
32802             });
32803         }
32804         
32805         if(this.html.length){
32806             cn.push({
32807                 tag: 'p',
32808                 cls: 'masonry-brick-text',
32809                 html: this.html
32810             });
32811         }
32812         
32813         if (!this.title.length && !this.html.length) {
32814             cfg.cn[1].cls += ' hide';
32815         }
32816         
32817         if(this.bgimage.length){
32818             cfg.cn.push({
32819                 tag: 'img',
32820                 cls: 'masonry-brick-image-view',
32821                 src: this.bgimage
32822             });
32823         }
32824         
32825         if(this.videourl.length){
32826             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32827             // youtube support only?
32828             cfg.cn.push({
32829                 tag: 'iframe',
32830                 cls: 'masonry-brick-image-view',
32831                 src: vurl,
32832                 frameborder : 0,
32833                 allowfullscreen : true
32834             });
32835         }
32836         
32837         return cfg;
32838         
32839     },
32840     
32841     getSplitAutoCreate : function()
32842     {
32843         var cls = 'masonry-brick masonry-brick-split';
32844         
32845         if(this.href.length){
32846             cls += ' masonry-brick-link';
32847         }
32848         
32849         if(this.bgimage.length){
32850             cls += ' masonry-brick-image';
32851         }
32852         
32853         if(this.size){
32854             cls += ' masonry-' + this.size + '-brick';
32855         }
32856         
32857         switch (this.placetitle) {
32858             case 'center' :
32859                 cls += ' masonry-center-title';
32860                 break;
32861             case 'bottom' :
32862                 cls += ' masonry-bottom-title';
32863                 break;
32864             default:
32865                 if(!this.bgimage.length){
32866                     cls += ' masonry-center-title';
32867                 }
32868
32869                 if(this.bgimage.length){
32870                     cls += ' masonry-bottom-title';
32871                 }
32872                 break;
32873         }
32874         
32875         if(this.cls){
32876             cls += ' ' + this.cls;
32877         }
32878         
32879         var cfg = {
32880             tag: (this.href.length) ? 'a' : 'div',
32881             cls: cls,
32882             cn: [
32883                 {
32884                     tag: 'div',
32885                     cls: 'masonry-brick-split-head',
32886                     cn: [
32887                         {
32888                             tag: 'div',
32889                             cls: 'masonry-brick-paragraph',
32890                             cn: []
32891                         }
32892                     ]
32893                 },
32894                 {
32895                     tag: 'div',
32896                     cls: 'masonry-brick-split-body',
32897                     cn: []
32898                 }
32899             ]
32900         };
32901         
32902         if(this.href.length){
32903             cfg.href = this.href;
32904         }
32905         
32906         if(this.title.length){
32907             cfg.cn[0].cn[0].cn.push({
32908                 tag: 'h4',
32909                 cls: 'masonry-brick-title',
32910                 html: this.title
32911             });
32912         }
32913         
32914         if(this.html.length){
32915             cfg.cn[1].cn.push({
32916                 tag: 'p',
32917                 cls: 'masonry-brick-text',
32918                 html: this.html
32919             });
32920         }
32921
32922         if(this.bgimage.length){
32923             cfg.cn[0].cn.push({
32924                 tag: 'img',
32925                 cls: 'masonry-brick-image-view',
32926                 src: this.bgimage
32927             });
32928         }
32929         
32930         if(this.videourl.length){
32931             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32932             // youtube support only?
32933             cfg.cn[0].cn.cn.push({
32934                 tag: 'iframe',
32935                 cls: 'masonry-brick-image-view',
32936                 src: vurl,
32937                 frameborder : 0,
32938                 allowfullscreen : true
32939             });
32940         }
32941         
32942         return cfg;
32943     },
32944     
32945     initEvents: function() 
32946     {
32947         switch (this.size) {
32948             case 'xs' :
32949                 this.x = 1;
32950                 this.y = 1;
32951                 break;
32952             case 'sm' :
32953                 this.x = 2;
32954                 this.y = 2;
32955                 break;
32956             case 'md' :
32957             case 'md-left' :
32958             case 'md-right' :
32959                 this.x = 3;
32960                 this.y = 3;
32961                 break;
32962             case 'tall' :
32963                 this.x = 2;
32964                 this.y = 3;
32965                 break;
32966             case 'wide' :
32967                 this.x = 3;
32968                 this.y = 2;
32969                 break;
32970             case 'wide-thin' :
32971                 this.x = 3;
32972                 this.y = 1;
32973                 break;
32974                         
32975             default :
32976                 break;
32977         }
32978         
32979         if(Roo.isTouch){
32980             this.el.on('touchstart', this.onTouchStart, this);
32981             this.el.on('touchmove', this.onTouchMove, this);
32982             this.el.on('touchend', this.onTouchEnd, this);
32983             this.el.on('contextmenu', this.onContextMenu, this);
32984         } else {
32985             this.el.on('mouseenter'  ,this.enter, this);
32986             this.el.on('mouseleave', this.leave, this);
32987             this.el.on('click', this.onClick, this);
32988         }
32989         
32990         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32991             this.parent().bricks.push(this);   
32992         }
32993         
32994     },
32995     
32996     onClick: function(e, el)
32997     {
32998         var time = this.endTimer - this.startTimer;
32999         // Roo.log(e.preventDefault());
33000         if(Roo.isTouch){
33001             if(time > 1000){
33002                 e.preventDefault();
33003                 return;
33004             }
33005         }
33006         
33007         if(!this.preventDefault){
33008             return;
33009         }
33010         
33011         e.preventDefault();
33012         
33013         if (this.activeClass != '') {
33014             this.selectBrick();
33015         }
33016         
33017         this.fireEvent('click', this, e);
33018     },
33019     
33020     enter: function(e, el)
33021     {
33022         e.preventDefault();
33023         
33024         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33025             return;
33026         }
33027         
33028         if(this.bgimage.length && this.html.length){
33029             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33030         }
33031     },
33032     
33033     leave: function(e, el)
33034     {
33035         e.preventDefault();
33036         
33037         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33038             return;
33039         }
33040         
33041         if(this.bgimage.length && this.html.length){
33042             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33043         }
33044     },
33045     
33046     onTouchStart: function(e, el)
33047     {
33048 //        e.preventDefault();
33049         
33050         this.touchmoved = false;
33051         
33052         if(!this.isFitContainer){
33053             return;
33054         }
33055         
33056         if(!this.bgimage.length || !this.html.length){
33057             return;
33058         }
33059         
33060         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33061         
33062         this.timer = new Date().getTime();
33063         
33064     },
33065     
33066     onTouchMove: function(e, el)
33067     {
33068         this.touchmoved = true;
33069     },
33070     
33071     onContextMenu : function(e,el)
33072     {
33073         e.preventDefault();
33074         e.stopPropagation();
33075         return false;
33076     },
33077     
33078     onTouchEnd: function(e, el)
33079     {
33080 //        e.preventDefault();
33081         
33082         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33083         
33084             this.leave(e,el);
33085             
33086             return;
33087         }
33088         
33089         if(!this.bgimage.length || !this.html.length){
33090             
33091             if(this.href.length){
33092                 window.location.href = this.href;
33093             }
33094             
33095             return;
33096         }
33097         
33098         if(!this.isFitContainer){
33099             return;
33100         }
33101         
33102         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33103         
33104         window.location.href = this.href;
33105     },
33106     
33107     //selection on single brick only
33108     selectBrick : function() {
33109         
33110         if (!this.parentId) {
33111             return;
33112         }
33113         
33114         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33115         var index = m.selectedBrick.indexOf(this.id);
33116         
33117         if ( index > -1) {
33118             m.selectedBrick.splice(index,1);
33119             this.el.removeClass(this.activeClass);
33120             return;
33121         }
33122         
33123         for(var i = 0; i < m.selectedBrick.length; i++) {
33124             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33125             b.el.removeClass(b.activeClass);
33126         }
33127         
33128         m.selectedBrick = [];
33129         
33130         m.selectedBrick.push(this.id);
33131         this.el.addClass(this.activeClass);
33132         return;
33133     },
33134     
33135     isSelected : function(){
33136         return this.el.hasClass(this.activeClass);
33137         
33138     }
33139 });
33140
33141 Roo.apply(Roo.bootstrap.MasonryBrick, {
33142     
33143     //groups: {},
33144     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33145      /**
33146     * register a Masonry Brick
33147     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33148     */
33149     
33150     register : function(brick)
33151     {
33152         //this.groups[brick.id] = brick;
33153         this.groups.add(brick.id, brick);
33154     },
33155     /**
33156     * fetch a  masonry brick based on the masonry brick ID
33157     * @param {string} the masonry brick to add
33158     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33159     */
33160     
33161     get: function(brick_id) 
33162     {
33163         // if (typeof(this.groups[brick_id]) == 'undefined') {
33164         //     return false;
33165         // }
33166         // return this.groups[brick_id] ;
33167         
33168         if(this.groups.key(brick_id)) {
33169             return this.groups.key(brick_id);
33170         }
33171         
33172         return false;
33173     }
33174     
33175     
33176     
33177 });
33178
33179  /*
33180  * - LGPL
33181  *
33182  * element
33183  * 
33184  */
33185
33186 /**
33187  * @class Roo.bootstrap.Brick
33188  * @extends Roo.bootstrap.Component
33189  * Bootstrap Brick class
33190  * 
33191  * @constructor
33192  * Create a new Brick
33193  * @param {Object} config The config object
33194  */
33195
33196 Roo.bootstrap.Brick = function(config){
33197     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33198     
33199     this.addEvents({
33200         // raw events
33201         /**
33202          * @event click
33203          * When a Brick is click
33204          * @param {Roo.bootstrap.Brick} this
33205          * @param {Roo.EventObject} e
33206          */
33207         "click" : true
33208     });
33209 };
33210
33211 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33212     
33213     /**
33214      * @cfg {String} title
33215      */   
33216     title : '',
33217     /**
33218      * @cfg {String} html
33219      */   
33220     html : '',
33221     /**
33222      * @cfg {String} bgimage
33223      */   
33224     bgimage : '',
33225     /**
33226      * @cfg {String} cls
33227      */   
33228     cls : '',
33229     /**
33230      * @cfg {String} href
33231      */   
33232     href : '',
33233     /**
33234      * @cfg {String} video
33235      */   
33236     video : '',
33237     /**
33238      * @cfg {Boolean} square
33239      */   
33240     square : true,
33241     
33242     getAutoCreate : function()
33243     {
33244         var cls = 'roo-brick';
33245         
33246         if(this.href.length){
33247             cls += ' roo-brick-link';
33248         }
33249         
33250         if(this.bgimage.length){
33251             cls += ' roo-brick-image';
33252         }
33253         
33254         if(!this.html.length && !this.bgimage.length){
33255             cls += ' roo-brick-center-title';
33256         }
33257         
33258         if(!this.html.length && this.bgimage.length){
33259             cls += ' roo-brick-bottom-title';
33260         }
33261         
33262         if(this.cls){
33263             cls += ' ' + this.cls;
33264         }
33265         
33266         var cfg = {
33267             tag: (this.href.length) ? 'a' : 'div',
33268             cls: cls,
33269             cn: [
33270                 {
33271                     tag: 'div',
33272                     cls: 'roo-brick-paragraph',
33273                     cn: []
33274                 }
33275             ]
33276         };
33277         
33278         if(this.href.length){
33279             cfg.href = this.href;
33280         }
33281         
33282         var cn = cfg.cn[0].cn;
33283         
33284         if(this.title.length){
33285             cn.push({
33286                 tag: 'h4',
33287                 cls: 'roo-brick-title',
33288                 html: this.title
33289             });
33290         }
33291         
33292         if(this.html.length){
33293             cn.push({
33294                 tag: 'p',
33295                 cls: 'roo-brick-text',
33296                 html: this.html
33297             });
33298         } else {
33299             cn.cls += ' hide';
33300         }
33301         
33302         if(this.bgimage.length){
33303             cfg.cn.push({
33304                 tag: 'img',
33305                 cls: 'roo-brick-image-view',
33306                 src: this.bgimage
33307             });
33308         }
33309         
33310         return cfg;
33311     },
33312     
33313     initEvents: function() 
33314     {
33315         if(this.title.length || this.html.length){
33316             this.el.on('mouseenter'  ,this.enter, this);
33317             this.el.on('mouseleave', this.leave, this);
33318         }
33319         
33320         Roo.EventManager.onWindowResize(this.resize, this); 
33321         
33322         if(this.bgimage.length){
33323             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33324             this.imageEl.on('load', this.onImageLoad, this);
33325             return;
33326         }
33327         
33328         this.resize();
33329     },
33330     
33331     onImageLoad : function()
33332     {
33333         this.resize();
33334     },
33335     
33336     resize : function()
33337     {
33338         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33339         
33340         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33341         
33342         if(this.bgimage.length){
33343             var image = this.el.select('.roo-brick-image-view', true).first();
33344             
33345             image.setWidth(paragraph.getWidth());
33346             
33347             if(this.square){
33348                 image.setHeight(paragraph.getWidth());
33349             }
33350             
33351             this.el.setHeight(image.getHeight());
33352             paragraph.setHeight(image.getHeight());
33353             
33354         }
33355         
33356     },
33357     
33358     enter: function(e, el)
33359     {
33360         e.preventDefault();
33361         
33362         if(this.bgimage.length){
33363             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33364             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33365         }
33366     },
33367     
33368     leave: function(e, el)
33369     {
33370         e.preventDefault();
33371         
33372         if(this.bgimage.length){
33373             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33374             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33375         }
33376     }
33377     
33378 });
33379
33380  
33381
33382  /*
33383  * - LGPL
33384  *
33385  * Number field 
33386  */
33387
33388 /**
33389  * @class Roo.bootstrap.NumberField
33390  * @extends Roo.bootstrap.Input
33391  * Bootstrap NumberField class
33392  * 
33393  * 
33394  * 
33395  * 
33396  * @constructor
33397  * Create a new NumberField
33398  * @param {Object} config The config object
33399  */
33400
33401 Roo.bootstrap.NumberField = function(config){
33402     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33403 };
33404
33405 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33406     
33407     /**
33408      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33409      */
33410     allowDecimals : true,
33411     /**
33412      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33413      */
33414     decimalSeparator : ".",
33415     /**
33416      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33417      */
33418     decimalPrecision : 2,
33419     /**
33420      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33421      */
33422     allowNegative : true,
33423     
33424     /**
33425      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33426      */
33427     allowZero: true,
33428     /**
33429      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33430      */
33431     minValue : Number.NEGATIVE_INFINITY,
33432     /**
33433      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33434      */
33435     maxValue : Number.MAX_VALUE,
33436     /**
33437      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33438      */
33439     minText : "The minimum value for this field is {0}",
33440     /**
33441      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33442      */
33443     maxText : "The maximum value for this field is {0}",
33444     /**
33445      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33446      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33447      */
33448     nanText : "{0} is not a valid number",
33449     /**
33450      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33451      */
33452     thousandsDelimiter : false,
33453     /**
33454      * @cfg {String} valueAlign alignment of value
33455      */
33456     valueAlign : "left",
33457
33458     getAutoCreate : function()
33459     {
33460         var hiddenInput = {
33461             tag: 'input',
33462             type: 'hidden',
33463             id: Roo.id(),
33464             cls: 'hidden-number-input'
33465         };
33466         
33467         if (this.name) {
33468             hiddenInput.name = this.name;
33469         }
33470         
33471         this.name = '';
33472         
33473         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33474         
33475         this.name = hiddenInput.name;
33476         
33477         if(cfg.cn.length > 0) {
33478             cfg.cn.push(hiddenInput);
33479         }
33480         
33481         return cfg;
33482     },
33483
33484     // private
33485     initEvents : function()
33486     {   
33487         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33488         
33489         var allowed = "0123456789";
33490         
33491         if(this.allowDecimals){
33492             allowed += this.decimalSeparator;
33493         }
33494         
33495         if(this.allowNegative){
33496             allowed += "-";
33497         }
33498         
33499         if(this.thousandsDelimiter) {
33500             allowed += ",";
33501         }
33502         
33503         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33504         
33505         var keyPress = function(e){
33506             
33507             var k = e.getKey();
33508             
33509             var c = e.getCharCode();
33510             
33511             if(
33512                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33513                     allowed.indexOf(String.fromCharCode(c)) === -1
33514             ){
33515                 e.stopEvent();
33516                 return;
33517             }
33518             
33519             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33520                 return;
33521             }
33522             
33523             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33524                 e.stopEvent();
33525             }
33526         };
33527         
33528         this.el.on("keypress", keyPress, this);
33529     },
33530     
33531     validateValue : function(value)
33532     {
33533         
33534         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33535             return false;
33536         }
33537         
33538         var num = this.parseValue(value);
33539         
33540         if(isNaN(num)){
33541             this.markInvalid(String.format(this.nanText, value));
33542             return false;
33543         }
33544         
33545         if(num < this.minValue){
33546             this.markInvalid(String.format(this.minText, this.minValue));
33547             return false;
33548         }
33549         
33550         if(num > this.maxValue){
33551             this.markInvalid(String.format(this.maxText, this.maxValue));
33552             return false;
33553         }
33554         
33555         return true;
33556     },
33557
33558     getValue : function()
33559     {
33560         var v = this.hiddenEl().getValue();
33561         
33562         return this.fixPrecision(this.parseValue(v));
33563     },
33564
33565     parseValue : function(value)
33566     {
33567         if(this.thousandsDelimiter) {
33568             value += "";
33569             r = new RegExp(",", "g");
33570             value = value.replace(r, "");
33571         }
33572         
33573         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33574         return isNaN(value) ? '' : value;
33575     },
33576
33577     fixPrecision : function(value)
33578     {
33579         if(this.thousandsDelimiter) {
33580             value += "";
33581             r = new RegExp(",", "g");
33582             value = value.replace(r, "");
33583         }
33584         
33585         var nan = isNaN(value);
33586         
33587         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33588             return nan ? '' : value;
33589         }
33590         return parseFloat(value).toFixed(this.decimalPrecision);
33591     },
33592
33593     setValue : function(v)
33594     {
33595         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33596         
33597         this.value = v;
33598         
33599         if(this.rendered){
33600             
33601             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33602             
33603             this.inputEl().dom.value = (v == '') ? '' :
33604                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33605             
33606             if(!this.allowZero && v === '0') {
33607                 this.hiddenEl().dom.value = '';
33608                 this.inputEl().dom.value = '';
33609             }
33610             
33611             this.validate();
33612         }
33613     },
33614
33615     decimalPrecisionFcn : function(v)
33616     {
33617         return Math.floor(v);
33618     },
33619
33620     beforeBlur : function()
33621     {
33622         var v = this.parseValue(this.getRawValue());
33623         
33624         if(v || v === 0 || v === ''){
33625             this.setValue(v);
33626         }
33627     },
33628     
33629     hiddenEl : function()
33630     {
33631         return this.el.select('input.hidden-number-input',true).first();
33632     }
33633     
33634 });
33635
33636  
33637
33638 /*
33639 * Licence: LGPL
33640 */
33641
33642 /**
33643  * @class Roo.bootstrap.DocumentSlider
33644  * @extends Roo.bootstrap.Component
33645  * Bootstrap DocumentSlider class
33646  * 
33647  * @constructor
33648  * Create a new DocumentViewer
33649  * @param {Object} config The config object
33650  */
33651
33652 Roo.bootstrap.DocumentSlider = function(config){
33653     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33654     
33655     this.files = [];
33656     
33657     this.addEvents({
33658         /**
33659          * @event initial
33660          * Fire after initEvent
33661          * @param {Roo.bootstrap.DocumentSlider} this
33662          */
33663         "initial" : true,
33664         /**
33665          * @event update
33666          * Fire after update
33667          * @param {Roo.bootstrap.DocumentSlider} this
33668          */
33669         "update" : true,
33670         /**
33671          * @event click
33672          * Fire after click
33673          * @param {Roo.bootstrap.DocumentSlider} this
33674          */
33675         "click" : true
33676     });
33677 };
33678
33679 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33680     
33681     files : false,
33682     
33683     indicator : 0,
33684     
33685     getAutoCreate : function()
33686     {
33687         var cfg = {
33688             tag : 'div',
33689             cls : 'roo-document-slider',
33690             cn : [
33691                 {
33692                     tag : 'div',
33693                     cls : 'roo-document-slider-header',
33694                     cn : [
33695                         {
33696                             tag : 'div',
33697                             cls : 'roo-document-slider-header-title'
33698                         }
33699                     ]
33700                 },
33701                 {
33702                     tag : 'div',
33703                     cls : 'roo-document-slider-body',
33704                     cn : [
33705                         {
33706                             tag : 'div',
33707                             cls : 'roo-document-slider-prev',
33708                             cn : [
33709                                 {
33710                                     tag : 'i',
33711                                     cls : 'fa fa-chevron-left'
33712                                 }
33713                             ]
33714                         },
33715                         {
33716                             tag : 'div',
33717                             cls : 'roo-document-slider-thumb',
33718                             cn : [
33719                                 {
33720                                     tag : 'img',
33721                                     cls : 'roo-document-slider-image'
33722                                 }
33723                             ]
33724                         },
33725                         {
33726                             tag : 'div',
33727                             cls : 'roo-document-slider-next',
33728                             cn : [
33729                                 {
33730                                     tag : 'i',
33731                                     cls : 'fa fa-chevron-right'
33732                                 }
33733                             ]
33734                         }
33735                     ]
33736                 }
33737             ]
33738         };
33739         
33740         return cfg;
33741     },
33742     
33743     initEvents : function()
33744     {
33745         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33746         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33747         
33748         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33749         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33750         
33751         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33752         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33753         
33754         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33755         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33756         
33757         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33758         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33759         
33760         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33761         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33762         
33763         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33764         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33765         
33766         this.thumbEl.on('click', this.onClick, this);
33767         
33768         this.prevIndicator.on('click', this.prev, this);
33769         
33770         this.nextIndicator.on('click', this.next, this);
33771         
33772     },
33773     
33774     initial : function()
33775     {
33776         if(this.files.length){
33777             this.indicator = 1;
33778             this.update()
33779         }
33780         
33781         this.fireEvent('initial', this);
33782     },
33783     
33784     update : function()
33785     {
33786         this.imageEl.attr('src', this.files[this.indicator - 1]);
33787         
33788         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33789         
33790         this.prevIndicator.show();
33791         
33792         if(this.indicator == 1){
33793             this.prevIndicator.hide();
33794         }
33795         
33796         this.nextIndicator.show();
33797         
33798         if(this.indicator == this.files.length){
33799             this.nextIndicator.hide();
33800         }
33801         
33802         this.thumbEl.scrollTo('top');
33803         
33804         this.fireEvent('update', this);
33805     },
33806     
33807     onClick : function(e)
33808     {
33809         e.preventDefault();
33810         
33811         this.fireEvent('click', this);
33812     },
33813     
33814     prev : function(e)
33815     {
33816         e.preventDefault();
33817         
33818         this.indicator = Math.max(1, this.indicator - 1);
33819         
33820         this.update();
33821     },
33822     
33823     next : function(e)
33824     {
33825         e.preventDefault();
33826         
33827         this.indicator = Math.min(this.files.length, this.indicator + 1);
33828         
33829         this.update();
33830     }
33831 });
33832 /*
33833  * - LGPL
33834  *
33835  * RadioSet
33836  *
33837  *
33838  */
33839
33840 /**
33841  * @class Roo.bootstrap.RadioSet
33842  * @extends Roo.bootstrap.Input
33843  * Bootstrap RadioSet class
33844  * @cfg {String} indicatorpos (left|right) default left
33845  * @cfg {Boolean} inline (true|false) inline the element (default true)
33846  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33847  * @constructor
33848  * Create a new RadioSet
33849  * @param {Object} config The config object
33850  */
33851
33852 Roo.bootstrap.RadioSet = function(config){
33853     
33854     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33855     
33856     this.radioes = [];
33857     
33858     Roo.bootstrap.RadioSet.register(this);
33859     
33860     this.addEvents({
33861         /**
33862         * @event check
33863         * Fires when the element is checked or unchecked.
33864         * @param {Roo.bootstrap.RadioSet} this This radio
33865         * @param {Roo.bootstrap.Radio} item The checked item
33866         */
33867        check : true,
33868        /**
33869         * @event click
33870         * Fires when the element is click.
33871         * @param {Roo.bootstrap.RadioSet} this This radio set
33872         * @param {Roo.bootstrap.Radio} item The checked item
33873         * @param {Roo.EventObject} e The event object
33874         */
33875        click : true
33876     });
33877     
33878 };
33879
33880 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33881
33882     radioes : false,
33883     
33884     inline : true,
33885     
33886     weight : '',
33887     
33888     indicatorpos : 'left',
33889     
33890     getAutoCreate : function()
33891     {
33892         var label = {
33893             tag : 'label',
33894             cls : 'roo-radio-set-label',
33895             cn : [
33896                 {
33897                     tag : 'span',
33898                     html : this.fieldLabel
33899                 }
33900             ]
33901         };
33902         
33903         if(this.indicatorpos == 'left'){
33904             label.cn.unshift({
33905                 tag : 'i',
33906                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33907                 tooltip : 'This field is required'
33908             });
33909         } else {
33910             label.cn.push({
33911                 tag : 'i',
33912                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33913                 tooltip : 'This field is required'
33914             });
33915         }
33916         
33917         var items = {
33918             tag : 'div',
33919             cls : 'roo-radio-set-items'
33920         };
33921         
33922         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33923         
33924         if (align === 'left' && this.fieldLabel.length) {
33925             
33926             items = {
33927                 cls : "roo-radio-set-right", 
33928                 cn: [
33929                     items
33930                 ]
33931             };
33932             
33933             if(this.labelWidth > 12){
33934                 label.style = "width: " + this.labelWidth + 'px';
33935             }
33936             
33937             if(this.labelWidth < 13 && this.labelmd == 0){
33938                 this.labelmd = this.labelWidth;
33939             }
33940             
33941             if(this.labellg > 0){
33942                 label.cls += ' col-lg-' + this.labellg;
33943                 items.cls += ' col-lg-' + (12 - this.labellg);
33944             }
33945             
33946             if(this.labelmd > 0){
33947                 label.cls += ' col-md-' + this.labelmd;
33948                 items.cls += ' col-md-' + (12 - this.labelmd);
33949             }
33950             
33951             if(this.labelsm > 0){
33952                 label.cls += ' col-sm-' + this.labelsm;
33953                 items.cls += ' col-sm-' + (12 - this.labelsm);
33954             }
33955             
33956             if(this.labelxs > 0){
33957                 label.cls += ' col-xs-' + this.labelxs;
33958                 items.cls += ' col-xs-' + (12 - this.labelxs);
33959             }
33960         }
33961         
33962         var cfg = {
33963             tag : 'div',
33964             cls : 'roo-radio-set',
33965             cn : [
33966                 {
33967                     tag : 'input',
33968                     cls : 'roo-radio-set-input',
33969                     type : 'hidden',
33970                     name : this.name,
33971                     value : this.value ? this.value :  ''
33972                 },
33973                 label,
33974                 items
33975             ]
33976         };
33977         
33978         if(this.weight.length){
33979             cfg.cls += ' roo-radio-' + this.weight;
33980         }
33981         
33982         if(this.inline) {
33983             cfg.cls += ' roo-radio-set-inline';
33984         }
33985         
33986         var settings=this;
33987         ['xs','sm','md','lg'].map(function(size){
33988             if (settings[size]) {
33989                 cfg.cls += ' col-' + size + '-' + settings[size];
33990             }
33991         });
33992         
33993         return cfg;
33994         
33995     },
33996
33997     initEvents : function()
33998     {
33999         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34000         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34001         
34002         if(!this.fieldLabel.length){
34003             this.labelEl.hide();
34004         }
34005         
34006         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34007         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34008         
34009         this.indicator = this.indicatorEl();
34010         
34011         if(this.indicator){
34012             this.indicator.addClass('invisible');
34013         }
34014         
34015         this.originalValue = this.getValue();
34016         
34017     },
34018     
34019     inputEl: function ()
34020     {
34021         return this.el.select('.roo-radio-set-input', true).first();
34022     },
34023     
34024     getChildContainer : function()
34025     {
34026         return this.itemsEl;
34027     },
34028     
34029     register : function(item)
34030     {
34031         this.radioes.push(item);
34032         
34033     },
34034     
34035     validate : function()
34036     {   
34037         if(this.getVisibilityEl().hasClass('hidden')){
34038             return true;
34039         }
34040         
34041         var valid = false;
34042         
34043         Roo.each(this.radioes, function(i){
34044             if(!i.checked){
34045                 return;
34046             }
34047             
34048             valid = true;
34049             return false;
34050         });
34051         
34052         if(this.allowBlank) {
34053             return true;
34054         }
34055         
34056         if(this.disabled || valid){
34057             this.markValid();
34058             return true;
34059         }
34060         
34061         this.markInvalid();
34062         return false;
34063         
34064     },
34065     
34066     markValid : function()
34067     {
34068         if(this.labelEl.isVisible(true)){
34069             this.indicatorEl().removeClass('visible');
34070             this.indicatorEl().addClass('invisible');
34071         }
34072         
34073         this.el.removeClass([this.invalidClass, this.validClass]);
34074         this.el.addClass(this.validClass);
34075         
34076         this.fireEvent('valid', this);
34077     },
34078     
34079     markInvalid : function(msg)
34080     {
34081         if(this.allowBlank || this.disabled){
34082             return;
34083         }
34084         
34085         if(this.labelEl.isVisible(true)){
34086             this.indicatorEl().removeClass('invisible');
34087             this.indicatorEl().addClass('visible');
34088         }
34089         
34090         this.el.removeClass([this.invalidClass, this.validClass]);
34091         this.el.addClass(this.invalidClass);
34092         
34093         this.fireEvent('invalid', this, msg);
34094         
34095     },
34096     
34097     setValue : function(v, suppressEvent)
34098     {   
34099         if(this.value === v){
34100             return;
34101         }
34102         
34103         this.value = v;
34104         
34105         if(this.rendered){
34106             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34107         }
34108         
34109         Roo.each(this.radioes, function(i){
34110             i.checked = false;
34111             i.el.removeClass('checked');
34112         });
34113         
34114         Roo.each(this.radioes, function(i){
34115             
34116             if(i.value === v || i.value.toString() === v.toString()){
34117                 i.checked = true;
34118                 i.el.addClass('checked');
34119                 
34120                 if(suppressEvent !== true){
34121                     this.fireEvent('check', this, i);
34122                 }
34123                 
34124                 return false;
34125             }
34126             
34127         }, this);
34128         
34129         this.validate();
34130     },
34131     
34132     clearInvalid : function(){
34133         
34134         if(!this.el || this.preventMark){
34135             return;
34136         }
34137         
34138         this.el.removeClass([this.invalidClass]);
34139         
34140         this.fireEvent('valid', this);
34141     }
34142     
34143 });
34144
34145 Roo.apply(Roo.bootstrap.RadioSet, {
34146     
34147     groups: {},
34148     
34149     register : function(set)
34150     {
34151         this.groups[set.name] = set;
34152     },
34153     
34154     get: function(name) 
34155     {
34156         if (typeof(this.groups[name]) == 'undefined') {
34157             return false;
34158         }
34159         
34160         return this.groups[name] ;
34161     }
34162     
34163 });
34164 /*
34165  * Based on:
34166  * Ext JS Library 1.1.1
34167  * Copyright(c) 2006-2007, Ext JS, LLC.
34168  *
34169  * Originally Released Under LGPL - original licence link has changed is not relivant.
34170  *
34171  * Fork - LGPL
34172  * <script type="text/javascript">
34173  */
34174
34175
34176 /**
34177  * @class Roo.bootstrap.SplitBar
34178  * @extends Roo.util.Observable
34179  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34180  * <br><br>
34181  * Usage:
34182  * <pre><code>
34183 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34184                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34185 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34186 split.minSize = 100;
34187 split.maxSize = 600;
34188 split.animate = true;
34189 split.on('moved', splitterMoved);
34190 </code></pre>
34191  * @constructor
34192  * Create a new SplitBar
34193  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34194  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34195  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34196  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34197                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34198                         position of the SplitBar).
34199  */
34200 Roo.bootstrap.SplitBar = function(cfg){
34201     
34202     /** @private */
34203     
34204     //{
34205     //  dragElement : elm
34206     //  resizingElement: el,
34207         // optional..
34208     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34209     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34210         // existingProxy ???
34211     //}
34212     
34213     this.el = Roo.get(cfg.dragElement, true);
34214     this.el.dom.unselectable = "on";
34215     /** @private */
34216     this.resizingEl = Roo.get(cfg.resizingElement, true);
34217
34218     /**
34219      * @private
34220      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34221      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34222      * @type Number
34223      */
34224     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34225     
34226     /**
34227      * The minimum size of the resizing element. (Defaults to 0)
34228      * @type Number
34229      */
34230     this.minSize = 0;
34231     
34232     /**
34233      * The maximum size of the resizing element. (Defaults to 2000)
34234      * @type Number
34235      */
34236     this.maxSize = 2000;
34237     
34238     /**
34239      * Whether to animate the transition to the new size
34240      * @type Boolean
34241      */
34242     this.animate = false;
34243     
34244     /**
34245      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34246      * @type Boolean
34247      */
34248     this.useShim = false;
34249     
34250     /** @private */
34251     this.shim = null;
34252     
34253     if(!cfg.existingProxy){
34254         /** @private */
34255         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34256     }else{
34257         this.proxy = Roo.get(cfg.existingProxy).dom;
34258     }
34259     /** @private */
34260     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34261     
34262     /** @private */
34263     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34264     
34265     /** @private */
34266     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34267     
34268     /** @private */
34269     this.dragSpecs = {};
34270     
34271     /**
34272      * @private The adapter to use to positon and resize elements
34273      */
34274     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34275     this.adapter.init(this);
34276     
34277     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34278         /** @private */
34279         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34280         this.el.addClass("roo-splitbar-h");
34281     }else{
34282         /** @private */
34283         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34284         this.el.addClass("roo-splitbar-v");
34285     }
34286     
34287     this.addEvents({
34288         /**
34289          * @event resize
34290          * Fires when the splitter is moved (alias for {@link #event-moved})
34291          * @param {Roo.bootstrap.SplitBar} this
34292          * @param {Number} newSize the new width or height
34293          */
34294         "resize" : true,
34295         /**
34296          * @event moved
34297          * Fires when the splitter is moved
34298          * @param {Roo.bootstrap.SplitBar} this
34299          * @param {Number} newSize the new width or height
34300          */
34301         "moved" : true,
34302         /**
34303          * @event beforeresize
34304          * Fires before the splitter is dragged
34305          * @param {Roo.bootstrap.SplitBar} this
34306          */
34307         "beforeresize" : true,
34308
34309         "beforeapply" : true
34310     });
34311
34312     Roo.util.Observable.call(this);
34313 };
34314
34315 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34316     onStartProxyDrag : function(x, y){
34317         this.fireEvent("beforeresize", this);
34318         if(!this.overlay){
34319             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34320             o.unselectable();
34321             o.enableDisplayMode("block");
34322             // all splitbars share the same overlay
34323             Roo.bootstrap.SplitBar.prototype.overlay = o;
34324         }
34325         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34326         this.overlay.show();
34327         Roo.get(this.proxy).setDisplayed("block");
34328         var size = this.adapter.getElementSize(this);
34329         this.activeMinSize = this.getMinimumSize();;
34330         this.activeMaxSize = this.getMaximumSize();;
34331         var c1 = size - this.activeMinSize;
34332         var c2 = Math.max(this.activeMaxSize - size, 0);
34333         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34334             this.dd.resetConstraints();
34335             this.dd.setXConstraint(
34336                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34337                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34338             );
34339             this.dd.setYConstraint(0, 0);
34340         }else{
34341             this.dd.resetConstraints();
34342             this.dd.setXConstraint(0, 0);
34343             this.dd.setYConstraint(
34344                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34345                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34346             );
34347          }
34348         this.dragSpecs.startSize = size;
34349         this.dragSpecs.startPoint = [x, y];
34350         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34351     },
34352     
34353     /** 
34354      * @private Called after the drag operation by the DDProxy
34355      */
34356     onEndProxyDrag : function(e){
34357         Roo.get(this.proxy).setDisplayed(false);
34358         var endPoint = Roo.lib.Event.getXY(e);
34359         if(this.overlay){
34360             this.overlay.hide();
34361         }
34362         var newSize;
34363         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34364             newSize = this.dragSpecs.startSize + 
34365                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34366                     endPoint[0] - this.dragSpecs.startPoint[0] :
34367                     this.dragSpecs.startPoint[0] - endPoint[0]
34368                 );
34369         }else{
34370             newSize = this.dragSpecs.startSize + 
34371                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34372                     endPoint[1] - this.dragSpecs.startPoint[1] :
34373                     this.dragSpecs.startPoint[1] - endPoint[1]
34374                 );
34375         }
34376         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34377         if(newSize != this.dragSpecs.startSize){
34378             if(this.fireEvent('beforeapply', this, newSize) !== false){
34379                 this.adapter.setElementSize(this, newSize);
34380                 this.fireEvent("moved", this, newSize);
34381                 this.fireEvent("resize", this, newSize);
34382             }
34383         }
34384     },
34385     
34386     /**
34387      * Get the adapter this SplitBar uses
34388      * @return The adapter object
34389      */
34390     getAdapter : function(){
34391         return this.adapter;
34392     },
34393     
34394     /**
34395      * Set the adapter this SplitBar uses
34396      * @param {Object} adapter A SplitBar adapter object
34397      */
34398     setAdapter : function(adapter){
34399         this.adapter = adapter;
34400         this.adapter.init(this);
34401     },
34402     
34403     /**
34404      * Gets the minimum size for the resizing element
34405      * @return {Number} The minimum size
34406      */
34407     getMinimumSize : function(){
34408         return this.minSize;
34409     },
34410     
34411     /**
34412      * Sets the minimum size for the resizing element
34413      * @param {Number} minSize The minimum size
34414      */
34415     setMinimumSize : function(minSize){
34416         this.minSize = minSize;
34417     },
34418     
34419     /**
34420      * Gets the maximum size for the resizing element
34421      * @return {Number} The maximum size
34422      */
34423     getMaximumSize : function(){
34424         return this.maxSize;
34425     },
34426     
34427     /**
34428      * Sets the maximum size for the resizing element
34429      * @param {Number} maxSize The maximum size
34430      */
34431     setMaximumSize : function(maxSize){
34432         this.maxSize = maxSize;
34433     },
34434     
34435     /**
34436      * Sets the initialize size for the resizing element
34437      * @param {Number} size The initial size
34438      */
34439     setCurrentSize : function(size){
34440         var oldAnimate = this.animate;
34441         this.animate = false;
34442         this.adapter.setElementSize(this, size);
34443         this.animate = oldAnimate;
34444     },
34445     
34446     /**
34447      * Destroy this splitbar. 
34448      * @param {Boolean} removeEl True to remove the element
34449      */
34450     destroy : function(removeEl){
34451         if(this.shim){
34452             this.shim.remove();
34453         }
34454         this.dd.unreg();
34455         this.proxy.parentNode.removeChild(this.proxy);
34456         if(removeEl){
34457             this.el.remove();
34458         }
34459     }
34460 });
34461
34462 /**
34463  * @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.
34464  */
34465 Roo.bootstrap.SplitBar.createProxy = function(dir){
34466     var proxy = new Roo.Element(document.createElement("div"));
34467     proxy.unselectable();
34468     var cls = 'roo-splitbar-proxy';
34469     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34470     document.body.appendChild(proxy.dom);
34471     return proxy.dom;
34472 };
34473
34474 /** 
34475  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34476  * Default Adapter. It assumes the splitter and resizing element are not positioned
34477  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34478  */
34479 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34480 };
34481
34482 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34483     // do nothing for now
34484     init : function(s){
34485     
34486     },
34487     /**
34488      * Called before drag operations to get the current size of the resizing element. 
34489      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34490      */
34491      getElementSize : function(s){
34492         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34493             return s.resizingEl.getWidth();
34494         }else{
34495             return s.resizingEl.getHeight();
34496         }
34497     },
34498     
34499     /**
34500      * Called after drag operations to set the size of the resizing element.
34501      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34502      * @param {Number} newSize The new size to set
34503      * @param {Function} onComplete A function to be invoked when resizing is complete
34504      */
34505     setElementSize : function(s, newSize, onComplete){
34506         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34507             if(!s.animate){
34508                 s.resizingEl.setWidth(newSize);
34509                 if(onComplete){
34510                     onComplete(s, newSize);
34511                 }
34512             }else{
34513                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34514             }
34515         }else{
34516             
34517             if(!s.animate){
34518                 s.resizingEl.setHeight(newSize);
34519                 if(onComplete){
34520                     onComplete(s, newSize);
34521                 }
34522             }else{
34523                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34524             }
34525         }
34526     }
34527 };
34528
34529 /** 
34530  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34531  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34532  * Adapter that  moves the splitter element to align with the resized sizing element. 
34533  * Used with an absolute positioned SplitBar.
34534  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34535  * document.body, make sure you assign an id to the body element.
34536  */
34537 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34538     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34539     this.container = Roo.get(container);
34540 };
34541
34542 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34543     init : function(s){
34544         this.basic.init(s);
34545     },
34546     
34547     getElementSize : function(s){
34548         return this.basic.getElementSize(s);
34549     },
34550     
34551     setElementSize : function(s, newSize, onComplete){
34552         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34553     },
34554     
34555     moveSplitter : function(s){
34556         var yes = Roo.bootstrap.SplitBar;
34557         switch(s.placement){
34558             case yes.LEFT:
34559                 s.el.setX(s.resizingEl.getRight());
34560                 break;
34561             case yes.RIGHT:
34562                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34563                 break;
34564             case yes.TOP:
34565                 s.el.setY(s.resizingEl.getBottom());
34566                 break;
34567             case yes.BOTTOM:
34568                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34569                 break;
34570         }
34571     }
34572 };
34573
34574 /**
34575  * Orientation constant - Create a vertical SplitBar
34576  * @static
34577  * @type Number
34578  */
34579 Roo.bootstrap.SplitBar.VERTICAL = 1;
34580
34581 /**
34582  * Orientation constant - Create a horizontal SplitBar
34583  * @static
34584  * @type Number
34585  */
34586 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34587
34588 /**
34589  * Placement constant - The resizing element is to the left of the splitter element
34590  * @static
34591  * @type Number
34592  */
34593 Roo.bootstrap.SplitBar.LEFT = 1;
34594
34595 /**
34596  * Placement constant - The resizing element is to the right of the splitter element
34597  * @static
34598  * @type Number
34599  */
34600 Roo.bootstrap.SplitBar.RIGHT = 2;
34601
34602 /**
34603  * Placement constant - The resizing element is positioned above the splitter element
34604  * @static
34605  * @type Number
34606  */
34607 Roo.bootstrap.SplitBar.TOP = 3;
34608
34609 /**
34610  * Placement constant - The resizing element is positioned under splitter element
34611  * @static
34612  * @type Number
34613  */
34614 Roo.bootstrap.SplitBar.BOTTOM = 4;
34615 Roo.namespace("Roo.bootstrap.layout");/*
34616  * Based on:
34617  * Ext JS Library 1.1.1
34618  * Copyright(c) 2006-2007, Ext JS, LLC.
34619  *
34620  * Originally Released Under LGPL - original licence link has changed is not relivant.
34621  *
34622  * Fork - LGPL
34623  * <script type="text/javascript">
34624  */
34625
34626 /**
34627  * @class Roo.bootstrap.layout.Manager
34628  * @extends Roo.bootstrap.Component
34629  * Base class for layout managers.
34630  */
34631 Roo.bootstrap.layout.Manager = function(config)
34632 {
34633     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34634
34635
34636
34637
34638
34639     /** false to disable window resize monitoring @type Boolean */
34640     this.monitorWindowResize = true;
34641     this.regions = {};
34642     this.addEvents({
34643         /**
34644          * @event layout
34645          * Fires when a layout is performed.
34646          * @param {Roo.LayoutManager} this
34647          */
34648         "layout" : true,
34649         /**
34650          * @event regionresized
34651          * Fires when the user resizes a region.
34652          * @param {Roo.LayoutRegion} region The resized region
34653          * @param {Number} newSize The new size (width for east/west, height for north/south)
34654          */
34655         "regionresized" : true,
34656         /**
34657          * @event regioncollapsed
34658          * Fires when a region is collapsed.
34659          * @param {Roo.LayoutRegion} region The collapsed region
34660          */
34661         "regioncollapsed" : true,
34662         /**
34663          * @event regionexpanded
34664          * Fires when a region is expanded.
34665          * @param {Roo.LayoutRegion} region The expanded region
34666          */
34667         "regionexpanded" : true
34668     });
34669     this.updating = false;
34670
34671     if (config.el) {
34672         this.el = Roo.get(config.el);
34673         this.initEvents();
34674     }
34675
34676 };
34677
34678 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34679
34680
34681     regions : null,
34682
34683     monitorWindowResize : true,
34684
34685
34686     updating : false,
34687
34688
34689     onRender : function(ct, position)
34690     {
34691         if(!this.el){
34692             this.el = Roo.get(ct);
34693             this.initEvents();
34694         }
34695         //this.fireEvent('render',this);
34696     },
34697
34698
34699     initEvents: function()
34700     {
34701
34702
34703         // ie scrollbar fix
34704         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34705             document.body.scroll = "no";
34706         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34707             this.el.position('relative');
34708         }
34709         this.id = this.el.id;
34710         this.el.addClass("roo-layout-container");
34711         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34712         if(this.el.dom != document.body ) {
34713             this.el.on('resize', this.layout,this);
34714             this.el.on('show', this.layout,this);
34715         }
34716
34717     },
34718
34719     /**
34720      * Returns true if this layout is currently being updated
34721      * @return {Boolean}
34722      */
34723     isUpdating : function(){
34724         return this.updating;
34725     },
34726
34727     /**
34728      * Suspend the LayoutManager from doing auto-layouts while
34729      * making multiple add or remove calls
34730      */
34731     beginUpdate : function(){
34732         this.updating = true;
34733     },
34734
34735     /**
34736      * Restore auto-layouts and optionally disable the manager from performing a layout
34737      * @param {Boolean} noLayout true to disable a layout update
34738      */
34739     endUpdate : function(noLayout){
34740         this.updating = false;
34741         if(!noLayout){
34742             this.layout();
34743         }
34744     },
34745
34746     layout: function(){
34747         // abstract...
34748     },
34749
34750     onRegionResized : function(region, newSize){
34751         this.fireEvent("regionresized", region, newSize);
34752         this.layout();
34753     },
34754
34755     onRegionCollapsed : function(region){
34756         this.fireEvent("regioncollapsed", region);
34757     },
34758
34759     onRegionExpanded : function(region){
34760         this.fireEvent("regionexpanded", region);
34761     },
34762
34763     /**
34764      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34765      * performs box-model adjustments.
34766      * @return {Object} The size as an object {width: (the width), height: (the height)}
34767      */
34768     getViewSize : function()
34769     {
34770         var size;
34771         if(this.el.dom != document.body){
34772             size = this.el.getSize();
34773         }else{
34774             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34775         }
34776         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34777         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34778         return size;
34779     },
34780
34781     /**
34782      * Returns the Element this layout is bound to.
34783      * @return {Roo.Element}
34784      */
34785     getEl : function(){
34786         return this.el;
34787     },
34788
34789     /**
34790      * Returns the specified region.
34791      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34792      * @return {Roo.LayoutRegion}
34793      */
34794     getRegion : function(target){
34795         return this.regions[target.toLowerCase()];
34796     },
34797
34798     onWindowResize : function(){
34799         if(this.monitorWindowResize){
34800             this.layout();
34801         }
34802     }
34803 });
34804 /*
34805  * Based on:
34806  * Ext JS Library 1.1.1
34807  * Copyright(c) 2006-2007, Ext JS, LLC.
34808  *
34809  * Originally Released Under LGPL - original licence link has changed is not relivant.
34810  *
34811  * Fork - LGPL
34812  * <script type="text/javascript">
34813  */
34814 /**
34815  * @class Roo.bootstrap.layout.Border
34816  * @extends Roo.bootstrap.layout.Manager
34817  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34818  * please see: examples/bootstrap/nested.html<br><br>
34819  
34820 <b>The container the layout is rendered into can be either the body element or any other element.
34821 If it is not the body element, the container needs to either be an absolute positioned element,
34822 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34823 the container size if it is not the body element.</b>
34824
34825 * @constructor
34826 * Create a new Border
34827 * @param {Object} config Configuration options
34828  */
34829 Roo.bootstrap.layout.Border = function(config){
34830     config = config || {};
34831     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34832     
34833     
34834     
34835     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34836         if(config[region]){
34837             config[region].region = region;
34838             this.addRegion(config[region]);
34839         }
34840     },this);
34841     
34842 };
34843
34844 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34845
34846 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34847     /**
34848      * Creates and adds a new region if it doesn't already exist.
34849      * @param {String} target The target region key (north, south, east, west or center).
34850      * @param {Object} config The regions config object
34851      * @return {BorderLayoutRegion} The new region
34852      */
34853     addRegion : function(config)
34854     {
34855         if(!this.regions[config.region]){
34856             var r = this.factory(config);
34857             this.bindRegion(r);
34858         }
34859         return this.regions[config.region];
34860     },
34861
34862     // private (kinda)
34863     bindRegion : function(r){
34864         this.regions[r.config.region] = r;
34865         
34866         r.on("visibilitychange",    this.layout, this);
34867         r.on("paneladded",          this.layout, this);
34868         r.on("panelremoved",        this.layout, this);
34869         r.on("invalidated",         this.layout, this);
34870         r.on("resized",             this.onRegionResized, this);
34871         r.on("collapsed",           this.onRegionCollapsed, this);
34872         r.on("expanded",            this.onRegionExpanded, this);
34873     },
34874
34875     /**
34876      * Performs a layout update.
34877      */
34878     layout : function()
34879     {
34880         if(this.updating) {
34881             return;
34882         }
34883         
34884         // render all the rebions if they have not been done alreayd?
34885         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34886             if(this.regions[region] && !this.regions[region].bodyEl){
34887                 this.regions[region].onRender(this.el)
34888             }
34889         },this);
34890         
34891         var size = this.getViewSize();
34892         var w = size.width;
34893         var h = size.height;
34894         var centerW = w;
34895         var centerH = h;
34896         var centerY = 0;
34897         var centerX = 0;
34898         //var x = 0, y = 0;
34899
34900         var rs = this.regions;
34901         var north = rs["north"];
34902         var south = rs["south"]; 
34903         var west = rs["west"];
34904         var east = rs["east"];
34905         var center = rs["center"];
34906         //if(this.hideOnLayout){ // not supported anymore
34907             //c.el.setStyle("display", "none");
34908         //}
34909         if(north && north.isVisible()){
34910             var b = north.getBox();
34911             var m = north.getMargins();
34912             b.width = w - (m.left+m.right);
34913             b.x = m.left;
34914             b.y = m.top;
34915             centerY = b.height + b.y + m.bottom;
34916             centerH -= centerY;
34917             north.updateBox(this.safeBox(b));
34918         }
34919         if(south && south.isVisible()){
34920             var b = south.getBox();
34921             var m = south.getMargins();
34922             b.width = w - (m.left+m.right);
34923             b.x = m.left;
34924             var totalHeight = (b.height + m.top + m.bottom);
34925             b.y = h - totalHeight + m.top;
34926             centerH -= totalHeight;
34927             south.updateBox(this.safeBox(b));
34928         }
34929         if(west && west.isVisible()){
34930             var b = west.getBox();
34931             var m = west.getMargins();
34932             b.height = centerH - (m.top+m.bottom);
34933             b.x = m.left;
34934             b.y = centerY + m.top;
34935             var totalWidth = (b.width + m.left + m.right);
34936             centerX += totalWidth;
34937             centerW -= totalWidth;
34938             west.updateBox(this.safeBox(b));
34939         }
34940         if(east && east.isVisible()){
34941             var b = east.getBox();
34942             var m = east.getMargins();
34943             b.height = centerH - (m.top+m.bottom);
34944             var totalWidth = (b.width + m.left + m.right);
34945             b.x = w - totalWidth + m.left;
34946             b.y = centerY + m.top;
34947             centerW -= totalWidth;
34948             east.updateBox(this.safeBox(b));
34949         }
34950         if(center){
34951             var m = center.getMargins();
34952             var centerBox = {
34953                 x: centerX + m.left,
34954                 y: centerY + m.top,
34955                 width: centerW - (m.left+m.right),
34956                 height: centerH - (m.top+m.bottom)
34957             };
34958             //if(this.hideOnLayout){
34959                 //center.el.setStyle("display", "block");
34960             //}
34961             center.updateBox(this.safeBox(centerBox));
34962         }
34963         this.el.repaint();
34964         this.fireEvent("layout", this);
34965     },
34966
34967     // private
34968     safeBox : function(box){
34969         box.width = Math.max(0, box.width);
34970         box.height = Math.max(0, box.height);
34971         return box;
34972     },
34973
34974     /**
34975      * Adds a ContentPanel (or subclass) to this layout.
34976      * @param {String} target The target region key (north, south, east, west or center).
34977      * @param {Roo.ContentPanel} panel The panel to add
34978      * @return {Roo.ContentPanel} The added panel
34979      */
34980     add : function(target, panel){
34981          
34982         target = target.toLowerCase();
34983         return this.regions[target].add(panel);
34984     },
34985
34986     /**
34987      * Remove a ContentPanel (or subclass) to this layout.
34988      * @param {String} target The target region key (north, south, east, west or center).
34989      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34990      * @return {Roo.ContentPanel} The removed panel
34991      */
34992     remove : function(target, panel){
34993         target = target.toLowerCase();
34994         return this.regions[target].remove(panel);
34995     },
34996
34997     /**
34998      * Searches all regions for a panel with the specified id
34999      * @param {String} panelId
35000      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35001      */
35002     findPanel : function(panelId){
35003         var rs = this.regions;
35004         for(var target in rs){
35005             if(typeof rs[target] != "function"){
35006                 var p = rs[target].getPanel(panelId);
35007                 if(p){
35008                     return p;
35009                 }
35010             }
35011         }
35012         return null;
35013     },
35014
35015     /**
35016      * Searches all regions for a panel with the specified id and activates (shows) it.
35017      * @param {String/ContentPanel} panelId The panels id or the panel itself
35018      * @return {Roo.ContentPanel} The shown panel or null
35019      */
35020     showPanel : function(panelId) {
35021       var rs = this.regions;
35022       for(var target in rs){
35023          var r = rs[target];
35024          if(typeof r != "function"){
35025             if(r.hasPanel(panelId)){
35026                return r.showPanel(panelId);
35027             }
35028          }
35029       }
35030       return null;
35031    },
35032
35033    /**
35034      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35035      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35036      */
35037    /*
35038     restoreState : function(provider){
35039         if(!provider){
35040             provider = Roo.state.Manager;
35041         }
35042         var sm = new Roo.LayoutStateManager();
35043         sm.init(this, provider);
35044     },
35045 */
35046  
35047  
35048     /**
35049      * Adds a xtype elements to the layout.
35050      * <pre><code>
35051
35052 layout.addxtype({
35053        xtype : 'ContentPanel',
35054        region: 'west',
35055        items: [ .... ]
35056    }
35057 );
35058
35059 layout.addxtype({
35060         xtype : 'NestedLayoutPanel',
35061         region: 'west',
35062         layout: {
35063            center: { },
35064            west: { }   
35065         },
35066         items : [ ... list of content panels or nested layout panels.. ]
35067    }
35068 );
35069 </code></pre>
35070      * @param {Object} cfg Xtype definition of item to add.
35071      */
35072     addxtype : function(cfg)
35073     {
35074         // basically accepts a pannel...
35075         // can accept a layout region..!?!?
35076         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35077         
35078         
35079         // theory?  children can only be panels??
35080         
35081         //if (!cfg.xtype.match(/Panel$/)) {
35082         //    return false;
35083         //}
35084         var ret = false;
35085         
35086         if (typeof(cfg.region) == 'undefined') {
35087             Roo.log("Failed to add Panel, region was not set");
35088             Roo.log(cfg);
35089             return false;
35090         }
35091         var region = cfg.region;
35092         delete cfg.region;
35093         
35094           
35095         var xitems = [];
35096         if (cfg.items) {
35097             xitems = cfg.items;
35098             delete cfg.items;
35099         }
35100         var nb = false;
35101         
35102         switch(cfg.xtype) 
35103         {
35104             case 'Content':  // ContentPanel (el, cfg)
35105             case 'Scroll':  // ContentPanel (el, cfg)
35106             case 'View': 
35107                 cfg.autoCreate = true;
35108                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35109                 //} else {
35110                 //    var el = this.el.createChild();
35111                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35112                 //}
35113                 
35114                 this.add(region, ret);
35115                 break;
35116             
35117             /*
35118             case 'TreePanel': // our new panel!
35119                 cfg.el = this.el.createChild();
35120                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35121                 this.add(region, ret);
35122                 break;
35123             */
35124             
35125             case 'Nest': 
35126                 // create a new Layout (which is  a Border Layout...
35127                 
35128                 var clayout = cfg.layout;
35129                 clayout.el  = this.el.createChild();
35130                 clayout.items   = clayout.items  || [];
35131                 
35132                 delete cfg.layout;
35133                 
35134                 // replace this exitems with the clayout ones..
35135                 xitems = clayout.items;
35136                  
35137                 // force background off if it's in center...
35138                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35139                     cfg.background = false;
35140                 }
35141                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35142                 
35143                 
35144                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35145                 //console.log('adding nested layout panel '  + cfg.toSource());
35146                 this.add(region, ret);
35147                 nb = {}; /// find first...
35148                 break;
35149             
35150             case 'Grid':
35151                 
35152                 // needs grid and region
35153                 
35154                 //var el = this.getRegion(region).el.createChild();
35155                 /*
35156                  *var el = this.el.createChild();
35157                 // create the grid first...
35158                 cfg.grid.container = el;
35159                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35160                 */
35161                 
35162                 if (region == 'center' && this.active ) {
35163                     cfg.background = false;
35164                 }
35165                 
35166                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35167                 
35168                 this.add(region, ret);
35169                 /*
35170                 if (cfg.background) {
35171                     // render grid on panel activation (if panel background)
35172                     ret.on('activate', function(gp) {
35173                         if (!gp.grid.rendered) {
35174                     //        gp.grid.render(el);
35175                         }
35176                     });
35177                 } else {
35178                   //  cfg.grid.render(el);
35179                 }
35180                 */
35181                 break;
35182            
35183            
35184             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35185                 // it was the old xcomponent building that caused this before.
35186                 // espeically if border is the top element in the tree.
35187                 ret = this;
35188                 break; 
35189                 
35190                     
35191                 
35192                 
35193                 
35194             default:
35195                 /*
35196                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35197                     
35198                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35199                     this.add(region, ret);
35200                 } else {
35201                 */
35202                     Roo.log(cfg);
35203                     throw "Can not add '" + cfg.xtype + "' to Border";
35204                     return null;
35205              
35206                                 
35207              
35208         }
35209         this.beginUpdate();
35210         // add children..
35211         var region = '';
35212         var abn = {};
35213         Roo.each(xitems, function(i)  {
35214             region = nb && i.region ? i.region : false;
35215             
35216             var add = ret.addxtype(i);
35217            
35218             if (region) {
35219                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35220                 if (!i.background) {
35221                     abn[region] = nb[region] ;
35222                 }
35223             }
35224             
35225         });
35226         this.endUpdate();
35227
35228         // make the last non-background panel active..
35229         //if (nb) { Roo.log(abn); }
35230         if (nb) {
35231             
35232             for(var r in abn) {
35233                 region = this.getRegion(r);
35234                 if (region) {
35235                     // tried using nb[r], but it does not work..
35236                      
35237                     region.showPanel(abn[r]);
35238                    
35239                 }
35240             }
35241         }
35242         return ret;
35243         
35244     },
35245     
35246     
35247 // private
35248     factory : function(cfg)
35249     {
35250         
35251         var validRegions = Roo.bootstrap.layout.Border.regions;
35252
35253         var target = cfg.region;
35254         cfg.mgr = this;
35255         
35256         var r = Roo.bootstrap.layout;
35257         Roo.log(target);
35258         switch(target){
35259             case "north":
35260                 return new r.North(cfg);
35261             case "south":
35262                 return new r.South(cfg);
35263             case "east":
35264                 return new r.East(cfg);
35265             case "west":
35266                 return new r.West(cfg);
35267             case "center":
35268                 return new r.Center(cfg);
35269         }
35270         throw 'Layout region "'+target+'" not supported.';
35271     }
35272     
35273     
35274 });
35275  /*
35276  * Based on:
35277  * Ext JS Library 1.1.1
35278  * Copyright(c) 2006-2007, Ext JS, LLC.
35279  *
35280  * Originally Released Under LGPL - original licence link has changed is not relivant.
35281  *
35282  * Fork - LGPL
35283  * <script type="text/javascript">
35284  */
35285  
35286 /**
35287  * @class Roo.bootstrap.layout.Basic
35288  * @extends Roo.util.Observable
35289  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35290  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35291  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35292  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35293  * @cfg {string}   region  the region that it inhabits..
35294  * @cfg {bool}   skipConfig skip config?
35295  * 
35296
35297  */
35298 Roo.bootstrap.layout.Basic = function(config){
35299     
35300     this.mgr = config.mgr;
35301     
35302     this.position = config.region;
35303     
35304     var skipConfig = config.skipConfig;
35305     
35306     this.events = {
35307         /**
35308          * @scope Roo.BasicLayoutRegion
35309          */
35310         
35311         /**
35312          * @event beforeremove
35313          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35314          * @param {Roo.LayoutRegion} this
35315          * @param {Roo.ContentPanel} panel The panel
35316          * @param {Object} e The cancel event object
35317          */
35318         "beforeremove" : true,
35319         /**
35320          * @event invalidated
35321          * Fires when the layout for this region is changed.
35322          * @param {Roo.LayoutRegion} this
35323          */
35324         "invalidated" : true,
35325         /**
35326          * @event visibilitychange
35327          * Fires when this region is shown or hidden 
35328          * @param {Roo.LayoutRegion} this
35329          * @param {Boolean} visibility true or false
35330          */
35331         "visibilitychange" : true,
35332         /**
35333          * @event paneladded
35334          * Fires when a panel is added. 
35335          * @param {Roo.LayoutRegion} this
35336          * @param {Roo.ContentPanel} panel The panel
35337          */
35338         "paneladded" : true,
35339         /**
35340          * @event panelremoved
35341          * Fires when a panel is removed. 
35342          * @param {Roo.LayoutRegion} this
35343          * @param {Roo.ContentPanel} panel The panel
35344          */
35345         "panelremoved" : true,
35346         /**
35347          * @event beforecollapse
35348          * Fires when this region before collapse.
35349          * @param {Roo.LayoutRegion} this
35350          */
35351         "beforecollapse" : true,
35352         /**
35353          * @event collapsed
35354          * Fires when this region is collapsed.
35355          * @param {Roo.LayoutRegion} this
35356          */
35357         "collapsed" : true,
35358         /**
35359          * @event expanded
35360          * Fires when this region is expanded.
35361          * @param {Roo.LayoutRegion} this
35362          */
35363         "expanded" : true,
35364         /**
35365          * @event slideshow
35366          * Fires when this region is slid into view.
35367          * @param {Roo.LayoutRegion} this
35368          */
35369         "slideshow" : true,
35370         /**
35371          * @event slidehide
35372          * Fires when this region slides out of view. 
35373          * @param {Roo.LayoutRegion} this
35374          */
35375         "slidehide" : true,
35376         /**
35377          * @event panelactivated
35378          * Fires when a panel is activated. 
35379          * @param {Roo.LayoutRegion} this
35380          * @param {Roo.ContentPanel} panel The activated panel
35381          */
35382         "panelactivated" : true,
35383         /**
35384          * @event resized
35385          * Fires when the user resizes this region. 
35386          * @param {Roo.LayoutRegion} this
35387          * @param {Number} newSize The new size (width for east/west, height for north/south)
35388          */
35389         "resized" : true
35390     };
35391     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35392     this.panels = new Roo.util.MixedCollection();
35393     this.panels.getKey = this.getPanelId.createDelegate(this);
35394     this.box = null;
35395     this.activePanel = null;
35396     // ensure listeners are added...
35397     
35398     if (config.listeners || config.events) {
35399         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35400             listeners : config.listeners || {},
35401             events : config.events || {}
35402         });
35403     }
35404     
35405     if(skipConfig !== true){
35406         this.applyConfig(config);
35407     }
35408 };
35409
35410 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35411 {
35412     getPanelId : function(p){
35413         return p.getId();
35414     },
35415     
35416     applyConfig : function(config){
35417         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35418         this.config = config;
35419         
35420     },
35421     
35422     /**
35423      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35424      * the width, for horizontal (north, south) the height.
35425      * @param {Number} newSize The new width or height
35426      */
35427     resizeTo : function(newSize){
35428         var el = this.el ? this.el :
35429                  (this.activePanel ? this.activePanel.getEl() : null);
35430         if(el){
35431             switch(this.position){
35432                 case "east":
35433                 case "west":
35434                     el.setWidth(newSize);
35435                     this.fireEvent("resized", this, newSize);
35436                 break;
35437                 case "north":
35438                 case "south":
35439                     el.setHeight(newSize);
35440                     this.fireEvent("resized", this, newSize);
35441                 break;                
35442             }
35443         }
35444     },
35445     
35446     getBox : function(){
35447         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35448     },
35449     
35450     getMargins : function(){
35451         return this.margins;
35452     },
35453     
35454     updateBox : function(box){
35455         this.box = box;
35456         var el = this.activePanel.getEl();
35457         el.dom.style.left = box.x + "px";
35458         el.dom.style.top = box.y + "px";
35459         this.activePanel.setSize(box.width, box.height);
35460     },
35461     
35462     /**
35463      * Returns the container element for this region.
35464      * @return {Roo.Element}
35465      */
35466     getEl : function(){
35467         return this.activePanel;
35468     },
35469     
35470     /**
35471      * Returns true if this region is currently visible.
35472      * @return {Boolean}
35473      */
35474     isVisible : function(){
35475         return this.activePanel ? true : false;
35476     },
35477     
35478     setActivePanel : function(panel){
35479         panel = this.getPanel(panel);
35480         if(this.activePanel && this.activePanel != panel){
35481             this.activePanel.setActiveState(false);
35482             this.activePanel.getEl().setLeftTop(-10000,-10000);
35483         }
35484         this.activePanel = panel;
35485         panel.setActiveState(true);
35486         if(this.box){
35487             panel.setSize(this.box.width, this.box.height);
35488         }
35489         this.fireEvent("panelactivated", this, panel);
35490         this.fireEvent("invalidated");
35491     },
35492     
35493     /**
35494      * Show the specified panel.
35495      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35496      * @return {Roo.ContentPanel} The shown panel or null
35497      */
35498     showPanel : function(panel){
35499         panel = this.getPanel(panel);
35500         if(panel){
35501             this.setActivePanel(panel);
35502         }
35503         return panel;
35504     },
35505     
35506     /**
35507      * Get the active panel for this region.
35508      * @return {Roo.ContentPanel} The active panel or null
35509      */
35510     getActivePanel : function(){
35511         return this.activePanel;
35512     },
35513     
35514     /**
35515      * Add the passed ContentPanel(s)
35516      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35517      * @return {Roo.ContentPanel} The panel added (if only one was added)
35518      */
35519     add : function(panel){
35520         if(arguments.length > 1){
35521             for(var i = 0, len = arguments.length; i < len; i++) {
35522                 this.add(arguments[i]);
35523             }
35524             return null;
35525         }
35526         if(this.hasPanel(panel)){
35527             this.showPanel(panel);
35528             return panel;
35529         }
35530         var el = panel.getEl();
35531         if(el.dom.parentNode != this.mgr.el.dom){
35532             this.mgr.el.dom.appendChild(el.dom);
35533         }
35534         if(panel.setRegion){
35535             panel.setRegion(this);
35536         }
35537         this.panels.add(panel);
35538         el.setStyle("position", "absolute");
35539         if(!panel.background){
35540             this.setActivePanel(panel);
35541             if(this.config.initialSize && this.panels.getCount()==1){
35542                 this.resizeTo(this.config.initialSize);
35543             }
35544         }
35545         this.fireEvent("paneladded", this, panel);
35546         return panel;
35547     },
35548     
35549     /**
35550      * Returns true if the panel is in this region.
35551      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35552      * @return {Boolean}
35553      */
35554     hasPanel : function(panel){
35555         if(typeof panel == "object"){ // must be panel obj
35556             panel = panel.getId();
35557         }
35558         return this.getPanel(panel) ? true : false;
35559     },
35560     
35561     /**
35562      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35563      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35564      * @param {Boolean} preservePanel Overrides the config preservePanel option
35565      * @return {Roo.ContentPanel} The panel that was removed
35566      */
35567     remove : function(panel, preservePanel){
35568         panel = this.getPanel(panel);
35569         if(!panel){
35570             return null;
35571         }
35572         var e = {};
35573         this.fireEvent("beforeremove", this, panel, e);
35574         if(e.cancel === true){
35575             return null;
35576         }
35577         var panelId = panel.getId();
35578         this.panels.removeKey(panelId);
35579         return panel;
35580     },
35581     
35582     /**
35583      * Returns the panel specified or null if it's not in this region.
35584      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35585      * @return {Roo.ContentPanel}
35586      */
35587     getPanel : function(id){
35588         if(typeof id == "object"){ // must be panel obj
35589             return id;
35590         }
35591         return this.panels.get(id);
35592     },
35593     
35594     /**
35595      * Returns this regions position (north/south/east/west/center).
35596      * @return {String} 
35597      */
35598     getPosition: function(){
35599         return this.position;    
35600     }
35601 });/*
35602  * Based on:
35603  * Ext JS Library 1.1.1
35604  * Copyright(c) 2006-2007, Ext JS, LLC.
35605  *
35606  * Originally Released Under LGPL - original licence link has changed is not relivant.
35607  *
35608  * Fork - LGPL
35609  * <script type="text/javascript">
35610  */
35611  
35612 /**
35613  * @class Roo.bootstrap.layout.Region
35614  * @extends Roo.bootstrap.layout.Basic
35615  * This class represents a region in a layout manager.
35616  
35617  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35618  * @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})
35619  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35620  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35621  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35622  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35623  * @cfg {String}    title           The title for the region (overrides panel titles)
35624  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35625  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35626  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35627  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35628  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35629  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35630  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35631  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35632  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35633  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35634
35635  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35636  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35637  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35638  * @cfg {Number}    width           For East/West panels
35639  * @cfg {Number}    height          For North/South panels
35640  * @cfg {Boolean}   split           To show the splitter
35641  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35642  * 
35643  * @cfg {string}   cls             Extra CSS classes to add to region
35644  * 
35645  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35646  * @cfg {string}   region  the region that it inhabits..
35647  *
35648
35649  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35650  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35651
35652  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35653  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35654  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35655  */
35656 Roo.bootstrap.layout.Region = function(config)
35657 {
35658     this.applyConfig(config);
35659
35660     var mgr = config.mgr;
35661     var pos = config.region;
35662     config.skipConfig = true;
35663     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35664     
35665     if (mgr.el) {
35666         this.onRender(mgr.el);   
35667     }
35668      
35669     this.visible = true;
35670     this.collapsed = false;
35671     this.unrendered_panels = [];
35672 };
35673
35674 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35675
35676     position: '', // set by wrapper (eg. north/south etc..)
35677     unrendered_panels : null,  // unrendered panels.
35678     createBody : function(){
35679         /** This region's body element 
35680         * @type Roo.Element */
35681         this.bodyEl = this.el.createChild({
35682                 tag: "div",
35683                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35684         });
35685     },
35686
35687     onRender: function(ctr, pos)
35688     {
35689         var dh = Roo.DomHelper;
35690         /** This region's container element 
35691         * @type Roo.Element */
35692         this.el = dh.append(ctr.dom, {
35693                 tag: "div",
35694                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35695             }, true);
35696         /** This region's title element 
35697         * @type Roo.Element */
35698     
35699         this.titleEl = dh.append(this.el.dom,
35700             {
35701                     tag: "div",
35702                     unselectable: "on",
35703                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35704                     children:[
35705                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35706                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35707                     ]}, true);
35708         
35709         this.titleEl.enableDisplayMode();
35710         /** This region's title text element 
35711         * @type HTMLElement */
35712         this.titleTextEl = this.titleEl.dom.firstChild;
35713         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35714         /*
35715         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35716         this.closeBtn.enableDisplayMode();
35717         this.closeBtn.on("click", this.closeClicked, this);
35718         this.closeBtn.hide();
35719     */
35720         this.createBody(this.config);
35721         if(this.config.hideWhenEmpty){
35722             this.hide();
35723             this.on("paneladded", this.validateVisibility, this);
35724             this.on("panelremoved", this.validateVisibility, this);
35725         }
35726         if(this.autoScroll){
35727             this.bodyEl.setStyle("overflow", "auto");
35728         }else{
35729             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35730         }
35731         //if(c.titlebar !== false){
35732             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35733                 this.titleEl.hide();
35734             }else{
35735                 this.titleEl.show();
35736                 if(this.config.title){
35737                     this.titleTextEl.innerHTML = this.config.title;
35738                 }
35739             }
35740         //}
35741         if(this.config.collapsed){
35742             this.collapse(true);
35743         }
35744         if(this.config.hidden){
35745             this.hide();
35746         }
35747         
35748         if (this.unrendered_panels && this.unrendered_panels.length) {
35749             for (var i =0;i< this.unrendered_panels.length; i++) {
35750                 this.add(this.unrendered_panels[i]);
35751             }
35752             this.unrendered_panels = null;
35753             
35754         }
35755         
35756     },
35757     
35758     applyConfig : function(c)
35759     {
35760         /*
35761          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35762             var dh = Roo.DomHelper;
35763             if(c.titlebar !== false){
35764                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35765                 this.collapseBtn.on("click", this.collapse, this);
35766                 this.collapseBtn.enableDisplayMode();
35767                 /*
35768                 if(c.showPin === true || this.showPin){
35769                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35770                     this.stickBtn.enableDisplayMode();
35771                     this.stickBtn.on("click", this.expand, this);
35772                     this.stickBtn.hide();
35773                 }
35774                 
35775             }
35776             */
35777             /** This region's collapsed element
35778             * @type Roo.Element */
35779             /*
35780              *
35781             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35782                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35783             ]}, true);
35784             
35785             if(c.floatable !== false){
35786                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35787                this.collapsedEl.on("click", this.collapseClick, this);
35788             }
35789
35790             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35791                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35792                    id: "message", unselectable: "on", style:{"float":"left"}});
35793                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35794              }
35795             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35796             this.expandBtn.on("click", this.expand, this);
35797             
35798         }
35799         
35800         if(this.collapseBtn){
35801             this.collapseBtn.setVisible(c.collapsible == true);
35802         }
35803         
35804         this.cmargins = c.cmargins || this.cmargins ||
35805                          (this.position == "west" || this.position == "east" ?
35806                              {top: 0, left: 2, right:2, bottom: 0} :
35807                              {top: 2, left: 0, right:0, bottom: 2});
35808         */
35809         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35810         
35811         
35812         this.bottomTabs = c.tabPosition != "top";
35813         
35814         this.autoScroll = c.autoScroll || false;
35815         
35816         
35817        
35818         
35819         this.duration = c.duration || .30;
35820         this.slideDuration = c.slideDuration || .45;
35821         this.config = c;
35822        
35823     },
35824     /**
35825      * Returns true if this region is currently visible.
35826      * @return {Boolean}
35827      */
35828     isVisible : function(){
35829         return this.visible;
35830     },
35831
35832     /**
35833      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35834      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35835      */
35836     //setCollapsedTitle : function(title){
35837     //    title = title || "&#160;";
35838      //   if(this.collapsedTitleTextEl){
35839       //      this.collapsedTitleTextEl.innerHTML = title;
35840        // }
35841     //},
35842
35843     getBox : function(){
35844         var b;
35845       //  if(!this.collapsed){
35846             b = this.el.getBox(false, true);
35847        // }else{
35848           //  b = this.collapsedEl.getBox(false, true);
35849         //}
35850         return b;
35851     },
35852
35853     getMargins : function(){
35854         return this.margins;
35855         //return this.collapsed ? this.cmargins : this.margins;
35856     },
35857 /*
35858     highlight : function(){
35859         this.el.addClass("x-layout-panel-dragover");
35860     },
35861
35862     unhighlight : function(){
35863         this.el.removeClass("x-layout-panel-dragover");
35864     },
35865 */
35866     updateBox : function(box)
35867     {
35868         if (!this.bodyEl) {
35869             return; // not rendered yet..
35870         }
35871         
35872         this.box = box;
35873         if(!this.collapsed){
35874             this.el.dom.style.left = box.x + "px";
35875             this.el.dom.style.top = box.y + "px";
35876             this.updateBody(box.width, box.height);
35877         }else{
35878             this.collapsedEl.dom.style.left = box.x + "px";
35879             this.collapsedEl.dom.style.top = box.y + "px";
35880             this.collapsedEl.setSize(box.width, box.height);
35881         }
35882         if(this.tabs){
35883             this.tabs.autoSizeTabs();
35884         }
35885     },
35886
35887     updateBody : function(w, h)
35888     {
35889         if(w !== null){
35890             this.el.setWidth(w);
35891             w -= this.el.getBorderWidth("rl");
35892             if(this.config.adjustments){
35893                 w += this.config.adjustments[0];
35894             }
35895         }
35896         if(h !== null && h > 0){
35897             this.el.setHeight(h);
35898             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35899             h -= this.el.getBorderWidth("tb");
35900             if(this.config.adjustments){
35901                 h += this.config.adjustments[1];
35902             }
35903             this.bodyEl.setHeight(h);
35904             if(this.tabs){
35905                 h = this.tabs.syncHeight(h);
35906             }
35907         }
35908         if(this.panelSize){
35909             w = w !== null ? w : this.panelSize.width;
35910             h = h !== null ? h : this.panelSize.height;
35911         }
35912         if(this.activePanel){
35913             var el = this.activePanel.getEl();
35914             w = w !== null ? w : el.getWidth();
35915             h = h !== null ? h : el.getHeight();
35916             this.panelSize = {width: w, height: h};
35917             this.activePanel.setSize(w, h);
35918         }
35919         if(Roo.isIE && this.tabs){
35920             this.tabs.el.repaint();
35921         }
35922     },
35923
35924     /**
35925      * Returns the container element for this region.
35926      * @return {Roo.Element}
35927      */
35928     getEl : function(){
35929         return this.el;
35930     },
35931
35932     /**
35933      * Hides this region.
35934      */
35935     hide : function(){
35936         //if(!this.collapsed){
35937             this.el.dom.style.left = "-2000px";
35938             this.el.hide();
35939         //}else{
35940          //   this.collapsedEl.dom.style.left = "-2000px";
35941          //   this.collapsedEl.hide();
35942        // }
35943         this.visible = false;
35944         this.fireEvent("visibilitychange", this, false);
35945     },
35946
35947     /**
35948      * Shows this region if it was previously hidden.
35949      */
35950     show : function(){
35951         //if(!this.collapsed){
35952             this.el.show();
35953         //}else{
35954         //    this.collapsedEl.show();
35955        // }
35956         this.visible = true;
35957         this.fireEvent("visibilitychange", this, true);
35958     },
35959 /*
35960     closeClicked : function(){
35961         if(this.activePanel){
35962             this.remove(this.activePanel);
35963         }
35964     },
35965
35966     collapseClick : function(e){
35967         if(this.isSlid){
35968            e.stopPropagation();
35969            this.slideIn();
35970         }else{
35971            e.stopPropagation();
35972            this.slideOut();
35973         }
35974     },
35975 */
35976     /**
35977      * Collapses this region.
35978      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35979      */
35980     /*
35981     collapse : function(skipAnim, skipCheck = false){
35982         if(this.collapsed) {
35983             return;
35984         }
35985         
35986         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35987             
35988             this.collapsed = true;
35989             if(this.split){
35990                 this.split.el.hide();
35991             }
35992             if(this.config.animate && skipAnim !== true){
35993                 this.fireEvent("invalidated", this);
35994                 this.animateCollapse();
35995             }else{
35996                 this.el.setLocation(-20000,-20000);
35997                 this.el.hide();
35998                 this.collapsedEl.show();
35999                 this.fireEvent("collapsed", this);
36000                 this.fireEvent("invalidated", this);
36001             }
36002         }
36003         
36004     },
36005 */
36006     animateCollapse : function(){
36007         // overridden
36008     },
36009
36010     /**
36011      * Expands this region if it was previously collapsed.
36012      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36013      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36014      */
36015     /*
36016     expand : function(e, skipAnim){
36017         if(e) {
36018             e.stopPropagation();
36019         }
36020         if(!this.collapsed || this.el.hasActiveFx()) {
36021             return;
36022         }
36023         if(this.isSlid){
36024             this.afterSlideIn();
36025             skipAnim = true;
36026         }
36027         this.collapsed = false;
36028         if(this.config.animate && skipAnim !== true){
36029             this.animateExpand();
36030         }else{
36031             this.el.show();
36032             if(this.split){
36033                 this.split.el.show();
36034             }
36035             this.collapsedEl.setLocation(-2000,-2000);
36036             this.collapsedEl.hide();
36037             this.fireEvent("invalidated", this);
36038             this.fireEvent("expanded", this);
36039         }
36040     },
36041 */
36042     animateExpand : function(){
36043         // overridden
36044     },
36045
36046     initTabs : function()
36047     {
36048         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36049         
36050         var ts = new Roo.bootstrap.panel.Tabs({
36051                 el: this.bodyEl.dom,
36052                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36053                 disableTooltips: this.config.disableTabTips,
36054                 toolbar : this.config.toolbar
36055             });
36056         
36057         if(this.config.hideTabs){
36058             ts.stripWrap.setDisplayed(false);
36059         }
36060         this.tabs = ts;
36061         ts.resizeTabs = this.config.resizeTabs === true;
36062         ts.minTabWidth = this.config.minTabWidth || 40;
36063         ts.maxTabWidth = this.config.maxTabWidth || 250;
36064         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36065         ts.monitorResize = false;
36066         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36067         ts.bodyEl.addClass('roo-layout-tabs-body');
36068         this.panels.each(this.initPanelAsTab, this);
36069     },
36070
36071     initPanelAsTab : function(panel){
36072         var ti = this.tabs.addTab(
36073             panel.getEl().id,
36074             panel.getTitle(),
36075             null,
36076             this.config.closeOnTab && panel.isClosable(),
36077             panel.tpl
36078         );
36079         if(panel.tabTip !== undefined){
36080             ti.setTooltip(panel.tabTip);
36081         }
36082         ti.on("activate", function(){
36083               this.setActivePanel(panel);
36084         }, this);
36085         
36086         if(this.config.closeOnTab){
36087             ti.on("beforeclose", function(t, e){
36088                 e.cancel = true;
36089                 this.remove(panel);
36090             }, this);
36091         }
36092         
36093         panel.tabItem = ti;
36094         
36095         return ti;
36096     },
36097
36098     updatePanelTitle : function(panel, title)
36099     {
36100         if(this.activePanel == panel){
36101             this.updateTitle(title);
36102         }
36103         if(this.tabs){
36104             var ti = this.tabs.getTab(panel.getEl().id);
36105             ti.setText(title);
36106             if(panel.tabTip !== undefined){
36107                 ti.setTooltip(panel.tabTip);
36108             }
36109         }
36110     },
36111
36112     updateTitle : function(title){
36113         if(this.titleTextEl && !this.config.title){
36114             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36115         }
36116     },
36117
36118     setActivePanel : function(panel)
36119     {
36120         panel = this.getPanel(panel);
36121         if(this.activePanel && this.activePanel != panel){
36122             if(this.activePanel.setActiveState(false) === false){
36123                 return;
36124             }
36125         }
36126         this.activePanel = panel;
36127         panel.setActiveState(true);
36128         if(this.panelSize){
36129             panel.setSize(this.panelSize.width, this.panelSize.height);
36130         }
36131         if(this.closeBtn){
36132             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36133         }
36134         this.updateTitle(panel.getTitle());
36135         if(this.tabs){
36136             this.fireEvent("invalidated", this);
36137         }
36138         this.fireEvent("panelactivated", this, panel);
36139     },
36140
36141     /**
36142      * Shows the specified panel.
36143      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36144      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36145      */
36146     showPanel : function(panel)
36147     {
36148         panel = this.getPanel(panel);
36149         if(panel){
36150             if(this.tabs){
36151                 var tab = this.tabs.getTab(panel.getEl().id);
36152                 if(tab.isHidden()){
36153                     this.tabs.unhideTab(tab.id);
36154                 }
36155                 tab.activate();
36156             }else{
36157                 this.setActivePanel(panel);
36158             }
36159         }
36160         return panel;
36161     },
36162
36163     /**
36164      * Get the active panel for this region.
36165      * @return {Roo.ContentPanel} The active panel or null
36166      */
36167     getActivePanel : function(){
36168         return this.activePanel;
36169     },
36170
36171     validateVisibility : function(){
36172         if(this.panels.getCount() < 1){
36173             this.updateTitle("&#160;");
36174             this.closeBtn.hide();
36175             this.hide();
36176         }else{
36177             if(!this.isVisible()){
36178                 this.show();
36179             }
36180         }
36181     },
36182
36183     /**
36184      * Adds the passed ContentPanel(s) to this region.
36185      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36186      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36187      */
36188     add : function(panel)
36189     {
36190         if(arguments.length > 1){
36191             for(var i = 0, len = arguments.length; i < len; i++) {
36192                 this.add(arguments[i]);
36193             }
36194             return null;
36195         }
36196         
36197         // if we have not been rendered yet, then we can not really do much of this..
36198         if (!this.bodyEl) {
36199             this.unrendered_panels.push(panel);
36200             return panel;
36201         }
36202         
36203         
36204         
36205         
36206         if(this.hasPanel(panel)){
36207             this.showPanel(panel);
36208             return panel;
36209         }
36210         panel.setRegion(this);
36211         this.panels.add(panel);
36212        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36213             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36214             // and hide them... ???
36215             this.bodyEl.dom.appendChild(panel.getEl().dom);
36216             if(panel.background !== true){
36217                 this.setActivePanel(panel);
36218             }
36219             this.fireEvent("paneladded", this, panel);
36220             return panel;
36221         }
36222         */
36223         if(!this.tabs){
36224             this.initTabs();
36225         }else{
36226             this.initPanelAsTab(panel);
36227         }
36228         
36229         
36230         if(panel.background !== true){
36231             this.tabs.activate(panel.getEl().id);
36232         }
36233         this.fireEvent("paneladded", this, panel);
36234         return panel;
36235     },
36236
36237     /**
36238      * Hides the tab for the specified panel.
36239      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36240      */
36241     hidePanel : function(panel){
36242         if(this.tabs && (panel = this.getPanel(panel))){
36243             this.tabs.hideTab(panel.getEl().id);
36244         }
36245     },
36246
36247     /**
36248      * Unhides the tab for a previously hidden panel.
36249      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36250      */
36251     unhidePanel : function(panel){
36252         if(this.tabs && (panel = this.getPanel(panel))){
36253             this.tabs.unhideTab(panel.getEl().id);
36254         }
36255     },
36256
36257     clearPanels : function(){
36258         while(this.panels.getCount() > 0){
36259              this.remove(this.panels.first());
36260         }
36261     },
36262
36263     /**
36264      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36265      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36266      * @param {Boolean} preservePanel Overrides the config preservePanel option
36267      * @return {Roo.ContentPanel} The panel that was removed
36268      */
36269     remove : function(panel, preservePanel)
36270     {
36271         panel = this.getPanel(panel);
36272         if(!panel){
36273             return null;
36274         }
36275         var e = {};
36276         this.fireEvent("beforeremove", this, panel, e);
36277         if(e.cancel === true){
36278             return null;
36279         }
36280         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36281         var panelId = panel.getId();
36282         this.panels.removeKey(panelId);
36283         if(preservePanel){
36284             document.body.appendChild(panel.getEl().dom);
36285         }
36286         if(this.tabs){
36287             this.tabs.removeTab(panel.getEl().id);
36288         }else if (!preservePanel){
36289             this.bodyEl.dom.removeChild(panel.getEl().dom);
36290         }
36291         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36292             var p = this.panels.first();
36293             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36294             tempEl.appendChild(p.getEl().dom);
36295             this.bodyEl.update("");
36296             this.bodyEl.dom.appendChild(p.getEl().dom);
36297             tempEl = null;
36298             this.updateTitle(p.getTitle());
36299             this.tabs = null;
36300             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36301             this.setActivePanel(p);
36302         }
36303         panel.setRegion(null);
36304         if(this.activePanel == panel){
36305             this.activePanel = null;
36306         }
36307         if(this.config.autoDestroy !== false && preservePanel !== true){
36308             try{panel.destroy();}catch(e){}
36309         }
36310         this.fireEvent("panelremoved", this, panel);
36311         return panel;
36312     },
36313
36314     /**
36315      * Returns the TabPanel component used by this region
36316      * @return {Roo.TabPanel}
36317      */
36318     getTabs : function(){
36319         return this.tabs;
36320     },
36321
36322     createTool : function(parentEl, className){
36323         var btn = Roo.DomHelper.append(parentEl, {
36324             tag: "div",
36325             cls: "x-layout-tools-button",
36326             children: [ {
36327                 tag: "div",
36328                 cls: "roo-layout-tools-button-inner " + className,
36329                 html: "&#160;"
36330             }]
36331         }, true);
36332         btn.addClassOnOver("roo-layout-tools-button-over");
36333         return btn;
36334     }
36335 });/*
36336  * Based on:
36337  * Ext JS Library 1.1.1
36338  * Copyright(c) 2006-2007, Ext JS, LLC.
36339  *
36340  * Originally Released Under LGPL - original licence link has changed is not relivant.
36341  *
36342  * Fork - LGPL
36343  * <script type="text/javascript">
36344  */
36345  
36346
36347
36348 /**
36349  * @class Roo.SplitLayoutRegion
36350  * @extends Roo.LayoutRegion
36351  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36352  */
36353 Roo.bootstrap.layout.Split = function(config){
36354     this.cursor = config.cursor;
36355     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36356 };
36357
36358 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36359 {
36360     splitTip : "Drag to resize.",
36361     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36362     useSplitTips : false,
36363
36364     applyConfig : function(config){
36365         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36366     },
36367     
36368     onRender : function(ctr,pos) {
36369         
36370         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36371         if(!this.config.split){
36372             return;
36373         }
36374         if(!this.split){
36375             
36376             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36377                             tag: "div",
36378                             id: this.el.id + "-split",
36379                             cls: "roo-layout-split roo-layout-split-"+this.position,
36380                             html: "&#160;"
36381             });
36382             /** The SplitBar for this region 
36383             * @type Roo.SplitBar */
36384             // does not exist yet...
36385             Roo.log([this.position, this.orientation]);
36386             
36387             this.split = new Roo.bootstrap.SplitBar({
36388                 dragElement : splitEl,
36389                 resizingElement: this.el,
36390                 orientation : this.orientation
36391             });
36392             
36393             this.split.on("moved", this.onSplitMove, this);
36394             this.split.useShim = this.config.useShim === true;
36395             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36396             if(this.useSplitTips){
36397                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36398             }
36399             //if(config.collapsible){
36400             //    this.split.el.on("dblclick", this.collapse,  this);
36401             //}
36402         }
36403         if(typeof this.config.minSize != "undefined"){
36404             this.split.minSize = this.config.minSize;
36405         }
36406         if(typeof this.config.maxSize != "undefined"){
36407             this.split.maxSize = this.config.maxSize;
36408         }
36409         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36410             this.hideSplitter();
36411         }
36412         
36413     },
36414
36415     getHMaxSize : function(){
36416          var cmax = this.config.maxSize || 10000;
36417          var center = this.mgr.getRegion("center");
36418          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36419     },
36420
36421     getVMaxSize : function(){
36422          var cmax = this.config.maxSize || 10000;
36423          var center = this.mgr.getRegion("center");
36424          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36425     },
36426
36427     onSplitMove : function(split, newSize){
36428         this.fireEvent("resized", this, newSize);
36429     },
36430     
36431     /** 
36432      * Returns the {@link Roo.SplitBar} for this region.
36433      * @return {Roo.SplitBar}
36434      */
36435     getSplitBar : function(){
36436         return this.split;
36437     },
36438     
36439     hide : function(){
36440         this.hideSplitter();
36441         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36442     },
36443
36444     hideSplitter : function(){
36445         if(this.split){
36446             this.split.el.setLocation(-2000,-2000);
36447             this.split.el.hide();
36448         }
36449     },
36450
36451     show : function(){
36452         if(this.split){
36453             this.split.el.show();
36454         }
36455         Roo.bootstrap.layout.Split.superclass.show.call(this);
36456     },
36457     
36458     beforeSlide: function(){
36459         if(Roo.isGecko){// firefox overflow auto bug workaround
36460             this.bodyEl.clip();
36461             if(this.tabs) {
36462                 this.tabs.bodyEl.clip();
36463             }
36464             if(this.activePanel){
36465                 this.activePanel.getEl().clip();
36466                 
36467                 if(this.activePanel.beforeSlide){
36468                     this.activePanel.beforeSlide();
36469                 }
36470             }
36471         }
36472     },
36473     
36474     afterSlide : function(){
36475         if(Roo.isGecko){// firefox overflow auto bug workaround
36476             this.bodyEl.unclip();
36477             if(this.tabs) {
36478                 this.tabs.bodyEl.unclip();
36479             }
36480             if(this.activePanel){
36481                 this.activePanel.getEl().unclip();
36482                 if(this.activePanel.afterSlide){
36483                     this.activePanel.afterSlide();
36484                 }
36485             }
36486         }
36487     },
36488
36489     initAutoHide : function(){
36490         if(this.autoHide !== false){
36491             if(!this.autoHideHd){
36492                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36493                 this.autoHideHd = {
36494                     "mouseout": function(e){
36495                         if(!e.within(this.el, true)){
36496                             st.delay(500);
36497                         }
36498                     },
36499                     "mouseover" : function(e){
36500                         st.cancel();
36501                     },
36502                     scope : this
36503                 };
36504             }
36505             this.el.on(this.autoHideHd);
36506         }
36507     },
36508
36509     clearAutoHide : function(){
36510         if(this.autoHide !== false){
36511             this.el.un("mouseout", this.autoHideHd.mouseout);
36512             this.el.un("mouseover", this.autoHideHd.mouseover);
36513         }
36514     },
36515
36516     clearMonitor : function(){
36517         Roo.get(document).un("click", this.slideInIf, this);
36518     },
36519
36520     // these names are backwards but not changed for compat
36521     slideOut : function(){
36522         if(this.isSlid || this.el.hasActiveFx()){
36523             return;
36524         }
36525         this.isSlid = true;
36526         if(this.collapseBtn){
36527             this.collapseBtn.hide();
36528         }
36529         this.closeBtnState = this.closeBtn.getStyle('display');
36530         this.closeBtn.hide();
36531         if(this.stickBtn){
36532             this.stickBtn.show();
36533         }
36534         this.el.show();
36535         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36536         this.beforeSlide();
36537         this.el.setStyle("z-index", 10001);
36538         this.el.slideIn(this.getSlideAnchor(), {
36539             callback: function(){
36540                 this.afterSlide();
36541                 this.initAutoHide();
36542                 Roo.get(document).on("click", this.slideInIf, this);
36543                 this.fireEvent("slideshow", this);
36544             },
36545             scope: this,
36546             block: true
36547         });
36548     },
36549
36550     afterSlideIn : function(){
36551         this.clearAutoHide();
36552         this.isSlid = false;
36553         this.clearMonitor();
36554         this.el.setStyle("z-index", "");
36555         if(this.collapseBtn){
36556             this.collapseBtn.show();
36557         }
36558         this.closeBtn.setStyle('display', this.closeBtnState);
36559         if(this.stickBtn){
36560             this.stickBtn.hide();
36561         }
36562         this.fireEvent("slidehide", this);
36563     },
36564
36565     slideIn : function(cb){
36566         if(!this.isSlid || this.el.hasActiveFx()){
36567             Roo.callback(cb);
36568             return;
36569         }
36570         this.isSlid = false;
36571         this.beforeSlide();
36572         this.el.slideOut(this.getSlideAnchor(), {
36573             callback: function(){
36574                 this.el.setLeftTop(-10000, -10000);
36575                 this.afterSlide();
36576                 this.afterSlideIn();
36577                 Roo.callback(cb);
36578             },
36579             scope: this,
36580             block: true
36581         });
36582     },
36583     
36584     slideInIf : function(e){
36585         if(!e.within(this.el)){
36586             this.slideIn();
36587         }
36588     },
36589
36590     animateCollapse : function(){
36591         this.beforeSlide();
36592         this.el.setStyle("z-index", 20000);
36593         var anchor = this.getSlideAnchor();
36594         this.el.slideOut(anchor, {
36595             callback : function(){
36596                 this.el.setStyle("z-index", "");
36597                 this.collapsedEl.slideIn(anchor, {duration:.3});
36598                 this.afterSlide();
36599                 this.el.setLocation(-10000,-10000);
36600                 this.el.hide();
36601                 this.fireEvent("collapsed", this);
36602             },
36603             scope: this,
36604             block: true
36605         });
36606     },
36607
36608     animateExpand : function(){
36609         this.beforeSlide();
36610         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36611         this.el.setStyle("z-index", 20000);
36612         this.collapsedEl.hide({
36613             duration:.1
36614         });
36615         this.el.slideIn(this.getSlideAnchor(), {
36616             callback : function(){
36617                 this.el.setStyle("z-index", "");
36618                 this.afterSlide();
36619                 if(this.split){
36620                     this.split.el.show();
36621                 }
36622                 this.fireEvent("invalidated", this);
36623                 this.fireEvent("expanded", this);
36624             },
36625             scope: this,
36626             block: true
36627         });
36628     },
36629
36630     anchors : {
36631         "west" : "left",
36632         "east" : "right",
36633         "north" : "top",
36634         "south" : "bottom"
36635     },
36636
36637     sanchors : {
36638         "west" : "l",
36639         "east" : "r",
36640         "north" : "t",
36641         "south" : "b"
36642     },
36643
36644     canchors : {
36645         "west" : "tl-tr",
36646         "east" : "tr-tl",
36647         "north" : "tl-bl",
36648         "south" : "bl-tl"
36649     },
36650
36651     getAnchor : function(){
36652         return this.anchors[this.position];
36653     },
36654
36655     getCollapseAnchor : function(){
36656         return this.canchors[this.position];
36657     },
36658
36659     getSlideAnchor : function(){
36660         return this.sanchors[this.position];
36661     },
36662
36663     getAlignAdj : function(){
36664         var cm = this.cmargins;
36665         switch(this.position){
36666             case "west":
36667                 return [0, 0];
36668             break;
36669             case "east":
36670                 return [0, 0];
36671             break;
36672             case "north":
36673                 return [0, 0];
36674             break;
36675             case "south":
36676                 return [0, 0];
36677             break;
36678         }
36679     },
36680
36681     getExpandAdj : function(){
36682         var c = this.collapsedEl, cm = this.cmargins;
36683         switch(this.position){
36684             case "west":
36685                 return [-(cm.right+c.getWidth()+cm.left), 0];
36686             break;
36687             case "east":
36688                 return [cm.right+c.getWidth()+cm.left, 0];
36689             break;
36690             case "north":
36691                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36692             break;
36693             case "south":
36694                 return [0, cm.top+cm.bottom+c.getHeight()];
36695             break;
36696         }
36697     }
36698 });/*
36699  * Based on:
36700  * Ext JS Library 1.1.1
36701  * Copyright(c) 2006-2007, Ext JS, LLC.
36702  *
36703  * Originally Released Under LGPL - original licence link has changed is not relivant.
36704  *
36705  * Fork - LGPL
36706  * <script type="text/javascript">
36707  */
36708 /*
36709  * These classes are private internal classes
36710  */
36711 Roo.bootstrap.layout.Center = function(config){
36712     config.region = "center";
36713     Roo.bootstrap.layout.Region.call(this, config);
36714     this.visible = true;
36715     this.minWidth = config.minWidth || 20;
36716     this.minHeight = config.minHeight || 20;
36717 };
36718
36719 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36720     hide : function(){
36721         // center panel can't be hidden
36722     },
36723     
36724     show : function(){
36725         // center panel can't be hidden
36726     },
36727     
36728     getMinWidth: function(){
36729         return this.minWidth;
36730     },
36731     
36732     getMinHeight: function(){
36733         return this.minHeight;
36734     }
36735 });
36736
36737
36738
36739
36740  
36741
36742
36743
36744
36745
36746 Roo.bootstrap.layout.North = function(config)
36747 {
36748     config.region = 'north';
36749     config.cursor = 'n-resize';
36750     
36751     Roo.bootstrap.layout.Split.call(this, config);
36752     
36753     
36754     if(this.split){
36755         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36756         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36757         this.split.el.addClass("roo-layout-split-v");
36758     }
36759     var size = config.initialSize || config.height;
36760     if(typeof size != "undefined"){
36761         this.el.setHeight(size);
36762     }
36763 };
36764 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36765 {
36766     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36767     
36768     
36769     
36770     getBox : function(){
36771         if(this.collapsed){
36772             return this.collapsedEl.getBox();
36773         }
36774         var box = this.el.getBox();
36775         if(this.split){
36776             box.height += this.split.el.getHeight();
36777         }
36778         return box;
36779     },
36780     
36781     updateBox : function(box){
36782         if(this.split && !this.collapsed){
36783             box.height -= this.split.el.getHeight();
36784             this.split.el.setLeft(box.x);
36785             this.split.el.setTop(box.y+box.height);
36786             this.split.el.setWidth(box.width);
36787         }
36788         if(this.collapsed){
36789             this.updateBody(box.width, null);
36790         }
36791         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36792     }
36793 });
36794
36795
36796
36797
36798
36799 Roo.bootstrap.layout.South = function(config){
36800     config.region = 'south';
36801     config.cursor = 's-resize';
36802     Roo.bootstrap.layout.Split.call(this, config);
36803     if(this.split){
36804         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36805         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36806         this.split.el.addClass("roo-layout-split-v");
36807     }
36808     var size = config.initialSize || config.height;
36809     if(typeof size != "undefined"){
36810         this.el.setHeight(size);
36811     }
36812 };
36813
36814 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36815     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36816     getBox : function(){
36817         if(this.collapsed){
36818             return this.collapsedEl.getBox();
36819         }
36820         var box = this.el.getBox();
36821         if(this.split){
36822             var sh = this.split.el.getHeight();
36823             box.height += sh;
36824             box.y -= sh;
36825         }
36826         return box;
36827     },
36828     
36829     updateBox : function(box){
36830         if(this.split && !this.collapsed){
36831             var sh = this.split.el.getHeight();
36832             box.height -= sh;
36833             box.y += sh;
36834             this.split.el.setLeft(box.x);
36835             this.split.el.setTop(box.y-sh);
36836             this.split.el.setWidth(box.width);
36837         }
36838         if(this.collapsed){
36839             this.updateBody(box.width, null);
36840         }
36841         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36842     }
36843 });
36844
36845 Roo.bootstrap.layout.East = function(config){
36846     config.region = "east";
36847     config.cursor = "e-resize";
36848     Roo.bootstrap.layout.Split.call(this, config);
36849     if(this.split){
36850         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36851         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36852         this.split.el.addClass("roo-layout-split-h");
36853     }
36854     var size = config.initialSize || config.width;
36855     if(typeof size != "undefined"){
36856         this.el.setWidth(size);
36857     }
36858 };
36859 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36860     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36861     getBox : function(){
36862         if(this.collapsed){
36863             return this.collapsedEl.getBox();
36864         }
36865         var box = this.el.getBox();
36866         if(this.split){
36867             var sw = this.split.el.getWidth();
36868             box.width += sw;
36869             box.x -= sw;
36870         }
36871         return box;
36872     },
36873
36874     updateBox : function(box){
36875         if(this.split && !this.collapsed){
36876             var sw = this.split.el.getWidth();
36877             box.width -= sw;
36878             this.split.el.setLeft(box.x);
36879             this.split.el.setTop(box.y);
36880             this.split.el.setHeight(box.height);
36881             box.x += sw;
36882         }
36883         if(this.collapsed){
36884             this.updateBody(null, box.height);
36885         }
36886         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36887     }
36888 });
36889
36890 Roo.bootstrap.layout.West = function(config){
36891     config.region = "west";
36892     config.cursor = "w-resize";
36893     
36894     Roo.bootstrap.layout.Split.call(this, config);
36895     if(this.split){
36896         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36897         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36898         this.split.el.addClass("roo-layout-split-h");
36899     }
36900     
36901 };
36902 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36903     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36904     
36905     onRender: function(ctr, pos)
36906     {
36907         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36908         var size = this.config.initialSize || this.config.width;
36909         if(typeof size != "undefined"){
36910             this.el.setWidth(size);
36911         }
36912     },
36913     
36914     getBox : function(){
36915         if(this.collapsed){
36916             return this.collapsedEl.getBox();
36917         }
36918         var box = this.el.getBox();
36919         if(this.split){
36920             box.width += this.split.el.getWidth();
36921         }
36922         return box;
36923     },
36924     
36925     updateBox : function(box){
36926         if(this.split && !this.collapsed){
36927             var sw = this.split.el.getWidth();
36928             box.width -= sw;
36929             this.split.el.setLeft(box.x+box.width);
36930             this.split.el.setTop(box.y);
36931             this.split.el.setHeight(box.height);
36932         }
36933         if(this.collapsed){
36934             this.updateBody(null, box.height);
36935         }
36936         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36937     }
36938 });
36939 Roo.namespace("Roo.bootstrap.panel");/*
36940  * Based on:
36941  * Ext JS Library 1.1.1
36942  * Copyright(c) 2006-2007, Ext JS, LLC.
36943  *
36944  * Originally Released Under LGPL - original licence link has changed is not relivant.
36945  *
36946  * Fork - LGPL
36947  * <script type="text/javascript">
36948  */
36949 /**
36950  * @class Roo.ContentPanel
36951  * @extends Roo.util.Observable
36952  * A basic ContentPanel element.
36953  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36954  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36955  * @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
36956  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36957  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36958  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36959  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36960  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36961  * @cfg {String} title          The title for this panel
36962  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36963  * @cfg {String} url            Calls {@link #setUrl} with this value
36964  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36965  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36966  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36967  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36968  * @cfg {Boolean} badges render the badges
36969
36970  * @constructor
36971  * Create a new ContentPanel.
36972  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36973  * @param {String/Object} config A string to set only the title or a config object
36974  * @param {String} content (optional) Set the HTML content for this panel
36975  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36976  */
36977 Roo.bootstrap.panel.Content = function( config){
36978     
36979     this.tpl = config.tpl || false;
36980     
36981     var el = config.el;
36982     var content = config.content;
36983
36984     if(config.autoCreate){ // xtype is available if this is called from factory
36985         el = Roo.id();
36986     }
36987     this.el = Roo.get(el);
36988     if(!this.el && config && config.autoCreate){
36989         if(typeof config.autoCreate == "object"){
36990             if(!config.autoCreate.id){
36991                 config.autoCreate.id = config.id||el;
36992             }
36993             this.el = Roo.DomHelper.append(document.body,
36994                         config.autoCreate, true);
36995         }else{
36996             var elcfg =  {   tag: "div",
36997                             cls: "roo-layout-inactive-content",
36998                             id: config.id||el
36999                             };
37000             if (config.html) {
37001                 elcfg.html = config.html;
37002                 
37003             }
37004                         
37005             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37006         }
37007     } 
37008     this.closable = false;
37009     this.loaded = false;
37010     this.active = false;
37011    
37012       
37013     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37014         
37015         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37016         
37017         this.wrapEl = this.el; //this.el.wrap();
37018         var ti = [];
37019         if (config.toolbar.items) {
37020             ti = config.toolbar.items ;
37021             delete config.toolbar.items ;
37022         }
37023         
37024         var nitems = [];
37025         this.toolbar.render(this.wrapEl, 'before');
37026         for(var i =0;i < ti.length;i++) {
37027           //  Roo.log(['add child', items[i]]);
37028             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37029         }
37030         this.toolbar.items = nitems;
37031         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37032         delete config.toolbar;
37033         
37034     }
37035     /*
37036     // xtype created footer. - not sure if will work as we normally have to render first..
37037     if (this.footer && !this.footer.el && this.footer.xtype) {
37038         if (!this.wrapEl) {
37039             this.wrapEl = this.el.wrap();
37040         }
37041     
37042         this.footer.container = this.wrapEl.createChild();
37043          
37044         this.footer = Roo.factory(this.footer, Roo);
37045         
37046     }
37047     */
37048     
37049      if(typeof config == "string"){
37050         this.title = config;
37051     }else{
37052         Roo.apply(this, config);
37053     }
37054     
37055     if(this.resizeEl){
37056         this.resizeEl = Roo.get(this.resizeEl, true);
37057     }else{
37058         this.resizeEl = this.el;
37059     }
37060     // handle view.xtype
37061     
37062  
37063     
37064     
37065     this.addEvents({
37066         /**
37067          * @event activate
37068          * Fires when this panel is activated. 
37069          * @param {Roo.ContentPanel} this
37070          */
37071         "activate" : true,
37072         /**
37073          * @event deactivate
37074          * Fires when this panel is activated. 
37075          * @param {Roo.ContentPanel} this
37076          */
37077         "deactivate" : true,
37078
37079         /**
37080          * @event resize
37081          * Fires when this panel is resized if fitToFrame is true.
37082          * @param {Roo.ContentPanel} this
37083          * @param {Number} width The width after any component adjustments
37084          * @param {Number} height The height after any component adjustments
37085          */
37086         "resize" : true,
37087         
37088          /**
37089          * @event render
37090          * Fires when this tab is created
37091          * @param {Roo.ContentPanel} this
37092          */
37093         "render" : true
37094         
37095         
37096         
37097     });
37098     
37099
37100     
37101     
37102     if(this.autoScroll){
37103         this.resizeEl.setStyle("overflow", "auto");
37104     } else {
37105         // fix randome scrolling
37106         //this.el.on('scroll', function() {
37107         //    Roo.log('fix random scolling');
37108         //    this.scrollTo('top',0); 
37109         //});
37110     }
37111     content = content || this.content;
37112     if(content){
37113         this.setContent(content);
37114     }
37115     if(config && config.url){
37116         this.setUrl(this.url, this.params, this.loadOnce);
37117     }
37118     
37119     
37120     
37121     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37122     
37123     if (this.view && typeof(this.view.xtype) != 'undefined') {
37124         this.view.el = this.el.appendChild(document.createElement("div"));
37125         this.view = Roo.factory(this.view); 
37126         this.view.render  &&  this.view.render(false, '');  
37127     }
37128     
37129     
37130     this.fireEvent('render', this);
37131 };
37132
37133 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37134     
37135     tabTip : '',
37136     
37137     setRegion : function(region){
37138         this.region = region;
37139         this.setActiveClass(region && !this.background);
37140     },
37141     
37142     
37143     setActiveClass: function(state)
37144     {
37145         if(state){
37146            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37147            this.el.setStyle('position','relative');
37148         }else{
37149            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37150            this.el.setStyle('position', 'absolute');
37151         } 
37152     },
37153     
37154     /**
37155      * Returns the toolbar for this Panel if one was configured. 
37156      * @return {Roo.Toolbar} 
37157      */
37158     getToolbar : function(){
37159         return this.toolbar;
37160     },
37161     
37162     setActiveState : function(active)
37163     {
37164         this.active = active;
37165         this.setActiveClass(active);
37166         if(!active){
37167             if(this.fireEvent("deactivate", this) === false){
37168                 return false;
37169             }
37170             return true;
37171         }
37172         this.fireEvent("activate", this);
37173         return true;
37174     },
37175     /**
37176      * Updates this panel's element
37177      * @param {String} content The new content
37178      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37179     */
37180     setContent : function(content, loadScripts){
37181         this.el.update(content, loadScripts);
37182     },
37183
37184     ignoreResize : function(w, h){
37185         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37186             return true;
37187         }else{
37188             this.lastSize = {width: w, height: h};
37189             return false;
37190         }
37191     },
37192     /**
37193      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37194      * @return {Roo.UpdateManager} The UpdateManager
37195      */
37196     getUpdateManager : function(){
37197         return this.el.getUpdateManager();
37198     },
37199      /**
37200      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37201      * @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:
37202 <pre><code>
37203 panel.load({
37204     url: "your-url.php",
37205     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37206     callback: yourFunction,
37207     scope: yourObject, //(optional scope)
37208     discardUrl: false,
37209     nocache: false,
37210     text: "Loading...",
37211     timeout: 30,
37212     scripts: false
37213 });
37214 </code></pre>
37215      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37216      * 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.
37217      * @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}
37218      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37219      * @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.
37220      * @return {Roo.ContentPanel} this
37221      */
37222     load : function(){
37223         var um = this.el.getUpdateManager();
37224         um.update.apply(um, arguments);
37225         return this;
37226     },
37227
37228
37229     /**
37230      * 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.
37231      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37232      * @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)
37233      * @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)
37234      * @return {Roo.UpdateManager} The UpdateManager
37235      */
37236     setUrl : function(url, params, loadOnce){
37237         if(this.refreshDelegate){
37238             this.removeListener("activate", this.refreshDelegate);
37239         }
37240         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37241         this.on("activate", this.refreshDelegate);
37242         return this.el.getUpdateManager();
37243     },
37244     
37245     _handleRefresh : function(url, params, loadOnce){
37246         if(!loadOnce || !this.loaded){
37247             var updater = this.el.getUpdateManager();
37248             updater.update(url, params, this._setLoaded.createDelegate(this));
37249         }
37250     },
37251     
37252     _setLoaded : function(){
37253         this.loaded = true;
37254     }, 
37255     
37256     /**
37257      * Returns this panel's id
37258      * @return {String} 
37259      */
37260     getId : function(){
37261         return this.el.id;
37262     },
37263     
37264     /** 
37265      * Returns this panel's element - used by regiosn to add.
37266      * @return {Roo.Element} 
37267      */
37268     getEl : function(){
37269         return this.wrapEl || this.el;
37270     },
37271     
37272    
37273     
37274     adjustForComponents : function(width, height)
37275     {
37276         //Roo.log('adjustForComponents ');
37277         if(this.resizeEl != this.el){
37278             width -= this.el.getFrameWidth('lr');
37279             height -= this.el.getFrameWidth('tb');
37280         }
37281         if(this.toolbar){
37282             var te = this.toolbar.getEl();
37283             te.setWidth(width);
37284             height -= te.getHeight();
37285         }
37286         if(this.footer){
37287             var te = this.footer.getEl();
37288             te.setWidth(width);
37289             height -= te.getHeight();
37290         }
37291         
37292         
37293         if(this.adjustments){
37294             width += this.adjustments[0];
37295             height += this.adjustments[1];
37296         }
37297         return {"width": width, "height": height};
37298     },
37299     
37300     setSize : function(width, height){
37301         if(this.fitToFrame && !this.ignoreResize(width, height)){
37302             if(this.fitContainer && this.resizeEl != this.el){
37303                 this.el.setSize(width, height);
37304             }
37305             var size = this.adjustForComponents(width, height);
37306             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37307             this.fireEvent('resize', this, size.width, size.height);
37308         }
37309     },
37310     
37311     /**
37312      * Returns this panel's title
37313      * @return {String} 
37314      */
37315     getTitle : function(){
37316         
37317         if (typeof(this.title) != 'object') {
37318             return this.title;
37319         }
37320         
37321         var t = '';
37322         for (var k in this.title) {
37323             if (!this.title.hasOwnProperty(k)) {
37324                 continue;
37325             }
37326             
37327             if (k.indexOf('-') >= 0) {
37328                 var s = k.split('-');
37329                 for (var i = 0; i<s.length; i++) {
37330                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37331                 }
37332             } else {
37333                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37334             }
37335         }
37336         return t;
37337     },
37338     
37339     /**
37340      * Set this panel's title
37341      * @param {String} title
37342      */
37343     setTitle : function(title){
37344         this.title = title;
37345         if(this.region){
37346             this.region.updatePanelTitle(this, title);
37347         }
37348     },
37349     
37350     /**
37351      * Returns true is this panel was configured to be closable
37352      * @return {Boolean} 
37353      */
37354     isClosable : function(){
37355         return this.closable;
37356     },
37357     
37358     beforeSlide : function(){
37359         this.el.clip();
37360         this.resizeEl.clip();
37361     },
37362     
37363     afterSlide : function(){
37364         this.el.unclip();
37365         this.resizeEl.unclip();
37366     },
37367     
37368     /**
37369      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37370      *   Will fail silently if the {@link #setUrl} method has not been called.
37371      *   This does not activate the panel, just updates its content.
37372      */
37373     refresh : function(){
37374         if(this.refreshDelegate){
37375            this.loaded = false;
37376            this.refreshDelegate();
37377         }
37378     },
37379     
37380     /**
37381      * Destroys this panel
37382      */
37383     destroy : function(){
37384         this.el.removeAllListeners();
37385         var tempEl = document.createElement("span");
37386         tempEl.appendChild(this.el.dom);
37387         tempEl.innerHTML = "";
37388         this.el.remove();
37389         this.el = null;
37390     },
37391     
37392     /**
37393      * form - if the content panel contains a form - this is a reference to it.
37394      * @type {Roo.form.Form}
37395      */
37396     form : false,
37397     /**
37398      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37399      *    This contains a reference to it.
37400      * @type {Roo.View}
37401      */
37402     view : false,
37403     
37404       /**
37405      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37406      * <pre><code>
37407
37408 layout.addxtype({
37409        xtype : 'Form',
37410        items: [ .... ]
37411    }
37412 );
37413
37414 </code></pre>
37415      * @param {Object} cfg Xtype definition of item to add.
37416      */
37417     
37418     
37419     getChildContainer: function () {
37420         return this.getEl();
37421     }
37422     
37423     
37424     /*
37425         var  ret = new Roo.factory(cfg);
37426         return ret;
37427         
37428         
37429         // add form..
37430         if (cfg.xtype.match(/^Form$/)) {
37431             
37432             var el;
37433             //if (this.footer) {
37434             //    el = this.footer.container.insertSibling(false, 'before');
37435             //} else {
37436                 el = this.el.createChild();
37437             //}
37438
37439             this.form = new  Roo.form.Form(cfg);
37440             
37441             
37442             if ( this.form.allItems.length) {
37443                 this.form.render(el.dom);
37444             }
37445             return this.form;
37446         }
37447         // should only have one of theses..
37448         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37449             // views.. should not be just added - used named prop 'view''
37450             
37451             cfg.el = this.el.appendChild(document.createElement("div"));
37452             // factory?
37453             
37454             var ret = new Roo.factory(cfg);
37455              
37456              ret.render && ret.render(false, ''); // render blank..
37457             this.view = ret;
37458             return ret;
37459         }
37460         return false;
37461     }
37462     \*/
37463 });
37464  
37465 /**
37466  * @class Roo.bootstrap.panel.Grid
37467  * @extends Roo.bootstrap.panel.Content
37468  * @constructor
37469  * Create a new GridPanel.
37470  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37471  * @param {Object} config A the config object
37472   
37473  */
37474
37475
37476
37477 Roo.bootstrap.panel.Grid = function(config)
37478 {
37479     
37480       
37481     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37482         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37483
37484     config.el = this.wrapper;
37485     //this.el = this.wrapper;
37486     
37487       if (config.container) {
37488         // ctor'ed from a Border/panel.grid
37489         
37490         
37491         this.wrapper.setStyle("overflow", "hidden");
37492         this.wrapper.addClass('roo-grid-container');
37493
37494     }
37495     
37496     
37497     if(config.toolbar){
37498         var tool_el = this.wrapper.createChild();    
37499         this.toolbar = Roo.factory(config.toolbar);
37500         var ti = [];
37501         if (config.toolbar.items) {
37502             ti = config.toolbar.items ;
37503             delete config.toolbar.items ;
37504         }
37505         
37506         var nitems = [];
37507         this.toolbar.render(tool_el);
37508         for(var i =0;i < ti.length;i++) {
37509           //  Roo.log(['add child', items[i]]);
37510             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37511         }
37512         this.toolbar.items = nitems;
37513         
37514         delete config.toolbar;
37515     }
37516     
37517     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37518     config.grid.scrollBody = true;;
37519     config.grid.monitorWindowResize = false; // turn off autosizing
37520     config.grid.autoHeight = false;
37521     config.grid.autoWidth = false;
37522     
37523     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37524     
37525     if (config.background) {
37526         // render grid on panel activation (if panel background)
37527         this.on('activate', function(gp) {
37528             if (!gp.grid.rendered) {
37529                 gp.grid.render(this.wrapper);
37530                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37531             }
37532         });
37533             
37534     } else {
37535         this.grid.render(this.wrapper);
37536         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37537
37538     }
37539     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37540     // ??? needed ??? config.el = this.wrapper;
37541     
37542     
37543     
37544   
37545     // xtype created footer. - not sure if will work as we normally have to render first..
37546     if (this.footer && !this.footer.el && this.footer.xtype) {
37547         
37548         var ctr = this.grid.getView().getFooterPanel(true);
37549         this.footer.dataSource = this.grid.dataSource;
37550         this.footer = Roo.factory(this.footer, Roo);
37551         this.footer.render(ctr);
37552         
37553     }
37554     
37555     
37556     
37557     
37558      
37559 };
37560
37561 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37562     getId : function(){
37563         return this.grid.id;
37564     },
37565     
37566     /**
37567      * Returns the grid for this panel
37568      * @return {Roo.bootstrap.Table} 
37569      */
37570     getGrid : function(){
37571         return this.grid;    
37572     },
37573     
37574     setSize : function(width, height){
37575         if(!this.ignoreResize(width, height)){
37576             var grid = this.grid;
37577             var size = this.adjustForComponents(width, height);
37578             var gridel = grid.getGridEl();
37579             gridel.setSize(size.width, size.height);
37580             /*
37581             var thd = grid.getGridEl().select('thead',true).first();
37582             var tbd = grid.getGridEl().select('tbody', true).first();
37583             if (tbd) {
37584                 tbd.setSize(width, height - thd.getHeight());
37585             }
37586             */
37587             grid.autoSize();
37588         }
37589     },
37590      
37591     
37592     
37593     beforeSlide : function(){
37594         this.grid.getView().scroller.clip();
37595     },
37596     
37597     afterSlide : function(){
37598         this.grid.getView().scroller.unclip();
37599     },
37600     
37601     destroy : function(){
37602         this.grid.destroy();
37603         delete this.grid;
37604         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37605     }
37606 });
37607
37608 /**
37609  * @class Roo.bootstrap.panel.Nest
37610  * @extends Roo.bootstrap.panel.Content
37611  * @constructor
37612  * Create a new Panel, that can contain a layout.Border.
37613  * 
37614  * 
37615  * @param {Roo.BorderLayout} layout The layout for this panel
37616  * @param {String/Object} config A string to set only the title or a config object
37617  */
37618 Roo.bootstrap.panel.Nest = function(config)
37619 {
37620     // construct with only one argument..
37621     /* FIXME - implement nicer consturctors
37622     if (layout.layout) {
37623         config = layout;
37624         layout = config.layout;
37625         delete config.layout;
37626     }
37627     if (layout.xtype && !layout.getEl) {
37628         // then layout needs constructing..
37629         layout = Roo.factory(layout, Roo);
37630     }
37631     */
37632     
37633     config.el =  config.layout.getEl();
37634     
37635     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37636     
37637     config.layout.monitorWindowResize = false; // turn off autosizing
37638     this.layout = config.layout;
37639     this.layout.getEl().addClass("roo-layout-nested-layout");
37640     
37641     
37642     
37643     
37644 };
37645
37646 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37647
37648     setSize : function(width, height){
37649         if(!this.ignoreResize(width, height)){
37650             var size = this.adjustForComponents(width, height);
37651             var el = this.layout.getEl();
37652             if (size.height < 1) {
37653                 el.setWidth(size.width);   
37654             } else {
37655                 el.setSize(size.width, size.height);
37656             }
37657             var touch = el.dom.offsetWidth;
37658             this.layout.layout();
37659             // ie requires a double layout on the first pass
37660             if(Roo.isIE && !this.initialized){
37661                 this.initialized = true;
37662                 this.layout.layout();
37663             }
37664         }
37665     },
37666     
37667     // activate all subpanels if not currently active..
37668     
37669     setActiveState : function(active){
37670         this.active = active;
37671         this.setActiveClass(active);
37672         
37673         if(!active){
37674             this.fireEvent("deactivate", this);
37675             return;
37676         }
37677         
37678         this.fireEvent("activate", this);
37679         // not sure if this should happen before or after..
37680         if (!this.layout) {
37681             return; // should not happen..
37682         }
37683         var reg = false;
37684         for (var r in this.layout.regions) {
37685             reg = this.layout.getRegion(r);
37686             if (reg.getActivePanel()) {
37687                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37688                 reg.setActivePanel(reg.getActivePanel());
37689                 continue;
37690             }
37691             if (!reg.panels.length) {
37692                 continue;
37693             }
37694             reg.showPanel(reg.getPanel(0));
37695         }
37696         
37697         
37698         
37699         
37700     },
37701     
37702     /**
37703      * Returns the nested BorderLayout for this panel
37704      * @return {Roo.BorderLayout} 
37705      */
37706     getLayout : function(){
37707         return this.layout;
37708     },
37709     
37710      /**
37711      * Adds a xtype elements to the layout of the nested panel
37712      * <pre><code>
37713
37714 panel.addxtype({
37715        xtype : 'ContentPanel',
37716        region: 'west',
37717        items: [ .... ]
37718    }
37719 );
37720
37721 panel.addxtype({
37722         xtype : 'NestedLayoutPanel',
37723         region: 'west',
37724         layout: {
37725            center: { },
37726            west: { }   
37727         },
37728         items : [ ... list of content panels or nested layout panels.. ]
37729    }
37730 );
37731 </code></pre>
37732      * @param {Object} cfg Xtype definition of item to add.
37733      */
37734     addxtype : function(cfg) {
37735         return this.layout.addxtype(cfg);
37736     
37737     }
37738 });        /*
37739  * Based on:
37740  * Ext JS Library 1.1.1
37741  * Copyright(c) 2006-2007, Ext JS, LLC.
37742  *
37743  * Originally Released Under LGPL - original licence link has changed is not relivant.
37744  *
37745  * Fork - LGPL
37746  * <script type="text/javascript">
37747  */
37748 /**
37749  * @class Roo.TabPanel
37750  * @extends Roo.util.Observable
37751  * A lightweight tab container.
37752  * <br><br>
37753  * Usage:
37754  * <pre><code>
37755 // basic tabs 1, built from existing content
37756 var tabs = new Roo.TabPanel("tabs1");
37757 tabs.addTab("script", "View Script");
37758 tabs.addTab("markup", "View Markup");
37759 tabs.activate("script");
37760
37761 // more advanced tabs, built from javascript
37762 var jtabs = new Roo.TabPanel("jtabs");
37763 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37764
37765 // set up the UpdateManager
37766 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37767 var updater = tab2.getUpdateManager();
37768 updater.setDefaultUrl("ajax1.htm");
37769 tab2.on('activate', updater.refresh, updater, true);
37770
37771 // Use setUrl for Ajax loading
37772 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37773 tab3.setUrl("ajax2.htm", null, true);
37774
37775 // Disabled tab
37776 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37777 tab4.disable();
37778
37779 jtabs.activate("jtabs-1");
37780  * </code></pre>
37781  * @constructor
37782  * Create a new TabPanel.
37783  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37784  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37785  */
37786 Roo.bootstrap.panel.Tabs = function(config){
37787     /**
37788     * The container element for this TabPanel.
37789     * @type Roo.Element
37790     */
37791     this.el = Roo.get(config.el);
37792     delete config.el;
37793     if(config){
37794         if(typeof config == "boolean"){
37795             this.tabPosition = config ? "bottom" : "top";
37796         }else{
37797             Roo.apply(this, config);
37798         }
37799     }
37800     
37801     if(this.tabPosition == "bottom"){
37802         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37803         this.el.addClass("roo-tabs-bottom");
37804     }
37805     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37806     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37807     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37808     if(Roo.isIE){
37809         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37810     }
37811     if(this.tabPosition != "bottom"){
37812         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37813          * @type Roo.Element
37814          */
37815         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37816         this.el.addClass("roo-tabs-top");
37817     }
37818     this.items = [];
37819
37820     this.bodyEl.setStyle("position", "relative");
37821
37822     this.active = null;
37823     this.activateDelegate = this.activate.createDelegate(this);
37824
37825     this.addEvents({
37826         /**
37827          * @event tabchange
37828          * Fires when the active tab changes
37829          * @param {Roo.TabPanel} this
37830          * @param {Roo.TabPanelItem} activePanel The new active tab
37831          */
37832         "tabchange": true,
37833         /**
37834          * @event beforetabchange
37835          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37836          * @param {Roo.TabPanel} this
37837          * @param {Object} e Set cancel to true on this object to cancel the tab change
37838          * @param {Roo.TabPanelItem} tab The tab being changed to
37839          */
37840         "beforetabchange" : true
37841     });
37842
37843     Roo.EventManager.onWindowResize(this.onResize, this);
37844     this.cpad = this.el.getPadding("lr");
37845     this.hiddenCount = 0;
37846
37847
37848     // toolbar on the tabbar support...
37849     if (this.toolbar) {
37850         alert("no toolbar support yet");
37851         this.toolbar  = false;
37852         /*
37853         var tcfg = this.toolbar;
37854         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37855         this.toolbar = new Roo.Toolbar(tcfg);
37856         if (Roo.isSafari) {
37857             var tbl = tcfg.container.child('table', true);
37858             tbl.setAttribute('width', '100%');
37859         }
37860         */
37861         
37862     }
37863    
37864
37865
37866     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37867 };
37868
37869 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37870     /*
37871      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37872      */
37873     tabPosition : "top",
37874     /*
37875      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37876      */
37877     currentTabWidth : 0,
37878     /*
37879      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37880      */
37881     minTabWidth : 40,
37882     /*
37883      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37884      */
37885     maxTabWidth : 250,
37886     /*
37887      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37888      */
37889     preferredTabWidth : 175,
37890     /*
37891      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37892      */
37893     resizeTabs : false,
37894     /*
37895      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37896      */
37897     monitorResize : true,
37898     /*
37899      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37900      */
37901     toolbar : false,
37902
37903     /**
37904      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37905      * @param {String} id The id of the div to use <b>or create</b>
37906      * @param {String} text The text for the tab
37907      * @param {String} content (optional) Content to put in the TabPanelItem body
37908      * @param {Boolean} closable (optional) True to create a close icon on the tab
37909      * @return {Roo.TabPanelItem} The created TabPanelItem
37910      */
37911     addTab : function(id, text, content, closable, tpl)
37912     {
37913         var item = new Roo.bootstrap.panel.TabItem({
37914             panel: this,
37915             id : id,
37916             text : text,
37917             closable : closable,
37918             tpl : tpl
37919         });
37920         this.addTabItem(item);
37921         if(content){
37922             item.setContent(content);
37923         }
37924         return item;
37925     },
37926
37927     /**
37928      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37929      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37930      * @return {Roo.TabPanelItem}
37931      */
37932     getTab : function(id){
37933         return this.items[id];
37934     },
37935
37936     /**
37937      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37938      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37939      */
37940     hideTab : function(id){
37941         var t = this.items[id];
37942         if(!t.isHidden()){
37943            t.setHidden(true);
37944            this.hiddenCount++;
37945            this.autoSizeTabs();
37946         }
37947     },
37948
37949     /**
37950      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37951      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37952      */
37953     unhideTab : function(id){
37954         var t = this.items[id];
37955         if(t.isHidden()){
37956            t.setHidden(false);
37957            this.hiddenCount--;
37958            this.autoSizeTabs();
37959         }
37960     },
37961
37962     /**
37963      * Adds an existing {@link Roo.TabPanelItem}.
37964      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37965      */
37966     addTabItem : function(item){
37967         this.items[item.id] = item;
37968         this.items.push(item);
37969       //  if(this.resizeTabs){
37970     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37971   //         this.autoSizeTabs();
37972 //        }else{
37973 //            item.autoSize();
37974        // }
37975     },
37976
37977     /**
37978      * Removes a {@link Roo.TabPanelItem}.
37979      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37980      */
37981     removeTab : function(id){
37982         var items = this.items;
37983         var tab = items[id];
37984         if(!tab) { return; }
37985         var index = items.indexOf(tab);
37986         if(this.active == tab && items.length > 1){
37987             var newTab = this.getNextAvailable(index);
37988             if(newTab) {
37989                 newTab.activate();
37990             }
37991         }
37992         this.stripEl.dom.removeChild(tab.pnode.dom);
37993         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37994             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37995         }
37996         items.splice(index, 1);
37997         delete this.items[tab.id];
37998         tab.fireEvent("close", tab);
37999         tab.purgeListeners();
38000         this.autoSizeTabs();
38001     },
38002
38003     getNextAvailable : function(start){
38004         var items = this.items;
38005         var index = start;
38006         // look for a next tab that will slide over to
38007         // replace the one being removed
38008         while(index < items.length){
38009             var item = items[++index];
38010             if(item && !item.isHidden()){
38011                 return item;
38012             }
38013         }
38014         // if one isn't found select the previous tab (on the left)
38015         index = start;
38016         while(index >= 0){
38017             var item = items[--index];
38018             if(item && !item.isHidden()){
38019                 return item;
38020             }
38021         }
38022         return null;
38023     },
38024
38025     /**
38026      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38027      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38028      */
38029     disableTab : function(id){
38030         var tab = this.items[id];
38031         if(tab && this.active != tab){
38032             tab.disable();
38033         }
38034     },
38035
38036     /**
38037      * Enables a {@link Roo.TabPanelItem} that is disabled.
38038      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38039      */
38040     enableTab : function(id){
38041         var tab = this.items[id];
38042         tab.enable();
38043     },
38044
38045     /**
38046      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38047      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38048      * @return {Roo.TabPanelItem} The TabPanelItem.
38049      */
38050     activate : function(id){
38051         var tab = this.items[id];
38052         if(!tab){
38053             return null;
38054         }
38055         if(tab == this.active || tab.disabled){
38056             return tab;
38057         }
38058         var e = {};
38059         this.fireEvent("beforetabchange", this, e, tab);
38060         if(e.cancel !== true && !tab.disabled){
38061             if(this.active){
38062                 this.active.hide();
38063             }
38064             this.active = this.items[id];
38065             this.active.show();
38066             this.fireEvent("tabchange", this, this.active);
38067         }
38068         return tab;
38069     },
38070
38071     /**
38072      * Gets the active {@link Roo.TabPanelItem}.
38073      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38074      */
38075     getActiveTab : function(){
38076         return this.active;
38077     },
38078
38079     /**
38080      * Updates the tab body element to fit the height of the container element
38081      * for overflow scrolling
38082      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38083      */
38084     syncHeight : function(targetHeight){
38085         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38086         var bm = this.bodyEl.getMargins();
38087         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38088         this.bodyEl.setHeight(newHeight);
38089         return newHeight;
38090     },
38091
38092     onResize : function(){
38093         if(this.monitorResize){
38094             this.autoSizeTabs();
38095         }
38096     },
38097
38098     /**
38099      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38100      */
38101     beginUpdate : function(){
38102         this.updating = true;
38103     },
38104
38105     /**
38106      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38107      */
38108     endUpdate : function(){
38109         this.updating = false;
38110         this.autoSizeTabs();
38111     },
38112
38113     /**
38114      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38115      */
38116     autoSizeTabs : function(){
38117         var count = this.items.length;
38118         var vcount = count - this.hiddenCount;
38119         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38120             return;
38121         }
38122         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38123         var availWidth = Math.floor(w / vcount);
38124         var b = this.stripBody;
38125         if(b.getWidth() > w){
38126             var tabs = this.items;
38127             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38128             if(availWidth < this.minTabWidth){
38129                 /*if(!this.sleft){    // incomplete scrolling code
38130                     this.createScrollButtons();
38131                 }
38132                 this.showScroll();
38133                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38134             }
38135         }else{
38136             if(this.currentTabWidth < this.preferredTabWidth){
38137                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38138             }
38139         }
38140     },
38141
38142     /**
38143      * Returns the number of tabs in this TabPanel.
38144      * @return {Number}
38145      */
38146      getCount : function(){
38147          return this.items.length;
38148      },
38149
38150     /**
38151      * Resizes all the tabs to the passed width
38152      * @param {Number} The new width
38153      */
38154     setTabWidth : function(width){
38155         this.currentTabWidth = width;
38156         for(var i = 0, len = this.items.length; i < len; i++) {
38157                 if(!this.items[i].isHidden()) {
38158                 this.items[i].setWidth(width);
38159             }
38160         }
38161     },
38162
38163     /**
38164      * Destroys this TabPanel
38165      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38166      */
38167     destroy : function(removeEl){
38168         Roo.EventManager.removeResizeListener(this.onResize, this);
38169         for(var i = 0, len = this.items.length; i < len; i++){
38170             this.items[i].purgeListeners();
38171         }
38172         if(removeEl === true){
38173             this.el.update("");
38174             this.el.remove();
38175         }
38176     },
38177     
38178     createStrip : function(container)
38179     {
38180         var strip = document.createElement("nav");
38181         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38182         container.appendChild(strip);
38183         return strip;
38184     },
38185     
38186     createStripList : function(strip)
38187     {
38188         // div wrapper for retard IE
38189         // returns the "tr" element.
38190         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38191         //'<div class="x-tabs-strip-wrap">'+
38192           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38193           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38194         return strip.firstChild; //.firstChild.firstChild.firstChild;
38195     },
38196     createBody : function(container)
38197     {
38198         var body = document.createElement("div");
38199         Roo.id(body, "tab-body");
38200         //Roo.fly(body).addClass("x-tabs-body");
38201         Roo.fly(body).addClass("tab-content");
38202         container.appendChild(body);
38203         return body;
38204     },
38205     createItemBody :function(bodyEl, id){
38206         var body = Roo.getDom(id);
38207         if(!body){
38208             body = document.createElement("div");
38209             body.id = id;
38210         }
38211         //Roo.fly(body).addClass("x-tabs-item-body");
38212         Roo.fly(body).addClass("tab-pane");
38213          bodyEl.insertBefore(body, bodyEl.firstChild);
38214         return body;
38215     },
38216     /** @private */
38217     createStripElements :  function(stripEl, text, closable, tpl)
38218     {
38219         var td = document.createElement("li"); // was td..
38220         
38221         
38222         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38223         
38224         
38225         stripEl.appendChild(td);
38226         /*if(closable){
38227             td.className = "x-tabs-closable";
38228             if(!this.closeTpl){
38229                 this.closeTpl = new Roo.Template(
38230                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38231                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38232                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38233                 );
38234             }
38235             var el = this.closeTpl.overwrite(td, {"text": text});
38236             var close = el.getElementsByTagName("div")[0];
38237             var inner = el.getElementsByTagName("em")[0];
38238             return {"el": el, "close": close, "inner": inner};
38239         } else {
38240         */
38241         // not sure what this is..
38242 //            if(!this.tabTpl){
38243                 //this.tabTpl = new Roo.Template(
38244                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38245                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38246                 //);
38247 //                this.tabTpl = new Roo.Template(
38248 //                   '<a href="#">' +
38249 //                   '<span unselectable="on"' +
38250 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38251 //                            ' >{text}</span></a>'
38252 //                );
38253 //                
38254 //            }
38255
38256
38257             var template = tpl || this.tabTpl || false;
38258             
38259             if(!template){
38260                 
38261                 template = new Roo.Template(
38262                    '<a href="#">' +
38263                    '<span unselectable="on"' +
38264                             (this.disableTooltips ? '' : ' title="{text}"') +
38265                             ' >{text}</span></a>'
38266                 );
38267             }
38268             
38269             switch (typeof(template)) {
38270                 case 'object' :
38271                     break;
38272                 case 'string' :
38273                     template = new Roo.Template(template);
38274                     break;
38275                 default :
38276                     break;
38277             }
38278             
38279             var el = template.overwrite(td, {"text": text});
38280             
38281             var inner = el.getElementsByTagName("span")[0];
38282             
38283             return {"el": el, "inner": inner};
38284             
38285     }
38286         
38287     
38288 });
38289
38290 /**
38291  * @class Roo.TabPanelItem
38292  * @extends Roo.util.Observable
38293  * Represents an individual item (tab plus body) in a TabPanel.
38294  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38295  * @param {String} id The id of this TabPanelItem
38296  * @param {String} text The text for the tab of this TabPanelItem
38297  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38298  */
38299 Roo.bootstrap.panel.TabItem = function(config){
38300     /**
38301      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38302      * @type Roo.TabPanel
38303      */
38304     this.tabPanel = config.panel;
38305     /**
38306      * The id for this TabPanelItem
38307      * @type String
38308      */
38309     this.id = config.id;
38310     /** @private */
38311     this.disabled = false;
38312     /** @private */
38313     this.text = config.text;
38314     /** @private */
38315     this.loaded = false;
38316     this.closable = config.closable;
38317
38318     /**
38319      * The body element for this TabPanelItem.
38320      * @type Roo.Element
38321      */
38322     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38323     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38324     this.bodyEl.setStyle("display", "block");
38325     this.bodyEl.setStyle("zoom", "1");
38326     //this.hideAction();
38327
38328     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38329     /** @private */
38330     this.el = Roo.get(els.el);
38331     this.inner = Roo.get(els.inner, true);
38332     this.textEl = Roo.get(this.el.dom.firstChild, true);
38333     this.pnode = Roo.get(els.el.parentNode, true);
38334 //    this.el.on("mousedown", this.onTabMouseDown, this);
38335     this.el.on("click", this.onTabClick, this);
38336     /** @private */
38337     if(config.closable){
38338         var c = Roo.get(els.close, true);
38339         c.dom.title = this.closeText;
38340         c.addClassOnOver("close-over");
38341         c.on("click", this.closeClick, this);
38342      }
38343
38344     this.addEvents({
38345          /**
38346          * @event activate
38347          * Fires when this tab becomes the active tab.
38348          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38349          * @param {Roo.TabPanelItem} this
38350          */
38351         "activate": true,
38352         /**
38353          * @event beforeclose
38354          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38355          * @param {Roo.TabPanelItem} this
38356          * @param {Object} e Set cancel to true on this object to cancel the close.
38357          */
38358         "beforeclose": true,
38359         /**
38360          * @event close
38361          * Fires when this tab is closed.
38362          * @param {Roo.TabPanelItem} this
38363          */
38364          "close": true,
38365         /**
38366          * @event deactivate
38367          * Fires when this tab is no longer the active tab.
38368          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38369          * @param {Roo.TabPanelItem} this
38370          */
38371          "deactivate" : true
38372     });
38373     this.hidden = false;
38374
38375     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38376 };
38377
38378 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38379            {
38380     purgeListeners : function(){
38381        Roo.util.Observable.prototype.purgeListeners.call(this);
38382        this.el.removeAllListeners();
38383     },
38384     /**
38385      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38386      */
38387     show : function(){
38388         this.pnode.addClass("active");
38389         this.showAction();
38390         if(Roo.isOpera){
38391             this.tabPanel.stripWrap.repaint();
38392         }
38393         this.fireEvent("activate", this.tabPanel, this);
38394     },
38395
38396     /**
38397      * Returns true if this tab is the active tab.
38398      * @return {Boolean}
38399      */
38400     isActive : function(){
38401         return this.tabPanel.getActiveTab() == this;
38402     },
38403
38404     /**
38405      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38406      */
38407     hide : function(){
38408         this.pnode.removeClass("active");
38409         this.hideAction();
38410         this.fireEvent("deactivate", this.tabPanel, this);
38411     },
38412
38413     hideAction : function(){
38414         this.bodyEl.hide();
38415         this.bodyEl.setStyle("position", "absolute");
38416         this.bodyEl.setLeft("-20000px");
38417         this.bodyEl.setTop("-20000px");
38418     },
38419
38420     showAction : function(){
38421         this.bodyEl.setStyle("position", "relative");
38422         this.bodyEl.setTop("");
38423         this.bodyEl.setLeft("");
38424         this.bodyEl.show();
38425     },
38426
38427     /**
38428      * Set the tooltip for the tab.
38429      * @param {String} tooltip The tab's tooltip
38430      */
38431     setTooltip : function(text){
38432         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38433             this.textEl.dom.qtip = text;
38434             this.textEl.dom.removeAttribute('title');
38435         }else{
38436             this.textEl.dom.title = text;
38437         }
38438     },
38439
38440     onTabClick : function(e){
38441         e.preventDefault();
38442         this.tabPanel.activate(this.id);
38443     },
38444
38445     onTabMouseDown : function(e){
38446         e.preventDefault();
38447         this.tabPanel.activate(this.id);
38448     },
38449 /*
38450     getWidth : function(){
38451         return this.inner.getWidth();
38452     },
38453
38454     setWidth : function(width){
38455         var iwidth = width - this.pnode.getPadding("lr");
38456         this.inner.setWidth(iwidth);
38457         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38458         this.pnode.setWidth(width);
38459     },
38460 */
38461     /**
38462      * Show or hide the tab
38463      * @param {Boolean} hidden True to hide or false to show.
38464      */
38465     setHidden : function(hidden){
38466         this.hidden = hidden;
38467         this.pnode.setStyle("display", hidden ? "none" : "");
38468     },
38469
38470     /**
38471      * Returns true if this tab is "hidden"
38472      * @return {Boolean}
38473      */
38474     isHidden : function(){
38475         return this.hidden;
38476     },
38477
38478     /**
38479      * Returns the text for this tab
38480      * @return {String}
38481      */
38482     getText : function(){
38483         return this.text;
38484     },
38485     /*
38486     autoSize : function(){
38487         //this.el.beginMeasure();
38488         this.textEl.setWidth(1);
38489         /*
38490          *  #2804 [new] Tabs in Roojs
38491          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38492          */
38493         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38494         //this.el.endMeasure();
38495     //},
38496
38497     /**
38498      * Sets the text for the tab (Note: this also sets the tooltip text)
38499      * @param {String} text The tab's text and tooltip
38500      */
38501     setText : function(text){
38502         this.text = text;
38503         this.textEl.update(text);
38504         this.setTooltip(text);
38505         //if(!this.tabPanel.resizeTabs){
38506         //    this.autoSize();
38507         //}
38508     },
38509     /**
38510      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38511      */
38512     activate : function(){
38513         this.tabPanel.activate(this.id);
38514     },
38515
38516     /**
38517      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38518      */
38519     disable : function(){
38520         if(this.tabPanel.active != this){
38521             this.disabled = true;
38522             this.pnode.addClass("disabled");
38523         }
38524     },
38525
38526     /**
38527      * Enables this TabPanelItem if it was previously disabled.
38528      */
38529     enable : function(){
38530         this.disabled = false;
38531         this.pnode.removeClass("disabled");
38532     },
38533
38534     /**
38535      * Sets the content for this TabPanelItem.
38536      * @param {String} content The content
38537      * @param {Boolean} loadScripts true to look for and load scripts
38538      */
38539     setContent : function(content, loadScripts){
38540         this.bodyEl.update(content, loadScripts);
38541     },
38542
38543     /**
38544      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38545      * @return {Roo.UpdateManager} The UpdateManager
38546      */
38547     getUpdateManager : function(){
38548         return this.bodyEl.getUpdateManager();
38549     },
38550
38551     /**
38552      * Set a URL to be used to load the content for this TabPanelItem.
38553      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38554      * @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)
38555      * @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)
38556      * @return {Roo.UpdateManager} The UpdateManager
38557      */
38558     setUrl : function(url, params, loadOnce){
38559         if(this.refreshDelegate){
38560             this.un('activate', this.refreshDelegate);
38561         }
38562         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38563         this.on("activate", this.refreshDelegate);
38564         return this.bodyEl.getUpdateManager();
38565     },
38566
38567     /** @private */
38568     _handleRefresh : function(url, params, loadOnce){
38569         if(!loadOnce || !this.loaded){
38570             var updater = this.bodyEl.getUpdateManager();
38571             updater.update(url, params, this._setLoaded.createDelegate(this));
38572         }
38573     },
38574
38575     /**
38576      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38577      *   Will fail silently if the setUrl method has not been called.
38578      *   This does not activate the panel, just updates its content.
38579      */
38580     refresh : function(){
38581         if(this.refreshDelegate){
38582            this.loaded = false;
38583            this.refreshDelegate();
38584         }
38585     },
38586
38587     /** @private */
38588     _setLoaded : function(){
38589         this.loaded = true;
38590     },
38591
38592     /** @private */
38593     closeClick : function(e){
38594         var o = {};
38595         e.stopEvent();
38596         this.fireEvent("beforeclose", this, o);
38597         if(o.cancel !== true){
38598             this.tabPanel.removeTab(this.id);
38599         }
38600     },
38601     /**
38602      * The text displayed in the tooltip for the close icon.
38603      * @type String
38604      */
38605     closeText : "Close this tab"
38606 });
38607 /**
38608 *    This script refer to:
38609 *    Title: International Telephone Input
38610 *    Author: Jack O'Connor
38611 *    Code version:  v12.1.12
38612 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38613 **/
38614
38615 Roo.bootstrap.PhoneInputData = function() {
38616     var d = [
38617       [
38618         "Afghanistan (‫افغانستان‬‎)",
38619         "af",
38620         "93"
38621       ],
38622       [
38623         "Albania (Shqipëri)",
38624         "al",
38625         "355"
38626       ],
38627       [
38628         "Algeria (‫الجزائر‬‎)",
38629         "dz",
38630         "213"
38631       ],
38632       [
38633         "American Samoa",
38634         "as",
38635         "1684"
38636       ],
38637       [
38638         "Andorra",
38639         "ad",
38640         "376"
38641       ],
38642       [
38643         "Angola",
38644         "ao",
38645         "244"
38646       ],
38647       [
38648         "Anguilla",
38649         "ai",
38650         "1264"
38651       ],
38652       [
38653         "Antigua and Barbuda",
38654         "ag",
38655         "1268"
38656       ],
38657       [
38658         "Argentina",
38659         "ar",
38660         "54"
38661       ],
38662       [
38663         "Armenia (Հայաստան)",
38664         "am",
38665         "374"
38666       ],
38667       [
38668         "Aruba",
38669         "aw",
38670         "297"
38671       ],
38672       [
38673         "Australia",
38674         "au",
38675         "61",
38676         0
38677       ],
38678       [
38679         "Austria (Österreich)",
38680         "at",
38681         "43"
38682       ],
38683       [
38684         "Azerbaijan (Azərbaycan)",
38685         "az",
38686         "994"
38687       ],
38688       [
38689         "Bahamas",
38690         "bs",
38691         "1242"
38692       ],
38693       [
38694         "Bahrain (‫البحرين‬‎)",
38695         "bh",
38696         "973"
38697       ],
38698       [
38699         "Bangladesh (বাংলাদেশ)",
38700         "bd",
38701         "880"
38702       ],
38703       [
38704         "Barbados",
38705         "bb",
38706         "1246"
38707       ],
38708       [
38709         "Belarus (Беларусь)",
38710         "by",
38711         "375"
38712       ],
38713       [
38714         "Belgium (België)",
38715         "be",
38716         "32"
38717       ],
38718       [
38719         "Belize",
38720         "bz",
38721         "501"
38722       ],
38723       [
38724         "Benin (Bénin)",
38725         "bj",
38726         "229"
38727       ],
38728       [
38729         "Bermuda",
38730         "bm",
38731         "1441"
38732       ],
38733       [
38734         "Bhutan (འབྲུག)",
38735         "bt",
38736         "975"
38737       ],
38738       [
38739         "Bolivia",
38740         "bo",
38741         "591"
38742       ],
38743       [
38744         "Bosnia and Herzegovina (Босна и Херцеговина)",
38745         "ba",
38746         "387"
38747       ],
38748       [
38749         "Botswana",
38750         "bw",
38751         "267"
38752       ],
38753       [
38754         "Brazil (Brasil)",
38755         "br",
38756         "55"
38757       ],
38758       [
38759         "British Indian Ocean Territory",
38760         "io",
38761         "246"
38762       ],
38763       [
38764         "British Virgin Islands",
38765         "vg",
38766         "1284"
38767       ],
38768       [
38769         "Brunei",
38770         "bn",
38771         "673"
38772       ],
38773       [
38774         "Bulgaria (България)",
38775         "bg",
38776         "359"
38777       ],
38778       [
38779         "Burkina Faso",
38780         "bf",
38781         "226"
38782       ],
38783       [
38784         "Burundi (Uburundi)",
38785         "bi",
38786         "257"
38787       ],
38788       [
38789         "Cambodia (កម្ពុជា)",
38790         "kh",
38791         "855"
38792       ],
38793       [
38794         "Cameroon (Cameroun)",
38795         "cm",
38796         "237"
38797       ],
38798       [
38799         "Canada",
38800         "ca",
38801         "1",
38802         1,
38803         ["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"]
38804       ],
38805       [
38806         "Cape Verde (Kabu Verdi)",
38807         "cv",
38808         "238"
38809       ],
38810       [
38811         "Caribbean Netherlands",
38812         "bq",
38813         "599",
38814         1
38815       ],
38816       [
38817         "Cayman Islands",
38818         "ky",
38819         "1345"
38820       ],
38821       [
38822         "Central African Republic (République centrafricaine)",
38823         "cf",
38824         "236"
38825       ],
38826       [
38827         "Chad (Tchad)",
38828         "td",
38829         "235"
38830       ],
38831       [
38832         "Chile",
38833         "cl",
38834         "56"
38835       ],
38836       [
38837         "China (中国)",
38838         "cn",
38839         "86"
38840       ],
38841       [
38842         "Christmas Island",
38843         "cx",
38844         "61",
38845         2
38846       ],
38847       [
38848         "Cocos (Keeling) Islands",
38849         "cc",
38850         "61",
38851         1
38852       ],
38853       [
38854         "Colombia",
38855         "co",
38856         "57"
38857       ],
38858       [
38859         "Comoros (‫جزر القمر‬‎)",
38860         "km",
38861         "269"
38862       ],
38863       [
38864         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38865         "cd",
38866         "243"
38867       ],
38868       [
38869         "Congo (Republic) (Congo-Brazzaville)",
38870         "cg",
38871         "242"
38872       ],
38873       [
38874         "Cook Islands",
38875         "ck",
38876         "682"
38877       ],
38878       [
38879         "Costa Rica",
38880         "cr",
38881         "506"
38882       ],
38883       [
38884         "Côte d’Ivoire",
38885         "ci",
38886         "225"
38887       ],
38888       [
38889         "Croatia (Hrvatska)",
38890         "hr",
38891         "385"
38892       ],
38893       [
38894         "Cuba",
38895         "cu",
38896         "53"
38897       ],
38898       [
38899         "Curaçao",
38900         "cw",
38901         "599",
38902         0
38903       ],
38904       [
38905         "Cyprus (Κύπρος)",
38906         "cy",
38907         "357"
38908       ],
38909       [
38910         "Czech Republic (Česká republika)",
38911         "cz",
38912         "420"
38913       ],
38914       [
38915         "Denmark (Danmark)",
38916         "dk",
38917         "45"
38918       ],
38919       [
38920         "Djibouti",
38921         "dj",
38922         "253"
38923       ],
38924       [
38925         "Dominica",
38926         "dm",
38927         "1767"
38928       ],
38929       [
38930         "Dominican Republic (República Dominicana)",
38931         "do",
38932         "1",
38933         2,
38934         ["809", "829", "849"]
38935       ],
38936       [
38937         "Ecuador",
38938         "ec",
38939         "593"
38940       ],
38941       [
38942         "Egypt (‫مصر‬‎)",
38943         "eg",
38944         "20"
38945       ],
38946       [
38947         "El Salvador",
38948         "sv",
38949         "503"
38950       ],
38951       [
38952         "Equatorial Guinea (Guinea Ecuatorial)",
38953         "gq",
38954         "240"
38955       ],
38956       [
38957         "Eritrea",
38958         "er",
38959         "291"
38960       ],
38961       [
38962         "Estonia (Eesti)",
38963         "ee",
38964         "372"
38965       ],
38966       [
38967         "Ethiopia",
38968         "et",
38969         "251"
38970       ],
38971       [
38972         "Falkland Islands (Islas Malvinas)",
38973         "fk",
38974         "500"
38975       ],
38976       [
38977         "Faroe Islands (Føroyar)",
38978         "fo",
38979         "298"
38980       ],
38981       [
38982         "Fiji",
38983         "fj",
38984         "679"
38985       ],
38986       [
38987         "Finland (Suomi)",
38988         "fi",
38989         "358",
38990         0
38991       ],
38992       [
38993         "France",
38994         "fr",
38995         "33"
38996       ],
38997       [
38998         "French Guiana (Guyane française)",
38999         "gf",
39000         "594"
39001       ],
39002       [
39003         "French Polynesia (Polynésie française)",
39004         "pf",
39005         "689"
39006       ],
39007       [
39008         "Gabon",
39009         "ga",
39010         "241"
39011       ],
39012       [
39013         "Gambia",
39014         "gm",
39015         "220"
39016       ],
39017       [
39018         "Georgia (საქართველო)",
39019         "ge",
39020         "995"
39021       ],
39022       [
39023         "Germany (Deutschland)",
39024         "de",
39025         "49"
39026       ],
39027       [
39028         "Ghana (Gaana)",
39029         "gh",
39030         "233"
39031       ],
39032       [
39033         "Gibraltar",
39034         "gi",
39035         "350"
39036       ],
39037       [
39038         "Greece (Ελλάδα)",
39039         "gr",
39040         "30"
39041       ],
39042       [
39043         "Greenland (Kalaallit Nunaat)",
39044         "gl",
39045         "299"
39046       ],
39047       [
39048         "Grenada",
39049         "gd",
39050         "1473"
39051       ],
39052       [
39053         "Guadeloupe",
39054         "gp",
39055         "590",
39056         0
39057       ],
39058       [
39059         "Guam",
39060         "gu",
39061         "1671"
39062       ],
39063       [
39064         "Guatemala",
39065         "gt",
39066         "502"
39067       ],
39068       [
39069         "Guernsey",
39070         "gg",
39071         "44",
39072         1
39073       ],
39074       [
39075         "Guinea (Guinée)",
39076         "gn",
39077         "224"
39078       ],
39079       [
39080         "Guinea-Bissau (Guiné Bissau)",
39081         "gw",
39082         "245"
39083       ],
39084       [
39085         "Guyana",
39086         "gy",
39087         "592"
39088       ],
39089       [
39090         "Haiti",
39091         "ht",
39092         "509"
39093       ],
39094       [
39095         "Honduras",
39096         "hn",
39097         "504"
39098       ],
39099       [
39100         "Hong Kong (香港)",
39101         "hk",
39102         "852"
39103       ],
39104       [
39105         "Hungary (Magyarország)",
39106         "hu",
39107         "36"
39108       ],
39109       [
39110         "Iceland (Ísland)",
39111         "is",
39112         "354"
39113       ],
39114       [
39115         "India (भारत)",
39116         "in",
39117         "91"
39118       ],
39119       [
39120         "Indonesia",
39121         "id",
39122         "62"
39123       ],
39124       [
39125         "Iran (‫ایران‬‎)",
39126         "ir",
39127         "98"
39128       ],
39129       [
39130         "Iraq (‫العراق‬‎)",
39131         "iq",
39132         "964"
39133       ],
39134       [
39135         "Ireland",
39136         "ie",
39137         "353"
39138       ],
39139       [
39140         "Isle of Man",
39141         "im",
39142         "44",
39143         2
39144       ],
39145       [
39146         "Israel (‫ישראל‬‎)",
39147         "il",
39148         "972"
39149       ],
39150       [
39151         "Italy (Italia)",
39152         "it",
39153         "39",
39154         0
39155       ],
39156       [
39157         "Jamaica",
39158         "jm",
39159         "1876"
39160       ],
39161       [
39162         "Japan (日本)",
39163         "jp",
39164         "81"
39165       ],
39166       [
39167         "Jersey",
39168         "je",
39169         "44",
39170         3
39171       ],
39172       [
39173         "Jordan (‫الأردن‬‎)",
39174         "jo",
39175         "962"
39176       ],
39177       [
39178         "Kazakhstan (Казахстан)",
39179         "kz",
39180         "7",
39181         1
39182       ],
39183       [
39184         "Kenya",
39185         "ke",
39186         "254"
39187       ],
39188       [
39189         "Kiribati",
39190         "ki",
39191         "686"
39192       ],
39193       [
39194         "Kosovo",
39195         "xk",
39196         "383"
39197       ],
39198       [
39199         "Kuwait (‫الكويت‬‎)",
39200         "kw",
39201         "965"
39202       ],
39203       [
39204         "Kyrgyzstan (Кыргызстан)",
39205         "kg",
39206         "996"
39207       ],
39208       [
39209         "Laos (ລາວ)",
39210         "la",
39211         "856"
39212       ],
39213       [
39214         "Latvia (Latvija)",
39215         "lv",
39216         "371"
39217       ],
39218       [
39219         "Lebanon (‫لبنان‬‎)",
39220         "lb",
39221         "961"
39222       ],
39223       [
39224         "Lesotho",
39225         "ls",
39226         "266"
39227       ],
39228       [
39229         "Liberia",
39230         "lr",
39231         "231"
39232       ],
39233       [
39234         "Libya (‫ليبيا‬‎)",
39235         "ly",
39236         "218"
39237       ],
39238       [
39239         "Liechtenstein",
39240         "li",
39241         "423"
39242       ],
39243       [
39244         "Lithuania (Lietuva)",
39245         "lt",
39246         "370"
39247       ],
39248       [
39249         "Luxembourg",
39250         "lu",
39251         "352"
39252       ],
39253       [
39254         "Macau (澳門)",
39255         "mo",
39256         "853"
39257       ],
39258       [
39259         "Macedonia (FYROM) (Македонија)",
39260         "mk",
39261         "389"
39262       ],
39263       [
39264         "Madagascar (Madagasikara)",
39265         "mg",
39266         "261"
39267       ],
39268       [
39269         "Malawi",
39270         "mw",
39271         "265"
39272       ],
39273       [
39274         "Malaysia",
39275         "my",
39276         "60"
39277       ],
39278       [
39279         "Maldives",
39280         "mv",
39281         "960"
39282       ],
39283       [
39284         "Mali",
39285         "ml",
39286         "223"
39287       ],
39288       [
39289         "Malta",
39290         "mt",
39291         "356"
39292       ],
39293       [
39294         "Marshall Islands",
39295         "mh",
39296         "692"
39297       ],
39298       [
39299         "Martinique",
39300         "mq",
39301         "596"
39302       ],
39303       [
39304         "Mauritania (‫موريتانيا‬‎)",
39305         "mr",
39306         "222"
39307       ],
39308       [
39309         "Mauritius (Moris)",
39310         "mu",
39311         "230"
39312       ],
39313       [
39314         "Mayotte",
39315         "yt",
39316         "262",
39317         1
39318       ],
39319       [
39320         "Mexico (México)",
39321         "mx",
39322         "52"
39323       ],
39324       [
39325         "Micronesia",
39326         "fm",
39327         "691"
39328       ],
39329       [
39330         "Moldova (Republica Moldova)",
39331         "md",
39332         "373"
39333       ],
39334       [
39335         "Monaco",
39336         "mc",
39337         "377"
39338       ],
39339       [
39340         "Mongolia (Монгол)",
39341         "mn",
39342         "976"
39343       ],
39344       [
39345         "Montenegro (Crna Gora)",
39346         "me",
39347         "382"
39348       ],
39349       [
39350         "Montserrat",
39351         "ms",
39352         "1664"
39353       ],
39354       [
39355         "Morocco (‫المغرب‬‎)",
39356         "ma",
39357         "212",
39358         0
39359       ],
39360       [
39361         "Mozambique (Moçambique)",
39362         "mz",
39363         "258"
39364       ],
39365       [
39366         "Myanmar (Burma) (မြန်မာ)",
39367         "mm",
39368         "95"
39369       ],
39370       [
39371         "Namibia (Namibië)",
39372         "na",
39373         "264"
39374       ],
39375       [
39376         "Nauru",
39377         "nr",
39378         "674"
39379       ],
39380       [
39381         "Nepal (नेपाल)",
39382         "np",
39383         "977"
39384       ],
39385       [
39386         "Netherlands (Nederland)",
39387         "nl",
39388         "31"
39389       ],
39390       [
39391         "New Caledonia (Nouvelle-Calédonie)",
39392         "nc",
39393         "687"
39394       ],
39395       [
39396         "New Zealand",
39397         "nz",
39398         "64"
39399       ],
39400       [
39401         "Nicaragua",
39402         "ni",
39403         "505"
39404       ],
39405       [
39406         "Niger (Nijar)",
39407         "ne",
39408         "227"
39409       ],
39410       [
39411         "Nigeria",
39412         "ng",
39413         "234"
39414       ],
39415       [
39416         "Niue",
39417         "nu",
39418         "683"
39419       ],
39420       [
39421         "Norfolk Island",
39422         "nf",
39423         "672"
39424       ],
39425       [
39426         "North Korea (조선 민주주의 인민 공화국)",
39427         "kp",
39428         "850"
39429       ],
39430       [
39431         "Northern Mariana Islands",
39432         "mp",
39433         "1670"
39434       ],
39435       [
39436         "Norway (Norge)",
39437         "no",
39438         "47",
39439         0
39440       ],
39441       [
39442         "Oman (‫عُمان‬‎)",
39443         "om",
39444         "968"
39445       ],
39446       [
39447         "Pakistan (‫پاکستان‬‎)",
39448         "pk",
39449         "92"
39450       ],
39451       [
39452         "Palau",
39453         "pw",
39454         "680"
39455       ],
39456       [
39457         "Palestine (‫فلسطين‬‎)",
39458         "ps",
39459         "970"
39460       ],
39461       [
39462         "Panama (Panamá)",
39463         "pa",
39464         "507"
39465       ],
39466       [
39467         "Papua New Guinea",
39468         "pg",
39469         "675"
39470       ],
39471       [
39472         "Paraguay",
39473         "py",
39474         "595"
39475       ],
39476       [
39477         "Peru (Perú)",
39478         "pe",
39479         "51"
39480       ],
39481       [
39482         "Philippines",
39483         "ph",
39484         "63"
39485       ],
39486       [
39487         "Poland (Polska)",
39488         "pl",
39489         "48"
39490       ],
39491       [
39492         "Portugal",
39493         "pt",
39494         "351"
39495       ],
39496       [
39497         "Puerto Rico",
39498         "pr",
39499         "1",
39500         3,
39501         ["787", "939"]
39502       ],
39503       [
39504         "Qatar (‫قطر‬‎)",
39505         "qa",
39506         "974"
39507       ],
39508       [
39509         "Réunion (La Réunion)",
39510         "re",
39511         "262",
39512         0
39513       ],
39514       [
39515         "Romania (România)",
39516         "ro",
39517         "40"
39518       ],
39519       [
39520         "Russia (Россия)",
39521         "ru",
39522         "7",
39523         0
39524       ],
39525       [
39526         "Rwanda",
39527         "rw",
39528         "250"
39529       ],
39530       [
39531         "Saint Barthélemy",
39532         "bl",
39533         "590",
39534         1
39535       ],
39536       [
39537         "Saint Helena",
39538         "sh",
39539         "290"
39540       ],
39541       [
39542         "Saint Kitts and Nevis",
39543         "kn",
39544         "1869"
39545       ],
39546       [
39547         "Saint Lucia",
39548         "lc",
39549         "1758"
39550       ],
39551       [
39552         "Saint Martin (Saint-Martin (partie française))",
39553         "mf",
39554         "590",
39555         2
39556       ],
39557       [
39558         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39559         "pm",
39560         "508"
39561       ],
39562       [
39563         "Saint Vincent and the Grenadines",
39564         "vc",
39565         "1784"
39566       ],
39567       [
39568         "Samoa",
39569         "ws",
39570         "685"
39571       ],
39572       [
39573         "San Marino",
39574         "sm",
39575         "378"
39576       ],
39577       [
39578         "São Tomé and Príncipe (São Tomé e Príncipe)",
39579         "st",
39580         "239"
39581       ],
39582       [
39583         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39584         "sa",
39585         "966"
39586       ],
39587       [
39588         "Senegal (Sénégal)",
39589         "sn",
39590         "221"
39591       ],
39592       [
39593         "Serbia (Србија)",
39594         "rs",
39595         "381"
39596       ],
39597       [
39598         "Seychelles",
39599         "sc",
39600         "248"
39601       ],
39602       [
39603         "Sierra Leone",
39604         "sl",
39605         "232"
39606       ],
39607       [
39608         "Singapore",
39609         "sg",
39610         "65"
39611       ],
39612       [
39613         "Sint Maarten",
39614         "sx",
39615         "1721"
39616       ],
39617       [
39618         "Slovakia (Slovensko)",
39619         "sk",
39620         "421"
39621       ],
39622       [
39623         "Slovenia (Slovenija)",
39624         "si",
39625         "386"
39626       ],
39627       [
39628         "Solomon Islands",
39629         "sb",
39630         "677"
39631       ],
39632       [
39633         "Somalia (Soomaaliya)",
39634         "so",
39635         "252"
39636       ],
39637       [
39638         "South Africa",
39639         "za",
39640         "27"
39641       ],
39642       [
39643         "South Korea (대한민국)",
39644         "kr",
39645         "82"
39646       ],
39647       [
39648         "South Sudan (‫جنوب السودان‬‎)",
39649         "ss",
39650         "211"
39651       ],
39652       [
39653         "Spain (España)",
39654         "es",
39655         "34"
39656       ],
39657       [
39658         "Sri Lanka (ශ්‍රී ලංකාව)",
39659         "lk",
39660         "94"
39661       ],
39662       [
39663         "Sudan (‫السودان‬‎)",
39664         "sd",
39665         "249"
39666       ],
39667       [
39668         "Suriname",
39669         "sr",
39670         "597"
39671       ],
39672       [
39673         "Svalbard and Jan Mayen",
39674         "sj",
39675         "47",
39676         1
39677       ],
39678       [
39679         "Swaziland",
39680         "sz",
39681         "268"
39682       ],
39683       [
39684         "Sweden (Sverige)",
39685         "se",
39686         "46"
39687       ],
39688       [
39689         "Switzerland (Schweiz)",
39690         "ch",
39691         "41"
39692       ],
39693       [
39694         "Syria (‫سوريا‬‎)",
39695         "sy",
39696         "963"
39697       ],
39698       [
39699         "Taiwan (台灣)",
39700         "tw",
39701         "886"
39702       ],
39703       [
39704         "Tajikistan",
39705         "tj",
39706         "992"
39707       ],
39708       [
39709         "Tanzania",
39710         "tz",
39711         "255"
39712       ],
39713       [
39714         "Thailand (ไทย)",
39715         "th",
39716         "66"
39717       ],
39718       [
39719         "Timor-Leste",
39720         "tl",
39721         "670"
39722       ],
39723       [
39724         "Togo",
39725         "tg",
39726         "228"
39727       ],
39728       [
39729         "Tokelau",
39730         "tk",
39731         "690"
39732       ],
39733       [
39734         "Tonga",
39735         "to",
39736         "676"
39737       ],
39738       [
39739         "Trinidad and Tobago",
39740         "tt",
39741         "1868"
39742       ],
39743       [
39744         "Tunisia (‫تونس‬‎)",
39745         "tn",
39746         "216"
39747       ],
39748       [
39749         "Turkey (Türkiye)",
39750         "tr",
39751         "90"
39752       ],
39753       [
39754         "Turkmenistan",
39755         "tm",
39756         "993"
39757       ],
39758       [
39759         "Turks and Caicos Islands",
39760         "tc",
39761         "1649"
39762       ],
39763       [
39764         "Tuvalu",
39765         "tv",
39766         "688"
39767       ],
39768       [
39769         "U.S. Virgin Islands",
39770         "vi",
39771         "1340"
39772       ],
39773       [
39774         "Uganda",
39775         "ug",
39776         "256"
39777       ],
39778       [
39779         "Ukraine (Україна)",
39780         "ua",
39781         "380"
39782       ],
39783       [
39784         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39785         "ae",
39786         "971"
39787       ],
39788       [
39789         "United Kingdom",
39790         "gb",
39791         "44",
39792         0
39793       ],
39794       [
39795         "United States",
39796         "us",
39797         "1",
39798         0
39799       ],
39800       [
39801         "Uruguay",
39802         "uy",
39803         "598"
39804       ],
39805       [
39806         "Uzbekistan (Oʻzbekiston)",
39807         "uz",
39808         "998"
39809       ],
39810       [
39811         "Vanuatu",
39812         "vu",
39813         "678"
39814       ],
39815       [
39816         "Vatican City (Città del Vaticano)",
39817         "va",
39818         "39",
39819         1
39820       ],
39821       [
39822         "Venezuela",
39823         "ve",
39824         "58"
39825       ],
39826       [
39827         "Vietnam (Việt Nam)",
39828         "vn",
39829         "84"
39830       ],
39831       [
39832         "Wallis and Futuna (Wallis-et-Futuna)",
39833         "wf",
39834         "681"
39835       ],
39836       [
39837         "Western Sahara (‫الصحراء الغربية‬‎)",
39838         "eh",
39839         "212",
39840         1
39841       ],
39842       [
39843         "Yemen (‫اليمن‬‎)",
39844         "ye",
39845         "967"
39846       ],
39847       [
39848         "Zambia",
39849         "zm",
39850         "260"
39851       ],
39852       [
39853         "Zimbabwe",
39854         "zw",
39855         "263"
39856       ],
39857       [
39858         "Åland Islands",
39859         "ax",
39860         "358",
39861         1
39862       ]
39863   ];
39864   
39865   return d;
39866 }/**
39867 *    This script refer to:
39868 *    Title: International Telephone Input
39869 *    Author: Jack O'Connor
39870 *    Code version:  v12.1.12
39871 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39872 **/
39873
39874 /**
39875  * @class Roo.bootstrap.PhoneInput
39876  * @extends Roo.bootstrap.TriggerField
39877  * An input with International dial-code selection
39878  
39879  * @cfg {String} defaultDialCode default '+852'
39880  * @cfg {Array} preferedCountries default []
39881   
39882  * @constructor
39883  * Create a new PhoneInput.
39884  * @param {Object} config Configuration options
39885  */
39886
39887 Roo.bootstrap.PhoneInput = function(config) {
39888     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39889 };
39890
39891 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39892         
39893         listWidth: undefined,
39894         
39895         selectedClass: 'active',
39896         
39897         invalidClass : "has-warning",
39898         
39899         validClass: 'has-success',
39900         
39901         allowed: '0123456789',
39902         
39903         max_length: 15,
39904         
39905         /**
39906          * @cfg {String} defaultDialCode The default dial code when initializing the input
39907          */
39908         defaultDialCode: '+852',
39909         
39910         /**
39911          * @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
39912          */
39913         preferedCountries: false,
39914         
39915         getAutoCreate : function()
39916         {
39917             var data = Roo.bootstrap.PhoneInputData();
39918             var align = this.labelAlign || this.parentLabelAlign();
39919             var id = Roo.id();
39920             
39921             this.allCountries = [];
39922             this.dialCodeMapping = [];
39923             
39924             for (var i = 0; i < data.length; i++) {
39925               var c = data[i];
39926               this.allCountries[i] = {
39927                 name: c[0],
39928                 iso2: c[1],
39929                 dialCode: c[2],
39930                 priority: c[3] || 0,
39931                 areaCodes: c[4] || null
39932               };
39933               this.dialCodeMapping[c[2]] = {
39934                   name: c[0],
39935                   iso2: c[1],
39936                   priority: c[3] || 0,
39937                   areaCodes: c[4] || null
39938               };
39939             }
39940             
39941             var cfg = {
39942                 cls: 'form-group',
39943                 cn: []
39944             };
39945             
39946             var input =  {
39947                 tag: 'input',
39948                 id : id,
39949                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39950                 maxlength: this.max_length,
39951                 cls : 'form-control tel-input',
39952                 autocomplete: 'new-password'
39953             };
39954             
39955             var hiddenInput = {
39956                 tag: 'input',
39957                 type: 'hidden',
39958                 cls: 'hidden-tel-input'
39959             };
39960             
39961             if (this.name) {
39962                 hiddenInput.name = this.name;
39963             }
39964             
39965             if (this.disabled) {
39966                 input.disabled = true;
39967             }
39968             
39969             var flag_container = {
39970                 tag: 'div',
39971                 cls: 'flag-box',
39972                 cn: [
39973                     {
39974                         tag: 'div',
39975                         cls: 'flag'
39976                     },
39977                     {
39978                         tag: 'div',
39979                         cls: 'caret'
39980                     }
39981                 ]
39982             };
39983             
39984             var box = {
39985                 tag: 'div',
39986                 cls: this.hasFeedback ? 'has-feedback' : '',
39987                 cn: [
39988                     hiddenInput,
39989                     input,
39990                     {
39991                         tag: 'input',
39992                         cls: 'dial-code-holder',
39993                         disabled: true
39994                     }
39995                 ]
39996             };
39997             
39998             var container = {
39999                 cls: 'roo-select2-container input-group',
40000                 cn: [
40001                     flag_container,
40002                     box
40003                 ]
40004             };
40005             
40006             if (this.fieldLabel.length) {
40007                 var indicator = {
40008                     tag: 'i',
40009                     tooltip: 'This field is required'
40010                 };
40011                 
40012                 var label = {
40013                     tag: 'label',
40014                     'for':  id,
40015                     cls: 'control-label',
40016                     cn: []
40017                 };
40018                 
40019                 var label_text = {
40020                     tag: 'span',
40021                     html: this.fieldLabel
40022                 };
40023                 
40024                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40025                 label.cn = [
40026                     indicator,
40027                     label_text
40028                 ];
40029                 
40030                 if(this.indicatorpos == 'right') {
40031                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40032                     label.cn = [
40033                         label_text,
40034                         indicator
40035                     ];
40036                 }
40037                 
40038                 if(align == 'left') {
40039                     container = {
40040                         tag: 'div',
40041                         cn: [
40042                             container
40043                         ]
40044                     };
40045                     
40046                     if(this.labelWidth > 12){
40047                         label.style = "width: " + this.labelWidth + 'px';
40048                     }
40049                     if(this.labelWidth < 13 && this.labelmd == 0){
40050                         this.labelmd = this.labelWidth;
40051                     }
40052                     if(this.labellg > 0){
40053                         label.cls += ' col-lg-' + this.labellg;
40054                         input.cls += ' col-lg-' + (12 - this.labellg);
40055                     }
40056                     if(this.labelmd > 0){
40057                         label.cls += ' col-md-' + this.labelmd;
40058                         container.cls += ' col-md-' + (12 - this.labelmd);
40059                     }
40060                     if(this.labelsm > 0){
40061                         label.cls += ' col-sm-' + this.labelsm;
40062                         container.cls += ' col-sm-' + (12 - this.labelsm);
40063                     }
40064                     if(this.labelxs > 0){
40065                         label.cls += ' col-xs-' + this.labelxs;
40066                         container.cls += ' col-xs-' + (12 - this.labelxs);
40067                     }
40068                 }
40069             }
40070             
40071             cfg.cn = [
40072                 label,
40073                 container
40074             ];
40075             
40076             var settings = this;
40077             
40078             ['xs','sm','md','lg'].map(function(size){
40079                 if (settings[size]) {
40080                     cfg.cls += ' col-' + size + '-' + settings[size];
40081                 }
40082             });
40083             
40084             this.store = new Roo.data.Store({
40085                 proxy : new Roo.data.MemoryProxy({}),
40086                 reader : new Roo.data.JsonReader({
40087                     fields : [
40088                         {
40089                             'name' : 'name',
40090                             'type' : 'string'
40091                         },
40092                         {
40093                             'name' : 'iso2',
40094                             'type' : 'string'
40095                         },
40096                         {
40097                             'name' : 'dialCode',
40098                             'type' : 'string'
40099                         },
40100                         {
40101                             'name' : 'priority',
40102                             'type' : 'string'
40103                         },
40104                         {
40105                             'name' : 'areaCodes',
40106                             'type' : 'string'
40107                         }
40108                     ]
40109                 })
40110             });
40111             
40112             if(!this.preferedCountries) {
40113                 this.preferedCountries = [
40114                     'hk',
40115                     'gb',
40116                     'us'
40117                 ];
40118             }
40119             
40120             var p = this.preferedCountries.reverse();
40121             
40122             if(p) {
40123                 for (var i = 0; i < p.length; i++) {
40124                     for (var j = 0; j < this.allCountries.length; j++) {
40125                         if(this.allCountries[j].iso2 == p[i]) {
40126                             var t = this.allCountries[j];
40127                             this.allCountries.splice(j,1);
40128                             this.allCountries.unshift(t);
40129                         }
40130                     } 
40131                 }
40132             }
40133             
40134             this.store.proxy.data = {
40135                 success: true,
40136                 data: this.allCountries
40137             };
40138             
40139             return cfg;
40140         },
40141         
40142         initEvents : function()
40143         {
40144             this.createList();
40145             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40146             
40147             this.indicator = this.indicatorEl();
40148             this.flag = this.flagEl();
40149             this.dialCodeHolder = this.dialCodeHolderEl();
40150             
40151             this.trigger = this.el.select('div.flag-box',true).first();
40152             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40153             
40154             var _this = this;
40155             
40156             (function(){
40157                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40158                 _this.list.setWidth(lw);
40159             }).defer(100);
40160             
40161             this.list.on('mouseover', this.onViewOver, this);
40162             this.list.on('mousemove', this.onViewMove, this);
40163             this.inputEl().on("keyup", this.onKeyUp, this);
40164             this.inputEl().on("keypress", this.onKeyPress, this);
40165             
40166             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40167
40168             this.view = new Roo.View(this.list, this.tpl, {
40169                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40170             });
40171             
40172             this.view.on('click', this.onViewClick, this);
40173             this.setValue(this.defaultDialCode);
40174         },
40175         
40176         onTriggerClick : function(e)
40177         {
40178             Roo.log('trigger click');
40179             if(this.disabled){
40180                 return;
40181             }
40182             
40183             if(this.isExpanded()){
40184                 this.collapse();
40185                 this.hasFocus = false;
40186             }else {
40187                 this.store.load({});
40188                 this.hasFocus = true;
40189                 this.expand();
40190             }
40191         },
40192         
40193         isExpanded : function()
40194         {
40195             return this.list.isVisible();
40196         },
40197         
40198         collapse : function()
40199         {
40200             if(!this.isExpanded()){
40201                 return;
40202             }
40203             this.list.hide();
40204             Roo.get(document).un('mousedown', this.collapseIf, this);
40205             Roo.get(document).un('mousewheel', this.collapseIf, this);
40206             this.fireEvent('collapse', this);
40207             this.validate();
40208         },
40209         
40210         expand : function()
40211         {
40212             Roo.log('expand');
40213
40214             if(this.isExpanded() || !this.hasFocus){
40215                 return;
40216             }
40217             
40218             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40219             this.list.setWidth(lw);
40220             
40221             this.list.show();
40222             this.restrictHeight();
40223             
40224             Roo.get(document).on('mousedown', this.collapseIf, this);
40225             Roo.get(document).on('mousewheel', this.collapseIf, this);
40226             
40227             this.fireEvent('expand', this);
40228         },
40229         
40230         restrictHeight : function()
40231         {
40232             this.list.alignTo(this.inputEl(), this.listAlign);
40233             this.list.alignTo(this.inputEl(), this.listAlign);
40234         },
40235         
40236         onViewOver : function(e, t)
40237         {
40238             if(this.inKeyMode){
40239                 return;
40240             }
40241             var item = this.view.findItemFromChild(t);
40242             
40243             if(item){
40244                 var index = this.view.indexOf(item);
40245                 this.select(index, false);
40246             }
40247         },
40248
40249         // private
40250         onViewClick : function(view, doFocus, el, e)
40251         {
40252             var index = this.view.getSelectedIndexes()[0];
40253             
40254             var r = this.store.getAt(index);
40255             
40256             if(r){
40257                 this.onSelect(r, index);
40258             }
40259             if(doFocus !== false && !this.blockFocus){
40260                 this.inputEl().focus();
40261             }
40262         },
40263         
40264         onViewMove : function(e, t)
40265         {
40266             this.inKeyMode = false;
40267         },
40268         
40269         select : function(index, scrollIntoView)
40270         {
40271             this.selectedIndex = index;
40272             this.view.select(index);
40273             if(scrollIntoView !== false){
40274                 var el = this.view.getNode(index);
40275                 if(el){
40276                     this.list.scrollChildIntoView(el, false);
40277                 }
40278             }
40279         },
40280         
40281         createList : function()
40282         {
40283             this.list = Roo.get(document.body).createChild({
40284                 tag: 'ul',
40285                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40286                 style: 'display:none'
40287             });
40288             
40289             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40290         },
40291         
40292         collapseIf : function(e)
40293         {
40294             var in_combo  = e.within(this.el);
40295             var in_list =  e.within(this.list);
40296             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40297             
40298             if (in_combo || in_list || is_list) {
40299                 return;
40300             }
40301             this.collapse();
40302         },
40303         
40304         onSelect : function(record, index)
40305         {
40306             if(this.fireEvent('beforeselect', this, record, index) !== false){
40307                 
40308                 this.setFlagClass(record.data.iso2);
40309                 this.setDialCode(record.data.dialCode);
40310                 this.hasFocus = false;
40311                 this.collapse();
40312                 this.fireEvent('select', this, record, index);
40313             }
40314         },
40315         
40316         flagEl : function()
40317         {
40318             var flag = this.el.select('div.flag',true).first();
40319             if(!flag){
40320                 return false;
40321             }
40322             return flag;
40323         },
40324         
40325         dialCodeHolderEl : function()
40326         {
40327             var d = this.el.select('input.dial-code-holder',true).first();
40328             if(!d){
40329                 return false;
40330             }
40331             return d;
40332         },
40333         
40334         setDialCode : function(v)
40335         {
40336             this.dialCodeHolder.dom.value = '+'+v;
40337         },
40338         
40339         setFlagClass : function(n)
40340         {
40341             this.flag.dom.className = 'flag '+n;
40342         },
40343         
40344         getValue : function()
40345         {
40346             var v = this.inputEl().getValue();
40347             if(this.dialCodeHolder) {
40348                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40349             }
40350             return v;
40351         },
40352         
40353         setValue : function(v)
40354         {
40355             var d = this.getDialCode(v);
40356             
40357             //invalid dial code
40358             if(v.length == 0 || !d || d.length == 0) {
40359                 if(this.rendered){
40360                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40361                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40362                 }
40363                 return;
40364             }
40365             
40366             //valid dial code
40367             this.setFlagClass(this.dialCodeMapping[d].iso2);
40368             this.setDialCode(d);
40369             this.inputEl().dom.value = v.replace('+'+d,'');
40370             this.hiddenEl().dom.value = this.getValue();
40371             
40372             this.validate();
40373         },
40374         
40375         getDialCode : function(v)
40376         {
40377             v = v ||  '';
40378             
40379             if (v.length == 0) {
40380                 return this.dialCodeHolder.dom.value;
40381             }
40382             
40383             var dialCode = "";
40384             if (v.charAt(0) != "+") {
40385                 return false;
40386             }
40387             var numericChars = "";
40388             for (var i = 1; i < v.length; i++) {
40389               var c = v.charAt(i);
40390               if (!isNaN(c)) {
40391                 numericChars += c;
40392                 if (this.dialCodeMapping[numericChars]) {
40393                   dialCode = v.substr(1, i);
40394                 }
40395                 if (numericChars.length == 4) {
40396                   break;
40397                 }
40398               }
40399             }
40400             return dialCode;
40401         },
40402         
40403         reset : function()
40404         {
40405             this.setValue(this.defaultDialCode);
40406             this.validate();
40407         },
40408         
40409         hiddenEl : function()
40410         {
40411             return this.el.select('input.hidden-tel-input',true).first();
40412         },
40413         
40414         // after setting val
40415         onKeyUp : function(e){
40416             this.setValue(this.getValue());
40417         },
40418         
40419         onKeyPress : function(e){
40420             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40421                 e.stopEvent();
40422             }
40423         }
40424         
40425 });
40426 /**
40427  * @class Roo.bootstrap.MoneyField
40428  * @extends Roo.bootstrap.ComboBox
40429  * Bootstrap MoneyField class
40430  * 
40431  * @constructor
40432  * Create a new MoneyField.
40433  * @param {Object} config Configuration options
40434  */
40435
40436 Roo.bootstrap.MoneyField = function(config) {
40437     
40438     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40439     
40440 };
40441
40442 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40443     
40444     /**
40445      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40446      */
40447     allowDecimals : true,
40448     /**
40449      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40450      */
40451     decimalSeparator : ".",
40452     /**
40453      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40454      */
40455     decimalPrecision : 0,
40456     /**
40457      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40458      */
40459     allowNegative : true,
40460     /**
40461      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40462      */
40463     allowZero: true,
40464     /**
40465      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40466      */
40467     minValue : Number.NEGATIVE_INFINITY,
40468     /**
40469      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40470      */
40471     maxValue : Number.MAX_VALUE,
40472     /**
40473      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40474      */
40475     minText : "The minimum value for this field is {0}",
40476     /**
40477      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40478      */
40479     maxText : "The maximum value for this field is {0}",
40480     /**
40481      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40482      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40483      */
40484     nanText : "{0} is not a valid number",
40485     /**
40486      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40487      */
40488     castInt : true,
40489     /**
40490      * @cfg {String} defaults currency of the MoneyField
40491      * value should be in lkey
40492      */
40493     defaultCurrency : false,
40494     /**
40495      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40496      */
40497     thousandsDelimiter : false,
40498     /**
40499      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40500      */
40501     max_length: false,
40502     
40503     inputlg : 9,
40504     inputmd : 9,
40505     inputsm : 9,
40506     inputxs : 6,
40507     
40508     store : false,
40509     
40510     getAutoCreate : function()
40511     {
40512         var align = this.labelAlign || this.parentLabelAlign();
40513         
40514         var id = Roo.id();
40515
40516         var cfg = {
40517             cls: 'form-group',
40518             cn: []
40519         };
40520
40521         var input =  {
40522             tag: 'input',
40523             id : id,
40524             cls : 'form-control roo-money-amount-input',
40525             autocomplete: 'new-password'
40526         };
40527         
40528         var hiddenInput = {
40529             tag: 'input',
40530             type: 'hidden',
40531             id: Roo.id(),
40532             cls: 'hidden-number-input'
40533         };
40534         
40535         if(this.max_length) {
40536             input.maxlength = this.max_length; 
40537         }
40538         
40539         if (this.name) {
40540             hiddenInput.name = this.name;
40541         }
40542
40543         if (this.disabled) {
40544             input.disabled = true;
40545         }
40546
40547         var clg = 12 - this.inputlg;
40548         var cmd = 12 - this.inputmd;
40549         var csm = 12 - this.inputsm;
40550         var cxs = 12 - this.inputxs;
40551         
40552         var container = {
40553             tag : 'div',
40554             cls : 'row roo-money-field',
40555             cn : [
40556                 {
40557                     tag : 'div',
40558                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40559                     cn : [
40560                         {
40561                             tag : 'div',
40562                             cls: 'roo-select2-container input-group',
40563                             cn: [
40564                                 {
40565                                     tag : 'input',
40566                                     cls : 'form-control roo-money-currency-input',
40567                                     autocomplete: 'new-password',
40568                                     readOnly : 1,
40569                                     name : this.currencyName
40570                                 },
40571                                 {
40572                                     tag :'span',
40573                                     cls : 'input-group-addon',
40574                                     cn : [
40575                                         {
40576                                             tag: 'span',
40577                                             cls: 'caret'
40578                                         }
40579                                     ]
40580                                 }
40581                             ]
40582                         }
40583                     ]
40584                 },
40585                 {
40586                     tag : 'div',
40587                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40588                     cn : [
40589                         {
40590                             tag: 'div',
40591                             cls: this.hasFeedback ? 'has-feedback' : '',
40592                             cn: [
40593                                 input
40594                             ]
40595                         }
40596                     ]
40597                 }
40598             ]
40599             
40600         };
40601         
40602         if (this.fieldLabel.length) {
40603             var indicator = {
40604                 tag: 'i',
40605                 tooltip: 'This field is required'
40606             };
40607
40608             var label = {
40609                 tag: 'label',
40610                 'for':  id,
40611                 cls: 'control-label',
40612                 cn: []
40613             };
40614
40615             var label_text = {
40616                 tag: 'span',
40617                 html: this.fieldLabel
40618             };
40619
40620             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40621             label.cn = [
40622                 indicator,
40623                 label_text
40624             ];
40625
40626             if(this.indicatorpos == 'right') {
40627                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40628                 label.cn = [
40629                     label_text,
40630                     indicator
40631                 ];
40632             }
40633
40634             if(align == 'left') {
40635                 container = {
40636                     tag: 'div',
40637                     cn: [
40638                         container
40639                     ]
40640                 };
40641
40642                 if(this.labelWidth > 12){
40643                     label.style = "width: " + this.labelWidth + 'px';
40644                 }
40645                 if(this.labelWidth < 13 && this.labelmd == 0){
40646                     this.labelmd = this.labelWidth;
40647                 }
40648                 if(this.labellg > 0){
40649                     label.cls += ' col-lg-' + this.labellg;
40650                     input.cls += ' col-lg-' + (12 - this.labellg);
40651                 }
40652                 if(this.labelmd > 0){
40653                     label.cls += ' col-md-' + this.labelmd;
40654                     container.cls += ' col-md-' + (12 - this.labelmd);
40655                 }
40656                 if(this.labelsm > 0){
40657                     label.cls += ' col-sm-' + this.labelsm;
40658                     container.cls += ' col-sm-' + (12 - this.labelsm);
40659                 }
40660                 if(this.labelxs > 0){
40661                     label.cls += ' col-xs-' + this.labelxs;
40662                     container.cls += ' col-xs-' + (12 - this.labelxs);
40663                 }
40664             }
40665         }
40666
40667         cfg.cn = [
40668             label,
40669             container,
40670             hiddenInput
40671         ];
40672         
40673         var settings = this;
40674
40675         ['xs','sm','md','lg'].map(function(size){
40676             if (settings[size]) {
40677                 cfg.cls += ' col-' + size + '-' + settings[size];
40678             }
40679         });
40680         
40681         return cfg;
40682     },
40683     
40684     initEvents : function()
40685     {
40686         this.indicator = this.indicatorEl();
40687         
40688         this.initCurrencyEvent();
40689         
40690         this.initNumberEvent();
40691     },
40692     
40693     initCurrencyEvent : function()
40694     {
40695         if (!this.store) {
40696             throw "can not find store for combo";
40697         }
40698         
40699         this.store = Roo.factory(this.store, Roo.data);
40700         this.store.parent = this;
40701         
40702         this.createList();
40703         
40704         this.triggerEl = this.el.select('.input-group-addon', true).first();
40705         
40706         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40707         
40708         var _this = this;
40709         
40710         (function(){
40711             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40712             _this.list.setWidth(lw);
40713         }).defer(100);
40714         
40715         this.list.on('mouseover', this.onViewOver, this);
40716         this.list.on('mousemove', this.onViewMove, this);
40717         this.list.on('scroll', this.onViewScroll, this);
40718         
40719         if(!this.tpl){
40720             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40721         }
40722         
40723         this.view = new Roo.View(this.list, this.tpl, {
40724             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40725         });
40726         
40727         this.view.on('click', this.onViewClick, this);
40728         
40729         this.store.on('beforeload', this.onBeforeLoad, this);
40730         this.store.on('load', this.onLoad, this);
40731         this.store.on('loadexception', this.onLoadException, this);
40732         
40733         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40734             "up" : function(e){
40735                 this.inKeyMode = true;
40736                 this.selectPrev();
40737             },
40738
40739             "down" : function(e){
40740                 if(!this.isExpanded()){
40741                     this.onTriggerClick();
40742                 }else{
40743                     this.inKeyMode = true;
40744                     this.selectNext();
40745                 }
40746             },
40747
40748             "enter" : function(e){
40749                 this.collapse();
40750                 
40751                 if(this.fireEvent("specialkey", this, e)){
40752                     this.onViewClick(false);
40753                 }
40754                 
40755                 return true;
40756             },
40757
40758             "esc" : function(e){
40759                 this.collapse();
40760             },
40761
40762             "tab" : function(e){
40763                 this.collapse();
40764                 
40765                 if(this.fireEvent("specialkey", this, e)){
40766                     this.onViewClick(false);
40767                 }
40768                 
40769                 return true;
40770             },
40771
40772             scope : this,
40773
40774             doRelay : function(foo, bar, hname){
40775                 if(hname == 'down' || this.scope.isExpanded()){
40776                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40777                 }
40778                 return true;
40779             },
40780
40781             forceKeyDown: true
40782         });
40783         
40784         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40785         
40786     },
40787     
40788     initNumberEvent : function(e)
40789     {
40790         this.inputEl().on("keydown" , this.fireKey,  this);
40791         this.inputEl().on("focus", this.onFocus,  this);
40792         this.inputEl().on("blur", this.onBlur,  this);
40793         
40794         this.inputEl().relayEvent('keyup', this);
40795         
40796         if(this.indicator){
40797             this.indicator.addClass('invisible');
40798         }
40799  
40800         this.originalValue = this.getValue();
40801         
40802         if(this.validationEvent == 'keyup'){
40803             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40804             this.inputEl().on('keyup', this.filterValidation, this);
40805         }
40806         else if(this.validationEvent !== false){
40807             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40808         }
40809         
40810         if(this.selectOnFocus){
40811             this.on("focus", this.preFocus, this);
40812             
40813         }
40814         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40815             this.inputEl().on("keypress", this.filterKeys, this);
40816         } else {
40817             this.inputEl().relayEvent('keypress', this);
40818         }
40819         
40820         var allowed = "0123456789";
40821         
40822         if(this.allowDecimals){
40823             allowed += this.decimalSeparator;
40824         }
40825         
40826         if(this.allowNegative){
40827             allowed += "-";
40828         }
40829         
40830         if(this.thousandsDelimiter) {
40831             allowed += ",";
40832         }
40833         
40834         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40835         
40836         var keyPress = function(e){
40837             
40838             var k = e.getKey();
40839             
40840             var c = e.getCharCode();
40841             
40842             if(
40843                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40844                     allowed.indexOf(String.fromCharCode(c)) === -1
40845             ){
40846                 e.stopEvent();
40847                 return;
40848             }
40849             
40850             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40851                 return;
40852             }
40853             
40854             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40855                 e.stopEvent();
40856             }
40857         };
40858         
40859         this.inputEl().on("keypress", keyPress, this);
40860         
40861     },
40862     
40863     onTriggerClick : function(e)
40864     {   
40865         if(this.disabled){
40866             return;
40867         }
40868         
40869         this.page = 0;
40870         this.loadNext = false;
40871         
40872         if(this.isExpanded()){
40873             this.collapse();
40874             return;
40875         }
40876         
40877         this.hasFocus = true;
40878         
40879         if(this.triggerAction == 'all') {
40880             this.doQuery(this.allQuery, true);
40881             return;
40882         }
40883         
40884         this.doQuery(this.getRawValue());
40885     },
40886     
40887     getCurrency : function()
40888     {   
40889         var v = this.currencyEl().getValue();
40890         
40891         return v;
40892     },
40893     
40894     restrictHeight : function()
40895     {
40896         this.list.alignTo(this.currencyEl(), this.listAlign);
40897         this.list.alignTo(this.currencyEl(), this.listAlign);
40898     },
40899     
40900     onViewClick : function(view, doFocus, el, e)
40901     {
40902         var index = this.view.getSelectedIndexes()[0];
40903         
40904         var r = this.store.getAt(index);
40905         
40906         if(r){
40907             this.onSelect(r, index);
40908         }
40909     },
40910     
40911     onSelect : function(record, index){
40912         
40913         if(this.fireEvent('beforeselect', this, record, index) !== false){
40914         
40915             this.setFromCurrencyData(index > -1 ? record.data : false);
40916             
40917             this.collapse();
40918             
40919             this.fireEvent('select', this, record, index);
40920         }
40921     },
40922     
40923     setFromCurrencyData : function(o)
40924     {
40925         var currency = '';
40926         
40927         this.lastCurrency = o;
40928         
40929         if (this.currencyField) {
40930             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40931         } else {
40932             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40933         }
40934         
40935         this.lastSelectionText = currency;
40936         
40937         //setting default currency
40938         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40939             this.setCurrency(this.defaultCurrency);
40940             return;
40941         }
40942         
40943         this.setCurrency(currency);
40944     },
40945     
40946     setFromData : function(o)
40947     {
40948         var c = {};
40949         
40950         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40951         
40952         this.setFromCurrencyData(c);
40953         
40954         var value = '';
40955         
40956         if (this.name) {
40957             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40958         } else {
40959             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40960         }
40961         
40962         this.setValue(value);
40963         
40964     },
40965     
40966     setCurrency : function(v)
40967     {   
40968         this.currencyValue = v;
40969         
40970         if(this.rendered){
40971             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40972             this.validate();
40973         }
40974     },
40975     
40976     setValue : function(v)
40977     {
40978         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40979         
40980         this.value = v;
40981         
40982         if(this.rendered){
40983             
40984             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40985             
40986             this.inputEl().dom.value = (v == '') ? '' :
40987                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40988             
40989             if(!this.allowZero && v === '0') {
40990                 this.hiddenEl().dom.value = '';
40991                 this.inputEl().dom.value = '';
40992             }
40993             
40994             this.validate();
40995         }
40996     },
40997     
40998     getRawValue : function()
40999     {
41000         var v = this.inputEl().getValue();
41001         
41002         return v;
41003     },
41004     
41005     getValue : function()
41006     {
41007         return this.fixPrecision(this.parseValue(this.getRawValue()));
41008     },
41009     
41010     parseValue : function(value)
41011     {
41012         if(this.thousandsDelimiter) {
41013             value += "";
41014             r = new RegExp(",", "g");
41015             value = value.replace(r, "");
41016         }
41017         
41018         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41019         return isNaN(value) ? '' : value;
41020         
41021     },
41022     
41023     fixPrecision : function(value)
41024     {
41025         if(this.thousandsDelimiter) {
41026             value += "";
41027             r = new RegExp(",", "g");
41028             value = value.replace(r, "");
41029         }
41030         
41031         var nan = isNaN(value);
41032         
41033         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41034             return nan ? '' : value;
41035         }
41036         return parseFloat(value).toFixed(this.decimalPrecision);
41037     },
41038     
41039     decimalPrecisionFcn : function(v)
41040     {
41041         return Math.floor(v);
41042     },
41043     
41044     validateValue : function(value)
41045     {
41046         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41047             return false;
41048         }
41049         
41050         var num = this.parseValue(value);
41051         
41052         if(isNaN(num)){
41053             this.markInvalid(String.format(this.nanText, value));
41054             return false;
41055         }
41056         
41057         if(num < this.minValue){
41058             this.markInvalid(String.format(this.minText, this.minValue));
41059             return false;
41060         }
41061         
41062         if(num > this.maxValue){
41063             this.markInvalid(String.format(this.maxText, this.maxValue));
41064             return false;
41065         }
41066         
41067         return true;
41068     },
41069     
41070     validate : function()
41071     {
41072         if(this.disabled || this.allowBlank){
41073             this.markValid();
41074             return true;
41075         }
41076         
41077         var currency = this.getCurrency();
41078         
41079         if(this.validateValue(this.getRawValue()) && currency.length){
41080             this.markValid();
41081             return true;
41082         }
41083         
41084         this.markInvalid();
41085         return false;
41086     },
41087     
41088     getName: function()
41089     {
41090         return this.name;
41091     },
41092     
41093     beforeBlur : function()
41094     {
41095         if(!this.castInt){
41096             return;
41097         }
41098         
41099         var v = this.parseValue(this.getRawValue());
41100         
41101         if(v || v == 0){
41102             this.setValue(v);
41103         }
41104     },
41105     
41106     onBlur : function()
41107     {
41108         this.beforeBlur();
41109         
41110         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41111             //this.el.removeClass(this.focusClass);
41112         }
41113         
41114         this.hasFocus = false;
41115         
41116         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41117             this.validate();
41118         }
41119         
41120         var v = this.getValue();
41121         
41122         if(String(v) !== String(this.startValue)){
41123             this.fireEvent('change', this, v, this.startValue);
41124         }
41125         
41126         this.fireEvent("blur", this);
41127     },
41128     
41129     inputEl : function()
41130     {
41131         return this.el.select('.roo-money-amount-input', true).first();
41132     },
41133     
41134     currencyEl : function()
41135     {
41136         return this.el.select('.roo-money-currency-input', true).first();
41137     },
41138     
41139     hiddenEl : function()
41140     {
41141         return this.el.select('input.hidden-number-input',true).first();
41142     }
41143     
41144 });