roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @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)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         
1528         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529             this.el.dom.src =  url;
1530             return;
1531         }
1532         
1533         this.el.select('img', true).first().dom.src =  url;
1534     }
1535     
1536     
1537    
1538 });
1539
1540  /*
1541  * - LGPL
1542  *
1543  * image
1544  * 
1545  */
1546
1547
1548 /**
1549  * @class Roo.bootstrap.Link
1550  * @extends Roo.bootstrap.Component
1551  * Bootstrap Link Class
1552  * @cfg {String} alt image alternative text
1553  * @cfg {String} href a tag href
1554  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555  * @cfg {String} html the content of the link.
1556  * @cfg {String} anchor name for the anchor link
1557  * @cfg {String} fa - favicon
1558
1559  * @cfg {Boolean} preventDefault (true | false) default false
1560
1561  * 
1562  * @constructor
1563  * Create a new Input
1564  * @param {Object} config The config object
1565  */
1566
1567 Roo.bootstrap.Link = function(config){
1568     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1569     
1570     this.addEvents({
1571         // img events
1572         /**
1573          * @event click
1574          * The img click event for the img.
1575          * @param {Roo.EventObject} e
1576          */
1577         "click" : true
1578     });
1579 };
1580
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1582     
1583     href: false,
1584     target: false,
1585     preventDefault: false,
1586     anchor : false,
1587     alt : false,
1588     fa: false,
1589
1590
1591     getAutoCreate : function()
1592     {
1593         var html = this.html || '';
1594         
1595         if (this.fa !== false) {
1596             html = '<i class="fa fa-' + this.fa + '"></i>';
1597         }
1598         var cfg = {
1599             tag: 'a'
1600         };
1601         // anchor's do not require html/href...
1602         if (this.anchor === false) {
1603             cfg.html = html;
1604             cfg.href = this.href || '#';
1605         } else {
1606             cfg.name = this.anchor;
1607             if (this.html !== false || this.fa !== false) {
1608                 cfg.html = html;
1609             }
1610             if (this.href !== false) {
1611                 cfg.href = this.href;
1612             }
1613         }
1614         
1615         if(this.alt !== false){
1616             cfg.alt = this.alt;
1617         }
1618         
1619         
1620         if(this.target !== false) {
1621             cfg.target = this.target;
1622         }
1623         
1624         return cfg;
1625     },
1626     
1627     initEvents: function() {
1628         
1629         if(!this.href || this.preventDefault){
1630             this.el.on('click', this.onClick, this);
1631         }
1632     },
1633     
1634     onClick : function(e)
1635     {
1636         if(this.preventDefault){
1637             e.preventDefault();
1638         }
1639         //Roo.log('img onclick');
1640         this.fireEvent('click', this, e);
1641     }
1642    
1643 });
1644
1645  /*
1646  * - LGPL
1647  *
1648  * header
1649  * 
1650  */
1651
1652 /**
1653  * @class Roo.bootstrap.Header
1654  * @extends Roo.bootstrap.Component
1655  * Bootstrap Header class
1656  * @cfg {String} html content of header
1657  * @cfg {Number} level (1|2|3|4|5|6) default 1
1658  * 
1659  * @constructor
1660  * Create a new Header
1661  * @param {Object} config The config object
1662  */
1663
1664
1665 Roo.bootstrap.Header  = function(config){
1666     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1667 };
1668
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1670     
1671     //href : false,
1672     html : false,
1673     level : 1,
1674     
1675     
1676     
1677     getAutoCreate : function(){
1678         
1679         
1680         
1681         var cfg = {
1682             tag: 'h' + (1 *this.level),
1683             html: this.html || ''
1684         } ;
1685         
1686         return cfg;
1687     }
1688    
1689 });
1690
1691  
1692
1693  /*
1694  * Based on:
1695  * Ext JS Library 1.1.1
1696  * Copyright(c) 2006-2007, Ext JS, LLC.
1697  *
1698  * Originally Released Under LGPL - original licence link has changed is not relivant.
1699  *
1700  * Fork - LGPL
1701  * <script type="text/javascript">
1702  */
1703  
1704 /**
1705  * @class Roo.bootstrap.MenuMgr
1706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1707  * @singleton
1708  */
1709 Roo.bootstrap.MenuMgr = function(){
1710    var menus, active, groups = {}, attached = false, lastShow = new Date();
1711
1712    // private - called when first menu is created
1713    function init(){
1714        menus = {};
1715        active = new Roo.util.MixedCollection();
1716        Roo.get(document).addKeyListener(27, function(){
1717            if(active.length > 0){
1718                hideAll();
1719            }
1720        });
1721    }
1722
1723    // private
1724    function hideAll(){
1725        if(active && active.length > 0){
1726            var c = active.clone();
1727            c.each(function(m){
1728                m.hide();
1729            });
1730        }
1731    }
1732
1733    // private
1734    function onHide(m){
1735        active.remove(m);
1736        if(active.length < 1){
1737            Roo.get(document).un("mouseup", onMouseDown);
1738             
1739            attached = false;
1740        }
1741    }
1742
1743    // private
1744    function onShow(m){
1745        var last = active.last();
1746        lastShow = new Date();
1747        active.add(m);
1748        if(!attached){
1749           Roo.get(document).on("mouseup", onMouseDown);
1750            
1751            attached = true;
1752        }
1753        if(m.parentMenu){
1754           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755           m.parentMenu.activeChild = m;
1756        }else if(last && last.isVisible()){
1757           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1758        }
1759    }
1760
1761    // private
1762    function onBeforeHide(m){
1763        if(m.activeChild){
1764            m.activeChild.hide();
1765        }
1766        if(m.autoHideTimer){
1767            clearTimeout(m.autoHideTimer);
1768            delete m.autoHideTimer;
1769        }
1770    }
1771
1772    // private
1773    function onBeforeShow(m){
1774        var pm = m.parentMenu;
1775        if(!pm && !m.allowOtherMenus){
1776            hideAll();
1777        }else if(pm && pm.activeChild && active != m){
1778            pm.activeChild.hide();
1779        }
1780    }
1781
1782    // private this should really trigger on mouseup..
1783    function onMouseDown(e){
1784         Roo.log("on Mouse Up");
1785         
1786         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787             Roo.log("MenuManager hideAll");
1788             hideAll();
1789             e.stopEvent();
1790         }
1791         
1792         
1793    }
1794
1795    // private
1796    function onBeforeCheck(mi, state){
1797        if(state){
1798            var g = groups[mi.group];
1799            for(var i = 0, l = g.length; i < l; i++){
1800                if(g[i] != mi){
1801                    g[i].setChecked(false);
1802                }
1803            }
1804        }
1805    }
1806
1807    return {
1808
1809        /**
1810         * Hides all menus that are currently visible
1811         */
1812        hideAll : function(){
1813             hideAll();  
1814        },
1815
1816        // private
1817        register : function(menu){
1818            if(!menus){
1819                init();
1820            }
1821            menus[menu.id] = menu;
1822            menu.on("beforehide", onBeforeHide);
1823            menu.on("hide", onHide);
1824            menu.on("beforeshow", onBeforeShow);
1825            menu.on("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                if(!groups[g]){
1829                    groups[g] = [];
1830                }
1831                groups[g].push(menu);
1832                menu.on("checkchange", onCheck);
1833            }
1834        },
1835
1836         /**
1837          * Returns a {@link Roo.menu.Menu} object
1838          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839          * be used to generate and return a new Menu instance.
1840          */
1841        get : function(menu){
1842            if(typeof menu == "string"){ // menu id
1843                return menus[menu];
1844            }else if(menu.events){  // menu instance
1845                return menu;
1846            }
1847            /*else if(typeof menu.length == 'number'){ // array of menu items?
1848                return new Roo.bootstrap.Menu({items:menu});
1849            }else{ // otherwise, must be a config
1850                return new Roo.bootstrap.Menu(menu);
1851            }
1852            */
1853            return false;
1854        },
1855
1856        // private
1857        unregister : function(menu){
1858            delete menus[menu.id];
1859            menu.un("beforehide", onBeforeHide);
1860            menu.un("hide", onHide);
1861            menu.un("beforeshow", onBeforeShow);
1862            menu.un("show", onShow);
1863            var g = menu.group;
1864            if(g && menu.events["checkchange"]){
1865                groups[g].remove(menu);
1866                menu.un("checkchange", onCheck);
1867            }
1868        },
1869
1870        // private
1871        registerCheckable : function(menuItem){
1872            var g = menuItem.group;
1873            if(g){
1874                if(!groups[g]){
1875                    groups[g] = [];
1876                }
1877                groups[g].push(menuItem);
1878                menuItem.on("beforecheckchange", onBeforeCheck);
1879            }
1880        },
1881
1882        // private
1883        unregisterCheckable : function(menuItem){
1884            var g = menuItem.group;
1885            if(g){
1886                groups[g].remove(menuItem);
1887                menuItem.un("beforecheckchange", onBeforeCheck);
1888            }
1889        }
1890    };
1891 }();/*
1892  * - LGPL
1893  *
1894  * menu
1895  * 
1896  */
1897
1898 /**
1899  * @class Roo.bootstrap.Menu
1900  * @extends Roo.bootstrap.Component
1901  * Bootstrap Menu class - container for MenuItems
1902  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1904  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1905  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1906  * 
1907  * @constructor
1908  * Create a new Menu
1909  * @param {Object} config The config object
1910  */
1911
1912
1913 Roo.bootstrap.Menu = function(config){
1914     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915     if (this.registerMenu && this.type != 'treeview')  {
1916         Roo.bootstrap.MenuMgr.register(this);
1917     }
1918     this.addEvents({
1919         /**
1920          * @event beforeshow
1921          * Fires before this menu is displayed
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforeshow : true,
1925         /**
1926          * @event beforehide
1927          * Fires before this menu is hidden
1928          * @param {Roo.menu.Menu} this
1929          */
1930         beforehide : true,
1931         /**
1932          * @event show
1933          * Fires after this menu is displayed
1934          * @param {Roo.menu.Menu} this
1935          */
1936         show : true,
1937         /**
1938          * @event hide
1939          * Fires after this menu is hidden
1940          * @param {Roo.menu.Menu} this
1941          */
1942         hide : true,
1943         /**
1944          * @event click
1945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          * @param {Roo.EventObject} e
1949          */
1950         click : true,
1951         /**
1952          * @event mouseover
1953          * Fires when the mouse is hovering over this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseover : true,
1959         /**
1960          * @event mouseout
1961          * Fires when the mouse exits this menu
1962          * @param {Roo.menu.Menu} this
1963          * @param {Roo.EventObject} e
1964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1965          */
1966         mouseout : true,
1967         /**
1968          * @event itemclick
1969          * Fires when a menu item contained in this menu is clicked
1970          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971          * @param {Roo.EventObject} e
1972          */
1973         itemclick: true
1974     });
1975     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1979     
1980    /// html : false,
1981     //align : '',
1982     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1983     type: false,
1984     /**
1985      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1986      */
1987     registerMenu : true,
1988     
1989     menuItems :false, // stores the menu items..
1990     
1991     hidden:true,
1992         
1993     parentMenu : false,
1994     
1995     stopEvent : true,
1996     
1997     isLink : false,
1998     
1999     getChildContainer : function() {
2000         return this.el;  
2001     },
2002     
2003     getAutoCreate : function(){
2004          
2005         //if (['right'].indexOf(this.align)!==-1) {
2006         //    cfg.cn[1].cls += ' pull-right'
2007         //}
2008         
2009         
2010         var cfg = {
2011             tag : 'ul',
2012             cls : 'dropdown-menu' ,
2013             style : 'z-index:1000'
2014             
2015         };
2016         
2017         if (this.type === 'submenu') {
2018             cfg.cls = 'submenu active';
2019         }
2020         if (this.type === 'treeview') {
2021             cfg.cls = 'treeview-menu';
2022         }
2023         
2024         return cfg;
2025     },
2026     initEvents : function() {
2027         
2028        // Roo.log("ADD event");
2029        // Roo.log(this.triggerEl.dom);
2030         
2031         this.triggerEl.on('click', this.onTriggerClick, this);
2032         
2033         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2034         
2035         this.triggerEl.addClass('dropdown-toggle');
2036         
2037         if (Roo.isTouch) {
2038             this.el.on('touchstart'  , this.onTouch, this);
2039         }
2040         this.el.on('click' , this.onClick, this);
2041
2042         this.el.on("mouseover", this.onMouseOver, this);
2043         this.el.on("mouseout", this.onMouseOut, this);
2044         
2045     },
2046     
2047     findTargetItem : function(e)
2048     {
2049         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2050         if(!t){
2051             return false;
2052         }
2053         //Roo.log(t);         Roo.log(t.id);
2054         if(t && t.id){
2055             //Roo.log(this.menuitems);
2056             return this.menuitems.get(t.id);
2057             
2058             //return this.items.get(t.menuItemId);
2059         }
2060         
2061         return false;
2062     },
2063     
2064     onTouch : function(e) 
2065     {
2066         Roo.log("menu.onTouch");
2067         //e.stopEvent(); this make the user popdown broken
2068         this.onClick(e);
2069     },
2070     
2071     onClick : function(e)
2072     {
2073         Roo.log("menu.onClick");
2074         
2075         var t = this.findTargetItem(e);
2076         if(!t || t.isContainer){
2077             return;
2078         }
2079         Roo.log(e);
2080         /*
2081         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2082             if(t == this.activeItem && t.shouldDeactivate(e)){
2083                 this.activeItem.deactivate();
2084                 delete this.activeItem;
2085                 return;
2086             }
2087             if(t.canActivate){
2088                 this.setActiveItem(t, true);
2089             }
2090             return;
2091             
2092             
2093         }
2094         */
2095        
2096         Roo.log('pass click event');
2097         
2098         t.onClick(e);
2099         
2100         this.fireEvent("click", this, t, e);
2101         
2102         var _this = this;
2103         
2104         (function() { _this.hide(); }).defer(100);
2105     },
2106     
2107     onMouseOver : function(e){
2108         var t  = this.findTargetItem(e);
2109         //Roo.log(t);
2110         //if(t){
2111         //    if(t.canActivate && !t.disabled){
2112         //        this.setActiveItem(t, true);
2113         //    }
2114         //}
2115         
2116         this.fireEvent("mouseover", this, e, t);
2117     },
2118     isVisible : function(){
2119         return !this.hidden;
2120     },
2121      onMouseOut : function(e){
2122         var t  = this.findTargetItem(e);
2123         
2124         //if(t ){
2125         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2126         //        this.activeItem.deactivate();
2127         //        delete this.activeItem;
2128         //    }
2129         //}
2130         this.fireEvent("mouseout", this, e, t);
2131     },
2132     
2133     
2134     /**
2135      * Displays this menu relative to another element
2136      * @param {String/HTMLElement/Roo.Element} element The element to align to
2137      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138      * the element (defaults to this.defaultAlign)
2139      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2140      */
2141     show : function(el, pos, parentMenu){
2142         this.parentMenu = parentMenu;
2143         if(!this.el){
2144             this.render();
2145         }
2146         this.fireEvent("beforeshow", this);
2147         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2148     },
2149      /**
2150      * Displays this menu at a specific xy position
2151      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2153      */
2154     showAt : function(xy, parentMenu, /* private: */_e){
2155         this.parentMenu = parentMenu;
2156         if(!this.el){
2157             this.render();
2158         }
2159         if(_e !== false){
2160             this.fireEvent("beforeshow", this);
2161             //xy = this.el.adjustForConstraints(xy);
2162         }
2163         
2164         //this.el.show();
2165         this.hideMenuItems();
2166         this.hidden = false;
2167         this.triggerEl.addClass('open');
2168         
2169         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2171         }
2172         
2173         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2174             this.el.setXY(xy);
2175         }
2176         
2177         this.focus();
2178         this.fireEvent("show", this);
2179     },
2180     
2181     focus : function(){
2182         return;
2183         if(!this.hidden){
2184             this.doFocus.defer(50, this);
2185         }
2186     },
2187
2188     doFocus : function(){
2189         if(!this.hidden){
2190             this.focusEl.focus();
2191         }
2192     },
2193
2194     /**
2195      * Hides this menu and optionally all parent menus
2196      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2197      */
2198     hide : function(deep)
2199     {
2200         
2201         this.hideMenuItems();
2202         if(this.el && this.isVisible()){
2203             this.fireEvent("beforehide", this);
2204             if(this.activeItem){
2205                 this.activeItem.deactivate();
2206                 this.activeItem = null;
2207             }
2208             this.triggerEl.removeClass('open');;
2209             this.hidden = true;
2210             this.fireEvent("hide", this);
2211         }
2212         if(deep === true && this.parentMenu){
2213             this.parentMenu.hide(true);
2214         }
2215     },
2216     
2217     onTriggerClick : function(e)
2218     {
2219         Roo.log('trigger click');
2220         
2221         var target = e.getTarget();
2222         
2223         Roo.log(target.nodeName.toLowerCase());
2224         
2225         if(target.nodeName.toLowerCase() === 'i'){
2226             e.preventDefault();
2227         }
2228         
2229     },
2230     
2231     onTriggerPress  : function(e)
2232     {
2233         Roo.log('trigger press');
2234         //Roo.log(e.getTarget());
2235        // Roo.log(this.triggerEl.dom);
2236        
2237         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238         var pel = Roo.get(e.getTarget());
2239         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240             Roo.log('is treeview or dropdown?');
2241             return;
2242         }
2243         
2244         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2245             return;
2246         }
2247         
2248         if (this.isVisible()) {
2249             Roo.log('hide');
2250             this.hide();
2251         } else {
2252             Roo.log('show');
2253             this.show(this.triggerEl, false, false);
2254         }
2255         
2256         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257             e.stopEvent();
2258         }
2259         
2260     },
2261        
2262     
2263     hideMenuItems : function()
2264     {
2265         Roo.log("hide Menu Items");
2266         if (!this.el) { 
2267             return;
2268         }
2269         //$(backdrop).remove()
2270         this.el.select('.open',true).each(function(aa) {
2271             
2272             aa.removeClass('open');
2273           //var parent = getParent($(this))
2274           //var relatedTarget = { relatedTarget: this }
2275           
2276            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277           //if (e.isDefaultPrevented()) return
2278            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2279         });
2280     },
2281     addxtypeChild : function (tree, cntr) {
2282         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2283           
2284         this.menuitems.add(comp);
2285         return comp;
2286
2287     },
2288     getEl : function()
2289     {
2290         Roo.log(this.el);
2291         return this.el;
2292     }
2293 });
2294
2295  
2296  /*
2297  * - LGPL
2298  *
2299  * menu item
2300  * 
2301  */
2302
2303
2304 /**
2305  * @class Roo.bootstrap.MenuItem
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap MenuItem class
2308  * @cfg {String} html the menu label
2309  * @cfg {String} href the link
2310  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2313  * @cfg {String} fa favicon to show on left of menu item.
2314  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2315  * 
2316  * 
2317  * @constructor
2318  * Create a new MenuItem
2319  * @param {Object} config The config object
2320  */
2321
2322
2323 Roo.bootstrap.MenuItem = function(config){
2324     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event click
2329          * The raw click event for the entire grid.
2330          * @param {Roo.bootstrap.MenuItem} this
2331          * @param {Roo.EventObject} e
2332          */
2333         "click" : true
2334     });
2335 };
2336
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2338     
2339     href : false,
2340     html : false,
2341     preventDefault: false,
2342     isContainer : false,
2343     active : false,
2344     fa: false,
2345     
2346     getAutoCreate : function(){
2347         
2348         if(this.isContainer){
2349             return {
2350                 tag: 'li',
2351                 cls: 'dropdown-menu-item'
2352             };
2353         }
2354         var ctag = {
2355             tag: 'span',
2356             html: 'Link'
2357         };
2358         
2359         var anc = {
2360             tag : 'a',
2361             href : '#',
2362             cn : [  ]
2363         };
2364         
2365         if (this.fa !== false) {
2366             anc.cn.push({
2367                 tag : 'i',
2368                 cls : 'fa fa-' + this.fa
2369             });
2370         }
2371         
2372         anc.cn.push(ctag);
2373         
2374         
2375         var cfg= {
2376             tag: 'li',
2377             cls: 'dropdown-menu-item',
2378             cn: [ anc ]
2379         };
2380         if (this.parent().type == 'treeview') {
2381             cfg.cls = 'treeview-menu';
2382         }
2383         if (this.active) {
2384             cfg.cls += ' active';
2385         }
2386         
2387         
2388         
2389         anc.href = this.href || cfg.cn[0].href ;
2390         ctag.html = this.html || cfg.cn[0].html ;
2391         return cfg;
2392     },
2393     
2394     initEvents: function()
2395     {
2396         if (this.parent().type == 'treeview') {
2397             this.el.select('a').on('click', this.onClick, this);
2398         }
2399         if (this.menu) {
2400             this.menu.parentType = this.xtype;
2401             this.menu.triggerEl = this.el;
2402             this.menu = this.addxtype(Roo.apply({}, this.menu));
2403         }
2404         
2405     },
2406     onClick : function(e)
2407     {
2408         Roo.log('item on click ');
2409         
2410         if(this.preventDefault){
2411             e.preventDefault();
2412         }
2413         //this.parent().hideMenuItems();
2414         
2415         this.fireEvent('click', this, e);
2416     },
2417     getEl : function()
2418     {
2419         return this.el;
2420     } 
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * menu separator
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.MenuSeparator
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap MenuSeparator class
2437  * 
2438  * @constructor
2439  * Create a new MenuItem
2440  * @param {Object} config The config object
2441  */
2442
2443
2444 Roo.bootstrap.MenuSeparator = function(config){
2445     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2449     
2450     getAutoCreate : function(){
2451         var cfg = {
2452             cls: 'divider',
2453             tag : 'li'
2454         };
2455         
2456         return cfg;
2457     }
2458    
2459 });
2460
2461  
2462
2463  
2464 /*
2465 * Licence: LGPL
2466 */
2467
2468 /**
2469  * @class Roo.bootstrap.Modal
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap Modal class
2472  * @cfg {String} title Title of dialog
2473  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2475  * @cfg {Boolean} specificTitle default false
2476  * @cfg {Array} buttons Array of buttons or standard button set..
2477  * @cfg {String} buttonPosition (left|right|center) default right
2478  * @cfg {Boolean} animate default true
2479  * @cfg {Boolean} allow_close default true
2480  * @cfg {Boolean} fitwindow default false
2481  * @cfg {String} size (sm|lg) default empty
2482  *
2483  *
2484  * @constructor
2485  * Create a new Modal Dialog
2486  * @param {Object} config The config object
2487  */
2488
2489 Roo.bootstrap.Modal = function(config){
2490     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event btnclick
2495          * The raw btnclick event for the button
2496          * @param {Roo.EventObject} e
2497          */
2498         "btnclick" : true
2499     });
2500     this.buttons = this.buttons || [];
2501
2502     if (this.tmpl) {
2503         this.tmpl = Roo.factory(this.tmpl);
2504     }
2505
2506 };
2507
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2509
2510     title : 'test dialog',
2511
2512     buttons : false,
2513
2514     // set on load...
2515
2516     html: false,
2517
2518     tmp: false,
2519
2520     specificTitle: false,
2521
2522     buttonPosition: 'right',
2523
2524     allow_close : true,
2525
2526     animate : true,
2527
2528     fitwindow: false,
2529
2530
2531      // private
2532     dialogEl: false,
2533     bodyEl:  false,
2534     footerEl:  false,
2535     titleEl:  false,
2536     closeEl:  false,
2537
2538     size: '',
2539
2540
2541     onRender : function(ct, position)
2542     {
2543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2544
2545         if(!this.el){
2546             var cfg = Roo.apply({},  this.getAutoCreate());
2547             cfg.id = Roo.id();
2548             //if(!cfg.name){
2549             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2550             //}
2551             //if (!cfg.name.length) {
2552             //    delete cfg.name;
2553            // }
2554             if (this.cls) {
2555                 cfg.cls += ' ' + this.cls;
2556             }
2557             if (this.style) {
2558                 cfg.style = this.style;
2559             }
2560             this.el = Roo.get(document.body).createChild(cfg, position);
2561         }
2562         //var type = this.el.dom.type;
2563
2564
2565         if(this.tabIndex !== undefined){
2566             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2567         }
2568
2569         this.dialogEl = this.el.select('.modal-dialog',true).first();
2570         this.bodyEl = this.el.select('.modal-body',true).first();
2571         this.closeEl = this.el.select('.modal-header .close', true).first();
2572         this.headerEl = this.el.select('.modal-header',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574         this.footerEl = this.el.select('.modal-footer',true).first();
2575
2576         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577         this.maskEl.enableDisplayMode("block");
2578         this.maskEl.hide();
2579         //this.el.addClass("x-dlg-modal");
2580
2581         if (this.buttons.length) {
2582             Roo.each(this.buttons, function(bb) {
2583                 var b = Roo.apply({}, bb);
2584                 b.xns = b.xns || Roo.bootstrap;
2585                 b.xtype = b.xtype || 'Button';
2586                 if (typeof(b.listeners) == 'undefined') {
2587                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2588                 }
2589
2590                 var btn = Roo.factory(b);
2591
2592                 btn.render(this.el.select('.modal-footer div').first());
2593
2594             },this);
2595         }
2596         // render the children.
2597         var nitems = [];
2598
2599         if(typeof(this.items) != 'undefined'){
2600             var items = this.items;
2601             delete this.items;
2602
2603             for(var i =0;i < items.length;i++) {
2604                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2605             }
2606         }
2607
2608         this.items = nitems;
2609
2610         // where are these used - they used to be body/close/footer
2611
2612
2613         this.initEvents();
2614         //this.el.addClass([this.fieldClass, this.cls]);
2615
2616     },
2617
2618     getAutoCreate : function(){
2619
2620
2621         var bdy = {
2622                 cls : 'modal-body',
2623                 html : this.html || ''
2624         };
2625
2626         var title = {
2627             tag: 'h4',
2628             cls : 'modal-title',
2629             html : this.title
2630         };
2631
2632         if(this.specificTitle){
2633             title = this.title;
2634
2635         };
2636
2637         var header = [];
2638         if (this.allow_close) {
2639             header.push({
2640                 tag: 'button',
2641                 cls : 'close',
2642                 html : '&times'
2643             });
2644         }
2645
2646         header.push(title);
2647
2648         var size = '';
2649
2650         if(this.size.length){
2651             size = 'modal-' + this.size;
2652         }
2653
2654         var modal = {
2655             cls: "modal",
2656             style : 'display: none',
2657             cn : [
2658                 {
2659                     cls: "modal-dialog " + size,
2660                     cn : [
2661                         {
2662                             cls : "modal-content",
2663                             cn : [
2664                                 {
2665                                     cls : 'modal-header',
2666                                     cn : header
2667                                 },
2668                                 bdy,
2669                                 {
2670                                     cls : 'modal-footer',
2671                                     cn : [
2672                                         {
2673                                             tag: 'div',
2674                                             cls: 'btn-' + this.buttonPosition
2675                                         }
2676                                     ]
2677
2678                                 }
2679
2680
2681                             ]
2682
2683                         }
2684                     ]
2685
2686                 }
2687             ]
2688         };
2689
2690         if(this.animate){
2691             modal.cls += ' fade';
2692         }
2693
2694         return modal;
2695
2696     },
2697     getChildContainer : function() {
2698
2699          return this.bodyEl;
2700
2701     },
2702     getButtonContainer : function() {
2703          return this.el.select('.modal-footer div',true).first();
2704
2705     },
2706     initEvents : function()
2707     {
2708         if (this.allow_close) {
2709             this.closeEl.on('click', this.hide, this);
2710         }
2711         Roo.EventManager.onWindowResize(this.resize, this, true);
2712
2713
2714     },
2715
2716     resize : function()
2717     {
2718         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2719         if (this.fitwindow) {
2720             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2722             this.setSize(w,h);
2723         }
2724     },
2725
2726     setSize : function(w,h)
2727     {
2728         if (!w && !h) {
2729             return;
2730         }
2731         this.resizeTo(w,h);
2732     },
2733
2734     show : function() {
2735
2736         if (!this.rendered) {
2737             this.render();
2738         }
2739
2740         this.el.setStyle('display', 'block');
2741
2742         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2743             var _this = this;
2744             (function(){
2745                 this.el.addClass('in');
2746             }).defer(50, this);
2747         }else{
2748             this.el.addClass('in');
2749
2750         }
2751
2752         // not sure how we can show data in here..
2753         //if (this.tmpl) {
2754         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2755         //}
2756
2757         Roo.get(document.body).addClass("x-body-masked");
2758         
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2761         this.maskEl.show();
2762         
2763         this.resize();
2764         
2765         this.fireEvent('show', this);
2766
2767         // set zindex here - otherwise it appears to be ignored...
2768         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2769
2770         (function () {
2771             this.items.forEach( function(e) {
2772                 e.layout ? e.layout() : false;
2773
2774             });
2775         }).defer(100,this);
2776
2777     },
2778     hide : function()
2779     {
2780         if(this.fireEvent("beforehide", this) !== false){
2781             this.maskEl.hide();
2782             Roo.get(document.body).removeClass("x-body-masked");
2783             this.el.removeClass('in');
2784             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2785
2786             if(this.animate){ // why
2787                 var _this = this;
2788                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2789             }else{
2790                 this.el.setStyle('display', 'none');
2791             }
2792             this.fireEvent('hide', this);
2793         }
2794     },
2795
2796     addButton : function(str, cb)
2797     {
2798
2799
2800         var b = Roo.apply({}, { html : str } );
2801         b.xns = b.xns || Roo.bootstrap;
2802         b.xtype = b.xtype || 'Button';
2803         if (typeof(b.listeners) == 'undefined') {
2804             b.listeners = { click : cb.createDelegate(this)  };
2805         }
2806
2807         var btn = Roo.factory(b);
2808
2809         btn.render(this.el.select('.modal-footer div').first());
2810
2811         return btn;
2812
2813     },
2814
2815     setDefaultButton : function(btn)
2816     {
2817         //this.el.select('.modal-footer').()
2818     },
2819     diff : false,
2820
2821     resizeTo: function(w,h)
2822     {
2823         // skip.. ?? why??
2824
2825         this.dialogEl.setWidth(w);
2826         if (this.diff === false) {
2827             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2828         }
2829
2830         this.bodyEl.setHeight(h-this.diff);
2831
2832
2833     },
2834     setContentSize  : function(w, h)
2835     {
2836
2837     },
2838     onButtonClick: function(btn,e)
2839     {
2840         //Roo.log([a,b,c]);
2841         this.fireEvent('btnclick', btn.name, e);
2842     },
2843      /**
2844      * Set the title of the Dialog
2845      * @param {String} str new Title
2846      */
2847     setTitle: function(str) {
2848         this.titleEl.dom.innerHTML = str;
2849     },
2850     /**
2851      * Set the body of the Dialog
2852      * @param {String} str new Title
2853      */
2854     setBody: function(str) {
2855         this.bodyEl.dom.innerHTML = str;
2856     },
2857     /**
2858      * Set the body of the Dialog using the template
2859      * @param {Obj} data - apply this data to the template and replace the body contents.
2860      */
2861     applyBody: function(obj)
2862     {
2863         if (!this.tmpl) {
2864             Roo.log("Error - using apply Body without a template");
2865             //code
2866         }
2867         this.tmpl.overwrite(this.bodyEl, obj);
2868     }
2869
2870 });
2871
2872
2873 Roo.apply(Roo.bootstrap.Modal,  {
2874     /**
2875          * Button config that displays a single OK button
2876          * @type Object
2877          */
2878         OK :  [{
2879             name : 'ok',
2880             weight : 'primary',
2881             html : 'OK'
2882         }],
2883         /**
2884          * Button config that displays Yes and No buttons
2885          * @type Object
2886          */
2887         YESNO : [
2888             {
2889                 name  : 'no',
2890                 html : 'No'
2891             },
2892             {
2893                 name  :'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             }
2897         ],
2898
2899         /**
2900          * Button config that displays OK and Cancel buttons
2901          * @type Object
2902          */
2903         OKCANCEL : [
2904             {
2905                name : 'cancel',
2906                 html : 'Cancel'
2907             },
2908             {
2909                 name : 'ok',
2910                 weight : 'primary',
2911                 html : 'OK'
2912             }
2913         ],
2914         /**
2915          * Button config that displays Yes, No and Cancel buttons
2916          * @type Object
2917          */
2918         YESNOCANCEL : [
2919             {
2920                 name : 'yes',
2921                 weight : 'primary',
2922                 html : 'Yes'
2923             },
2924             {
2925                 name : 'no',
2926                 html : 'No'
2927             },
2928             {
2929                 name : 'cancel',
2930                 html : 'Cancel'
2931             }
2932         ],
2933         
2934         zIndex : 10001
2935 });
2936 /*
2937  * - LGPL
2938  *
2939  * messagebox - can be used as a replace
2940  * 
2941  */
2942 /**
2943  * @class Roo.MessageBox
2944  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2945  * Example usage:
2946  *<pre><code>
2947 // Basic alert:
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2949
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2952     if (btn == 'ok'){
2953         // process text value...
2954     }
2955 });
2956
2957 // Show a dialog using config options:
2958 Roo.Msg.show({
2959    title:'Save Changes?',
2960    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961    buttons: Roo.Msg.YESNOCANCEL,
2962    fn: processResult,
2963    animEl: 'elId'
2964 });
2965 </code></pre>
2966  * @singleton
2967  */
2968 Roo.bootstrap.MessageBox = function(){
2969     var dlg, opt, mask, waitTimer;
2970     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971     var buttons, activeTextEl, bwidth;
2972
2973     
2974     // private
2975     var handleButton = function(button){
2976         dlg.hide();
2977         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2978     };
2979
2980     // private
2981     var handleHide = function(){
2982         if(opt && opt.cls){
2983             dlg.el.removeClass(opt.cls);
2984         }
2985         //if(waitTimer){
2986         //    Roo.TaskMgr.stop(waitTimer);
2987         //    waitTimer = null;
2988         //}
2989     };
2990
2991     // private
2992     var updateButtons = function(b){
2993         var width = 0;
2994         if(!b){
2995             buttons["ok"].hide();
2996             buttons["cancel"].hide();
2997             buttons["yes"].hide();
2998             buttons["no"].hide();
2999             //dlg.footer.dom.style.display = 'none';
3000             return width;
3001         }
3002         dlg.footerEl.dom.style.display = '';
3003         for(var k in buttons){
3004             if(typeof buttons[k] != "function"){
3005                 if(b[k]){
3006                     buttons[k].show();
3007                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008                     width += buttons[k].el.getWidth()+15;
3009                 }else{
3010                     buttons[k].hide();
3011                 }
3012             }
3013         }
3014         return width;
3015     };
3016
3017     // private
3018     var handleEsc = function(d, k, e){
3019         if(opt && opt.closable !== false){
3020             dlg.hide();
3021         }
3022         if(e){
3023             e.stopEvent();
3024         }
3025     };
3026
3027     return {
3028         /**
3029          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030          * @return {Roo.BasicDialog} The BasicDialog element
3031          */
3032         getDialog : function(){
3033            if(!dlg){
3034                 dlg = new Roo.bootstrap.Modal( {
3035                     //draggable: true,
3036                     //resizable:false,
3037                     //constraintoviewport:false,
3038                     //fixedcenter:true,
3039                     //collapsible : false,
3040                     //shim:true,
3041                     //modal: true,
3042                 //    width: 'auto',
3043                   //  height:100,
3044                     //buttonAlign:"center",
3045                     closeClick : function(){
3046                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3047                             handleButton("no");
3048                         }else{
3049                             handleButton("cancel");
3050                         }
3051                     }
3052                 });
3053                 dlg.render();
3054                 dlg.on("hide", handleHide);
3055                 mask = dlg.mask;
3056                 //dlg.addKeyListener(27, handleEsc);
3057                 buttons = {};
3058                 this.buttons = buttons;
3059                 var bt = this.buttonText;
3060                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3064                 //Roo.log(buttons);
3065                 bodyEl = dlg.bodyEl.createChild({
3066
3067                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068                         '<textarea class="roo-mb-textarea"></textarea>' +
3069                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3070                 });
3071                 msgEl = bodyEl.dom.firstChild;
3072                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073                 textboxEl.enableDisplayMode();
3074                 textboxEl.addKeyListener([10,13], function(){
3075                     if(dlg.isVisible() && opt && opt.buttons){
3076                         if(opt.buttons.ok){
3077                             handleButton("ok");
3078                         }else if(opt.buttons.yes){
3079                             handleButton("yes");
3080                         }
3081                     }
3082                 });
3083                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084                 textareaEl.enableDisplayMode();
3085                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086                 progressEl.enableDisplayMode();
3087                 
3088                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089                 //var pf = progressEl.dom.firstChild;
3090                 //if (pf) {
3091                     //pp = Roo.get(pf.firstChild);
3092                     //pp.setHeight(pf.offsetHeight);
3093                 //}
3094                 
3095             }
3096             return dlg;
3097         },
3098
3099         /**
3100          * Updates the message box body text
3101          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102          * the XHTML-compliant non-breaking space character '&amp;#160;')
3103          * @return {Roo.MessageBox} This message box
3104          */
3105         updateText : function(text)
3106         {
3107             if(!dlg.isVisible() && !opt.width){
3108                 dlg.dialogEl.setWidth(this.maxWidth);
3109                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3110             }
3111             msgEl.innerHTML = text || '&#160;';
3112       
3113             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3115             var w = Math.max(
3116                     Math.min(opt.width || cw , this.maxWidth), 
3117                     Math.max(opt.minWidth || this.minWidth, bwidth)
3118             );
3119             if(opt.prompt){
3120                 activeTextEl.setWidth(w);
3121             }
3122             if(dlg.isVisible()){
3123                 dlg.fixedcenter = false;
3124             }
3125             // to big, make it scroll. = But as usual stupid IE does not support
3126             // !important..
3127             
3128             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3131             } else {
3132                 bodyEl.dom.style.height = '';
3133                 bodyEl.dom.style.overflowY = '';
3134             }
3135             if (cw > w) {
3136                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3137             } else {
3138                 bodyEl.dom.style.overflowX = '';
3139             }
3140             
3141             dlg.setContentSize(w, bodyEl.getHeight());
3142             if(dlg.isVisible()){
3143                 dlg.fixedcenter = true;
3144             }
3145             return this;
3146         },
3147
3148         /**
3149          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3150          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153          * @return {Roo.MessageBox} This message box
3154          */
3155         updateProgress : function(value, text){
3156             if(text){
3157                 this.updateText(text);
3158             }
3159             if (pp) { // weird bug on my firefox - for some reason this is not defined
3160                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3161             }
3162             return this;
3163         },        
3164
3165         /**
3166          * Returns true if the message box is currently displayed
3167          * @return {Boolean} True if the message box is visible, else false
3168          */
3169         isVisible : function(){
3170             return dlg && dlg.isVisible();  
3171         },
3172
3173         /**
3174          * Hides the message box if it is displayed
3175          */
3176         hide : function(){
3177             if(this.isVisible()){
3178                 dlg.hide();
3179             }  
3180         },
3181
3182         /**
3183          * Displays a new message box, or reinitializes an existing message box, based on the config options
3184          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185          * The following config object properties are supported:
3186          * <pre>
3187 Property    Type             Description
3188 ----------  ---------------  ------------------------------------------------------------------------------------
3189 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3190                                    closes (defaults to undefined)
3191 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3194                                    progress and wait dialogs will ignore this property and always hide the
3195                                    close button as they can only be closed programmatically.
3196 cls               String           A custom CSS class to apply to the message box element
3197 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3198                                    displayed (defaults to 75)
3199 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3200                                    function will be btn (the name of the button that was clicked, if applicable,
3201                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3202                                    Progress and wait dialogs will ignore this option since they do not respond to
3203                                    user actions and can only be closed programmatically, so any required function
3204                                    should be called by the same code after it closes the dialog.
3205 icon              String           A CSS class that provides a background image to be used as an icon for
3206                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3208 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3209 modal             Boolean          False to allow user interaction with the page while the message box is
3210                                    displayed (defaults to true)
3211 msg               String           A string that will replace the existing message box body text (defaults
3212                                    to the XHTML-compliant non-breaking space character '&#160;')
3213 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3214 progress          Boolean          True to display a progress bar (defaults to false)
3215 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3218 title             String           The title text
3219 value             String           The string value to set into the active textbox element if displayed
3220 wait              Boolean          True to display a progress bar (defaults to false)
3221 width             Number           The width of the dialog in pixels
3222 </pre>
3223          *
3224          * Example usage:
3225          * <pre><code>
3226 Roo.Msg.show({
3227    title: 'Address',
3228    msg: 'Please enter your address:',
3229    width: 300,
3230    buttons: Roo.MessageBox.OKCANCEL,
3231    multiline: true,
3232    fn: saveAddress,
3233    animEl: 'addAddressBtn'
3234 });
3235 </code></pre>
3236          * @param {Object} config Configuration options
3237          * @return {Roo.MessageBox} This message box
3238          */
3239         show : function(options)
3240         {
3241             
3242             // this causes nightmares if you show one dialog after another
3243             // especially on callbacks..
3244              
3245             if(this.isVisible()){
3246                 
3247                 this.hide();
3248                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3250                 Roo.log("New Dialog Message:" +  options.msg )
3251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3253                 
3254             }
3255             var d = this.getDialog();
3256             opt = options;
3257             d.setTitle(opt.title || "&#160;");
3258             d.closeEl.setDisplayed(opt.closable !== false);
3259             activeTextEl = textboxEl;
3260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3261             if(opt.prompt){
3262                 if(opt.multiline){
3263                     textboxEl.hide();
3264                     textareaEl.show();
3265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3266                         opt.multiline : this.defaultTextHeight);
3267                     activeTextEl = textareaEl;
3268                 }else{
3269                     textboxEl.show();
3270                     textareaEl.hide();
3271                 }
3272             }else{
3273                 textboxEl.hide();
3274                 textareaEl.hide();
3275             }
3276             progressEl.setDisplayed(opt.progress === true);
3277             this.updateProgress(0);
3278             activeTextEl.dom.value = opt.value || "";
3279             if(opt.prompt){
3280                 dlg.setDefaultButton(activeTextEl);
3281             }else{
3282                 var bs = opt.buttons;
3283                 var db = null;
3284                 if(bs && bs.ok){
3285                     db = buttons["ok"];
3286                 }else if(bs && bs.yes){
3287                     db = buttons["yes"];
3288                 }
3289                 dlg.setDefaultButton(db);
3290             }
3291             bwidth = updateButtons(opt.buttons);
3292             this.updateText(opt.msg);
3293             if(opt.cls){
3294                 d.el.addClass(opt.cls);
3295             }
3296             d.proxyDrag = opt.proxyDrag === true;
3297             d.modal = opt.modal !== false;
3298             d.mask = opt.modal !== false ? mask : false;
3299             if(!d.isVisible()){
3300                 // force it to the end of the z-index stack so it gets a cursor in FF
3301                 document.body.appendChild(dlg.el.dom);
3302                 d.animateTarget = null;
3303                 d.show(options.animEl);
3304             }
3305             return this;
3306         },
3307
3308         /**
3309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311          * and closing the message box when the process is complete.
3312          * @param {String} title The title bar text
3313          * @param {String} msg The message box body text
3314          * @return {Roo.MessageBox} This message box
3315          */
3316         progress : function(title, msg){
3317             this.show({
3318                 title : title,
3319                 msg : msg,
3320                 buttons: false,
3321                 progress:true,
3322                 closable:false,
3323                 minWidth: this.minProgressWidth,
3324                 modal : true
3325             });
3326             return this;
3327         },
3328
3329         /**
3330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331          * If a callback function is passed it will be called after the user clicks the button, and the
3332          * id of the button that was clicked will be passed as the only parameter to the callback
3333          * (could also be the top-right close button).
3334          * @param {String} title The title bar text
3335          * @param {String} msg The message box body text
3336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337          * @param {Object} scope (optional) The scope of the callback function
3338          * @return {Roo.MessageBox} This message box
3339          */
3340         alert : function(title, msg, fn, scope)
3341         {
3342             this.show({
3343                 title : title,
3344                 msg : msg,
3345                 buttons: this.OK,
3346                 fn: fn,
3347                 closable : false,
3348                 scope : scope,
3349                 modal : true
3350             });
3351             return this;
3352         },
3353
3354         /**
3355          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3356          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357          * You are responsible for closing the message box when the process is complete.
3358          * @param {String} msg The message box body text
3359          * @param {String} title (optional) The title bar text
3360          * @return {Roo.MessageBox} This message box
3361          */
3362         wait : function(msg, title){
3363             this.show({
3364                 title : title,
3365                 msg : msg,
3366                 buttons: false,
3367                 closable:false,
3368                 progress:true,
3369                 modal:true,
3370                 width:300,
3371                 wait:true
3372             });
3373             waitTimer = Roo.TaskMgr.start({
3374                 run: function(i){
3375                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3376                 },
3377                 interval: 1000
3378             });
3379             return this;
3380         },
3381
3382         /**
3383          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386          * @param {String} title The title bar text
3387          * @param {String} msg The message box body text
3388          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389          * @param {Object} scope (optional) The scope of the callback function
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         confirm : function(title, msg, fn, scope){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: this.YESNO,
3397                 fn: fn,
3398                 scope : scope,
3399                 modal : true
3400             });
3401             return this;
3402         },
3403
3404         /**
3405          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3407          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408          * (could also be the top-right close button) and the text that was entered will be passed as the two
3409          * parameters to the callback.
3410          * @param {String} title The title bar text
3411          * @param {String} msg The message box body text
3412          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413          * @param {Object} scope (optional) The scope of the callback function
3414          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416          * @return {Roo.MessageBox} This message box
3417          */
3418         prompt : function(title, msg, fn, scope, multiline){
3419             this.show({
3420                 title : title,
3421                 msg : msg,
3422                 buttons: this.OKCANCEL,
3423                 fn: fn,
3424                 minWidth:250,
3425                 scope : scope,
3426                 prompt:true,
3427                 multiline: multiline,
3428                 modal : true
3429             });
3430             return this;
3431         },
3432
3433         /**
3434          * Button config that displays a single OK button
3435          * @type Object
3436          */
3437         OK : {ok:true},
3438         /**
3439          * Button config that displays Yes and No buttons
3440          * @type Object
3441          */
3442         YESNO : {yes:true, no:true},
3443         /**
3444          * Button config that displays OK and Cancel buttons
3445          * @type Object
3446          */
3447         OKCANCEL : {ok:true, cancel:true},
3448         /**
3449          * Button config that displays Yes, No and Cancel buttons
3450          * @type Object
3451          */
3452         YESNOCANCEL : {yes:true, no:true, cancel:true},
3453
3454         /**
3455          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3456          * @type Number
3457          */
3458         defaultTextHeight : 75,
3459         /**
3460          * The maximum width in pixels of the message box (defaults to 600)
3461          * @type Number
3462          */
3463         maxWidth : 600,
3464         /**
3465          * The minimum width in pixels of the message box (defaults to 100)
3466          * @type Number
3467          */
3468         minWidth : 100,
3469         /**
3470          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3471          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3472          * @type Number
3473          */
3474         minProgressWidth : 250,
3475         /**
3476          * An object containing the default button text strings that can be overriden for localized language support.
3477          * Supported properties are: ok, cancel, yes and no.
3478          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3479          * @type Object
3480          */
3481         buttonText : {
3482             ok : "OK",
3483             cancel : "Cancel",
3484             yes : "Yes",
3485             no : "No"
3486         }
3487     };
3488 }();
3489
3490 /**
3491  * Shorthand for {@link Roo.MessageBox}
3492  */
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3495 /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.Navbar
3504  * @extends Roo.bootstrap.Component
3505  * Bootstrap Navbar class
3506
3507  * @constructor
3508  * Create a new Navbar
3509  * @param {Object} config The config object
3510  */
3511
3512
3513 Roo.bootstrap.Navbar = function(config){
3514     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3515     this.addEvents({
3516         // raw events
3517         /**
3518          * @event beforetoggle
3519          * Fire before toggle the menu
3520          * @param {Roo.EventObject} e
3521          */
3522         "beforetoggle" : true
3523     });
3524 };
3525
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3527     
3528     
3529    
3530     // private
3531     navItems : false,
3532     loadMask : false,
3533     
3534     
3535     getAutoCreate : function(){
3536         
3537         
3538         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3539         
3540     },
3541     
3542     initEvents :function ()
3543     {
3544         //Roo.log(this.el.select('.navbar-toggle',true));
3545         this.el.select('.navbar-toggle',true).on('click', function() {
3546             if(this.fireEvent('beforetoggle', this) !== false){
3547                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3548             }
3549             
3550         }, this);
3551         
3552         var mark = {
3553             tag: "div",
3554             cls:"x-dlg-mask"
3555         };
3556         
3557         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3558         
3559         var size = this.el.getSize();
3560         this.maskEl.setSize(size.width, size.height);
3561         this.maskEl.enableDisplayMode("block");
3562         this.maskEl.hide();
3563         
3564         if(this.loadMask){
3565             this.maskEl.show();
3566         }
3567     },
3568     
3569     
3570     getChildContainer : function()
3571     {
3572         if (this.el.select('.collapse').getCount()) {
3573             return this.el.select('.collapse',true).first();
3574         }
3575         
3576         return this.el;
3577     },
3578     
3579     mask : function()
3580     {
3581         this.maskEl.show();
3582     },
3583     
3584     unmask : function()
3585     {
3586         this.maskEl.hide();
3587     } 
3588     
3589     
3590     
3591     
3592 });
3593
3594
3595
3596  
3597
3598  /*
3599  * - LGPL
3600  *
3601  * navbar
3602  * 
3603  */
3604
3605 /**
3606  * @class Roo.bootstrap.NavSimplebar
3607  * @extends Roo.bootstrap.Navbar
3608  * Bootstrap Sidebar class
3609  *
3610  * @cfg {Boolean} inverse is inverted color
3611  * 
3612  * @cfg {String} type (nav | pills | tabs)
3613  * @cfg {Boolean} arrangement stacked | justified
3614  * @cfg {String} align (left | right) alignment
3615  * 
3616  * @cfg {Boolean} main (true|false) main nav bar? default false
3617  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3618  * 
3619  * @cfg {String} tag (header|footer|nav|div) default is nav 
3620
3621  * 
3622  * 
3623  * 
3624  * @constructor
3625  * Create a new Sidebar
3626  * @param {Object} config The config object
3627  */
3628
3629
3630 Roo.bootstrap.NavSimplebar = function(config){
3631     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3632 };
3633
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3635     
3636     inverse: false,
3637     
3638     type: false,
3639     arrangement: '',
3640     align : false,
3641     
3642     
3643     
3644     main : false,
3645     
3646     
3647     tag : false,
3648     
3649     
3650     getAutoCreate : function(){
3651         
3652         
3653         var cfg = {
3654             tag : this.tag || 'div',
3655             cls : 'navbar'
3656         };
3657           
3658         
3659         cfg.cn = [
3660             {
3661                 cls: 'nav',
3662                 tag : 'ul'
3663             }
3664         ];
3665         
3666          
3667         this.type = this.type || 'nav';
3668         if (['tabs','pills'].indexOf(this.type)!==-1) {
3669             cfg.cn[0].cls += ' nav-' + this.type
3670         
3671         
3672         } else {
3673             if (this.type!=='nav') {
3674                 Roo.log('nav type must be nav/tabs/pills')
3675             }
3676             cfg.cn[0].cls += ' navbar-nav'
3677         }
3678         
3679         
3680         
3681         
3682         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683             cfg.cn[0].cls += ' nav-' + this.arrangement;
3684         }
3685         
3686         
3687         if (this.align === 'right') {
3688             cfg.cn[0].cls += ' navbar-right';
3689         }
3690         
3691         if (this.inverse) {
3692             cfg.cls += ' navbar-inverse';
3693             
3694         }
3695         
3696         
3697         return cfg;
3698     
3699         
3700     }
3701     
3702     
3703     
3704 });
3705
3706
3707
3708  
3709
3710  
3711        /*
3712  * - LGPL
3713  *
3714  * navbar
3715  * 
3716  */
3717
3718 /**
3719  * @class Roo.bootstrap.NavHeaderbar
3720  * @extends Roo.bootstrap.NavSimplebar
3721  * Bootstrap Sidebar class
3722  *
3723  * @cfg {String} brand what is brand
3724  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725  * @cfg {String} brand_href href of the brand
3726  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3727  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3730  * 
3731  * @constructor
3732  * Create a new Sidebar
3733  * @param {Object} config The config object
3734  */
3735
3736
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3739       
3740 };
3741
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3743     
3744     position: '',
3745     brand: '',
3746     brand_href: false,
3747     srButton : true,
3748     autohide : false,
3749     desktopCenter : false,
3750    
3751     
3752     getAutoCreate : function(){
3753         
3754         var   cfg = {
3755             tag: this.nav || 'nav',
3756             cls: 'navbar',
3757             role: 'navigation',
3758             cn: []
3759         };
3760         
3761         var cn = cfg.cn;
3762         if (this.desktopCenter) {
3763             cn.push({cls : 'container', cn : []});
3764             cn = cn[0].cn;
3765         }
3766         
3767         if(this.srButton){
3768             cn.push({
3769                 tag: 'div',
3770                 cls: 'navbar-header',
3771                 cn: [
3772                     {
3773                         tag: 'button',
3774                         type: 'button',
3775                         cls: 'navbar-toggle',
3776                         'data-toggle': 'collapse',
3777                         cn: [
3778                             {
3779                                 tag: 'span',
3780                                 cls: 'sr-only',
3781                                 html: 'Toggle navigation'
3782                             },
3783                             {
3784                                 tag: 'span',
3785                                 cls: 'icon-bar'
3786                             },
3787                             {
3788                                 tag: 'span',
3789                                 cls: 'icon-bar'
3790                             },
3791                             {
3792                                 tag: 'span',
3793                                 cls: 'icon-bar'
3794                             }
3795                         ]
3796                     }
3797                 ]
3798             });
3799         }
3800         
3801         cn.push({
3802             tag: 'div',
3803             cls: 'collapse navbar-collapse',
3804             cn : []
3805         });
3806         
3807         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3808         
3809         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810             cfg.cls += ' navbar-' + this.position;
3811             
3812             // tag can override this..
3813             
3814             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3815         }
3816         
3817         if (this.brand !== '') {
3818             cn[0].cn.push({
3819                 tag: 'a',
3820                 href: this.brand_href ? this.brand_href : '#',
3821                 cls: 'navbar-brand',
3822                 cn: [
3823                 this.brand
3824                 ]
3825             });
3826         }
3827         
3828         if(this.main){
3829             cfg.cls += ' main-nav';
3830         }
3831         
3832         
3833         return cfg;
3834
3835         
3836     },
3837     getHeaderChildContainer : function()
3838     {
3839         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840             return this.el.select('.navbar-header',true).first();
3841         }
3842         
3843         return this.getChildContainer();
3844     },
3845     
3846     
3847     initEvents : function()
3848     {
3849         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3850         
3851         if (this.autohide) {
3852             
3853             var prevScroll = 0;
3854             var ft = this.el;
3855             
3856             Roo.get(document).on('scroll',function(e) {
3857                 var ns = Roo.get(document).getScroll().top;
3858                 var os = prevScroll;
3859                 prevScroll = ns;
3860                 
3861                 if(ns > os){
3862                     ft.removeClass('slideDown');
3863                     ft.addClass('slideUp');
3864                     return;
3865                 }
3866                 ft.removeClass('slideUp');
3867                 ft.addClass('slideDown');
3868                  
3869               
3870           },this);
3871         }
3872     }    
3873     
3874 });
3875
3876
3877
3878  
3879
3880  /*
3881  * - LGPL
3882  *
3883  * navbar
3884  * 
3885  */
3886
3887 /**
3888  * @class Roo.bootstrap.NavSidebar
3889  * @extends Roo.bootstrap.Navbar
3890  * Bootstrap Sidebar class
3891  * 
3892  * @constructor
3893  * Create a new Sidebar
3894  * @param {Object} config The config object
3895  */
3896
3897
3898 Roo.bootstrap.NavSidebar = function(config){
3899     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3900 };
3901
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3903     
3904     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3905     
3906     getAutoCreate : function(){
3907         
3908         
3909         return  {
3910             tag: 'div',
3911             cls: 'sidebar sidebar-nav'
3912         };
3913     
3914         
3915     }
3916     
3917     
3918     
3919 });
3920
3921
3922
3923  
3924
3925  /*
3926  * - LGPL
3927  *
3928  * nav group
3929  * 
3930  */
3931
3932 /**
3933  * @class Roo.bootstrap.NavGroup
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap NavGroup class
3936  * @cfg {String} align (left|right)
3937  * @cfg {Boolean} inverse
3938  * @cfg {String} type (nav|pills|tab) default nav
3939  * @cfg {String} navId - reference Id for navbar.
3940
3941  * 
3942  * @constructor
3943  * Create a new nav group
3944  * @param {Object} config The config object
3945  */
3946
3947 Roo.bootstrap.NavGroup = function(config){
3948     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3949     this.navItems = [];
3950    
3951     Roo.bootstrap.NavGroup.register(this);
3952      this.addEvents({
3953         /**
3954              * @event changed
3955              * Fires when the active item changes
3956              * @param {Roo.bootstrap.NavGroup} this
3957              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3959          */
3960         'changed': true
3961      });
3962     
3963 };
3964
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3966     
3967     align: '',
3968     inverse: false,
3969     form: false,
3970     type: 'nav',
3971     navId : '',
3972     // private
3973     
3974     navItems : false, 
3975     
3976     getAutoCreate : function()
3977     {
3978         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3979         
3980         cfg = {
3981             tag : 'ul',
3982             cls: 'nav' 
3983         };
3984         
3985         if (['tabs','pills'].indexOf(this.type)!==-1) {
3986             cfg.cls += ' nav-' + this.type
3987         } else {
3988             if (this.type!=='nav') {
3989                 Roo.log('nav type must be nav/tabs/pills')
3990             }
3991             cfg.cls += ' navbar-nav'
3992         }
3993         
3994         if (this.parent().sidebar) {
3995             cfg = {
3996                 tag: 'ul',
3997                 cls: 'dashboard-menu sidebar-menu'
3998             };
3999             
4000             return cfg;
4001         }
4002         
4003         if (this.form === true) {
4004             cfg = {
4005                 tag: 'form',
4006                 cls: 'navbar-form'
4007             };
4008             
4009             if (this.align === 'right') {
4010                 cfg.cls += ' navbar-right';
4011             } else {
4012                 cfg.cls += ' navbar-left';
4013             }
4014         }
4015         
4016         if (this.align === 'right') {
4017             cfg.cls += ' navbar-right';
4018         }
4019         
4020         if (this.inverse) {
4021             cfg.cls += ' navbar-inverse';
4022             
4023         }
4024         
4025         
4026         return cfg;
4027     },
4028     /**
4029     * sets the active Navigation item
4030     * @param {Roo.bootstrap.NavItem} the new current navitem
4031     */
4032     setActiveItem : function(item)
4033     {
4034         var prev = false;
4035         Roo.each(this.navItems, function(v){
4036             if (v == item) {
4037                 return ;
4038             }
4039             if (v.isActive()) {
4040                 v.setActive(false, true);
4041                 prev = v;
4042                 
4043             }
4044             
4045         });
4046
4047         item.setActive(true, true);
4048         this.fireEvent('changed', this, item, prev);
4049         
4050         
4051     },
4052     /**
4053     * gets the active Navigation item
4054     * @return {Roo.bootstrap.NavItem} the current navitem
4055     */
4056     getActive : function()
4057     {
4058         
4059         var prev = false;
4060         Roo.each(this.navItems, function(v){
4061             
4062             if (v.isActive()) {
4063                 prev = v;
4064                 
4065             }
4066             
4067         });
4068         return prev;
4069     },
4070     
4071     indexOfNav : function()
4072     {
4073         
4074         var prev = false;
4075         Roo.each(this.navItems, function(v,i){
4076             
4077             if (v.isActive()) {
4078                 prev = i;
4079                 
4080             }
4081             
4082         });
4083         return prev;
4084     },
4085     /**
4086     * adds a Navigation item
4087     * @param {Roo.bootstrap.NavItem} the navitem to add
4088     */
4089     addItem : function(cfg)
4090     {
4091         var cn = new Roo.bootstrap.NavItem(cfg);
4092         this.register(cn);
4093         cn.parentId = this.id;
4094         cn.onRender(this.el, null);
4095         return cn;
4096     },
4097     /**
4098     * register a Navigation item
4099     * @param {Roo.bootstrap.NavItem} the navitem to add
4100     */
4101     register : function(item)
4102     {
4103         this.navItems.push( item);
4104         item.navId = this.navId;
4105     
4106     },
4107     
4108     /**
4109     * clear all the Navigation item
4110     */
4111    
4112     clearAll : function()
4113     {
4114         this.navItems = [];
4115         this.el.dom.innerHTML = '';
4116     },
4117     
4118     getNavItem: function(tabId)
4119     {
4120         var ret = false;
4121         Roo.each(this.navItems, function(e) {
4122             if (e.tabId == tabId) {
4123                ret =  e;
4124                return false;
4125             }
4126             return true;
4127             
4128         });
4129         return ret;
4130     },
4131     
4132     setActiveNext : function()
4133     {
4134         var i = this.indexOfNav(this.getActive());
4135         if (i > this.navItems.length) {
4136             return;
4137         }
4138         this.setActiveItem(this.navItems[i+1]);
4139     },
4140     setActivePrev : function()
4141     {
4142         var i = this.indexOfNav(this.getActive());
4143         if (i  < 1) {
4144             return;
4145         }
4146         this.setActiveItem(this.navItems[i-1]);
4147     },
4148     clearWasActive : function(except) {
4149         Roo.each(this.navItems, function(e) {
4150             if (e.tabId != except.tabId && e.was_active) {
4151                e.was_active = false;
4152                return false;
4153             }
4154             return true;
4155             
4156         });
4157     },
4158     getWasActive : function ()
4159     {
4160         var r = false;
4161         Roo.each(this.navItems, function(e) {
4162             if (e.was_active) {
4163                r = e;
4164                return false;
4165             }
4166             return true;
4167             
4168         });
4169         return r;
4170     }
4171     
4172     
4173 });
4174
4175  
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4177     
4178     groups: {},
4179      /**
4180     * register a Navigation Group
4181     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4182     */
4183     register : function(navgrp)
4184     {
4185         this.groups[navgrp.navId] = navgrp;
4186         
4187     },
4188     /**
4189     * fetch a Navigation Group based on the navigation ID
4190     * @param {string} the navgroup to add
4191     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4192     */
4193     get: function(navId) {
4194         if (typeof(this.groups[navId]) == 'undefined') {
4195             return false;
4196             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4197         }
4198         return this.groups[navId] ;
4199     }
4200     
4201     
4202     
4203 });
4204
4205  /*
4206  * - LGPL
4207  *
4208  * row
4209  * 
4210  */
4211
4212 /**
4213  * @class Roo.bootstrap.NavItem
4214  * @extends Roo.bootstrap.Component
4215  * Bootstrap Navbar.NavItem class
4216  * @cfg {String} href  link to
4217  * @cfg {String} html content of button
4218  * @cfg {String} badge text inside badge
4219  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220  * @cfg {String} glyphicon name of glyphicon
4221  * @cfg {String} icon name of font awesome icon
4222  * @cfg {Boolean} active Is item active
4223  * @cfg {Boolean} disabled Is item disabled
4224  
4225  * @cfg {Boolean} preventDefault (true | false) default false
4226  * @cfg {String} tabId the tab that this item activates.
4227  * @cfg {String} tagtype (a|span) render as a href or span?
4228  * @cfg {Boolean} animateRef (true|false) link to element default false  
4229   
4230  * @constructor
4231  * Create a new Navbar Item
4232  * @param {Object} config The config object
4233  */
4234 Roo.bootstrap.NavItem = function(config){
4235     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4236     this.addEvents({
4237         // raw events
4238         /**
4239          * @event click
4240          * The raw click event for the entire grid.
4241          * @param {Roo.EventObject} e
4242          */
4243         "click" : true,
4244          /**
4245             * @event changed
4246             * Fires when the active item active state changes
4247             * @param {Roo.bootstrap.NavItem} this
4248             * @param {boolean} state the new state
4249              
4250          */
4251         'changed': true,
4252         /**
4253             * @event scrollto
4254             * Fires when scroll to element
4255             * @param {Roo.bootstrap.NavItem} this
4256             * @param {Object} options
4257             * @param {Roo.EventObject} e
4258              
4259          */
4260         'scrollto': true
4261     });
4262    
4263 };
4264
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4266     
4267     href: false,
4268     html: '',
4269     badge: '',
4270     icon: false,
4271     glyphicon: false,
4272     active: false,
4273     preventDefault : false,
4274     tabId : false,
4275     tagtype : 'a',
4276     disabled : false,
4277     animateRef : false,
4278     was_active : false,
4279     
4280     getAutoCreate : function(){
4281          
4282         var cfg = {
4283             tag: 'li',
4284             cls: 'nav-item'
4285             
4286         };
4287         
4288         if (this.active) {
4289             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4290         }
4291         if (this.disabled) {
4292             cfg.cls += ' disabled';
4293         }
4294         
4295         if (this.href || this.html || this.glyphicon || this.icon) {
4296             cfg.cn = [
4297                 {
4298                     tag: this.tagtype,
4299                     href : this.href || "#",
4300                     html: this.html || ''
4301                 }
4302             ];
4303             
4304             if (this.icon) {
4305                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4306             }
4307
4308             if(this.glyphicon) {
4309                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4310             }
4311             
4312             if (this.menu) {
4313                 
4314                 cfg.cn[0].html += " <span class='caret'></span>";
4315              
4316             }
4317             
4318             if (this.badge !== '') {
4319                  
4320                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4321             }
4322         }
4323         
4324         
4325         
4326         return cfg;
4327     },
4328     initEvents: function() 
4329     {
4330         if (typeof (this.menu) != 'undefined') {
4331             this.menu.parentType = this.xtype;
4332             this.menu.triggerEl = this.el;
4333             this.menu = this.addxtype(Roo.apply({}, this.menu));
4334         }
4335         
4336         this.el.select('a',true).on('click', this.onClick, this);
4337         
4338         if(this.tagtype == 'span'){
4339             this.el.select('span',true).on('click', this.onClick, this);
4340         }
4341        
4342         // at this point parent should be available..
4343         this.parent().register(this);
4344     },
4345     
4346     onClick : function(e)
4347     {
4348         if (e.getTarget('.dropdown-menu-item')) {
4349             // did you click on a menu itemm.... - then don't trigger onclick..
4350             return;
4351         }
4352         
4353         if(
4354                 this.preventDefault || 
4355                 this.href == '#' 
4356         ){
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359         }
4360         
4361         if (this.disabled) {
4362             return;
4363         }
4364         
4365         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366         if (tg && tg.transition) {
4367             Roo.log("waiting for the transitionend");
4368             return;
4369         }
4370         
4371         
4372         
4373         //Roo.log("fire event clicked");
4374         if(this.fireEvent('click', this, e) === false){
4375             return;
4376         };
4377         
4378         if(this.tagtype == 'span'){
4379             return;
4380         }
4381         
4382         //Roo.log(this.href);
4383         var ael = this.el.select('a',true).first();
4384         //Roo.log(ael);
4385         
4386         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389                 return; // ignore... - it's a 'hash' to another page.
4390             }
4391             Roo.log("NavItem - prevent Default?");
4392             e.preventDefault();
4393             this.scrollToElement(e);
4394         }
4395         
4396         
4397         var p =  this.parent();
4398    
4399         if (['tabs','pills'].indexOf(p.type)!==-1) {
4400             if (typeof(p.setActiveItem) !== 'undefined') {
4401                 p.setActiveItem(this);
4402             }
4403         }
4404         
4405         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407             // remove the collapsed menu expand...
4408             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4409         }
4410     },
4411     
4412     isActive: function () {
4413         return this.active
4414     },
4415     setActive : function(state, fire, is_was_active)
4416     {
4417         if (this.active && !state && this.navId) {
4418             this.was_active = true;
4419             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420             if (nv) {
4421                 nv.clearWasActive(this);
4422             }
4423             
4424         }
4425         this.active = state;
4426         
4427         if (!state ) {
4428             this.el.removeClass('active');
4429         } else if (!this.el.hasClass('active')) {
4430             this.el.addClass('active');
4431         }
4432         if (fire) {
4433             this.fireEvent('changed', this, state);
4434         }
4435         
4436         // show a panel if it's registered and related..
4437         
4438         if (!this.navId || !this.tabId || !state || is_was_active) {
4439             return;
4440         }
4441         
4442         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4443         if (!tg) {
4444             return;
4445         }
4446         var pan = tg.getPanelByName(this.tabId);
4447         if (!pan) {
4448             return;
4449         }
4450         // if we can not flip to new panel - go back to old nav highlight..
4451         if (false == tg.showPanel(pan)) {
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 var onav = nv.getWasActive();
4455                 if (onav) {
4456                     onav.setActive(true, false, true);
4457                 }
4458             }
4459             
4460         }
4461         
4462         
4463         
4464     },
4465      // this should not be here...
4466     setDisabled : function(state)
4467     {
4468         this.disabled = state;
4469         if (!state ) {
4470             this.el.removeClass('disabled');
4471         } else if (!this.el.hasClass('disabled')) {
4472             this.el.addClass('disabled');
4473         }
4474         
4475     },
4476     
4477     /**
4478      * Fetch the element to display the tooltip on.
4479      * @return {Roo.Element} defaults to this.el
4480      */
4481     tooltipEl : function()
4482     {
4483         return this.el.select('' + this.tagtype + '', true).first();
4484     },
4485     
4486     scrollToElement : function(e)
4487     {
4488         var c = document.body;
4489         
4490         /*
4491          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4492          */
4493         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494             c = document.documentElement;
4495         }
4496         
4497         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4498         
4499         if(!target){
4500             return;
4501         }
4502
4503         var o = target.calcOffsetsTo(c);
4504         
4505         var options = {
4506             target : target,
4507             value : o[1]
4508         };
4509         
4510         this.fireEvent('scrollto', this, options, e);
4511         
4512         Roo.get(c).scrollTo('top', options.value, true);
4513         
4514         return;
4515     }
4516 });
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * sidebar item
4523  *
4524  *  li
4525  *    <span> icon </span>
4526  *    <span> text </span>
4527  *    <span>badge </span>
4528  */
4529
4530 /**
4531  * @class Roo.bootstrap.NavSidebarItem
4532  * @extends Roo.bootstrap.NavItem
4533  * Bootstrap Navbar.NavSidebarItem class
4534  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535  * {bool} open is the menu open
4536  * @constructor
4537  * Create a new Navbar Button
4538  * @param {Object} config The config object
4539  */
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4542     this.addEvents({
4543         // raw events
4544         /**
4545          * @event click
4546          * The raw click event for the entire grid.
4547          * @param {Roo.EventObject} e
4548          */
4549         "click" : true,
4550          /**
4551             * @event changed
4552             * Fires when the active item active state changes
4553             * @param {Roo.bootstrap.NavSidebarItem} this
4554             * @param {boolean} state the new state
4555              
4556          */
4557         'changed': true
4558     });
4559    
4560 };
4561
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4563     
4564     badgeWeight : 'default',
4565     
4566     open: false,
4567     
4568     getAutoCreate : function(){
4569         
4570         
4571         var a = {
4572                 tag: 'a',
4573                 href : this.href || '#',
4574                 cls: '',
4575                 html : '',
4576                 cn : []
4577         };
4578         var cfg = {
4579             tag: 'li',
4580             cls: '',
4581             cn: [ a ]
4582         };
4583         var span = {
4584             tag: 'span',
4585             html : this.html || ''
4586         };
4587         
4588         
4589         if (this.active) {
4590             cfg.cls += ' active';
4591         }
4592         
4593         if (this.disabled) {
4594             cfg.cls += ' disabled';
4595         }
4596         if (this.open) {
4597             cfg.cls += ' open x-open';
4598         }
4599         // left icon..
4600         if (this.glyphicon || this.icon) {
4601             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4602             a.cn.push({ tag : 'i', cls : c }) ;
4603         }
4604         // html..
4605         a.cn.push(span);
4606         // then badge..
4607         if (this.badge !== '') {
4608             
4609             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4610         }
4611         // fi
4612         if (this.menu) {
4613             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614             a.cls += 'dropdown-toggle treeview' ;
4615         }
4616         
4617         return cfg;
4618          
4619            
4620     },
4621     
4622     initEvents : function()
4623     { 
4624         if (typeof (this.menu) != 'undefined') {
4625             this.menu.parentType = this.xtype;
4626             this.menu.triggerEl = this.el;
4627             this.menu = this.addxtype(Roo.apply({}, this.menu));
4628         }
4629         
4630         this.el.on('click', this.onClick, this);
4631        
4632     
4633         if(this.badge !== ''){
4634  
4635             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4636         }
4637         
4638     },
4639     
4640     onClick : function(e)
4641     {
4642         if(this.disabled){
4643             e.preventDefault();
4644             return;
4645         }
4646         
4647         if(this.preventDefault){
4648             e.preventDefault();
4649         }
4650         
4651         this.fireEvent('click', this);
4652     },
4653     
4654     disable : function()
4655     {
4656         this.setDisabled(true);
4657     },
4658     
4659     enable : function()
4660     {
4661         this.setDisabled(false);
4662     },
4663     
4664     setDisabled : function(state)
4665     {
4666         if(this.disabled == state){
4667             return;
4668         }
4669         
4670         this.disabled = state;
4671         
4672         if (state) {
4673             this.el.addClass('disabled');
4674             return;
4675         }
4676         
4677         this.el.removeClass('disabled');
4678         
4679         return;
4680     },
4681     
4682     setActive : function(state)
4683     {
4684         if(this.active == state){
4685             return;
4686         }
4687         
4688         this.active = state;
4689         
4690         if (state) {
4691             this.el.addClass('active');
4692             return;
4693         }
4694         
4695         this.el.removeClass('active');
4696         
4697         return;
4698     },
4699     
4700     isActive: function () 
4701     {
4702         return this.active;
4703     },
4704     
4705     setBadge : function(str)
4706     {
4707         if(!this.badgeEl){
4708             return;
4709         }
4710         
4711         this.badgeEl.dom.innerHTML = str;
4712     }
4713     
4714    
4715      
4716  
4717 });
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * row
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Row
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Row class (contains columns...)
4731  * 
4732  * @constructor
4733  * Create a new Row
4734  * @param {Object} config The config object
4735  */
4736
4737 Roo.bootstrap.Row = function(config){
4738     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4739 };
4740
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4742     
4743     getAutoCreate : function(){
4744        return {
4745             cls: 'row clearfix'
4746        };
4747     }
4748     
4749     
4750 });
4751
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * element
4758  * 
4759  */
4760
4761 /**
4762  * @class Roo.bootstrap.Element
4763  * @extends Roo.bootstrap.Component
4764  * Bootstrap Element class
4765  * @cfg {String} html contents of the element
4766  * @cfg {String} tag tag of the element
4767  * @cfg {String} cls class of the element
4768  * @cfg {Boolean} preventDefault (true|false) default false
4769  * @cfg {Boolean} clickable (true|false) default false
4770  * 
4771  * @constructor
4772  * Create a new Element
4773  * @param {Object} config The config object
4774  */
4775
4776 Roo.bootstrap.Element = function(config){
4777     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4778     
4779     this.addEvents({
4780         // raw events
4781         /**
4782          * @event click
4783          * When a element is chick
4784          * @param {Roo.bootstrap.Element} this
4785          * @param {Roo.EventObject} e
4786          */
4787         "click" : true
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4792     
4793     tag: 'div',
4794     cls: '',
4795     html: '',
4796     preventDefault: false, 
4797     clickable: false,
4798     
4799     getAutoCreate : function(){
4800         
4801         var cfg = {
4802             tag: this.tag,
4803             cls: this.cls,
4804             html: this.html
4805         };
4806         
4807         return cfg;
4808     },
4809     
4810     initEvents: function() 
4811     {
4812         Roo.bootstrap.Element.superclass.initEvents.call(this);
4813         
4814         if(this.clickable){
4815             this.el.on('click', this.onClick, this);
4816         }
4817         
4818     },
4819     
4820     onClick : function(e)
4821     {
4822         if(this.preventDefault){
4823             e.preventDefault();
4824         }
4825         
4826         this.fireEvent('click', this, e);
4827     },
4828     
4829     getValue : function()
4830     {
4831         return this.el.dom.innerHTML;
4832     },
4833     
4834     setValue : function(value)
4835     {
4836         this.el.dom.innerHTML = value;
4837     }
4838    
4839 });
4840
4841  
4842
4843  /*
4844  * - LGPL
4845  *
4846  * pagination
4847  * 
4848  */
4849
4850 /**
4851  * @class Roo.bootstrap.Pagination
4852  * @extends Roo.bootstrap.Component
4853  * Bootstrap Pagination class
4854  * @cfg {String} size xs | sm | md | lg
4855  * @cfg {Boolean} inverse false | true
4856  * 
4857  * @constructor
4858  * Create a new Pagination
4859  * @param {Object} config The config object
4860  */
4861
4862 Roo.bootstrap.Pagination = function(config){
4863     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4864 };
4865
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4867     
4868     cls: false,
4869     size: false,
4870     inverse: false,
4871     
4872     getAutoCreate : function(){
4873         var cfg = {
4874             tag: 'ul',
4875                 cls: 'pagination'
4876         };
4877         if (this.inverse) {
4878             cfg.cls += ' inverse';
4879         }
4880         if (this.html) {
4881             cfg.html=this.html;
4882         }
4883         if (this.cls) {
4884             cfg.cls += " " + this.cls;
4885         }
4886         return cfg;
4887     }
4888    
4889 });
4890
4891  
4892
4893  /*
4894  * - LGPL
4895  *
4896  * Pagination item
4897  * 
4898  */
4899
4900
4901 /**
4902  * @class Roo.bootstrap.PaginationItem
4903  * @extends Roo.bootstrap.Component
4904  * Bootstrap PaginationItem class
4905  * @cfg {String} html text
4906  * @cfg {String} href the link
4907  * @cfg {Boolean} preventDefault (true | false) default true
4908  * @cfg {Boolean} active (true | false) default false
4909  * @cfg {Boolean} disabled default false
4910  * 
4911  * 
4912  * @constructor
4913  * Create a new PaginationItem
4914  * @param {Object} config The config object
4915  */
4916
4917
4918 Roo.bootstrap.PaginationItem = function(config){
4919     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4920     this.addEvents({
4921         // raw events
4922         /**
4923          * @event click
4924          * The raw click event for the entire grid.
4925          * @param {Roo.EventObject} e
4926          */
4927         "click" : true
4928     });
4929 };
4930
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4932     
4933     href : false,
4934     html : false,
4935     preventDefault: true,
4936     active : false,
4937     cls : false,
4938     disabled: false,
4939     
4940     getAutoCreate : function(){
4941         var cfg= {
4942             tag: 'li',
4943             cn: [
4944                 {
4945                     tag : 'a',
4946                     href : this.href ? this.href : '#',
4947                     html : this.html ? this.html : ''
4948                 }
4949             ]
4950         };
4951         
4952         if(this.cls){
4953             cfg.cls = this.cls;
4954         }
4955         
4956         if(this.disabled){
4957             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4958         }
4959         
4960         if(this.active){
4961             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4962         }
4963         
4964         return cfg;
4965     },
4966     
4967     initEvents: function() {
4968         
4969         this.el.on('click', this.onClick, this);
4970         
4971     },
4972     onClick : function(e)
4973     {
4974         Roo.log('PaginationItem on click ');
4975         if(this.preventDefault){
4976             e.preventDefault();
4977         }
4978         
4979         if(this.disabled){
4980             return;
4981         }
4982         
4983         this.fireEvent('click', this, e);
4984     }
4985    
4986 });
4987
4988  
4989
4990  /*
4991  * - LGPL
4992  *
4993  * slider
4994  * 
4995  */
4996
4997
4998 /**
4999  * @class Roo.bootstrap.Slider
5000  * @extends Roo.bootstrap.Component
5001  * Bootstrap Slider class
5002  *    
5003  * @constructor
5004  * Create a new Slider
5005  * @param {Object} config The config object
5006  */
5007
5008 Roo.bootstrap.Slider = function(config){
5009     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5010 };
5011
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5013     
5014     getAutoCreate : function(){
5015         
5016         var cfg = {
5017             tag: 'div',
5018             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5019             cn: [
5020                 {
5021                     tag: 'a',
5022                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5023                 }
5024             ]
5025         };
5026         
5027         return cfg;
5028     }
5029    
5030 });
5031
5032  /*
5033  * Based on:
5034  * Ext JS Library 1.1.1
5035  * Copyright(c) 2006-2007, Ext JS, LLC.
5036  *
5037  * Originally Released Under LGPL - original licence link has changed is not relivant.
5038  *
5039  * Fork - LGPL
5040  * <script type="text/javascript">
5041  */
5042  
5043
5044 /**
5045  * @class Roo.grid.ColumnModel
5046  * @extends Roo.util.Observable
5047  * This is the default implementation of a ColumnModel used by the Grid. It defines
5048  * the columns in the grid.
5049  * <br>Usage:<br>
5050  <pre><code>
5051  var colModel = new Roo.grid.ColumnModel([
5052         {header: "Ticker", width: 60, sortable: true, locked: true},
5053         {header: "Company Name", width: 150, sortable: true},
5054         {header: "Market Cap.", width: 100, sortable: true},
5055         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056         {header: "Employees", width: 100, sortable: true, resizable: false}
5057  ]);
5058  </code></pre>
5059  * <p>
5060  
5061  * The config options listed for this class are options which may appear in each
5062  * individual column definition.
5063  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5064  * @constructor
5065  * @param {Object} config An Array of column config objects. See this class's
5066  * config objects for details.
5067 */
5068 Roo.grid.ColumnModel = function(config){
5069         /**
5070      * The config passed into the constructor
5071      */
5072     this.config = config;
5073     this.lookup = {};
5074
5075     // if no id, create one
5076     // if the column does not have a dataIndex mapping,
5077     // map it to the order it is in the config
5078     for(var i = 0, len = config.length; i < len; i++){
5079         var c = config[i];
5080         if(typeof c.dataIndex == "undefined"){
5081             c.dataIndex = i;
5082         }
5083         if(typeof c.renderer == "string"){
5084             c.renderer = Roo.util.Format[c.renderer];
5085         }
5086         if(typeof c.id == "undefined"){
5087             c.id = Roo.id();
5088         }
5089         if(c.editor && c.editor.xtype){
5090             c.editor  = Roo.factory(c.editor, Roo.grid);
5091         }
5092         if(c.editor && c.editor.isFormField){
5093             c.editor = new Roo.grid.GridEditor(c.editor);
5094         }
5095         this.lookup[c.id] = c;
5096     }
5097
5098     /**
5099      * The width of columns which have no width specified (defaults to 100)
5100      * @type Number
5101      */
5102     this.defaultWidth = 100;
5103
5104     /**
5105      * Default sortable of columns which have no sortable specified (defaults to false)
5106      * @type Boolean
5107      */
5108     this.defaultSortable = false;
5109
5110     this.addEvents({
5111         /**
5112              * @event widthchange
5113              * Fires when the width of a column changes.
5114              * @param {ColumnModel} this
5115              * @param {Number} columnIndex The column index
5116              * @param {Number} newWidth The new width
5117              */
5118             "widthchange": true,
5119         /**
5120              * @event headerchange
5121              * Fires when the text of a header changes.
5122              * @param {ColumnModel} this
5123              * @param {Number} columnIndex The column index
5124              * @param {Number} newText The new header text
5125              */
5126             "headerchange": true,
5127         /**
5128              * @event hiddenchange
5129              * Fires when a column is hidden or "unhidden".
5130              * @param {ColumnModel} this
5131              * @param {Number} columnIndex The column index
5132              * @param {Boolean} hidden true if hidden, false otherwise
5133              */
5134             "hiddenchange": true,
5135             /**
5136          * @event columnmoved
5137          * Fires when a column is moved.
5138          * @param {ColumnModel} this
5139          * @param {Number} oldIndex
5140          * @param {Number} newIndex
5141          */
5142         "columnmoved" : true,
5143         /**
5144          * @event columlockchange
5145          * Fires when a column's locked state is changed
5146          * @param {ColumnModel} this
5147          * @param {Number} colIndex
5148          * @param {Boolean} locked true if locked
5149          */
5150         "columnlockchange" : true
5151     });
5152     Roo.grid.ColumnModel.superclass.constructor.call(this);
5153 };
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5155     /**
5156      * @cfg {String} header The header text to display in the Grid view.
5157      */
5158     /**
5159      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161      * specified, the column's index is used as an index into the Record's data Array.
5162      */
5163     /**
5164      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5166      */
5167     /**
5168      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169      * Defaults to the value of the {@link #defaultSortable} property.
5170      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5171      */
5172     /**
5173      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5174      */
5175     /**
5176      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5177      */
5178     /**
5179      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5180      */
5181     /**
5182      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5183      */
5184     /**
5185      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5189      */
5190        /**
5191      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5192      */
5193     /**
5194      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5195      */
5196     /**
5197      * @cfg {String} cursor (Optional)
5198      */
5199     /**
5200      * @cfg {String} tooltip (Optional)
5201      */
5202     /**
5203      * @cfg {Number} xs (Optional)
5204      */
5205     /**
5206      * @cfg {Number} sm (Optional)
5207      */
5208     /**
5209      * @cfg {Number} md (Optional)
5210      */
5211     /**
5212      * @cfg {Number} lg (Optional)
5213      */
5214     /**
5215      * Returns the id of the column at the specified index.
5216      * @param {Number} index The column index
5217      * @return {String} the id
5218      */
5219     getColumnId : function(index){
5220         return this.config[index].id;
5221     },
5222
5223     /**
5224      * Returns the column for a specified id.
5225      * @param {String} id The column id
5226      * @return {Object} the column
5227      */
5228     getColumnById : function(id){
5229         return this.lookup[id];
5230     },
5231
5232     
5233     /**
5234      * Returns the column for a specified dataIndex.
5235      * @param {String} dataIndex The column dataIndex
5236      * @return {Object|Boolean} the column or false if not found
5237      */
5238     getColumnByDataIndex: function(dataIndex){
5239         var index = this.findColumnIndex(dataIndex);
5240         return index > -1 ? this.config[index] : false;
5241     },
5242     
5243     /**
5244      * Returns the index for a specified column id.
5245      * @param {String} id The column id
5246      * @return {Number} the index, or -1 if not found
5247      */
5248     getIndexById : function(id){
5249         for(var i = 0, len = this.config.length; i < len; i++){
5250             if(this.config[i].id == id){
5251                 return i;
5252             }
5253         }
5254         return -1;
5255     },
5256     
5257     /**
5258      * Returns the index for a specified column dataIndex.
5259      * @param {String} dataIndex The column dataIndex
5260      * @return {Number} the index, or -1 if not found
5261      */
5262     
5263     findColumnIndex : function(dataIndex){
5264         for(var i = 0, len = this.config.length; i < len; i++){
5265             if(this.config[i].dataIndex == dataIndex){
5266                 return i;
5267             }
5268         }
5269         return -1;
5270     },
5271     
5272     
5273     moveColumn : function(oldIndex, newIndex){
5274         var c = this.config[oldIndex];
5275         this.config.splice(oldIndex, 1);
5276         this.config.splice(newIndex, 0, c);
5277         this.dataMap = null;
5278         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5279     },
5280
5281     isLocked : function(colIndex){
5282         return this.config[colIndex].locked === true;
5283     },
5284
5285     setLocked : function(colIndex, value, suppressEvent){
5286         if(this.isLocked(colIndex) == value){
5287             return;
5288         }
5289         this.config[colIndex].locked = value;
5290         if(!suppressEvent){
5291             this.fireEvent("columnlockchange", this, colIndex, value);
5292         }
5293     },
5294
5295     getTotalLockedWidth : function(){
5296         var totalWidth = 0;
5297         for(var i = 0; i < this.config.length; i++){
5298             if(this.isLocked(i) && !this.isHidden(i)){
5299                 this.totalWidth += this.getColumnWidth(i);
5300             }
5301         }
5302         return totalWidth;
5303     },
5304
5305     getLockedCount : function(){
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             if(!this.isLocked(i)){
5308                 return i;
5309             }
5310         }
5311         
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the number of columns.
5317      * @return {Number}
5318      */
5319     getColumnCount : function(visibleOnly){
5320         if(visibleOnly === true){
5321             var c = 0;
5322             for(var i = 0, len = this.config.length; i < len; i++){
5323                 if(!this.isHidden(i)){
5324                     c++;
5325                 }
5326             }
5327             return c;
5328         }
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334      * @param {Function} fn
5335      * @param {Object} scope (optional)
5336      * @return {Array} result
5337      */
5338     getColumnsBy : function(fn, scope){
5339         var r = [];
5340         for(var i = 0, len = this.config.length; i < len; i++){
5341             var c = this.config[i];
5342             if(fn.call(scope||this, c, i) === true){
5343                 r[r.length] = c;
5344             }
5345         }
5346         return r;
5347     },
5348
5349     /**
5350      * Returns true if the specified column is sortable.
5351      * @param {Number} col The column index
5352      * @return {Boolean}
5353      */
5354     isSortable : function(col){
5355         if(typeof this.config[col].sortable == "undefined"){
5356             return this.defaultSortable;
5357         }
5358         return this.config[col].sortable;
5359     },
5360
5361     /**
5362      * Returns the rendering (formatting) function defined for the column.
5363      * @param {Number} col The column index.
5364      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5365      */
5366     getRenderer : function(col){
5367         if(!this.config[col].renderer){
5368             return Roo.grid.ColumnModel.defaultRenderer;
5369         }
5370         return this.config[col].renderer;
5371     },
5372
5373     /**
5374      * Sets the rendering (formatting) function for a column.
5375      * @param {Number} col The column index
5376      * @param {Function} fn The function to use to process the cell's raw data
5377      * to return HTML markup for the grid view. The render function is called with
5378      * the following parameters:<ul>
5379      * <li>Data value.</li>
5380      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381      * <li>css A CSS style string to apply to the table cell.</li>
5382      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384      * <li>Row index</li>
5385      * <li>Column index</li>
5386      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5387      */
5388     setRenderer : function(col, fn){
5389         this.config[col].renderer = fn;
5390     },
5391
5392     /**
5393      * Returns the width for the specified column.
5394      * @param {Number} col The column index
5395      * @return {Number}
5396      */
5397     getColumnWidth : function(col){
5398         return this.config[col].width * 1 || this.defaultWidth;
5399     },
5400
5401     /**
5402      * Sets the width for a column.
5403      * @param {Number} col The column index
5404      * @param {Number} width The new width
5405      */
5406     setColumnWidth : function(col, width, suppressEvent){
5407         this.config[col].width = width;
5408         this.totalWidth = null;
5409         if(!suppressEvent){
5410              this.fireEvent("widthchange", this, col, width);
5411         }
5412     },
5413
5414     /**
5415      * Returns the total width of all columns.
5416      * @param {Boolean} includeHidden True to include hidden column widths
5417      * @return {Number}
5418      */
5419     getTotalWidth : function(includeHidden){
5420         if(!this.totalWidth){
5421             this.totalWidth = 0;
5422             for(var i = 0, len = this.config.length; i < len; i++){
5423                 if(includeHidden || !this.isHidden(i)){
5424                     this.totalWidth += this.getColumnWidth(i);
5425                 }
5426             }
5427         }
5428         return this.totalWidth;
5429     },
5430
5431     /**
5432      * Returns the header for the specified column.
5433      * @param {Number} col The column index
5434      * @return {String}
5435      */
5436     getColumnHeader : function(col){
5437         return this.config[col].header;
5438     },
5439
5440     /**
5441      * Sets the header for a column.
5442      * @param {Number} col The column index
5443      * @param {String} header The new header
5444      */
5445     setColumnHeader : function(col, header){
5446         this.config[col].header = header;
5447         this.fireEvent("headerchange", this, col, header);
5448     },
5449
5450     /**
5451      * Returns the tooltip for the specified column.
5452      * @param {Number} col The column index
5453      * @return {String}
5454      */
5455     getColumnTooltip : function(col){
5456             return this.config[col].tooltip;
5457     },
5458     /**
5459      * Sets the tooltip for a column.
5460      * @param {Number} col The column index
5461      * @param {String} tooltip The new tooltip
5462      */
5463     setColumnTooltip : function(col, tooltip){
5464             this.config[col].tooltip = tooltip;
5465     },
5466
5467     /**
5468      * Returns the dataIndex for the specified column.
5469      * @param {Number} col The column index
5470      * @return {Number}
5471      */
5472     getDataIndex : function(col){
5473         return this.config[col].dataIndex;
5474     },
5475
5476     /**
5477      * Sets the dataIndex for a column.
5478      * @param {Number} col The column index
5479      * @param {Number} dataIndex The new dataIndex
5480      */
5481     setDataIndex : function(col, dataIndex){
5482         this.config[col].dataIndex = dataIndex;
5483     },
5484
5485     
5486     
5487     /**
5488      * Returns true if the cell is editable.
5489      * @param {Number} colIndex The column index
5490      * @param {Number} rowIndex The row index - this is nto actually used..?
5491      * @return {Boolean}
5492      */
5493     isCellEditable : function(colIndex, rowIndex){
5494         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5495     },
5496
5497     /**
5498      * Returns the editor defined for the cell/column.
5499      * return false or null to disable editing.
5500      * @param {Number} colIndex The column index
5501      * @param {Number} rowIndex The row index
5502      * @return {Object}
5503      */
5504     getCellEditor : function(colIndex, rowIndex){
5505         return this.config[colIndex].editor;
5506     },
5507
5508     /**
5509      * Sets if a column is editable.
5510      * @param {Number} col The column index
5511      * @param {Boolean} editable True if the column is editable
5512      */
5513     setEditable : function(col, editable){
5514         this.config[col].editable = editable;
5515     },
5516
5517
5518     /**
5519      * Returns true if the column is hidden.
5520      * @param {Number} colIndex The column index
5521      * @return {Boolean}
5522      */
5523     isHidden : function(colIndex){
5524         return this.config[colIndex].hidden;
5525     },
5526
5527
5528     /**
5529      * Returns true if the column width cannot be changed
5530      */
5531     isFixed : function(colIndex){
5532         return this.config[colIndex].fixed;
5533     },
5534
5535     /**
5536      * Returns true if the column can be resized
5537      * @return {Boolean}
5538      */
5539     isResizable : function(colIndex){
5540         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5541     },
5542     /**
5543      * Sets if a column is hidden.
5544      * @param {Number} colIndex The column index
5545      * @param {Boolean} hidden True if the column is hidden
5546      */
5547     setHidden : function(colIndex, hidden){
5548         this.config[colIndex].hidden = hidden;
5549         this.totalWidth = null;
5550         this.fireEvent("hiddenchange", this, colIndex, hidden);
5551     },
5552
5553     /**
5554      * Sets the editor for a column.
5555      * @param {Number} col The column index
5556      * @param {Object} editor The editor object
5557      */
5558     setEditor : function(col, editor){
5559         this.config[col].editor = editor;
5560     }
5561 });
5562
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5564 {
5565     if(typeof value == "object") {
5566         return value;
5567     }
5568         if(typeof value == "string" && value.length < 1){
5569             return "&#160;";
5570         }
5571     
5572         return String.format("{0}", value);
5573 };
5574
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5577 /*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587  
5588 /**
5589  * @class Roo.LoadMask
5590  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5591  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5593  * element's UpdateManager load indicator and will be destroyed after the initial load.
5594  * @constructor
5595  * Create a new LoadMask
5596  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597  * @param {Object} config The config object
5598  */
5599 Roo.LoadMask = function(el, config){
5600     this.el = Roo.get(el);
5601     Roo.apply(this, config);
5602     if(this.store){
5603         this.store.on('beforeload', this.onBeforeLoad, this);
5604         this.store.on('load', this.onLoad, this);
5605         this.store.on('loadexception', this.onLoadException, this);
5606         this.removeMask = false;
5607     }else{
5608         var um = this.el.getUpdateManager();
5609         um.showLoadIndicator = false; // disable the default indicator
5610         um.on('beforeupdate', this.onBeforeLoad, this);
5611         um.on('update', this.onLoad, this);
5612         um.on('failure', this.onLoad, this);
5613         this.removeMask = true;
5614     }
5615 };
5616
5617 Roo.LoadMask.prototype = {
5618     /**
5619      * @cfg {Boolean} removeMask
5620      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5622      */
5623     /**
5624      * @cfg {String} msg
5625      * The text to display in a centered loading message box (defaults to 'Loading...')
5626      */
5627     msg : 'Loading...',
5628     /**
5629      * @cfg {String} msgCls
5630      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5631      */
5632     msgCls : 'x-mask-loading',
5633
5634     /**
5635      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5636      * @type Boolean
5637      */
5638     disabled: false,
5639
5640     /**
5641      * Disables the mask to prevent it from being displayed
5642      */
5643     disable : function(){
5644        this.disabled = true;
5645     },
5646
5647     /**
5648      * Enables the mask so that it can be displayed
5649      */
5650     enable : function(){
5651         this.disabled = false;
5652     },
5653     
5654     onLoadException : function()
5655     {
5656         Roo.log(arguments);
5657         
5658         if (typeof(arguments[3]) != 'undefined') {
5659             Roo.MessageBox.alert("Error loading",arguments[3]);
5660         } 
5661         /*
5662         try {
5663             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5665             }   
5666         } catch(e) {
5667             
5668         }
5669         */
5670     
5671         
5672         
5673         this.el.unmask(this.removeMask);
5674     },
5675     // private
5676     onLoad : function()
5677     {
5678         this.el.unmask(this.removeMask);
5679     },
5680
5681     // private
5682     onBeforeLoad : function(){
5683         if(!this.disabled){
5684             this.el.mask(this.msg, this.msgCls);
5685         }
5686     },
5687
5688     // private
5689     destroy : function(){
5690         if(this.store){
5691             this.store.un('beforeload', this.onBeforeLoad, this);
5692             this.store.un('load', this.onLoad, this);
5693             this.store.un('loadexception', this.onLoadException, this);
5694         }else{
5695             var um = this.el.getUpdateManager();
5696             um.un('beforeupdate', this.onBeforeLoad, this);
5697             um.un('update', this.onLoad, this);
5698             um.un('failure', this.onLoad, this);
5699         }
5700     }
5701 };/*
5702  * - LGPL
5703  *
5704  * table
5705  * 
5706  */
5707
5708 /**
5709  * @class Roo.bootstrap.Table
5710  * @extends Roo.bootstrap.Component
5711  * Bootstrap Table class
5712  * @cfg {String} cls table class
5713  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714  * @cfg {String} bgcolor Specifies the background color for a table
5715  * @cfg {Number} border Specifies whether the table cells should have borders or not
5716  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717  * @cfg {Number} cellspacing Specifies the space between cells
5718  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720  * @cfg {String} sortable Specifies that the table should be sortable
5721  * @cfg {String} summary Specifies a summary of the content of a table
5722  * @cfg {Number} width Specifies the width of a table
5723  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5724  * 
5725  * @cfg {boolean} striped Should the rows be alternative striped
5726  * @cfg {boolean} bordered Add borders to the table
5727  * @cfg {boolean} hover Add hover highlighting
5728  * @cfg {boolean} condensed Format condensed
5729  * @cfg {boolean} responsive Format condensed
5730  * @cfg {Boolean} loadMask (true|false) default false
5731  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733  * @cfg {Boolean} rowSelection (true|false) default false
5734  * @cfg {Boolean} cellSelection (true|false) default false
5735  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5737  
5738  * 
5739  * @constructor
5740  * Create a new Table
5741  * @param {Object} config The config object
5742  */
5743
5744 Roo.bootstrap.Table = function(config){
5745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5746     
5747   
5748     
5749     // BC...
5750     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5754     
5755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5756     if (this.sm) {
5757         this.sm.grid = this;
5758         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759         this.sm = this.selModel;
5760         this.sm.xmodule = this.xmodule || false;
5761     }
5762     
5763     if (this.cm && typeof(this.cm.config) == 'undefined') {
5764         this.colModel = new Roo.grid.ColumnModel(this.cm);
5765         this.cm = this.colModel;
5766         this.cm.xmodule = this.xmodule || false;
5767     }
5768     if (this.store) {
5769         this.store= Roo.factory(this.store, Roo.data);
5770         this.ds = this.store;
5771         this.ds.xmodule = this.xmodule || false;
5772          
5773     }
5774     if (this.footer && this.store) {
5775         this.footer.dataSource = this.ds;
5776         this.footer = Roo.factory(this.footer);
5777     }
5778     
5779     /** @private */
5780     this.addEvents({
5781         /**
5782          * @event cellclick
5783          * Fires when a cell is clicked
5784          * @param {Roo.bootstrap.Table} this
5785          * @param {Roo.Element} el
5786          * @param {Number} rowIndex
5787          * @param {Number} columnIndex
5788          * @param {Roo.EventObject} e
5789          */
5790         "cellclick" : true,
5791         /**
5792          * @event celldblclick
5793          * Fires when a cell is double clicked
5794          * @param {Roo.bootstrap.Table} this
5795          * @param {Roo.Element} el
5796          * @param {Number} rowIndex
5797          * @param {Number} columnIndex
5798          * @param {Roo.EventObject} e
5799          */
5800         "celldblclick" : true,
5801         /**
5802          * @event rowclick
5803          * Fires when a row is clicked
5804          * @param {Roo.bootstrap.Table} this
5805          * @param {Roo.Element} el
5806          * @param {Number} rowIndex
5807          * @param {Roo.EventObject} e
5808          */
5809         "rowclick" : true,
5810         /**
5811          * @event rowdblclick
5812          * Fires when a row is double clicked
5813          * @param {Roo.bootstrap.Table} this
5814          * @param {Roo.Element} el
5815          * @param {Number} rowIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "rowdblclick" : true,
5819         /**
5820          * @event mouseover
5821          * Fires when a mouseover occur
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "mouseover" : true,
5829         /**
5830          * @event mouseout
5831          * Fires when a mouseout occur
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Number} columnIndex
5836          * @param {Roo.EventObject} e
5837          */
5838         "mouseout" : true,
5839         /**
5840          * @event rowclass
5841          * Fires when a row is rendered, so you can change add a style to it.
5842          * @param {Roo.bootstrap.Table} this
5843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5844          */
5845         'rowclass' : true,
5846           /**
5847          * @event rowsrendered
5848          * Fires when all the  rows have been rendered
5849          * @param {Roo.bootstrap.Table} this
5850          */
5851         'rowsrendered' : true,
5852         /**
5853          * @event contextmenu
5854          * The raw contextmenu event for the entire grid.
5855          * @param {Roo.EventObject} e
5856          */
5857         "contextmenu" : true,
5858         /**
5859          * @event rowcontextmenu
5860          * Fires when a row is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} rowIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "rowcontextmenu" : true,
5866         /**
5867          * @event cellcontextmenu
5868          * Fires when a cell is right clicked
5869          * @param {Roo.bootstrap.Table} this
5870          * @param {Number} rowIndex
5871          * @param {Number} cellIndex
5872          * @param {Roo.EventObject} e
5873          */
5874          "cellcontextmenu" : true,
5875          /**
5876          * @event headercontextmenu
5877          * Fires when a header is right clicked
5878          * @param {Roo.bootstrap.Table} this
5879          * @param {Number} columnIndex
5880          * @param {Roo.EventObject} e
5881          */
5882         "headercontextmenu" : true
5883     });
5884 };
5885
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5887     
5888     cls: false,
5889     align: false,
5890     bgcolor: false,
5891     border: false,
5892     cellpadding: false,
5893     cellspacing: false,
5894     frame: false,
5895     rules: false,
5896     sortable: false,
5897     summary: false,
5898     width: false,
5899     striped : false,
5900     scrollBody : false,
5901     bordered: false,
5902     hover:  false,
5903     condensed : false,
5904     responsive : false,
5905     sm : false,
5906     cm : false,
5907     store : false,
5908     loadMask : false,
5909     footerShow : true,
5910     headerShow : true,
5911   
5912     rowSelection : false,
5913     cellSelection : false,
5914     layout : false,
5915     
5916     // Roo.Element - the tbody
5917     mainBody: false,
5918     // Roo.Element - thead element
5919     mainHead: false,
5920     
5921     container: false, // used by gridpanel...
5922     
5923     getAutoCreate : function()
5924     {
5925         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5926         
5927         cfg = {
5928             tag: 'table',
5929             cls : 'table',
5930             cn : []
5931         };
5932         if (this.scrollBody) {
5933             cfg.cls += ' table-body-fixed';
5934         }    
5935         if (this.striped) {
5936             cfg.cls += ' table-striped';
5937         }
5938         
5939         if (this.hover) {
5940             cfg.cls += ' table-hover';
5941         }
5942         if (this.bordered) {
5943             cfg.cls += ' table-bordered';
5944         }
5945         if (this.condensed) {
5946             cfg.cls += ' table-condensed';
5947         }
5948         if (this.responsive) {
5949             cfg.cls += ' table-responsive';
5950         }
5951         
5952         if (this.cls) {
5953             cfg.cls+=  ' ' +this.cls;
5954         }
5955         
5956         // this lot should be simplifed...
5957         
5958         if (this.align) {
5959             cfg.align=this.align;
5960         }
5961         if (this.bgcolor) {
5962             cfg.bgcolor=this.bgcolor;
5963         }
5964         if (this.border) {
5965             cfg.border=this.border;
5966         }
5967         if (this.cellpadding) {
5968             cfg.cellpadding=this.cellpadding;
5969         }
5970         if (this.cellspacing) {
5971             cfg.cellspacing=this.cellspacing;
5972         }
5973         if (this.frame) {
5974             cfg.frame=this.frame;
5975         }
5976         if (this.rules) {
5977             cfg.rules=this.rules;
5978         }
5979         if (this.sortable) {
5980             cfg.sortable=this.sortable;
5981         }
5982         if (this.summary) {
5983             cfg.summary=this.summary;
5984         }
5985         if (this.width) {
5986             cfg.width=this.width;
5987         }
5988         if (this.layout) {
5989             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5990         }
5991         
5992         if(this.store || this.cm){
5993             if(this.headerShow){
5994                 cfg.cn.push(this.renderHeader());
5995             }
5996             
5997             cfg.cn.push(this.renderBody());
5998             
5999             if(this.footerShow){
6000                 cfg.cn.push(this.renderFooter());
6001             }
6002             // where does this come from?
6003             //cfg.cls+=  ' TableGrid';
6004         }
6005         
6006         return { cn : [ cfg ] };
6007     },
6008     
6009     initEvents : function()
6010     {   
6011         if(!this.store || !this.cm){
6012             return;
6013         }
6014         if (this.selModel) {
6015             this.selModel.initEvents();
6016         }
6017         
6018         
6019         //Roo.log('initEvents with ds!!!!');
6020         
6021         this.mainBody = this.el.select('tbody', true).first();
6022         this.mainHead = this.el.select('thead', true).first();
6023         
6024         
6025         
6026         
6027         var _this = this;
6028         
6029         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030             e.on('click', _this.sort, _this);
6031         });
6032         
6033         this.mainBody.on("click", this.onClick, this);
6034         this.mainBody.on("dblclick", this.onDblClick, this);
6035         
6036         // why is this done????? = it breaks dialogs??
6037         //this.parent().el.setStyle('position', 'relative');
6038         
6039         
6040         if (this.footer) {
6041             this.footer.parentId = this.id;
6042             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6043         } 
6044         
6045         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6046         
6047         this.store.on('load', this.onLoad, this);
6048         this.store.on('beforeload', this.onBeforeLoad, this);
6049         this.store.on('update', this.onUpdate, this);
6050         this.store.on('add', this.onAdd, this);
6051         this.store.on("clear", this.clear, this);
6052         
6053         this.el.on("contextmenu", this.onContextMenu, this);
6054         
6055         this.mainBody.on('scroll', this.onBodyScroll, this);
6056         
6057         
6058     },
6059     
6060     onContextMenu : function(e, t)
6061     {
6062         this.processEvent("contextmenu", e);
6063     },
6064     
6065     processEvent : function(name, e)
6066     {
6067         if (name != 'touchstart' ) {
6068             this.fireEvent(name, e);    
6069         }
6070         
6071         var t = e.getTarget();
6072         
6073         var cell = Roo.get(t);
6074         
6075         if(!cell){
6076             return;
6077         }
6078         
6079         if(cell.findParent('tfoot', false, true)){
6080             return;
6081         }
6082         
6083         if(cell.findParent('thead', false, true)){
6084             
6085             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086                 cell = Roo.get(t).findParent('th', false, true);
6087                 if (!cell) {
6088                     Roo.log("failed to find th in thead?");
6089                     Roo.log(e.getTarget());
6090                     return;
6091                 }
6092             }
6093             
6094             var cellIndex = cell.dom.cellIndex;
6095             
6096             var ename = name == 'touchstart' ? 'click' : name;
6097             this.fireEvent("header" + ename, this, cellIndex, e);
6098             
6099             return;
6100         }
6101         
6102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103             cell = Roo.get(t).findParent('td', false, true);
6104             if (!cell) {
6105                 Roo.log("failed to find th in tbody?");
6106                 Roo.log(e.getTarget());
6107                 return;
6108             }
6109         }
6110         
6111         var row = cell.findParent('tr', false, true);
6112         var cellIndex = cell.dom.cellIndex;
6113         var rowIndex = row.dom.rowIndex - 1;
6114         
6115         if(row !== false){
6116             
6117             this.fireEvent("row" + name, this, rowIndex, e);
6118             
6119             if(cell !== false){
6120             
6121                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6122             }
6123         }
6124         
6125     },
6126     
6127     onMouseover : function(e, el)
6128     {
6129         var cell = Roo.get(el);
6130         
6131         if(!cell){
6132             return;
6133         }
6134         
6135         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136             cell = cell.findParent('td', false, true);
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1; // start from 0
6142         
6143         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6144         
6145     },
6146     
6147     onMouseout : function(e, el)
6148     {
6149         var cell = Roo.get(el);
6150         
6151         if(!cell){
6152             return;
6153         }
6154         
6155         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156             cell = cell.findParent('td', false, true);
6157         }
6158         
6159         var row = cell.findParent('tr', false, true);
6160         var cellIndex = cell.dom.cellIndex;
6161         var rowIndex = row.dom.rowIndex - 1; // start from 0
6162         
6163         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6164         
6165     },
6166     
6167     onClick : function(e, el)
6168     {
6169         var cell = Roo.get(el);
6170         
6171         if(!cell || (!this.cellSelection && !this.rowSelection)){
6172             return;
6173         }
6174         
6175         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176             cell = cell.findParent('td', false, true);
6177         }
6178         
6179         if(!cell || typeof(cell) == 'undefined'){
6180             return;
6181         }
6182         
6183         var row = cell.findParent('tr', false, true);
6184         
6185         if(!row || typeof(row) == 'undefined'){
6186             return;
6187         }
6188         
6189         var cellIndex = cell.dom.cellIndex;
6190         var rowIndex = this.getRowIndex(row);
6191         
6192         // why??? - should these not be based on SelectionModel?
6193         if(this.cellSelection){
6194             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6195         }
6196         
6197         if(this.rowSelection){
6198             this.fireEvent('rowclick', this, row, rowIndex, e);
6199         }
6200         
6201         
6202     },
6203         
6204     onDblClick : function(e,el)
6205     {
6206         var cell = Roo.get(el);
6207         
6208         if(!cell || (!this.cellSelection && !this.rowSelection)){
6209             return;
6210         }
6211         
6212         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213             cell = cell.findParent('td', false, true);
6214         }
6215         
6216         if(!cell || typeof(cell) == 'undefined'){
6217             return;
6218         }
6219         
6220         var row = cell.findParent('tr', false, true);
6221         
6222         if(!row || typeof(row) == 'undefined'){
6223             return;
6224         }
6225         
6226         var cellIndex = cell.dom.cellIndex;
6227         var rowIndex = this.getRowIndex(row);
6228         
6229         if(this.cellSelection){
6230             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6231         }
6232         
6233         if(this.rowSelection){
6234             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6235         }
6236     },
6237     
6238     sort : function(e,el)
6239     {
6240         var col = Roo.get(el);
6241         
6242         if(!col.hasClass('sortable')){
6243             return;
6244         }
6245         
6246         var sort = col.attr('sort');
6247         var dir = 'ASC';
6248         
6249         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6250             dir = 'DESC';
6251         }
6252         
6253         this.store.sortInfo = {field : sort, direction : dir};
6254         
6255         if (this.footer) {
6256             Roo.log("calling footer first");
6257             this.footer.onClick('first');
6258         } else {
6259         
6260             this.store.load({ params : { start : 0 } });
6261         }
6262     },
6263     
6264     renderHeader : function()
6265     {
6266         var header = {
6267             tag: 'thead',
6268             cn : []
6269         };
6270         
6271         var cm = this.cm;
6272         this.totalWidth = 0;
6273         
6274         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6275             
6276             var config = cm.config[i];
6277             
6278             var c = {
6279                 tag: 'th',
6280                 style : '',
6281                 html: cm.getColumnHeader(i)
6282             };
6283             
6284             var hh = '';
6285             
6286             if(typeof(config.sortable) != 'undefined' && config.sortable){
6287                 c.cls = 'sortable';
6288                 c.html = '<i class="glyphicon"></i>' + c.html;
6289             }
6290             
6291             if(typeof(config.lgHeader) != 'undefined'){
6292                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6293             }
6294             
6295             if(typeof(config.mdHeader) != 'undefined'){
6296                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6297             }
6298             
6299             if(typeof(config.smHeader) != 'undefined'){
6300                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6301             }
6302             
6303             if(typeof(config.xsHeader) != 'undefined'){
6304                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6305             }
6306             
6307             if(hh.length){
6308                 c.html = hh;
6309             }
6310             
6311             if(typeof(config.tooltip) != 'undefined'){
6312                 c.tooltip = config.tooltip;
6313             }
6314             
6315             if(typeof(config.colspan) != 'undefined'){
6316                 c.colspan = config.colspan;
6317             }
6318             
6319             if(typeof(config.hidden) != 'undefined' && config.hidden){
6320                 c.style += ' display:none;';
6321             }
6322             
6323             if(typeof(config.dataIndex) != 'undefined'){
6324                 c.sort = config.dataIndex;
6325             }
6326             
6327            
6328             
6329             if(typeof(config.align) != 'undefined' && config.align.length){
6330                 c.style += ' text-align:' + config.align + ';';
6331             }
6332             
6333             if(typeof(config.width) != 'undefined'){
6334                 c.style += ' width:' + config.width + 'px;';
6335                 this.totalWidth += config.width;
6336             } else {
6337                 this.totalWidth += 100; // assume minimum of 100 per column?
6338             }
6339             
6340             if(typeof(config.cls) != 'undefined'){
6341                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6342             }
6343             
6344             ['xs','sm','md','lg'].map(function(size){
6345                 
6346                 if(typeof(config[size]) == 'undefined'){
6347                     return;
6348                 }
6349                 
6350                 if (!config[size]) { // 0 = hidden
6351                     c.cls += ' hidden-' + size;
6352                     return;
6353                 }
6354                 
6355                 c.cls += ' col-' + size + '-' + config[size];
6356
6357             });
6358             
6359             header.cn.push(c)
6360         }
6361         
6362         return header;
6363     },
6364     
6365     renderBody : function()
6366     {
6367         var body = {
6368             tag: 'tbody',
6369             cn : [
6370                 {
6371                     tag: 'tr',
6372                     cn : [
6373                         {
6374                             tag : 'td',
6375                             colspan :  this.cm.getColumnCount()
6376                         }
6377                     ]
6378                 }
6379             ]
6380         };
6381         
6382         return body;
6383     },
6384     
6385     renderFooter : function()
6386     {
6387         var footer = {
6388             tag: 'tfoot',
6389             cn : [
6390                 {
6391                     tag: 'tr',
6392                     cn : [
6393                         {
6394                             tag : 'td',
6395                             colspan :  this.cm.getColumnCount()
6396                         }
6397                     ]
6398                 }
6399             ]
6400         };
6401         
6402         return footer;
6403     },
6404     
6405     
6406     
6407     onLoad : function()
6408     {
6409 //        Roo.log('ds onload');
6410         this.clear();
6411         
6412         var _this = this;
6413         var cm = this.cm;
6414         var ds = this.store;
6415         
6416         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418             if (_this.store.sortInfo) {
6419                     
6420                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6422                 }
6423                 
6424                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6426                 }
6427             }
6428         });
6429         
6430         var tbody =  this.mainBody;
6431               
6432         if(ds.getCount() > 0){
6433             ds.data.each(function(d,rowIndex){
6434                 var row =  this.renderRow(cm, ds, rowIndex);
6435                 
6436                 tbody.createChild(row);
6437                 
6438                 var _this = this;
6439                 
6440                 if(row.cellObjects.length){
6441                     Roo.each(row.cellObjects, function(r){
6442                         _this.renderCellObject(r);
6443                     })
6444                 }
6445                 
6446             }, this);
6447         }
6448         
6449         Roo.each(this.el.select('tbody td', true).elements, function(e){
6450             e.on('mouseover', _this.onMouseover, _this);
6451         });
6452         
6453         Roo.each(this.el.select('tbody td', true).elements, function(e){
6454             e.on('mouseout', _this.onMouseout, _this);
6455         });
6456         this.fireEvent('rowsrendered', this);
6457         //if(this.loadMask){
6458         //    this.maskEl.hide();
6459         //}
6460         
6461         this.autoSize();
6462     },
6463     
6464     
6465     onUpdate : function(ds,record)
6466     {
6467         this.refreshRow(record);
6468         this.autoSize();
6469     },
6470     
6471     onRemove : function(ds, record, index, isUpdate){
6472         if(isUpdate !== true){
6473             this.fireEvent("beforerowremoved", this, index, record);
6474         }
6475         var bt = this.mainBody.dom;
6476         
6477         var rows = this.el.select('tbody > tr', true).elements;
6478         
6479         if(typeof(rows[index]) != 'undefined'){
6480             bt.removeChild(rows[index].dom);
6481         }
6482         
6483 //        if(bt.rows[index]){
6484 //            bt.removeChild(bt.rows[index]);
6485 //        }
6486         
6487         if(isUpdate !== true){
6488             //this.stripeRows(index);
6489             //this.syncRowHeights(index, index);
6490             //this.layout();
6491             this.fireEvent("rowremoved", this, index, record);
6492         }
6493     },
6494     
6495     onAdd : function(ds, records, rowIndex)
6496     {
6497         //Roo.log('on Add called');
6498         // - note this does not handle multiple adding very well..
6499         var bt = this.mainBody.dom;
6500         for (var i =0 ; i < records.length;i++) {
6501             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502             //Roo.log(records[i]);
6503             //Roo.log(this.store.getAt(rowIndex+i));
6504             this.insertRow(this.store, rowIndex + i, false);
6505             return;
6506         }
6507         
6508     },
6509     
6510     
6511     refreshRow : function(record){
6512         var ds = this.store, index;
6513         if(typeof record == 'number'){
6514             index = record;
6515             record = ds.getAt(index);
6516         }else{
6517             index = ds.indexOf(record);
6518         }
6519         this.insertRow(ds, index, true);
6520         this.autoSize();
6521         this.onRemove(ds, record, index+1, true);
6522         this.autoSize();
6523         //this.syncRowHeights(index, index);
6524         //this.layout();
6525         this.fireEvent("rowupdated", this, index, record);
6526     },
6527     
6528     insertRow : function(dm, rowIndex, isUpdate){
6529         
6530         if(!isUpdate){
6531             this.fireEvent("beforerowsinserted", this, rowIndex);
6532         }
6533             //var s = this.getScrollState();
6534         var row = this.renderRow(this.cm, this.store, rowIndex);
6535         // insert before rowIndex..
6536         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6537         
6538         var _this = this;
6539                 
6540         if(row.cellObjects.length){
6541             Roo.each(row.cellObjects, function(r){
6542                 _this.renderCellObject(r);
6543             })
6544         }
6545             
6546         if(!isUpdate){
6547             this.fireEvent("rowsinserted", this, rowIndex);
6548             //this.syncRowHeights(firstRow, lastRow);
6549             //this.stripeRows(firstRow);
6550             //this.layout();
6551         }
6552         
6553     },
6554     
6555     
6556     getRowDom : function(rowIndex)
6557     {
6558         var rows = this.el.select('tbody > tr', true).elements;
6559         
6560         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6561         
6562     },
6563     // returns the object tree for a tr..
6564   
6565     
6566     renderRow : function(cm, ds, rowIndex) 
6567     {
6568         
6569         var d = ds.getAt(rowIndex);
6570         
6571         var row = {
6572             tag : 'tr',
6573             cn : []
6574         };
6575             
6576         var cellObjects = [];
6577         
6578         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579             var config = cm.config[i];
6580             
6581             var renderer = cm.getRenderer(i);
6582             var value = '';
6583             var id = false;
6584             
6585             if(typeof(renderer) !== 'undefined'){
6586                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6587             }
6588             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589             // and are rendered into the cells after the row is rendered - using the id for the element.
6590             
6591             if(typeof(value) === 'object'){
6592                 id = Roo.id();
6593                 cellObjects.push({
6594                     container : id,
6595                     cfg : value 
6596                 })
6597             }
6598             
6599             var rowcfg = {
6600                 record: d,
6601                 rowIndex : rowIndex,
6602                 colIndex : i,
6603                 rowClass : ''
6604             };
6605
6606             this.fireEvent('rowclass', this, rowcfg);
6607             
6608             var td = {
6609                 tag: 'td',
6610                 cls : rowcfg.rowClass,
6611                 style: '',
6612                 html: (typeof(value) === 'object') ? '' : value
6613             };
6614             
6615             if (id) {
6616                 td.id = id;
6617             }
6618             
6619             if(typeof(config.colspan) != 'undefined'){
6620                 td.colspan = config.colspan;
6621             }
6622             
6623             if(typeof(config.hidden) != 'undefined' && config.hidden){
6624                 td.style += ' display:none;';
6625             }
6626             
6627             if(typeof(config.align) != 'undefined' && config.align.length){
6628                 td.style += ' text-align:' + config.align + ';';
6629             }
6630             
6631             if(typeof(config.width) != 'undefined'){
6632                 td.style += ' width:' +  config.width + 'px;';
6633             }
6634             
6635             if(typeof(config.cursor) != 'undefined'){
6636                 td.style += ' cursor:' +  config.cursor + ';';
6637             }
6638             
6639             if(typeof(config.cls) != 'undefined'){
6640                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6641             }
6642             
6643             ['xs','sm','md','lg'].map(function(size){
6644                 
6645                 if(typeof(config[size]) == 'undefined'){
6646                     return;
6647                 }
6648                 
6649                 if (!config[size]) { // 0 = hidden
6650                     td.cls += ' hidden-' + size;
6651                     return;
6652                 }
6653                 
6654                 td.cls += ' col-' + size + '-' + config[size];
6655
6656             });
6657              
6658             row.cn.push(td);
6659            
6660         }
6661         
6662         row.cellObjects = cellObjects;
6663         
6664         return row;
6665           
6666     },
6667     
6668     
6669     
6670     onBeforeLoad : function()
6671     {
6672         //Roo.log('ds onBeforeLoad');
6673         
6674         //this.clear();
6675         
6676         //if(this.loadMask){
6677         //    this.maskEl.show();
6678         //}
6679     },
6680      /**
6681      * Remove all rows
6682      */
6683     clear : function()
6684     {
6685         this.el.select('tbody', true).first().dom.innerHTML = '';
6686     },
6687     /**
6688      * Show or hide a row.
6689      * @param {Number} rowIndex to show or hide
6690      * @param {Boolean} state hide
6691      */
6692     setRowVisibility : function(rowIndex, state)
6693     {
6694         var bt = this.mainBody.dom;
6695         
6696         var rows = this.el.select('tbody > tr', true).elements;
6697         
6698         if(typeof(rows[rowIndex]) == 'undefined'){
6699             return;
6700         }
6701         rows[rowIndex].dom.style.display = state ? '' : 'none';
6702     },
6703     
6704     
6705     getSelectionModel : function(){
6706         if(!this.selModel){
6707             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6708         }
6709         return this.selModel;
6710     },
6711     /*
6712      * Render the Roo.bootstrap object from renderder
6713      */
6714     renderCellObject : function(r)
6715     {
6716         var _this = this;
6717         
6718         var t = r.cfg.render(r.container);
6719         
6720         if(r.cfg.cn){
6721             Roo.each(r.cfg.cn, function(c){
6722                 var child = {
6723                     container: t.getChildContainer(),
6724                     cfg: c
6725                 };
6726                 _this.renderCellObject(child);
6727             })
6728         }
6729     },
6730     
6731     getRowIndex : function(row)
6732     {
6733         var rowIndex = -1;
6734         
6735         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6736             if(el != row){
6737                 return;
6738             }
6739             
6740             rowIndex = index;
6741         });
6742         
6743         return rowIndex;
6744     },
6745      /**
6746      * Returns the grid's underlying element = used by panel.Grid
6747      * @return {Element} The element
6748      */
6749     getGridEl : function(){
6750         return this.el;
6751     },
6752      /**
6753      * Forces a resize - used by panel.Grid
6754      * @return {Element} The element
6755      */
6756     autoSize : function()
6757     {
6758         //var ctr = Roo.get(this.container.dom.parentElement);
6759         var ctr = Roo.get(this.el.dom);
6760         
6761         var thd = this.getGridEl().select('thead',true).first();
6762         var tbd = this.getGridEl().select('tbody', true).first();
6763         var tfd = this.getGridEl().select('tfoot', true).first();
6764         
6765         var cw = ctr.getWidth();
6766         
6767         if (tbd) {
6768             
6769             tbd.setSize(ctr.getWidth(),
6770                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6771             );
6772             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6773             cw -= barsize;
6774         }
6775         cw = Math.max(cw, this.totalWidth);
6776         this.getGridEl().select('tr',true).setWidth(cw);
6777         // resize 'expandable coloumn?
6778         
6779         return; // we doe not have a view in this design..
6780         
6781     },
6782     onBodyScroll: function()
6783     {
6784         
6785         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786         this.mainHead.setStyle({
6787                     'position' : 'relative',
6788                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6789         });
6790         
6791         
6792     }
6793 });
6794
6795  
6796
6797  /*
6798  * - LGPL
6799  *
6800  * table cell
6801  * 
6802  */
6803
6804 /**
6805  * @class Roo.bootstrap.TableCell
6806  * @extends Roo.bootstrap.Component
6807  * Bootstrap TableCell class
6808  * @cfg {String} html cell contain text
6809  * @cfg {String} cls cell class
6810  * @cfg {String} tag cell tag (td|th) default td
6811  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812  * @cfg {String} align Aligns the content in a cell
6813  * @cfg {String} axis Categorizes cells
6814  * @cfg {String} bgcolor Specifies the background color of a cell
6815  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816  * @cfg {Number} colspan Specifies the number of columns a cell should span
6817  * @cfg {String} headers Specifies one or more header cells a cell is related to
6818  * @cfg {Number} height Sets the height of a cell
6819  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820  * @cfg {Number} rowspan Sets the number of rows a cell should span
6821  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822  * @cfg {String} valign Vertical aligns the content in a cell
6823  * @cfg {Number} width Specifies the width of a cell
6824  * 
6825  * @constructor
6826  * Create a new TableCell
6827  * @param {Object} config The config object
6828  */
6829
6830 Roo.bootstrap.TableCell = function(config){
6831     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6832 };
6833
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6835     
6836     html: false,
6837     cls: false,
6838     tag: false,
6839     abbr: false,
6840     align: false,
6841     axis: false,
6842     bgcolor: false,
6843     charoff: false,
6844     colspan: false,
6845     headers: false,
6846     height: false,
6847     nowrap: false,
6848     rowspan: false,
6849     scope: false,
6850     valign: false,
6851     width: false,
6852     
6853     
6854     getAutoCreate : function(){
6855         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6856         
6857         cfg = {
6858             tag: 'td'
6859         };
6860         
6861         if(this.tag){
6862             cfg.tag = this.tag;
6863         }
6864         
6865         if (this.html) {
6866             cfg.html=this.html
6867         }
6868         if (this.cls) {
6869             cfg.cls=this.cls
6870         }
6871         if (this.abbr) {
6872             cfg.abbr=this.abbr
6873         }
6874         if (this.align) {
6875             cfg.align=this.align
6876         }
6877         if (this.axis) {
6878             cfg.axis=this.axis
6879         }
6880         if (this.bgcolor) {
6881             cfg.bgcolor=this.bgcolor
6882         }
6883         if (this.charoff) {
6884             cfg.charoff=this.charoff
6885         }
6886         if (this.colspan) {
6887             cfg.colspan=this.colspan
6888         }
6889         if (this.headers) {
6890             cfg.headers=this.headers
6891         }
6892         if (this.height) {
6893             cfg.height=this.height
6894         }
6895         if (this.nowrap) {
6896             cfg.nowrap=this.nowrap
6897         }
6898         if (this.rowspan) {
6899             cfg.rowspan=this.rowspan
6900         }
6901         if (this.scope) {
6902             cfg.scope=this.scope
6903         }
6904         if (this.valign) {
6905             cfg.valign=this.valign
6906         }
6907         if (this.width) {
6908             cfg.width=this.width
6909         }
6910         
6911         
6912         return cfg;
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * table row
6923  * 
6924  */
6925
6926 /**
6927  * @class Roo.bootstrap.TableRow
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap TableRow class
6930  * @cfg {String} cls row class
6931  * @cfg {String} align Aligns the content in a table row
6932  * @cfg {String} bgcolor Specifies a background color for a table row
6933  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934  * @cfg {String} valign Vertical aligns the content in a table row
6935  * 
6936  * @constructor
6937  * Create a new TableRow
6938  * @param {Object} config The config object
6939  */
6940
6941 Roo.bootstrap.TableRow = function(config){
6942     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6943 };
6944
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6946     
6947     cls: false,
6948     align: false,
6949     bgcolor: false,
6950     charoff: false,
6951     valign: false,
6952     
6953     getAutoCreate : function(){
6954         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6955         
6956         cfg = {
6957             tag: 'tr'
6958         };
6959             
6960         if(this.cls){
6961             cfg.cls = this.cls;
6962         }
6963         if(this.align){
6964             cfg.align = this.align;
6965         }
6966         if(this.bgcolor){
6967             cfg.bgcolor = this.bgcolor;
6968         }
6969         if(this.charoff){
6970             cfg.charoff = this.charoff;
6971         }
6972         if(this.valign){
6973             cfg.valign = this.valign;
6974         }
6975         
6976         return cfg;
6977     }
6978    
6979 });
6980
6981  
6982
6983  /*
6984  * - LGPL
6985  *
6986  * table body
6987  * 
6988  */
6989
6990 /**
6991  * @class Roo.bootstrap.TableBody
6992  * @extends Roo.bootstrap.Component
6993  * Bootstrap TableBody class
6994  * @cfg {String} cls element class
6995  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996  * @cfg {String} align Aligns the content inside the element
6997  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6999  * 
7000  * @constructor
7001  * Create a new TableBody
7002  * @param {Object} config The config object
7003  */
7004
7005 Roo.bootstrap.TableBody = function(config){
7006     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7007 };
7008
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7010     
7011     cls: false,
7012     tag: false,
7013     align: false,
7014     charoff: false,
7015     valign: false,
7016     
7017     getAutoCreate : function(){
7018         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7019         
7020         cfg = {
7021             tag: 'tbody'
7022         };
7023             
7024         if (this.cls) {
7025             cfg.cls=this.cls
7026         }
7027         if(this.tag){
7028             cfg.tag = this.tag;
7029         }
7030         
7031         if(this.align){
7032             cfg.align = this.align;
7033         }
7034         if(this.charoff){
7035             cfg.charoff = this.charoff;
7036         }
7037         if(this.valign){
7038             cfg.valign = this.valign;
7039         }
7040         
7041         return cfg;
7042     }
7043     
7044     
7045 //    initEvents : function()
7046 //    {
7047 //        
7048 //        if(!this.store){
7049 //            return;
7050 //        }
7051 //        
7052 //        this.store = Roo.factory(this.store, Roo.data);
7053 //        this.store.on('load', this.onLoad, this);
7054 //        
7055 //        this.store.load();
7056 //        
7057 //    },
7058 //    
7059 //    onLoad: function () 
7060 //    {   
7061 //        this.fireEvent('load', this);
7062 //    }
7063 //    
7064 //   
7065 });
7066
7067  
7068
7069  /*
7070  * Based on:
7071  * Ext JS Library 1.1.1
7072  * Copyright(c) 2006-2007, Ext JS, LLC.
7073  *
7074  * Originally Released Under LGPL - original licence link has changed is not relivant.
7075  *
7076  * Fork - LGPL
7077  * <script type="text/javascript">
7078  */
7079
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7082  /**
7083  * @class Roo.form.Action
7084  * Internal Class used to handle form actions
7085  * @constructor
7086  * @param {Roo.form.BasicForm} el The form element or its id
7087  * @param {Object} config Configuration options
7088  */
7089
7090  
7091  
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7094     this.form = form;
7095     this.options = options || {};
7096 };
7097 /**
7098  * Client Validation Failed
7099  * @const 
7100  */
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7102 /**
7103  * Server Validation Failed
7104  * @const 
7105  */
7106 Roo.form.Action.SERVER_INVALID = 'server';
7107  /**
7108  * Connect to Server Failed
7109  * @const 
7110  */
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7112 /**
7113  * Reading Data from Server Failed
7114  * @const 
7115  */
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7117
7118 Roo.form.Action.prototype = {
7119     type : 'default',
7120     failureType : undefined,
7121     response : undefined,
7122     result : undefined,
7123
7124     // interface method
7125     run : function(options){
7126
7127     },
7128
7129     // interface method
7130     success : function(response){
7131
7132     },
7133
7134     // interface method
7135     handleResponse : function(response){
7136
7137     },
7138
7139     // default connection failure
7140     failure : function(response){
7141         
7142         this.response = response;
7143         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144         this.form.afterAction(this, false);
7145     },
7146
7147     processResponse : function(response){
7148         this.response = response;
7149         if(!response.responseText){
7150             return true;
7151         }
7152         this.result = this.handleResponse(response);
7153         return this.result;
7154     },
7155
7156     // utility functions used internally
7157     getUrl : function(appendParams){
7158         var url = this.options.url || this.form.url || this.form.el.dom.action;
7159         if(appendParams){
7160             var p = this.getParams();
7161             if(p){
7162                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7163             }
7164         }
7165         return url;
7166     },
7167
7168     getMethod : function(){
7169         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7170     },
7171
7172     getParams : function(){
7173         var bp = this.form.baseParams;
7174         var p = this.options.params;
7175         if(p){
7176             if(typeof p == "object"){
7177                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178             }else if(typeof p == 'string' && bp){
7179                 p += '&' + Roo.urlEncode(bp);
7180             }
7181         }else if(bp){
7182             p = Roo.urlEncode(bp);
7183         }
7184         return p;
7185     },
7186
7187     createCallback : function(){
7188         return {
7189             success: this.success,
7190             failure: this.failure,
7191             scope: this,
7192             timeout: (this.form.timeout*1000),
7193             upload: this.form.fileUpload ? this.success : undefined
7194         };
7195     }
7196 };
7197
7198 Roo.form.Action.Submit = function(form, options){
7199     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7200 };
7201
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7203     type : 'submit',
7204
7205     haveProgress : false,
7206     uploadComplete : false,
7207     
7208     // uploadProgress indicator.
7209     uploadProgress : function()
7210     {
7211         if (!this.form.progressUrl) {
7212             return;
7213         }
7214         
7215         if (!this.haveProgress) {
7216             Roo.MessageBox.progress("Uploading", "Uploading");
7217         }
7218         if (this.uploadComplete) {
7219            Roo.MessageBox.hide();
7220            return;
7221         }
7222         
7223         this.haveProgress = true;
7224    
7225         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7226         
7227         var c = new Roo.data.Connection();
7228         c.request({
7229             url : this.form.progressUrl,
7230             params: {
7231                 id : uid
7232             },
7233             method: 'GET',
7234             success : function(req){
7235                //console.log(data);
7236                 var rdata = false;
7237                 var edata;
7238                 try  {
7239                    rdata = Roo.decode(req.responseText)
7240                 } catch (e) {
7241                     Roo.log("Invalid data from server..");
7242                     Roo.log(edata);
7243                     return;
7244                 }
7245                 if (!rdata || !rdata.success) {
7246                     Roo.log(rdata);
7247                     Roo.MessageBox.alert(Roo.encode(rdata));
7248                     return;
7249                 }
7250                 var data = rdata.data;
7251                 
7252                 if (this.uploadComplete) {
7253                    Roo.MessageBox.hide();
7254                    return;
7255                 }
7256                    
7257                 if (data){
7258                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7260                     );
7261                 }
7262                 this.uploadProgress.defer(2000,this);
7263             },
7264        
7265             failure: function(data) {
7266                 Roo.log('progress url failed ');
7267                 Roo.log(data);
7268             },
7269             scope : this
7270         });
7271            
7272     },
7273     
7274     
7275     run : function()
7276     {
7277         // run get Values on the form, so it syncs any secondary forms.
7278         this.form.getValues();
7279         
7280         var o = this.options;
7281         var method = this.getMethod();
7282         var isPost = method == 'POST';
7283         if(o.clientValidation === false || this.form.isValid()){
7284             
7285             if (this.form.progressUrl) {
7286                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287                     (new Date() * 1) + '' + Math.random());
7288                     
7289             } 
7290             
7291             
7292             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293                 form:this.form.el.dom,
7294                 url:this.getUrl(!isPost),
7295                 method: method,
7296                 params:isPost ? this.getParams() : null,
7297                 isUpload: this.form.fileUpload
7298             }));
7299             
7300             this.uploadProgress();
7301
7302         }else if (o.clientValidation !== false){ // client validation failed
7303             this.failureType = Roo.form.Action.CLIENT_INVALID;
7304             this.form.afterAction(this, false);
7305         }
7306     },
7307
7308     success : function(response)
7309     {
7310         this.uploadComplete= true;
7311         if (this.haveProgress) {
7312             Roo.MessageBox.hide();
7313         }
7314         
7315         
7316         var result = this.processResponse(response);
7317         if(result === true || result.success){
7318             this.form.afterAction(this, true);
7319             return;
7320         }
7321         if(result.errors){
7322             this.form.markInvalid(result.errors);
7323             this.failureType = Roo.form.Action.SERVER_INVALID;
7324         }
7325         this.form.afterAction(this, false);
7326     },
7327     failure : function(response)
7328     {
7329         this.uploadComplete= true;
7330         if (this.haveProgress) {
7331             Roo.MessageBox.hide();
7332         }
7333         
7334         this.response = response;
7335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336         this.form.afterAction(this, false);
7337     },
7338     
7339     handleResponse : function(response){
7340         if(this.form.errorReader){
7341             var rs = this.form.errorReader.read(response);
7342             var errors = [];
7343             if(rs.records){
7344                 for(var i = 0, len = rs.records.length; i < len; i++) {
7345                     var r = rs.records[i];
7346                     errors[i] = r.data;
7347                 }
7348             }
7349             if(errors.length < 1){
7350                 errors = null;
7351             }
7352             return {
7353                 success : rs.success,
7354                 errors : errors
7355             };
7356         }
7357         var ret = false;
7358         try {
7359             ret = Roo.decode(response.responseText);
7360         } catch (e) {
7361             ret = {
7362                 success: false,
7363                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7364                 errors : []
7365             };
7366         }
7367         return ret;
7368         
7369     }
7370 });
7371
7372
7373 Roo.form.Action.Load = function(form, options){
7374     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375     this.reader = this.form.reader;
7376 };
7377
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7379     type : 'load',
7380
7381     run : function(){
7382         
7383         Roo.Ajax.request(Roo.apply(
7384                 this.createCallback(), {
7385                     method:this.getMethod(),
7386                     url:this.getUrl(false),
7387                     params:this.getParams()
7388         }));
7389     },
7390
7391     success : function(response){
7392         
7393         var result = this.processResponse(response);
7394         if(result === true || !result.success || !result.data){
7395             this.failureType = Roo.form.Action.LOAD_FAILURE;
7396             this.form.afterAction(this, false);
7397             return;
7398         }
7399         this.form.clearInvalid();
7400         this.form.setValues(result.data);
7401         this.form.afterAction(this, true);
7402     },
7403
7404     handleResponse : function(response){
7405         if(this.form.reader){
7406             var rs = this.form.reader.read(response);
7407             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7408             return {
7409                 success : rs.success,
7410                 data : data
7411             };
7412         }
7413         return Roo.decode(response.responseText);
7414     }
7415 });
7416
7417 Roo.form.Action.ACTION_TYPES = {
7418     'load' : Roo.form.Action.Load,
7419     'submit' : Roo.form.Action.Submit
7420 };/*
7421  * - LGPL
7422  *
7423  * form
7424  *
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.Form
7429  * @extends Roo.bootstrap.Component
7430  * Bootstrap Form class
7431  * @cfg {String} method  GET | POST (default POST)
7432  * @cfg {String} labelAlign top | left (default top)
7433  * @cfg {String} align left  | right - for navbars
7434  * @cfg {Boolean} loadMask load mask when submit (default true)
7435
7436  *
7437  * @constructor
7438  * Create a new Form
7439  * @param {Object} config The config object
7440  */
7441
7442
7443 Roo.bootstrap.Form = function(config){
7444     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7445     
7446     Roo.bootstrap.Form.popover.apply();
7447     
7448     this.addEvents({
7449         /**
7450          * @event clientvalidation
7451          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7452          * @param {Form} this
7453          * @param {Boolean} valid true if the form has passed client-side validation
7454          */
7455         clientvalidation: true,
7456         /**
7457          * @event beforeaction
7458          * Fires before any action is performed. Return false to cancel the action.
7459          * @param {Form} this
7460          * @param {Action} action The action to be performed
7461          */
7462         beforeaction: true,
7463         /**
7464          * @event actionfailed
7465          * Fires when an action fails.
7466          * @param {Form} this
7467          * @param {Action} action The action that failed
7468          */
7469         actionfailed : true,
7470         /**
7471          * @event actioncomplete
7472          * Fires when an action is completed.
7473          * @param {Form} this
7474          * @param {Action} action The action that completed
7475          */
7476         actioncomplete : true
7477     });
7478
7479 };
7480
7481 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7482
7483      /**
7484      * @cfg {String} method
7485      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7486      */
7487     method : 'POST',
7488     /**
7489      * @cfg {String} url
7490      * The URL to use for form actions if one isn't supplied in the action options.
7491      */
7492     /**
7493      * @cfg {Boolean} fileUpload
7494      * Set to true if this form is a file upload.
7495      */
7496
7497     /**
7498      * @cfg {Object} baseParams
7499      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7500      */
7501
7502     /**
7503      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7504      */
7505     timeout: 30,
7506     /**
7507      * @cfg {Sting} align (left|right) for navbar forms
7508      */
7509     align : 'left',
7510
7511     // private
7512     activeAction : null,
7513
7514     /**
7515      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7516      * element by passing it or its id or mask the form itself by passing in true.
7517      * @type Mixed
7518      */
7519     waitMsgTarget : false,
7520
7521     loadMask : true,
7522     
7523     /**
7524      * @cfg {Boolean} errPopover (true|false) default false
7525      */
7526     errPopover : false,
7527
7528     getAutoCreate : function(){
7529
7530         var cfg = {
7531             tag: 'form',
7532             method : this.method || 'POST',
7533             id : this.id || Roo.id(),
7534             cls : ''
7535         };
7536         if (this.parent().xtype.match(/^Nav/)) {
7537             cfg.cls = 'navbar-form navbar-' + this.align;
7538
7539         }
7540
7541         if (this.labelAlign == 'left' ) {
7542             cfg.cls += ' form-horizontal';
7543         }
7544
7545
7546         return cfg;
7547     },
7548     initEvents : function()
7549     {
7550         this.el.on('submit', this.onSubmit, this);
7551         // this was added as random key presses on the form where triggering form submit.
7552         this.el.on('keypress', function(e) {
7553             if (e.getCharCode() != 13) {
7554                 return true;
7555             }
7556             // we might need to allow it for textareas.. and some other items.
7557             // check e.getTarget().
7558
7559             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7560                 return true;
7561             }
7562
7563             Roo.log("keypress blocked");
7564
7565             e.preventDefault();
7566             return false;
7567         });
7568         
7569     },
7570     // private
7571     onSubmit : function(e){
7572         e.stopEvent();
7573     },
7574
7575      /**
7576      * Returns true if client-side validation on the form is successful.
7577      * @return Boolean
7578      */
7579     isValid : function(){
7580         var items = this.getItems();
7581         var valid = true;
7582         var target = false;
7583         
7584         items.each(function(f){
7585             
7586             if(f.validate()){
7587                 return;
7588             }
7589             
7590             valid = false;
7591
7592             if(!target && f.el.isVisible(true)){
7593                 target = f;
7594             }
7595            
7596         });
7597         
7598         if(this.errPopover && !valid){
7599             Roo.bootstrap.Form.popover.mask(this, target);
7600         }
7601         
7602         return valid;
7603     },
7604     
7605     /**
7606      * Returns true if any fields in this form have changed since their original load.
7607      * @return Boolean
7608      */
7609     isDirty : function(){
7610         var dirty = false;
7611         var items = this.getItems();
7612         items.each(function(f){
7613            if(f.isDirty()){
7614                dirty = true;
7615                return false;
7616            }
7617            return true;
7618         });
7619         return dirty;
7620     },
7621      /**
7622      * Performs a predefined action (submit or load) or custom actions you define on this form.
7623      * @param {String} actionName The name of the action type
7624      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7625      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7626      * accept other config options):
7627      * <pre>
7628 Property          Type             Description
7629 ----------------  ---------------  ----------------------------------------------------------------------------------
7630 url               String           The url for the action (defaults to the form's url)
7631 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7632 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7633 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7634                                    validate the form on the client (defaults to false)
7635      * </pre>
7636      * @return {BasicForm} this
7637      */
7638     doAction : function(action, options){
7639         if(typeof action == 'string'){
7640             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7641         }
7642         if(this.fireEvent('beforeaction', this, action) !== false){
7643             this.beforeAction(action);
7644             action.run.defer(100, action);
7645         }
7646         return this;
7647     },
7648
7649     // private
7650     beforeAction : function(action){
7651         var o = action.options;
7652
7653         if(this.loadMask){
7654             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7655         }
7656         // not really supported yet.. ??
7657
7658         //if(this.waitMsgTarget === true){
7659         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7660         //}else if(this.waitMsgTarget){
7661         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7662         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7663         //}else {
7664         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7665        // }
7666
7667     },
7668
7669     // private
7670     afterAction : function(action, success){
7671         this.activeAction = null;
7672         var o = action.options;
7673
7674         //if(this.waitMsgTarget === true){
7675             this.el.unmask();
7676         //}else if(this.waitMsgTarget){
7677         //    this.waitMsgTarget.unmask();
7678         //}else{
7679         //    Roo.MessageBox.updateProgress(1);
7680         //    Roo.MessageBox.hide();
7681        // }
7682         //
7683         if(success){
7684             if(o.reset){
7685                 this.reset();
7686             }
7687             Roo.callback(o.success, o.scope, [this, action]);
7688             this.fireEvent('actioncomplete', this, action);
7689
7690         }else{
7691
7692             // failure condition..
7693             // we have a scenario where updates need confirming.
7694             // eg. if a locking scenario exists..
7695             // we look for { errors : { needs_confirm : true }} in the response.
7696             if (
7697                 (typeof(action.result) != 'undefined')  &&
7698                 (typeof(action.result.errors) != 'undefined')  &&
7699                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7700            ){
7701                 var _t = this;
7702                 Roo.log("not supported yet");
7703                  /*
7704
7705                 Roo.MessageBox.confirm(
7706                     "Change requires confirmation",
7707                     action.result.errorMsg,
7708                     function(r) {
7709                         if (r != 'yes') {
7710                             return;
7711                         }
7712                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7713                     }
7714
7715                 );
7716                 */
7717
7718
7719                 return;
7720             }
7721
7722             Roo.callback(o.failure, o.scope, [this, action]);
7723             // show an error message if no failed handler is set..
7724             if (!this.hasListener('actionfailed')) {
7725                 Roo.log("need to add dialog support");
7726                 /*
7727                 Roo.MessageBox.alert("Error",
7728                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7729                         action.result.errorMsg :
7730                         "Saving Failed, please check your entries or try again"
7731                 );
7732                 */
7733             }
7734
7735             this.fireEvent('actionfailed', this, action);
7736         }
7737
7738     },
7739     /**
7740      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7741      * @param {String} id The value to search for
7742      * @return Field
7743      */
7744     findField : function(id){
7745         var items = this.getItems();
7746         var field = items.get(id);
7747         if(!field){
7748              items.each(function(f){
7749                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7750                     field = f;
7751                     return false;
7752                 }
7753                 return true;
7754             });
7755         }
7756         return field || null;
7757     },
7758      /**
7759      * Mark fields in this form invalid in bulk.
7760      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7761      * @return {BasicForm} this
7762      */
7763     markInvalid : function(errors){
7764         if(errors instanceof Array){
7765             for(var i = 0, len = errors.length; i < len; i++){
7766                 var fieldError = errors[i];
7767                 var f = this.findField(fieldError.id);
7768                 if(f){
7769                     f.markInvalid(fieldError.msg);
7770                 }
7771             }
7772         }else{
7773             var field, id;
7774             for(id in errors){
7775                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7776                     field.markInvalid(errors[id]);
7777                 }
7778             }
7779         }
7780         //Roo.each(this.childForms || [], function (f) {
7781         //    f.markInvalid(errors);
7782         //});
7783
7784         return this;
7785     },
7786
7787     /**
7788      * Set values for fields in this form in bulk.
7789      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7790      * @return {BasicForm} this
7791      */
7792     setValues : function(values){
7793         if(values instanceof Array){ // array of objects
7794             for(var i = 0, len = values.length; i < len; i++){
7795                 var v = values[i];
7796                 var f = this.findField(v.id);
7797                 if(f){
7798                     f.setValue(v.value);
7799                     if(this.trackResetOnLoad){
7800                         f.originalValue = f.getValue();
7801                     }
7802                 }
7803             }
7804         }else{ // object hash
7805             var field, id;
7806             for(id in values){
7807                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7808
7809                     if (field.setFromData &&
7810                         field.valueField &&
7811                         field.displayField &&
7812                         // combos' with local stores can
7813                         // be queried via setValue()
7814                         // to set their value..
7815                         (field.store && !field.store.isLocal)
7816                         ) {
7817                         // it's a combo
7818                         var sd = { };
7819                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7820                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7821                         field.setFromData(sd);
7822
7823                     } else {
7824                         field.setValue(values[id]);
7825                     }
7826
7827
7828                     if(this.trackResetOnLoad){
7829                         field.originalValue = field.getValue();
7830                     }
7831                 }
7832             }
7833         }
7834
7835         //Roo.each(this.childForms || [], function (f) {
7836         //    f.setValues(values);
7837         //});
7838
7839         return this;
7840     },
7841
7842     /**
7843      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7844      * they are returned as an array.
7845      * @param {Boolean} asString
7846      * @return {Object}
7847      */
7848     getValues : function(asString){
7849         //if (this.childForms) {
7850             // copy values from the child forms
7851         //    Roo.each(this.childForms, function (f) {
7852         //        this.setValues(f.getValues());
7853         //    }, this);
7854         //}
7855
7856
7857
7858         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7859         if(asString === true){
7860             return fs;
7861         }
7862         return Roo.urlDecode(fs);
7863     },
7864
7865     /**
7866      * Returns the fields in this form as an object with key/value pairs.
7867      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7868      * @return {Object}
7869      */
7870     getFieldValues : function(with_hidden)
7871     {
7872         var items = this.getItems();
7873         var ret = {};
7874         items.each(function(f){
7875             if (!f.getName()) {
7876                 return;
7877             }
7878             var v = f.getValue();
7879             if (f.inputType =='radio') {
7880                 if (typeof(ret[f.getName()]) == 'undefined') {
7881                     ret[f.getName()] = ''; // empty..
7882                 }
7883
7884                 if (!f.el.dom.checked) {
7885                     return;
7886
7887                 }
7888                 v = f.el.dom.value;
7889
7890             }
7891
7892             // not sure if this supported any more..
7893             if ((typeof(v) == 'object') && f.getRawValue) {
7894                 v = f.getRawValue() ; // dates..
7895             }
7896             // combo boxes where name != hiddenName...
7897             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7898                 ret[f.name] = f.getRawValue();
7899             }
7900             ret[f.getName()] = v;
7901         });
7902
7903         return ret;
7904     },
7905
7906     /**
7907      * Clears all invalid messages in this form.
7908      * @return {BasicForm} this
7909      */
7910     clearInvalid : function(){
7911         var items = this.getItems();
7912
7913         items.each(function(f){
7914            f.clearInvalid();
7915         });
7916
7917
7918
7919         return this;
7920     },
7921
7922     /**
7923      * Resets this form.
7924      * @return {BasicForm} this
7925      */
7926     reset : function(){
7927         var items = this.getItems();
7928         items.each(function(f){
7929             f.reset();
7930         });
7931
7932         Roo.each(this.childForms || [], function (f) {
7933             f.reset();
7934         });
7935
7936
7937         return this;
7938     },
7939     getItems : function()
7940     {
7941         var r=new Roo.util.MixedCollection(false, function(o){
7942             return o.id || (o.id = Roo.id());
7943         });
7944         var iter = function(el) {
7945             if (el.inputEl) {
7946                 r.add(el);
7947             }
7948             if (!el.items) {
7949                 return;
7950             }
7951             Roo.each(el.items,function(e) {
7952                 iter(e);
7953             });
7954
7955
7956         };
7957
7958         iter(this);
7959         return r;
7960
7961
7962
7963
7964     }
7965
7966 });
7967
7968 Roo.apply(Roo.bootstrap.Form, {
7969     
7970     popover : {
7971         
7972         isApplied : false,
7973         
7974         isMasked : false,
7975         
7976         form : false,
7977         
7978         target : false,
7979         
7980         oIndex : false,
7981         
7982         toolTip : false,
7983         
7984         intervalID : false,
7985     
7986         apply : function()
7987         {
7988             if(this.isApplied){
7989                 return;
7990             }
7991             
7992             this.toolTip = new Roo.bootstrap.Tooltip({
7993                 cls : 'roo-form-error-popover',
7994                 alignment : {
7995                     'left' : ['r-l', [-2,0], 'right'],
7996                     'right' : ['l-r', [2,0], 'left'],
7997                     'bottom' : ['tl-bl', [0,2], 'top'],
7998                     'top' : [ 'bl-tl', [0,-2], 'bottom']
7999                 }
8000             });
8001             
8002             this.toolTip.render(Roo.get(document.body));
8003
8004             this.toolTip.el.setVisibilityMode(Roo.Element.DISPLAY);
8005             
8006             Roo.get(document.body).on('click', function(){
8007                 this.unmask();
8008             }, this);
8009             
8010             this.isApplied = true
8011         },
8012         
8013         mask : function(form, target)
8014         {
8015             this.form = form;
8016             
8017             this.target = target;
8018             
8019             if(!this.form.errPopover){
8020                 return;
8021             }
8022
8023             this.oIndex = target.el.getStyle('z-index');
8024             
8025             this.target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8026         
8027             this.target.el.addClass('roo-invalid-outline');
8028             
8029             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8030             
8031             var scrolled = scrollable.getScroll();
8032             
8033             var ot = this.target.el.calcOffsetsTo(scrollable);
8034             
8035             var scrollTo = 0;
8036             
8037             if(ot[1] <= scrolled.top){
8038                 scrollTo = ot[1] - 100;
8039             } else {
8040                 scrollTo = ot[1] + Roo.lib.Dom.getViewHeight() - 100;
8041             }
8042             
8043             scrollable.scrollTo('top', scrollTo);
8044             
8045             this.toolTip.bindEl = this.target.el;
8046         
8047             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8048
8049             var tip = this.target.blankText;
8050
8051             if(this.target.getValue() !== '' && this.target.regexText.length){
8052                 tip = this.target.regexText;
8053             }
8054
8055             this.toolTip.show(tip);
8056             
8057             this.intervalID = window.setInterval(function() {
8058                 Roo.bootstrap.Form.popover.unmask();
8059             }, 10000);
8060
8061             window.onwheel = function(){ return false;};
8062             
8063             (function(){ this.isMasked = true; }).defer(500, this);
8064             
8065         },
8066         
8067         unmask : function()
8068         {
8069             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errPopover){
8070                 return;
8071             }
8072             
8073             if(this.oIndex){
8074                 this.target.el.setStyle('z-index', this.oIndex);
8075             }
8076             
8077             this.target.el.removeClass('roo-invalid-outline');
8078             
8079             this.toolTip.hide();
8080             
8081             this.toolTip.el.hide();
8082             
8083             window.onwheel = function(){ return true;};
8084             
8085             if(this.intervalID){
8086                 window.clearInterval(this.intervalID);
8087                 this.intervalID = false;
8088             }
8089             
8090             this.isMasked = false;
8091             
8092         }
8093         
8094     }
8095     
8096 });
8097
8098 /*
8099  * Based on:
8100  * Ext JS Library 1.1.1
8101  * Copyright(c) 2006-2007, Ext JS, LLC.
8102  *
8103  * Originally Released Under LGPL - original licence link has changed is not relivant.
8104  *
8105  * Fork - LGPL
8106  * <script type="text/javascript">
8107  */
8108 /**
8109  * @class Roo.form.VTypes
8110  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8111  * @singleton
8112  */
8113 Roo.form.VTypes = function(){
8114     // closure these in so they are only created once.
8115     var alpha = /^[a-zA-Z_]+$/;
8116     var alphanum = /^[a-zA-Z0-9_]+$/;
8117     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8118     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8119
8120     // All these messages and functions are configurable
8121     return {
8122         /**
8123          * The function used to validate email addresses
8124          * @param {String} value The email address
8125          */
8126         'email' : function(v){
8127             return email.test(v);
8128         },
8129         /**
8130          * The error text to display when the email validation function returns false
8131          * @type String
8132          */
8133         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8134         /**
8135          * The keystroke filter mask to be applied on email input
8136          * @type RegExp
8137          */
8138         'emailMask' : /[a-z0-9_\.\-@]/i,
8139
8140         /**
8141          * The function used to validate URLs
8142          * @param {String} value The URL
8143          */
8144         'url' : function(v){
8145             return url.test(v);
8146         },
8147         /**
8148          * The error text to display when the url validation function returns false
8149          * @type String
8150          */
8151         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8152         
8153         /**
8154          * The function used to validate alpha values
8155          * @param {String} value The value
8156          */
8157         'alpha' : function(v){
8158             return alpha.test(v);
8159         },
8160         /**
8161          * The error text to display when the alpha validation function returns false
8162          * @type String
8163          */
8164         'alphaText' : 'This field should only contain letters and _',
8165         /**
8166          * The keystroke filter mask to be applied on alpha input
8167          * @type RegExp
8168          */
8169         'alphaMask' : /[a-z_]/i,
8170
8171         /**
8172          * The function used to validate alphanumeric values
8173          * @param {String} value The value
8174          */
8175         'alphanum' : function(v){
8176             return alphanum.test(v);
8177         },
8178         /**
8179          * The error text to display when the alphanumeric validation function returns false
8180          * @type String
8181          */
8182         'alphanumText' : 'This field should only contain letters, numbers and _',
8183         /**
8184          * The keystroke filter mask to be applied on alphanumeric input
8185          * @type RegExp
8186          */
8187         'alphanumMask' : /[a-z0-9_]/i
8188     };
8189 }();/*
8190  * - LGPL
8191  *
8192  * Input
8193  * 
8194  */
8195
8196 /**
8197  * @class Roo.bootstrap.Input
8198  * @extends Roo.bootstrap.Component
8199  * Bootstrap Input class
8200  * @cfg {Boolean} disabled is it disabled
8201  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8202  * @cfg {String} name name of the input
8203  * @cfg {string} fieldLabel - the label associated
8204  * @cfg {string} placeholder - placeholder to put in text.
8205  * @cfg {string}  before - input group add on before
8206  * @cfg {string} after - input group add on after
8207  * @cfg {string} size - (lg|sm) or leave empty..
8208  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8209  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8210  * @cfg {Number} md colspan out of 12 for computer-sized screens
8211  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8212  * @cfg {string} value default value of the input
8213  * @cfg {Number} labelWidth set the width of label 
8214  * @cfg {Number} labellg set the width of label (1-12)
8215  * @cfg {Number} labelmd set the width of label (1-12)
8216  * @cfg {Number} labelsm set the width of label (1-12)
8217  * @cfg {Number} labelxs set the width of label (1-12)
8218  * @cfg {String} labelAlign (top|left)
8219  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8220  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8221  * @cfg {String} indicatorpos (left|right) default left
8222
8223  * @cfg {String} align (left|center|right) Default left
8224  * @cfg {Boolean} forceFeedback (true|false) Default false
8225  * 
8226  * 
8227  * 
8228  * 
8229  * @constructor
8230  * Create a new Input
8231  * @param {Object} config The config object
8232  */
8233
8234 Roo.bootstrap.Input = function(config){
8235     
8236     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8237     
8238     this.addEvents({
8239         /**
8240          * @event focus
8241          * Fires when this field receives input focus.
8242          * @param {Roo.form.Field} this
8243          */
8244         focus : true,
8245         /**
8246          * @event blur
8247          * Fires when this field loses input focus.
8248          * @param {Roo.form.Field} this
8249          */
8250         blur : true,
8251         /**
8252          * @event specialkey
8253          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8254          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8255          * @param {Roo.form.Field} this
8256          * @param {Roo.EventObject} e The event object
8257          */
8258         specialkey : true,
8259         /**
8260          * @event change
8261          * Fires just before the field blurs if the field value has changed.
8262          * @param {Roo.form.Field} this
8263          * @param {Mixed} newValue The new value
8264          * @param {Mixed} oldValue The original value
8265          */
8266         change : true,
8267         /**
8268          * @event invalid
8269          * Fires after the field has been marked as invalid.
8270          * @param {Roo.form.Field} this
8271          * @param {String} msg The validation message
8272          */
8273         invalid : true,
8274         /**
8275          * @event valid
8276          * Fires after the field has been validated with no errors.
8277          * @param {Roo.form.Field} this
8278          */
8279         valid : true,
8280          /**
8281          * @event keyup
8282          * Fires after the key up
8283          * @param {Roo.form.Field} this
8284          * @param {Roo.EventObject}  e The event Object
8285          */
8286         keyup : true
8287     });
8288 };
8289
8290 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8291      /**
8292      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8293       automatic validation (defaults to "keyup").
8294      */
8295     validationEvent : "keyup",
8296      /**
8297      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8298      */
8299     validateOnBlur : true,
8300     /**
8301      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8302      */
8303     validationDelay : 250,
8304      /**
8305      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8306      */
8307     focusClass : "x-form-focus",  // not needed???
8308     
8309        
8310     /**
8311      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8312      */
8313     invalidClass : "has-warning",
8314     
8315     /**
8316      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8317      */
8318     validClass : "has-success",
8319     
8320     /**
8321      * @cfg {Boolean} hasFeedback (true|false) default true
8322      */
8323     hasFeedback : true,
8324     
8325     /**
8326      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8327      */
8328     invalidFeedbackClass : "glyphicon-warning-sign",
8329     
8330     /**
8331      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8332      */
8333     validFeedbackClass : "glyphicon-ok",
8334     
8335     /**
8336      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8337      */
8338     selectOnFocus : false,
8339     
8340      /**
8341      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8342      */
8343     maskRe : null,
8344        /**
8345      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8346      */
8347     vtype : null,
8348     
8349       /**
8350      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8351      */
8352     disableKeyFilter : false,
8353     
8354        /**
8355      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8356      */
8357     disabled : false,
8358      /**
8359      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8360      */
8361     allowBlank : true,
8362     /**
8363      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8364      */
8365     blankText : "Please complete this mandatory field",
8366     
8367      /**
8368      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8369      */
8370     minLength : 0,
8371     /**
8372      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8373      */
8374     maxLength : Number.MAX_VALUE,
8375     /**
8376      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8377      */
8378     minLengthText : "The minimum length for this field is {0}",
8379     /**
8380      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8381      */
8382     maxLengthText : "The maximum length for this field is {0}",
8383   
8384     
8385     /**
8386      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8387      * If available, this function will be called only after the basic validators all return true, and will be passed the
8388      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8389      */
8390     validator : null,
8391     /**
8392      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8393      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8394      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8395      */
8396     regex : null,
8397     /**
8398      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8399      */
8400     regexText : "",
8401     
8402     autocomplete: false,
8403     
8404     
8405     fieldLabel : '',
8406     inputType : 'text',
8407     
8408     name : false,
8409     placeholder: false,
8410     before : false,
8411     after : false,
8412     size : false,
8413     hasFocus : false,
8414     preventMark: false,
8415     isFormField : true,
8416     value : '',
8417     labelWidth : 2,
8418     labelAlign : false,
8419     readOnly : false,
8420     align : false,
8421     formatedValue : false,
8422     forceFeedback : false,
8423     
8424     indicatorpos : 'left',
8425     
8426     labellg : 0,
8427     labelmd : 0,
8428     labelsm : 0,
8429     labelxs : 0,
8430     
8431     parentLabelAlign : function()
8432     {
8433         var parent = this;
8434         while (parent.parent()) {
8435             parent = parent.parent();
8436             if (typeof(parent.labelAlign) !='undefined') {
8437                 return parent.labelAlign;
8438             }
8439         }
8440         return 'left';
8441         
8442     },
8443     
8444     getAutoCreate : function()
8445     {
8446         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8447         
8448         var id = Roo.id();
8449         
8450         var cfg = {};
8451         
8452         if(this.inputType != 'hidden'){
8453             cfg.cls = 'form-group' //input-group
8454         }
8455         
8456         var input =  {
8457             tag: 'input',
8458             id : id,
8459             type : this.inputType,
8460             value : this.value,
8461             cls : 'form-control',
8462             placeholder : this.placeholder || '',
8463             autocomplete : this.autocomplete || 'new-password'
8464         };
8465         
8466         if(this.align){
8467             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8468         }
8469         
8470         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8471             input.maxLength = this.maxLength;
8472         }
8473         
8474         if (this.disabled) {
8475             input.disabled=true;
8476         }
8477         
8478         if (this.readOnly) {
8479             input.readonly=true;
8480         }
8481         
8482         if (this.name) {
8483             input.name = this.name;
8484         }
8485         
8486         if (this.size) {
8487             input.cls += ' input-' + this.size;
8488         }
8489         
8490         var settings=this;
8491         ['xs','sm','md','lg'].map(function(size){
8492             if (settings[size]) {
8493                 cfg.cls += ' col-' + size + '-' + settings[size];
8494             }
8495         });
8496         
8497         var inputblock = input;
8498         
8499         var feedback = {
8500             tag: 'span',
8501             cls: 'glyphicon form-control-feedback'
8502         };
8503             
8504         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8505             
8506             inputblock = {
8507                 cls : 'has-feedback',
8508                 cn :  [
8509                     input,
8510                     feedback
8511                 ] 
8512             };  
8513         }
8514         
8515         if (this.before || this.after) {
8516             
8517             inputblock = {
8518                 cls : 'input-group',
8519                 cn :  [] 
8520             };
8521             
8522             if (this.before && typeof(this.before) == 'string') {
8523                 
8524                 inputblock.cn.push({
8525                     tag :'span',
8526                     cls : 'roo-input-before input-group-addon',
8527                     html : this.before
8528                 });
8529             }
8530             if (this.before && typeof(this.before) == 'object') {
8531                 this.before = Roo.factory(this.before);
8532                 
8533                 inputblock.cn.push({
8534                     tag :'span',
8535                     cls : 'roo-input-before input-group-' +
8536                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8537                 });
8538             }
8539             
8540             inputblock.cn.push(input);
8541             
8542             if (this.after && typeof(this.after) == 'string') {
8543                 inputblock.cn.push({
8544                     tag :'span',
8545                     cls : 'roo-input-after input-group-addon',
8546                     html : this.after
8547                 });
8548             }
8549             if (this.after && typeof(this.after) == 'object') {
8550                 this.after = Roo.factory(this.after);
8551                 
8552                 inputblock.cn.push({
8553                     tag :'span',
8554                     cls : 'roo-input-after input-group-' +
8555                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8556                 });
8557             }
8558             
8559             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8560                 inputblock.cls += ' has-feedback';
8561                 inputblock.cn.push(feedback);
8562             }
8563         };
8564         
8565         if (align ==='left' && this.fieldLabel.length) {
8566             
8567             cfg.cn = [
8568                 {
8569                     tag : 'i',
8570                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8571                     tooltip : 'This field is required'
8572                 },
8573                 {
8574                     tag: 'label',
8575                     'for' :  id,
8576                     cls : 'control-label',
8577                     html : this.fieldLabel
8578
8579                 },
8580                 {
8581                     cls : "", 
8582                     cn: [
8583                         inputblock
8584                     ]
8585                 }
8586             ];
8587             
8588             var labelCfg = cfg.cn[1];
8589             var contentCfg = cfg.cn[2];
8590             
8591             if(this.indicatorpos == 'right'){
8592                 cfg.cn = [
8593                     {
8594                         tag: 'label',
8595                         'for' :  id,
8596                         cls : 'control-label',
8597                         html : this.fieldLabel
8598
8599                     },
8600                     {
8601                         tag : 'i',
8602                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8603                         tooltip : 'This field is required'
8604                     },
8605                     {
8606                         cls : "",
8607                         cn: [
8608                             inputblock
8609                         ]
8610                     }
8611
8612                 ];
8613                 
8614                 labelCfg = cfg.cn[0];
8615                 contentCfg = cfg.cn[2];
8616             
8617             }
8618             
8619             if(this.labelWidth > 12){
8620                 labelCfg.style = "width: " + this.labelWidth + 'px';
8621             }
8622             
8623             if(this.labelWidth < 13 && this.labelmd == 0){
8624                 this.labelmd = this.labelWidth;
8625             }
8626             
8627             if(this.labellg > 0){
8628                 labelCfg.cls += ' col-lg-' + this.labellg;
8629                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8630             }
8631             
8632             if(this.labelmd > 0){
8633                 labelCfg.cls += ' col-md-' + this.labelmd;
8634                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8635             }
8636             
8637             if(this.labelsm > 0){
8638                 labelCfg.cls += ' col-sm-' + this.labelsm;
8639                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8640             }
8641             
8642             if(this.labelxs > 0){
8643                 labelCfg.cls += ' col-xs-' + this.labelxs;
8644                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8645             }
8646             
8647             
8648         } else if ( this.fieldLabel.length) {
8649                 
8650             cfg.cn = [
8651                 {
8652                     tag : 'i',
8653                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8654                     tooltip : 'This field is required'
8655                 },
8656                 {
8657                     tag: 'label',
8658                    //cls : 'input-group-addon',
8659                     html : this.fieldLabel
8660
8661                 },
8662
8663                inputblock
8664
8665            ];
8666            
8667            if(this.indicatorpos == 'right'){
8668                 
8669                 cfg.cn = [
8670                     {
8671                         tag: 'label',
8672                        //cls : 'input-group-addon',
8673                         html : this.fieldLabel
8674
8675                     },
8676                     {
8677                         tag : 'i',
8678                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8679                         tooltip : 'This field is required'
8680                     },
8681
8682                    inputblock
8683
8684                ];
8685
8686             }
8687
8688         } else {
8689             
8690             cfg.cn = [
8691
8692                     inputblock
8693
8694             ];
8695                 
8696                 
8697         };
8698         
8699         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8700            cfg.cls += ' navbar-form';
8701         }
8702         
8703         if (this.parentType === 'NavGroup') {
8704            cfg.cls += ' navbar-form';
8705            cfg.tag = 'li';
8706         }
8707         
8708         return cfg;
8709         
8710     },
8711     /**
8712      * return the real input element.
8713      */
8714     inputEl: function ()
8715     {
8716         return this.el.select('input.form-control',true).first();
8717     },
8718     
8719     tooltipEl : function()
8720     {
8721         return this.inputEl();
8722     },
8723     
8724     indicatorEl : function()
8725     {
8726         var indicator = this.el.select('i.roo-required-indicator',true).first();
8727         
8728         if(!indicator){
8729             return false;
8730         }
8731         
8732         return indicator;
8733         
8734     },
8735     
8736     setDisabled : function(v)
8737     {
8738         var i  = this.inputEl().dom;
8739         if (!v) {
8740             i.removeAttribute('disabled');
8741             return;
8742             
8743         }
8744         i.setAttribute('disabled','true');
8745     },
8746     initEvents : function()
8747     {
8748           
8749         this.inputEl().on("keydown" , this.fireKey,  this);
8750         this.inputEl().on("focus", this.onFocus,  this);
8751         this.inputEl().on("blur", this.onBlur,  this);
8752         
8753         this.inputEl().relayEvent('keyup', this);
8754         
8755         this.indicator = this.indicatorEl();
8756         
8757         if(this.indicator){
8758             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8759             this.indicator.hide();
8760         }
8761  
8762         // reference to original value for reset
8763         this.originalValue = this.getValue();
8764         //Roo.form.TextField.superclass.initEvents.call(this);
8765         if(this.validationEvent == 'keyup'){
8766             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8767             this.inputEl().on('keyup', this.filterValidation, this);
8768         }
8769         else if(this.validationEvent !== false){
8770             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8771         }
8772         
8773         if(this.selectOnFocus){
8774             this.on("focus", this.preFocus, this);
8775             
8776         }
8777         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8778             this.inputEl().on("keypress", this.filterKeys, this);
8779         } else {
8780             this.inputEl().relayEvent('keypress', this);
8781         }
8782        /* if(this.grow){
8783             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8784             this.el.on("click", this.autoSize,  this);
8785         }
8786         */
8787         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8788             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8789         }
8790         
8791         if (typeof(this.before) == 'object') {
8792             this.before.render(this.el.select('.roo-input-before',true).first());
8793         }
8794         if (typeof(this.after) == 'object') {
8795             this.after.render(this.el.select('.roo-input-after',true).first());
8796         }
8797         
8798         
8799     },
8800     filterValidation : function(e){
8801         if(!e.isNavKeyPress()){
8802             this.validationTask.delay(this.validationDelay);
8803         }
8804     },
8805      /**
8806      * Validates the field value
8807      * @return {Boolean} True if the value is valid, else false
8808      */
8809     validate : function(){
8810         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8811         if(this.disabled || this.validateValue(this.getRawValue())){
8812             this.markValid();
8813             return true;
8814         }
8815         
8816         this.markInvalid();
8817         return false;
8818     },
8819     
8820     
8821     /**
8822      * Validates a value according to the field's validation rules and marks the field as invalid
8823      * if the validation fails
8824      * @param {Mixed} value The value to validate
8825      * @return {Boolean} True if the value is valid, else false
8826      */
8827     validateValue : function(value){
8828         if(value.length < 1)  { // if it's blank
8829             if(this.allowBlank){
8830                 return true;
8831             }
8832             return false;
8833         }
8834         
8835         if(value.length < this.minLength){
8836             return false;
8837         }
8838         if(value.length > this.maxLength){
8839             return false;
8840         }
8841         if(this.vtype){
8842             var vt = Roo.form.VTypes;
8843             if(!vt[this.vtype](value, this)){
8844                 return false;
8845             }
8846         }
8847         if(typeof this.validator == "function"){
8848             var msg = this.validator(value);
8849             if(msg !== true){
8850                 return false;
8851             }
8852         }
8853         
8854         if(this.regex && !this.regex.test(value)){
8855             return false;
8856         }
8857         
8858         return true;
8859     },
8860
8861     
8862     
8863      // private
8864     fireKey : function(e){
8865         //Roo.log('field ' + e.getKey());
8866         if(e.isNavKeyPress()){
8867             this.fireEvent("specialkey", this, e);
8868         }
8869     },
8870     focus : function (selectText){
8871         if(this.rendered){
8872             this.inputEl().focus();
8873             if(selectText === true){
8874                 this.inputEl().dom.select();
8875             }
8876         }
8877         return this;
8878     } ,
8879     
8880     onFocus : function(){
8881         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8882            // this.el.addClass(this.focusClass);
8883         }
8884         if(!this.hasFocus){
8885             this.hasFocus = true;
8886             this.startValue = this.getValue();
8887             this.fireEvent("focus", this);
8888         }
8889     },
8890     
8891     beforeBlur : Roo.emptyFn,
8892
8893     
8894     // private
8895     onBlur : function(){
8896         this.beforeBlur();
8897         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8898             //this.el.removeClass(this.focusClass);
8899         }
8900         this.hasFocus = false;
8901         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8902             this.validate();
8903         }
8904         var v = this.getValue();
8905         if(String(v) !== String(this.startValue)){
8906             this.fireEvent('change', this, v, this.startValue);
8907         }
8908         this.fireEvent("blur", this);
8909     },
8910     
8911     /**
8912      * Resets the current field value to the originally loaded value and clears any validation messages
8913      */
8914     reset : function(){
8915         this.setValue(this.originalValue);
8916         this.validate();
8917     },
8918      /**
8919      * Returns the name of the field
8920      * @return {Mixed} name The name field
8921      */
8922     getName: function(){
8923         return this.name;
8924     },
8925      /**
8926      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8927      * @return {Mixed} value The field value
8928      */
8929     getValue : function(){
8930         
8931         var v = this.inputEl().getValue();
8932         
8933         return v;
8934     },
8935     /**
8936      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8937      * @return {Mixed} value The field value
8938      */
8939     getRawValue : function(){
8940         var v = this.inputEl().getValue();
8941         
8942         return v;
8943     },
8944     
8945     /**
8946      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8947      * @param {Mixed} value The value to set
8948      */
8949     setRawValue : function(v){
8950         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8951     },
8952     
8953     selectText : function(start, end){
8954         var v = this.getRawValue();
8955         if(v.length > 0){
8956             start = start === undefined ? 0 : start;
8957             end = end === undefined ? v.length : end;
8958             var d = this.inputEl().dom;
8959             if(d.setSelectionRange){
8960                 d.setSelectionRange(start, end);
8961             }else if(d.createTextRange){
8962                 var range = d.createTextRange();
8963                 range.moveStart("character", start);
8964                 range.moveEnd("character", v.length-end);
8965                 range.select();
8966             }
8967         }
8968     },
8969     
8970     /**
8971      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8972      * @param {Mixed} value The value to set
8973      */
8974     setValue : function(v){
8975         this.value = v;
8976         if(this.rendered){
8977             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8978             this.validate();
8979         }
8980     },
8981     
8982     /*
8983     processValue : function(value){
8984         if(this.stripCharsRe){
8985             var newValue = value.replace(this.stripCharsRe, '');
8986             if(newValue !== value){
8987                 this.setRawValue(newValue);
8988                 return newValue;
8989             }
8990         }
8991         return value;
8992     },
8993   */
8994     preFocus : function(){
8995         
8996         if(this.selectOnFocus){
8997             this.inputEl().dom.select();
8998         }
8999     },
9000     filterKeys : function(e){
9001         var k = e.getKey();
9002         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9003             return;
9004         }
9005         var c = e.getCharCode(), cc = String.fromCharCode(c);
9006         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9007             return;
9008         }
9009         if(!this.maskRe.test(cc)){
9010             e.stopEvent();
9011         }
9012     },
9013      /**
9014      * Clear any invalid styles/messages for this field
9015      */
9016     clearInvalid : function(){
9017         
9018         if(!this.el || this.preventMark){ // not rendered
9019             return;
9020         }
9021         
9022         if(this.indicator){
9023             this.indicator.hide();
9024         }
9025         
9026         this.el.removeClass(this.invalidClass);
9027         
9028         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9029             
9030             var feedback = this.el.select('.form-control-feedback', true).first();
9031             
9032             if(feedback){
9033                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9034             }
9035             
9036         }
9037         
9038         this.fireEvent('valid', this);
9039     },
9040     
9041      /**
9042      * Mark this field as valid
9043      */
9044     markValid : function()
9045     {
9046         if(!this.el  || this.preventMark){ // not rendered
9047             return;
9048         }
9049         
9050         this.el.removeClass([this.invalidClass, this.validClass]);
9051         
9052         var feedback = this.el.select('.form-control-feedback', true).first();
9053             
9054         if(feedback){
9055             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9056         }
9057
9058         if(this.disabled){
9059             return;
9060         }
9061         
9062         if(this.allowBlank && !this.getRawValue().length){
9063             return;
9064         }
9065         
9066         if(this.indicator){
9067             this.indicator.hide();
9068         }
9069         
9070         this.el.addClass(this.validClass);
9071         
9072         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9073             
9074             var feedback = this.el.select('.form-control-feedback', true).first();
9075             
9076             if(feedback){
9077                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9078                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9079             }
9080             
9081         }
9082         
9083         this.fireEvent('valid', this);
9084     },
9085     
9086      /**
9087      * Mark this field as invalid
9088      * @param {String} msg The validation message
9089      */
9090     markInvalid : function(msg)
9091     {
9092         if(!this.el  || this.preventMark){ // not rendered
9093             return;
9094         }
9095         
9096         this.el.removeClass([this.invalidClass, this.validClass]);
9097         
9098         var feedback = this.el.select('.form-control-feedback', true).first();
9099             
9100         if(feedback){
9101             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9102         }
9103
9104         if(this.disabled){
9105             return;
9106         }
9107         
9108         if(this.allowBlank && !this.getRawValue().length){
9109             return;
9110         }
9111         
9112         if(this.indicator){
9113             this.indicator.show();
9114         }
9115         
9116         this.el.addClass(this.invalidClass);
9117         
9118         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9119             
9120             var feedback = this.el.select('.form-control-feedback', true).first();
9121             
9122             if(feedback){
9123                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9124                 
9125                 if(this.getValue().length || this.forceFeedback){
9126                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9127                 }
9128                 
9129             }
9130             
9131         }
9132         
9133         this.fireEvent('invalid', this, msg);
9134     },
9135     // private
9136     SafariOnKeyDown : function(event)
9137     {
9138         // this is a workaround for a password hang bug on chrome/ webkit.
9139         if (this.inputEl().dom.type != 'password') {
9140             return;
9141         }
9142         
9143         var isSelectAll = false;
9144         
9145         if(this.inputEl().dom.selectionEnd > 0){
9146             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9147         }
9148         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9149             event.preventDefault();
9150             this.setValue('');
9151             return;
9152         }
9153         
9154         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9155             
9156             event.preventDefault();
9157             // this is very hacky as keydown always get's upper case.
9158             //
9159             var cc = String.fromCharCode(event.getCharCode());
9160             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9161             
9162         }
9163     },
9164     adjustWidth : function(tag, w){
9165         tag = tag.toLowerCase();
9166         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9167             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9168                 if(tag == 'input'){
9169                     return w + 2;
9170                 }
9171                 if(tag == 'textarea'){
9172                     return w-2;
9173                 }
9174             }else if(Roo.isOpera){
9175                 if(tag == 'input'){
9176                     return w + 2;
9177                 }
9178                 if(tag == 'textarea'){
9179                     return w-2;
9180                 }
9181             }
9182         }
9183         return w;
9184     }
9185     
9186 });
9187
9188  
9189 /*
9190  * - LGPL
9191  *
9192  * Input
9193  * 
9194  */
9195
9196 /**
9197  * @class Roo.bootstrap.TextArea
9198  * @extends Roo.bootstrap.Input
9199  * Bootstrap TextArea class
9200  * @cfg {Number} cols Specifies the visible width of a text area
9201  * @cfg {Number} rows Specifies the visible number of lines in a text area
9202  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9203  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9204  * @cfg {string} html text
9205  * 
9206  * @constructor
9207  * Create a new TextArea
9208  * @param {Object} config The config object
9209  */
9210
9211 Roo.bootstrap.TextArea = function(config){
9212     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9213    
9214 };
9215
9216 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9217      
9218     cols : false,
9219     rows : 5,
9220     readOnly : false,
9221     warp : 'soft',
9222     resize : false,
9223     value: false,
9224     html: false,
9225     
9226     getAutoCreate : function(){
9227         
9228         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9229         
9230         var id = Roo.id();
9231         
9232         var cfg = {};
9233         
9234         var input =  {
9235             tag: 'textarea',
9236             id : id,
9237             warp : this.warp,
9238             rows : this.rows,
9239             value : this.value || '',
9240             html: this.html || '',
9241             cls : 'form-control',
9242             placeholder : this.placeholder || '' 
9243             
9244         };
9245         
9246         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9247             input.maxLength = this.maxLength;
9248         }
9249         
9250         if(this.resize){
9251             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9252         }
9253         
9254         if(this.cols){
9255             input.cols = this.cols;
9256         }
9257         
9258         if (this.readOnly) {
9259             input.readonly = true;
9260         }
9261         
9262         if (this.name) {
9263             input.name = this.name;
9264         }
9265         
9266         if (this.size) {
9267             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9268         }
9269         
9270         var settings=this;
9271         ['xs','sm','md','lg'].map(function(size){
9272             if (settings[size]) {
9273                 cfg.cls += ' col-' + size + '-' + settings[size];
9274             }
9275         });
9276         
9277         var inputblock = input;
9278         
9279         if(this.hasFeedback && !this.allowBlank){
9280             
9281             var feedback = {
9282                 tag: 'span',
9283                 cls: 'glyphicon form-control-feedback'
9284             };
9285
9286             inputblock = {
9287                 cls : 'has-feedback',
9288                 cn :  [
9289                     input,
9290                     feedback
9291                 ] 
9292             };  
9293         }
9294         
9295         
9296         if (this.before || this.after) {
9297             
9298             inputblock = {
9299                 cls : 'input-group',
9300                 cn :  [] 
9301             };
9302             if (this.before) {
9303                 inputblock.cn.push({
9304                     tag :'span',
9305                     cls : 'input-group-addon',
9306                     html : this.before
9307                 });
9308             }
9309             
9310             inputblock.cn.push(input);
9311             
9312             if(this.hasFeedback && !this.allowBlank){
9313                 inputblock.cls += ' has-feedback';
9314                 inputblock.cn.push(feedback);
9315             }
9316             
9317             if (this.after) {
9318                 inputblock.cn.push({
9319                     tag :'span',
9320                     cls : 'input-group-addon',
9321                     html : this.after
9322                 });
9323             }
9324             
9325         }
9326         
9327         if (align ==='left' && this.fieldLabel.length) {
9328             cfg.cn = [
9329                 {
9330                     tag: 'label',
9331                     'for' :  id,
9332                     cls : 'control-label',
9333                     html : this.fieldLabel
9334                 },
9335                 {
9336                     cls : "",
9337                     cn: [
9338                         inputblock
9339                     ]
9340                 }
9341
9342             ];
9343             
9344             if(this.labelWidth > 12){
9345                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9346             }
9347
9348             if(this.labelWidth < 13 && this.labelmd == 0){
9349                 this.labelmd = this.labelWidth;
9350             }
9351
9352             if(this.labellg > 0){
9353                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9354                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9355             }
9356
9357             if(this.labelmd > 0){
9358                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9359                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9360             }
9361
9362             if(this.labelsm > 0){
9363                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9364                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9365             }
9366
9367             if(this.labelxs > 0){
9368                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9369                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9370             }
9371             
9372         } else if ( this.fieldLabel.length) {
9373             cfg.cn = [
9374
9375                {
9376                    tag: 'label',
9377                    //cls : 'input-group-addon',
9378                    html : this.fieldLabel
9379
9380                },
9381
9382                inputblock
9383
9384            ];
9385
9386         } else {
9387
9388             cfg.cn = [
9389
9390                 inputblock
9391
9392             ];
9393                 
9394         }
9395         
9396         if (this.disabled) {
9397             input.disabled=true;
9398         }
9399         
9400         return cfg;
9401         
9402     },
9403     /**
9404      * return the real textarea element.
9405      */
9406     inputEl: function ()
9407     {
9408         return this.el.select('textarea.form-control',true).first();
9409     },
9410     
9411     /**
9412      * Clear any invalid styles/messages for this field
9413      */
9414     clearInvalid : function()
9415     {
9416         
9417         if(!this.el || this.preventMark){ // not rendered
9418             return;
9419         }
9420         
9421         var label = this.el.select('label', true).first();
9422         var icon = this.el.select('i.fa-star', true).first();
9423         
9424         if(label && icon){
9425             icon.remove();
9426         }
9427         
9428         this.el.removeClass(this.invalidClass);
9429         
9430         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9431             
9432             var feedback = this.el.select('.form-control-feedback', true).first();
9433             
9434             if(feedback){
9435                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9436             }
9437             
9438         }
9439         
9440         this.fireEvent('valid', this);
9441     },
9442     
9443      /**
9444      * Mark this field as valid
9445      */
9446     markValid : function()
9447     {
9448         if(!this.el  || this.preventMark){ // not rendered
9449             return;
9450         }
9451         
9452         this.el.removeClass([this.invalidClass, this.validClass]);
9453         
9454         var feedback = this.el.select('.form-control-feedback', true).first();
9455             
9456         if(feedback){
9457             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9458         }
9459
9460         if(this.disabled || this.allowBlank){
9461             return;
9462         }
9463         
9464         var label = this.el.select('label', true).first();
9465         var icon = this.el.select('i.fa-star', true).first();
9466         
9467         if(label && icon){
9468             icon.remove();
9469         }
9470         
9471         this.el.addClass(this.validClass);
9472         
9473         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9474             
9475             var feedback = this.el.select('.form-control-feedback', true).first();
9476             
9477             if(feedback){
9478                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9479                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9480             }
9481             
9482         }
9483         
9484         this.fireEvent('valid', this);
9485     },
9486     
9487      /**
9488      * Mark this field as invalid
9489      * @param {String} msg The validation message
9490      */
9491     markInvalid : function(msg)
9492     {
9493         if(!this.el  || this.preventMark){ // not rendered
9494             return;
9495         }
9496         
9497         this.el.removeClass([this.invalidClass, this.validClass]);
9498         
9499         var feedback = this.el.select('.form-control-feedback', true).first();
9500             
9501         if(feedback){
9502             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9503         }
9504
9505         if(this.disabled || this.allowBlank){
9506             return;
9507         }
9508         
9509         var label = this.el.select('label', true).first();
9510         var icon = this.el.select('i.fa-star', true).first();
9511         
9512         if(!this.getValue().length && label && !icon){
9513             this.el.createChild({
9514                 tag : 'i',
9515                 cls : 'text-danger fa fa-lg fa-star',
9516                 tooltip : 'This field is required',
9517                 style : 'margin-right:5px;'
9518             }, label, true);
9519         }
9520
9521         this.el.addClass(this.invalidClass);
9522         
9523         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9524             
9525             var feedback = this.el.select('.form-control-feedback', true).first();
9526             
9527             if(feedback){
9528                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9529                 
9530                 if(this.getValue().length || this.forceFeedback){
9531                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9532                 }
9533                 
9534             }
9535             
9536         }
9537         
9538         this.fireEvent('invalid', this, msg);
9539     }
9540 });
9541
9542  
9543 /*
9544  * - LGPL
9545  *
9546  * trigger field - base class for combo..
9547  * 
9548  */
9549  
9550 /**
9551  * @class Roo.bootstrap.TriggerField
9552  * @extends Roo.bootstrap.Input
9553  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9554  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9555  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9556  * for which you can provide a custom implementation.  For example:
9557  * <pre><code>
9558 var trigger = new Roo.bootstrap.TriggerField();
9559 trigger.onTriggerClick = myTriggerFn;
9560 trigger.applyTo('my-field');
9561 </code></pre>
9562  *
9563  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9564  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9565  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9566  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9567  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9568
9569  * @constructor
9570  * Create a new TriggerField.
9571  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9572  * to the base TextField)
9573  */
9574 Roo.bootstrap.TriggerField = function(config){
9575     this.mimicing = false;
9576     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9577 };
9578
9579 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9580     /**
9581      * @cfg {String} triggerClass A CSS class to apply to the trigger
9582      */
9583      /**
9584      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9585      */
9586     hideTrigger:false,
9587
9588     /**
9589      * @cfg {Boolean} removable (true|false) special filter default false
9590      */
9591     removable : false,
9592     
9593     /** @cfg {Boolean} grow @hide */
9594     /** @cfg {Number} growMin @hide */
9595     /** @cfg {Number} growMax @hide */
9596
9597     /**
9598      * @hide 
9599      * @method
9600      */
9601     autoSize: Roo.emptyFn,
9602     // private
9603     monitorTab : true,
9604     // private
9605     deferHeight : true,
9606
9607     
9608     actionMode : 'wrap',
9609     
9610     caret : false,
9611     
9612     
9613     getAutoCreate : function(){
9614        
9615         var align = this.labelAlign || this.parentLabelAlign();
9616         
9617         var id = Roo.id();
9618         
9619         var cfg = {
9620             cls: 'form-group' //input-group
9621         };
9622         
9623         
9624         var input =  {
9625             tag: 'input',
9626             id : id,
9627             type : this.inputType,
9628             cls : 'form-control',
9629             autocomplete: 'new-password',
9630             placeholder : this.placeholder || '' 
9631             
9632         };
9633         if (this.name) {
9634             input.name = this.name;
9635         }
9636         if (this.size) {
9637             input.cls += ' input-' + this.size;
9638         }
9639         
9640         if (this.disabled) {
9641             input.disabled=true;
9642         }
9643         
9644         var inputblock = input;
9645         
9646         if(this.hasFeedback && !this.allowBlank){
9647             
9648             var feedback = {
9649                 tag: 'span',
9650                 cls: 'glyphicon form-control-feedback'
9651             };
9652             
9653             if(this.removable && !this.editable && !this.tickable){
9654                 inputblock = {
9655                     cls : 'has-feedback',
9656                     cn :  [
9657                         inputblock,
9658                         {
9659                             tag: 'button',
9660                             html : 'x',
9661                             cls : 'roo-combo-removable-btn close'
9662                         },
9663                         feedback
9664                     ] 
9665                 };
9666             } else {
9667                 inputblock = {
9668                     cls : 'has-feedback',
9669                     cn :  [
9670                         inputblock,
9671                         feedback
9672                     ] 
9673                 };
9674             }
9675
9676         } else {
9677             if(this.removable && !this.editable && !this.tickable){
9678                 inputblock = {
9679                     cls : 'roo-removable',
9680                     cn :  [
9681                         inputblock,
9682                         {
9683                             tag: 'button',
9684                             html : 'x',
9685                             cls : 'roo-combo-removable-btn close'
9686                         }
9687                     ] 
9688                 };
9689             }
9690         }
9691         
9692         if (this.before || this.after) {
9693             
9694             inputblock = {
9695                 cls : 'input-group',
9696                 cn :  [] 
9697             };
9698             if (this.before) {
9699                 inputblock.cn.push({
9700                     tag :'span',
9701                     cls : 'input-group-addon',
9702                     html : this.before
9703                 });
9704             }
9705             
9706             inputblock.cn.push(input);
9707             
9708             if(this.hasFeedback && !this.allowBlank){
9709                 inputblock.cls += ' has-feedback';
9710                 inputblock.cn.push(feedback);
9711             }
9712             
9713             if (this.after) {
9714                 inputblock.cn.push({
9715                     tag :'span',
9716                     cls : 'input-group-addon',
9717                     html : this.after
9718                 });
9719             }
9720             
9721         };
9722         
9723         var box = {
9724             tag: 'div',
9725             cn: [
9726                 {
9727                     tag: 'input',
9728                     type : 'hidden',
9729                     cls: 'form-hidden-field'
9730                 },
9731                 inputblock
9732             ]
9733             
9734         };
9735         
9736         if(this.multiple){
9737             box = {
9738                 tag: 'div',
9739                 cn: [
9740                     {
9741                         tag: 'input',
9742                         type : 'hidden',
9743                         cls: 'form-hidden-field'
9744                     },
9745                     {
9746                         tag: 'ul',
9747                         cls: 'roo-select2-choices',
9748                         cn:[
9749                             {
9750                                 tag: 'li',
9751                                 cls: 'roo-select2-search-field',
9752                                 cn: [
9753
9754                                     inputblock
9755                                 ]
9756                             }
9757                         ]
9758                     }
9759                 ]
9760             }
9761         };
9762         
9763         var combobox = {
9764             cls: 'roo-select2-container input-group',
9765             cn: [
9766                 box
9767 //                {
9768 //                    tag: 'ul',
9769 //                    cls: 'typeahead typeahead-long dropdown-menu',
9770 //                    style: 'display:none'
9771 //                }
9772             ]
9773         };
9774         
9775         if(!this.multiple && this.showToggleBtn){
9776             
9777             var caret = {
9778                         tag: 'span',
9779                         cls: 'caret'
9780              };
9781             if (this.caret != false) {
9782                 caret = {
9783                      tag: 'i',
9784                      cls: 'fa fa-' + this.caret
9785                 };
9786                 
9787             }
9788             
9789             combobox.cn.push({
9790                 tag :'span',
9791                 cls : 'input-group-addon btn dropdown-toggle',
9792                 cn : [
9793                     caret,
9794                     {
9795                         tag: 'span',
9796                         cls: 'combobox-clear',
9797                         cn  : [
9798                             {
9799                                 tag : 'i',
9800                                 cls: 'icon-remove'
9801                             }
9802                         ]
9803                     }
9804                 ]
9805
9806             })
9807         }
9808         
9809         if(this.multiple){
9810             combobox.cls += ' roo-select2-container-multi';
9811         }
9812         
9813         if (align ==='left' && this.fieldLabel.length) {
9814             
9815             cfg.cn = [
9816                 {
9817                     tag : 'i',
9818                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9819                     tooltip : 'This field is required'
9820                 },
9821                 {
9822                     tag: 'label',
9823                     'for' :  id,
9824                     cls : 'control-label',
9825                     html : this.fieldLabel
9826
9827                 },
9828                 {
9829                     cls : "", 
9830                     cn: [
9831                         combobox
9832                     ]
9833                 }
9834
9835             ];
9836             
9837             var labelCfg = cfg.cn[1];
9838             var contentCfg = cfg.cn[2];
9839             
9840             if(this.indicatorpos == 'right'){
9841                 cfg.cn = [
9842                     {
9843                         tag: 'label',
9844                         'for' :  id,
9845                         cls : 'control-label',
9846                         html : this.fieldLabel
9847
9848                     },
9849                     {
9850                         tag : 'i',
9851                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9852                         tooltip : 'This field is required'
9853                     },
9854                     {
9855                         cls : "", 
9856                         cn: [
9857                             combobox
9858                         ]
9859                     }
9860
9861                 ];
9862                 
9863                 labelCfg = cfg.cn[0];
9864                 contentCfg = cfg.cn[2];
9865             }
9866             
9867             if(this.labelWidth > 12){
9868                 labelCfg.style = "width: " + this.labelWidth + 'px';
9869             }
9870             
9871             if(this.labelWidth < 13 && this.labelmd == 0){
9872                 this.labelmd = this.labelWidth;
9873             }
9874             
9875             if(this.labellg > 0){
9876                 labelCfg.cls += ' col-lg-' + this.labellg;
9877                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9878             }
9879             
9880             if(this.labelmd > 0){
9881                 labelCfg.cls += ' col-md-' + this.labelmd;
9882                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9883             }
9884             
9885             if(this.labelsm > 0){
9886                 labelCfg.cls += ' col-sm-' + this.labelsm;
9887                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9888             }
9889             
9890             if(this.labelxs > 0){
9891                 labelCfg.cls += ' col-xs-' + this.labelxs;
9892                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9893             }
9894             
9895         } else if ( this.fieldLabel.length) {
9896 //                Roo.log(" label");
9897             cfg.cn = [
9898                 {
9899                    tag : 'i',
9900                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9901                    tooltip : 'This field is required'
9902                },
9903                {
9904                    tag: 'label',
9905                    //cls : 'input-group-addon',
9906                    html : this.fieldLabel
9907
9908                },
9909
9910                combobox
9911
9912             ];
9913             
9914             if(this.indicatorpos == 'right'){
9915                 
9916                 cfg.cn = [
9917                     {
9918                        tag: 'label',
9919                        //cls : 'input-group-addon',
9920                        html : this.fieldLabel
9921
9922                     },
9923                     {
9924                        tag : 'i',
9925                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9926                        tooltip : 'This field is required'
9927                     },
9928                     
9929                     combobox
9930
9931                 ];
9932
9933             }
9934
9935         } else {
9936             
9937 //                Roo.log(" no label && no align");
9938                 cfg = combobox
9939                      
9940                 
9941         }
9942         
9943         var settings=this;
9944         ['xs','sm','md','lg'].map(function(size){
9945             if (settings[size]) {
9946                 cfg.cls += ' col-' + size + '-' + settings[size];
9947             }
9948         });
9949         
9950         return cfg;
9951         
9952     },
9953     
9954     
9955     
9956     // private
9957     onResize : function(w, h){
9958 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9959 //        if(typeof w == 'number'){
9960 //            var x = w - this.trigger.getWidth();
9961 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9962 //            this.trigger.setStyle('left', x+'px');
9963 //        }
9964     },
9965
9966     // private
9967     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9968
9969     // private
9970     getResizeEl : function(){
9971         return this.inputEl();
9972     },
9973
9974     // private
9975     getPositionEl : function(){
9976         return this.inputEl();
9977     },
9978
9979     // private
9980     alignErrorIcon : function(){
9981         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9982     },
9983
9984     // private
9985     initEvents : function(){
9986         
9987         this.createList();
9988         
9989         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9990         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9991         if(!this.multiple && this.showToggleBtn){
9992             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9993             if(this.hideTrigger){
9994                 this.trigger.setDisplayed(false);
9995             }
9996             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9997         }
9998         
9999         if(this.multiple){
10000             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10001         }
10002         
10003         if(this.removable && !this.editable && !this.tickable){
10004             var close = this.closeTriggerEl();
10005             
10006             if(close){
10007                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10008                 close.on('click', this.removeBtnClick, this, close);
10009             }
10010         }
10011         
10012         //this.trigger.addClassOnOver('x-form-trigger-over');
10013         //this.trigger.addClassOnClick('x-form-trigger-click');
10014         
10015         //if(!this.width){
10016         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10017         //}
10018     },
10019     
10020     closeTriggerEl : function()
10021     {
10022         var close = this.el.select('.roo-combo-removable-btn', true).first();
10023         return close ? close : false;
10024     },
10025     
10026     removeBtnClick : function(e, h, el)
10027     {
10028         e.preventDefault();
10029         
10030         if(this.fireEvent("remove", this) !== false){
10031             this.reset();
10032             this.fireEvent("afterremove", this)
10033         }
10034     },
10035     
10036     createList : function()
10037     {
10038         this.list = Roo.get(document.body).createChild({
10039             tag: 'ul',
10040             cls: 'typeahead typeahead-long dropdown-menu',
10041             style: 'display:none'
10042         });
10043         
10044         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10045         
10046     },
10047
10048     // private
10049     initTrigger : function(){
10050        
10051     },
10052
10053     // private
10054     onDestroy : function(){
10055         if(this.trigger){
10056             this.trigger.removeAllListeners();
10057           //  this.trigger.remove();
10058         }
10059         //if(this.wrap){
10060         //    this.wrap.remove();
10061         //}
10062         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10063     },
10064
10065     // private
10066     onFocus : function(){
10067         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10068         /*
10069         if(!this.mimicing){
10070             this.wrap.addClass('x-trigger-wrap-focus');
10071             this.mimicing = true;
10072             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10073             if(this.monitorTab){
10074                 this.el.on("keydown", this.checkTab, this);
10075             }
10076         }
10077         */
10078     },
10079
10080     // private
10081     checkTab : function(e){
10082         if(e.getKey() == e.TAB){
10083             this.triggerBlur();
10084         }
10085     },
10086
10087     // private
10088     onBlur : function(){
10089         // do nothing
10090     },
10091
10092     // private
10093     mimicBlur : function(e, t){
10094         /*
10095         if(!this.wrap.contains(t) && this.validateBlur()){
10096             this.triggerBlur();
10097         }
10098         */
10099     },
10100
10101     // private
10102     triggerBlur : function(){
10103         this.mimicing = false;
10104         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10105         if(this.monitorTab){
10106             this.el.un("keydown", this.checkTab, this);
10107         }
10108         //this.wrap.removeClass('x-trigger-wrap-focus');
10109         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10110     },
10111
10112     // private
10113     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10114     validateBlur : function(e, t){
10115         return true;
10116     },
10117
10118     // private
10119     onDisable : function(){
10120         this.inputEl().dom.disabled = true;
10121         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10122         //if(this.wrap){
10123         //    this.wrap.addClass('x-item-disabled');
10124         //}
10125     },
10126
10127     // private
10128     onEnable : function(){
10129         this.inputEl().dom.disabled = false;
10130         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10131         //if(this.wrap){
10132         //    this.el.removeClass('x-item-disabled');
10133         //}
10134     },
10135
10136     // private
10137     onShow : function(){
10138         var ae = this.getActionEl();
10139         
10140         if(ae){
10141             ae.dom.style.display = '';
10142             ae.dom.style.visibility = 'visible';
10143         }
10144     },
10145
10146     // private
10147     
10148     onHide : function(){
10149         var ae = this.getActionEl();
10150         ae.dom.style.display = 'none';
10151     },
10152
10153     /**
10154      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10155      * by an implementing function.
10156      * @method
10157      * @param {EventObject} e
10158      */
10159     onTriggerClick : Roo.emptyFn
10160 });
10161  /*
10162  * Based on:
10163  * Ext JS Library 1.1.1
10164  * Copyright(c) 2006-2007, Ext JS, LLC.
10165  *
10166  * Originally Released Under LGPL - original licence link has changed is not relivant.
10167  *
10168  * Fork - LGPL
10169  * <script type="text/javascript">
10170  */
10171
10172
10173 /**
10174  * @class Roo.data.SortTypes
10175  * @singleton
10176  * Defines the default sorting (casting?) comparison functions used when sorting data.
10177  */
10178 Roo.data.SortTypes = {
10179     /**
10180      * Default sort that does nothing
10181      * @param {Mixed} s The value being converted
10182      * @return {Mixed} The comparison value
10183      */
10184     none : function(s){
10185         return s;
10186     },
10187     
10188     /**
10189      * The regular expression used to strip tags
10190      * @type {RegExp}
10191      * @property
10192      */
10193     stripTagsRE : /<\/?[^>]+>/gi,
10194     
10195     /**
10196      * Strips all HTML tags to sort on text only
10197      * @param {Mixed} s The value being converted
10198      * @return {String} The comparison value
10199      */
10200     asText : function(s){
10201         return String(s).replace(this.stripTagsRE, "");
10202     },
10203     
10204     /**
10205      * Strips all HTML tags to sort on text only - Case insensitive
10206      * @param {Mixed} s The value being converted
10207      * @return {String} The comparison value
10208      */
10209     asUCText : function(s){
10210         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10211     },
10212     
10213     /**
10214      * Case insensitive string
10215      * @param {Mixed} s The value being converted
10216      * @return {String} The comparison value
10217      */
10218     asUCString : function(s) {
10219         return String(s).toUpperCase();
10220     },
10221     
10222     /**
10223      * Date sorting
10224      * @param {Mixed} s The value being converted
10225      * @return {Number} The comparison value
10226      */
10227     asDate : function(s) {
10228         if(!s){
10229             return 0;
10230         }
10231         if(s instanceof Date){
10232             return s.getTime();
10233         }
10234         return Date.parse(String(s));
10235     },
10236     
10237     /**
10238      * Float sorting
10239      * @param {Mixed} s The value being converted
10240      * @return {Float} The comparison value
10241      */
10242     asFloat : function(s) {
10243         var val = parseFloat(String(s).replace(/,/g, ""));
10244         if(isNaN(val)) {
10245             val = 0;
10246         }
10247         return val;
10248     },
10249     
10250     /**
10251      * Integer sorting
10252      * @param {Mixed} s The value being converted
10253      * @return {Number} The comparison value
10254      */
10255     asInt : function(s) {
10256         var val = parseInt(String(s).replace(/,/g, ""));
10257         if(isNaN(val)) {
10258             val = 0;
10259         }
10260         return val;
10261     }
10262 };/*
10263  * Based on:
10264  * Ext JS Library 1.1.1
10265  * Copyright(c) 2006-2007, Ext JS, LLC.
10266  *
10267  * Originally Released Under LGPL - original licence link has changed is not relivant.
10268  *
10269  * Fork - LGPL
10270  * <script type="text/javascript">
10271  */
10272
10273 /**
10274 * @class Roo.data.Record
10275  * Instances of this class encapsulate both record <em>definition</em> information, and record
10276  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10277  * to access Records cached in an {@link Roo.data.Store} object.<br>
10278  * <p>
10279  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10280  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10281  * objects.<br>
10282  * <p>
10283  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10284  * @constructor
10285  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10286  * {@link #create}. The parameters are the same.
10287  * @param {Array} data An associative Array of data values keyed by the field name.
10288  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10289  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10290  * not specified an integer id is generated.
10291  */
10292 Roo.data.Record = function(data, id){
10293     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10294     this.data = data;
10295 };
10296
10297 /**
10298  * Generate a constructor for a specific record layout.
10299  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10300  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10301  * Each field definition object may contain the following properties: <ul>
10302  * <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,
10303  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10304  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10305  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10306  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10307  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10308  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10309  * this may be omitted.</p></li>
10310  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10311  * <ul><li>auto (Default, implies no conversion)</li>
10312  * <li>string</li>
10313  * <li>int</li>
10314  * <li>float</li>
10315  * <li>boolean</li>
10316  * <li>date</li></ul></p></li>
10317  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10318  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10319  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10320  * by the Reader into an object that will be stored in the Record. It is passed the
10321  * following parameters:<ul>
10322  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10323  * </ul></p></li>
10324  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10325  * </ul>
10326  * <br>usage:<br><pre><code>
10327 var TopicRecord = Roo.data.Record.create(
10328     {name: 'title', mapping: 'topic_title'},
10329     {name: 'author', mapping: 'username'},
10330     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10331     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10332     {name: 'lastPoster', mapping: 'user2'},
10333     {name: 'excerpt', mapping: 'post_text'}
10334 );
10335
10336 var myNewRecord = new TopicRecord({
10337     title: 'Do my job please',
10338     author: 'noobie',
10339     totalPosts: 1,
10340     lastPost: new Date(),
10341     lastPoster: 'Animal',
10342     excerpt: 'No way dude!'
10343 });
10344 myStore.add(myNewRecord);
10345 </code></pre>
10346  * @method create
10347  * @static
10348  */
10349 Roo.data.Record.create = function(o){
10350     var f = function(){
10351         f.superclass.constructor.apply(this, arguments);
10352     };
10353     Roo.extend(f, Roo.data.Record);
10354     var p = f.prototype;
10355     p.fields = new Roo.util.MixedCollection(false, function(field){
10356         return field.name;
10357     });
10358     for(var i = 0, len = o.length; i < len; i++){
10359         p.fields.add(new Roo.data.Field(o[i]));
10360     }
10361     f.getField = function(name){
10362         return p.fields.get(name);  
10363     };
10364     return f;
10365 };
10366
10367 Roo.data.Record.AUTO_ID = 1000;
10368 Roo.data.Record.EDIT = 'edit';
10369 Roo.data.Record.REJECT = 'reject';
10370 Roo.data.Record.COMMIT = 'commit';
10371
10372 Roo.data.Record.prototype = {
10373     /**
10374      * Readonly flag - true if this record has been modified.
10375      * @type Boolean
10376      */
10377     dirty : false,
10378     editing : false,
10379     error: null,
10380     modified: null,
10381
10382     // private
10383     join : function(store){
10384         this.store = store;
10385     },
10386
10387     /**
10388      * Set the named field to the specified value.
10389      * @param {String} name The name of the field to set.
10390      * @param {Object} value The value to set the field to.
10391      */
10392     set : function(name, value){
10393         if(this.data[name] == value){
10394             return;
10395         }
10396         this.dirty = true;
10397         if(!this.modified){
10398             this.modified = {};
10399         }
10400         if(typeof this.modified[name] == 'undefined'){
10401             this.modified[name] = this.data[name];
10402         }
10403         this.data[name] = value;
10404         if(!this.editing && this.store){
10405             this.store.afterEdit(this);
10406         }       
10407     },
10408
10409     /**
10410      * Get the value of the named field.
10411      * @param {String} name The name of the field to get the value of.
10412      * @return {Object} The value of the field.
10413      */
10414     get : function(name){
10415         return this.data[name]; 
10416     },
10417
10418     // private
10419     beginEdit : function(){
10420         this.editing = true;
10421         this.modified = {}; 
10422     },
10423
10424     // private
10425     cancelEdit : function(){
10426         this.editing = false;
10427         delete this.modified;
10428     },
10429
10430     // private
10431     endEdit : function(){
10432         this.editing = false;
10433         if(this.dirty && this.store){
10434             this.store.afterEdit(this);
10435         }
10436     },
10437
10438     /**
10439      * Usually called by the {@link Roo.data.Store} which owns the Record.
10440      * Rejects all changes made to the Record since either creation, or the last commit operation.
10441      * Modified fields are reverted to their original values.
10442      * <p>
10443      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10444      * of reject operations.
10445      */
10446     reject : function(){
10447         var m = this.modified;
10448         for(var n in m){
10449             if(typeof m[n] != "function"){
10450                 this.data[n] = m[n];
10451             }
10452         }
10453         this.dirty = false;
10454         delete this.modified;
10455         this.editing = false;
10456         if(this.store){
10457             this.store.afterReject(this);
10458         }
10459     },
10460
10461     /**
10462      * Usually called by the {@link Roo.data.Store} which owns the Record.
10463      * Commits all changes made to the Record since either creation, or the last commit operation.
10464      * <p>
10465      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10466      * of commit operations.
10467      */
10468     commit : function(){
10469         this.dirty = false;
10470         delete this.modified;
10471         this.editing = false;
10472         if(this.store){
10473             this.store.afterCommit(this);
10474         }
10475     },
10476
10477     // private
10478     hasError : function(){
10479         return this.error != null;
10480     },
10481
10482     // private
10483     clearError : function(){
10484         this.error = null;
10485     },
10486
10487     /**
10488      * Creates a copy of this record.
10489      * @param {String} id (optional) A new record id if you don't want to use this record's id
10490      * @return {Record}
10491      */
10492     copy : function(newId) {
10493         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10494     }
10495 };/*
10496  * Based on:
10497  * Ext JS Library 1.1.1
10498  * Copyright(c) 2006-2007, Ext JS, LLC.
10499  *
10500  * Originally Released Under LGPL - original licence link has changed is not relivant.
10501  *
10502  * Fork - LGPL
10503  * <script type="text/javascript">
10504  */
10505
10506
10507
10508 /**
10509  * @class Roo.data.Store
10510  * @extends Roo.util.Observable
10511  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10512  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10513  * <p>
10514  * 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
10515  * has no knowledge of the format of the data returned by the Proxy.<br>
10516  * <p>
10517  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10518  * instances from the data object. These records are cached and made available through accessor functions.
10519  * @constructor
10520  * Creates a new Store.
10521  * @param {Object} config A config object containing the objects needed for the Store to access data,
10522  * and read the data into Records.
10523  */
10524 Roo.data.Store = function(config){
10525     this.data = new Roo.util.MixedCollection(false);
10526     this.data.getKey = function(o){
10527         return o.id;
10528     };
10529     this.baseParams = {};
10530     // private
10531     this.paramNames = {
10532         "start" : "start",
10533         "limit" : "limit",
10534         "sort" : "sort",
10535         "dir" : "dir",
10536         "multisort" : "_multisort"
10537     };
10538
10539     if(config && config.data){
10540         this.inlineData = config.data;
10541         delete config.data;
10542     }
10543
10544     Roo.apply(this, config);
10545     
10546     if(this.reader){ // reader passed
10547         this.reader = Roo.factory(this.reader, Roo.data);
10548         this.reader.xmodule = this.xmodule || false;
10549         if(!this.recordType){
10550             this.recordType = this.reader.recordType;
10551         }
10552         if(this.reader.onMetaChange){
10553             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10554         }
10555     }
10556
10557     if(this.recordType){
10558         this.fields = this.recordType.prototype.fields;
10559     }
10560     this.modified = [];
10561
10562     this.addEvents({
10563         /**
10564          * @event datachanged
10565          * Fires when the data cache has changed, and a widget which is using this Store
10566          * as a Record cache should refresh its view.
10567          * @param {Store} this
10568          */
10569         datachanged : true,
10570         /**
10571          * @event metachange
10572          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10573          * @param {Store} this
10574          * @param {Object} meta The JSON metadata
10575          */
10576         metachange : true,
10577         /**
10578          * @event add
10579          * Fires when Records have been added to the Store
10580          * @param {Store} this
10581          * @param {Roo.data.Record[]} records The array of Records added
10582          * @param {Number} index The index at which the record(s) were added
10583          */
10584         add : true,
10585         /**
10586          * @event remove
10587          * Fires when a Record has been removed from the Store
10588          * @param {Store} this
10589          * @param {Roo.data.Record} record The Record that was removed
10590          * @param {Number} index The index at which the record was removed
10591          */
10592         remove : true,
10593         /**
10594          * @event update
10595          * Fires when a Record has been updated
10596          * @param {Store} this
10597          * @param {Roo.data.Record} record The Record that was updated
10598          * @param {String} operation The update operation being performed.  Value may be one of:
10599          * <pre><code>
10600  Roo.data.Record.EDIT
10601  Roo.data.Record.REJECT
10602  Roo.data.Record.COMMIT
10603          * </code></pre>
10604          */
10605         update : true,
10606         /**
10607          * @event clear
10608          * Fires when the data cache has been cleared.
10609          * @param {Store} this
10610          */
10611         clear : true,
10612         /**
10613          * @event beforeload
10614          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10615          * the load action will be canceled.
10616          * @param {Store} this
10617          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10618          */
10619         beforeload : true,
10620         /**
10621          * @event beforeloadadd
10622          * Fires after a new set of Records has been loaded.
10623          * @param {Store} this
10624          * @param {Roo.data.Record[]} records The Records that were loaded
10625          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10626          */
10627         beforeloadadd : true,
10628         /**
10629          * @event load
10630          * Fires after a new set of Records has been loaded, before they are added to the store.
10631          * @param {Store} this
10632          * @param {Roo.data.Record[]} records The Records that were loaded
10633          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10634          * @params {Object} return from reader
10635          */
10636         load : true,
10637         /**
10638          * @event loadexception
10639          * Fires if an exception occurs in the Proxy during loading.
10640          * Called with the signature of the Proxy's "loadexception" event.
10641          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10642          * 
10643          * @param {Proxy} 
10644          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10645          * @param {Object} load options 
10646          * @param {Object} jsonData from your request (normally this contains the Exception)
10647          */
10648         loadexception : true
10649     });
10650     
10651     if(this.proxy){
10652         this.proxy = Roo.factory(this.proxy, Roo.data);
10653         this.proxy.xmodule = this.xmodule || false;
10654         this.relayEvents(this.proxy,  ["loadexception"]);
10655     }
10656     this.sortToggle = {};
10657     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10658
10659     Roo.data.Store.superclass.constructor.call(this);
10660
10661     if(this.inlineData){
10662         this.loadData(this.inlineData);
10663         delete this.inlineData;
10664     }
10665 };
10666
10667 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10668      /**
10669     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10670     * without a remote query - used by combo/forms at present.
10671     */
10672     
10673     /**
10674     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10675     */
10676     /**
10677     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10678     */
10679     /**
10680     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10681     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10682     */
10683     /**
10684     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10685     * on any HTTP request
10686     */
10687     /**
10688     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10689     */
10690     /**
10691     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10692     */
10693     multiSort: false,
10694     /**
10695     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10696     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10697     */
10698     remoteSort : false,
10699
10700     /**
10701     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10702      * loaded or when a record is removed. (defaults to false).
10703     */
10704     pruneModifiedRecords : false,
10705
10706     // private
10707     lastOptions : null,
10708
10709     /**
10710      * Add Records to the Store and fires the add event.
10711      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10712      */
10713     add : function(records){
10714         records = [].concat(records);
10715         for(var i = 0, len = records.length; i < len; i++){
10716             records[i].join(this);
10717         }
10718         var index = this.data.length;
10719         this.data.addAll(records);
10720         this.fireEvent("add", this, records, index);
10721     },
10722
10723     /**
10724      * Remove a Record from the Store and fires the remove event.
10725      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10726      */
10727     remove : function(record){
10728         var index = this.data.indexOf(record);
10729         this.data.removeAt(index);
10730         if(this.pruneModifiedRecords){
10731             this.modified.remove(record);
10732         }
10733         this.fireEvent("remove", this, record, index);
10734     },
10735
10736     /**
10737      * Remove all Records from the Store and fires the clear event.
10738      */
10739     removeAll : function(){
10740         this.data.clear();
10741         if(this.pruneModifiedRecords){
10742             this.modified = [];
10743         }
10744         this.fireEvent("clear", this);
10745     },
10746
10747     /**
10748      * Inserts Records to the Store at the given index and fires the add event.
10749      * @param {Number} index The start index at which to insert the passed Records.
10750      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10751      */
10752     insert : function(index, records){
10753         records = [].concat(records);
10754         for(var i = 0, len = records.length; i < len; i++){
10755             this.data.insert(index, records[i]);
10756             records[i].join(this);
10757         }
10758         this.fireEvent("add", this, records, index);
10759     },
10760
10761     /**
10762      * Get the index within the cache of the passed Record.
10763      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10764      * @return {Number} The index of the passed Record. Returns -1 if not found.
10765      */
10766     indexOf : function(record){
10767         return this.data.indexOf(record);
10768     },
10769
10770     /**
10771      * Get the index within the cache of the Record with the passed id.
10772      * @param {String} id The id of the Record to find.
10773      * @return {Number} The index of the Record. Returns -1 if not found.
10774      */
10775     indexOfId : function(id){
10776         return this.data.indexOfKey(id);
10777     },
10778
10779     /**
10780      * Get the Record with the specified id.
10781      * @param {String} id The id of the Record to find.
10782      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10783      */
10784     getById : function(id){
10785         return this.data.key(id);
10786     },
10787
10788     /**
10789      * Get the Record at the specified index.
10790      * @param {Number} index The index of the Record to find.
10791      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10792      */
10793     getAt : function(index){
10794         return this.data.itemAt(index);
10795     },
10796
10797     /**
10798      * Returns a range of Records between specified indices.
10799      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10800      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10801      * @return {Roo.data.Record[]} An array of Records
10802      */
10803     getRange : function(start, end){
10804         return this.data.getRange(start, end);
10805     },
10806
10807     // private
10808     storeOptions : function(o){
10809         o = Roo.apply({}, o);
10810         delete o.callback;
10811         delete o.scope;
10812         this.lastOptions = o;
10813     },
10814
10815     /**
10816      * Loads the Record cache from the configured Proxy using the configured Reader.
10817      * <p>
10818      * If using remote paging, then the first load call must specify the <em>start</em>
10819      * and <em>limit</em> properties in the options.params property to establish the initial
10820      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10821      * <p>
10822      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10823      * and this call will return before the new data has been loaded. Perform any post-processing
10824      * in a callback function, or in a "load" event handler.</strong>
10825      * <p>
10826      * @param {Object} options An object containing properties which control loading options:<ul>
10827      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10828      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10829      * passed the following arguments:<ul>
10830      * <li>r : Roo.data.Record[]</li>
10831      * <li>options: Options object from the load call</li>
10832      * <li>success: Boolean success indicator</li></ul></li>
10833      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10834      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10835      * </ul>
10836      */
10837     load : function(options){
10838         options = options || {};
10839         if(this.fireEvent("beforeload", this, options) !== false){
10840             this.storeOptions(options);
10841             var p = Roo.apply(options.params || {}, this.baseParams);
10842             // if meta was not loaded from remote source.. try requesting it.
10843             if (!this.reader.metaFromRemote) {
10844                 p._requestMeta = 1;
10845             }
10846             if(this.sortInfo && this.remoteSort){
10847                 var pn = this.paramNames;
10848                 p[pn["sort"]] = this.sortInfo.field;
10849                 p[pn["dir"]] = this.sortInfo.direction;
10850             }
10851             if (this.multiSort) {
10852                 var pn = this.paramNames;
10853                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10854             }
10855             
10856             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10857         }
10858     },
10859
10860     /**
10861      * Reloads the Record cache from the configured Proxy using the configured Reader and
10862      * the options from the last load operation performed.
10863      * @param {Object} options (optional) An object containing properties which may override the options
10864      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10865      * the most recently used options are reused).
10866      */
10867     reload : function(options){
10868         this.load(Roo.applyIf(options||{}, this.lastOptions));
10869     },
10870
10871     // private
10872     // Called as a callback by the Reader during a load operation.
10873     loadRecords : function(o, options, success){
10874         if(!o || success === false){
10875             if(success !== false){
10876                 this.fireEvent("load", this, [], options, o);
10877             }
10878             if(options.callback){
10879                 options.callback.call(options.scope || this, [], options, false);
10880             }
10881             return;
10882         }
10883         // if data returned failure - throw an exception.
10884         if (o.success === false) {
10885             // show a message if no listener is registered.
10886             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10887                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10888             }
10889             // loadmask wil be hooked into this..
10890             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10891             return;
10892         }
10893         var r = o.records, t = o.totalRecords || r.length;
10894         
10895         this.fireEvent("beforeloadadd", this, r, options, o);
10896         
10897         if(!options || options.add !== true){
10898             if(this.pruneModifiedRecords){
10899                 this.modified = [];
10900             }
10901             for(var i = 0, len = r.length; i < len; i++){
10902                 r[i].join(this);
10903             }
10904             if(this.snapshot){
10905                 this.data = this.snapshot;
10906                 delete this.snapshot;
10907             }
10908             this.data.clear();
10909             this.data.addAll(r);
10910             this.totalLength = t;
10911             this.applySort();
10912             this.fireEvent("datachanged", this);
10913         }else{
10914             this.totalLength = Math.max(t, this.data.length+r.length);
10915             this.add(r);
10916         }
10917         this.fireEvent("load", this, r, options, o);
10918         if(options.callback){
10919             options.callback.call(options.scope || this, r, options, true);
10920         }
10921     },
10922
10923
10924     /**
10925      * Loads data from a passed data block. A Reader which understands the format of the data
10926      * must have been configured in the constructor.
10927      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10928      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10929      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10930      */
10931     loadData : function(o, append){
10932         var r = this.reader.readRecords(o);
10933         this.loadRecords(r, {add: append}, true);
10934     },
10935
10936     /**
10937      * Gets the number of cached records.
10938      * <p>
10939      * <em>If using paging, this may not be the total size of the dataset. If the data object
10940      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10941      * the data set size</em>
10942      */
10943     getCount : function(){
10944         return this.data.length || 0;
10945     },
10946
10947     /**
10948      * Gets the total number of records in the dataset as returned by the server.
10949      * <p>
10950      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10951      * the dataset size</em>
10952      */
10953     getTotalCount : function(){
10954         return this.totalLength || 0;
10955     },
10956
10957     /**
10958      * Returns the sort state of the Store as an object with two properties:
10959      * <pre><code>
10960  field {String} The name of the field by which the Records are sorted
10961  direction {String} The sort order, "ASC" or "DESC"
10962      * </code></pre>
10963      */
10964     getSortState : function(){
10965         return this.sortInfo;
10966     },
10967
10968     // private
10969     applySort : function(){
10970         if(this.sortInfo && !this.remoteSort){
10971             var s = this.sortInfo, f = s.field;
10972             var st = this.fields.get(f).sortType;
10973             var fn = function(r1, r2){
10974                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10975                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10976             };
10977             this.data.sort(s.direction, fn);
10978             if(this.snapshot && this.snapshot != this.data){
10979                 this.snapshot.sort(s.direction, fn);
10980             }
10981         }
10982     },
10983
10984     /**
10985      * Sets the default sort column and order to be used by the next load operation.
10986      * @param {String} fieldName The name of the field to sort by.
10987      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10988      */
10989     setDefaultSort : function(field, dir){
10990         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10991     },
10992
10993     /**
10994      * Sort the Records.
10995      * If remote sorting is used, the sort is performed on the server, and the cache is
10996      * reloaded. If local sorting is used, the cache is sorted internally.
10997      * @param {String} fieldName The name of the field to sort by.
10998      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10999      */
11000     sort : function(fieldName, dir){
11001         var f = this.fields.get(fieldName);
11002         if(!dir){
11003             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11004             
11005             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11006                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11007             }else{
11008                 dir = f.sortDir;
11009             }
11010         }
11011         this.sortToggle[f.name] = dir;
11012         this.sortInfo = {field: f.name, direction: dir};
11013         if(!this.remoteSort){
11014             this.applySort();
11015             this.fireEvent("datachanged", this);
11016         }else{
11017             this.load(this.lastOptions);
11018         }
11019     },
11020
11021     /**
11022      * Calls the specified function for each of the Records in the cache.
11023      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11024      * Returning <em>false</em> aborts and exits the iteration.
11025      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11026      */
11027     each : function(fn, scope){
11028         this.data.each(fn, scope);
11029     },
11030
11031     /**
11032      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11033      * (e.g., during paging).
11034      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11035      */
11036     getModifiedRecords : function(){
11037         return this.modified;
11038     },
11039
11040     // private
11041     createFilterFn : function(property, value, anyMatch){
11042         if(!value.exec){ // not a regex
11043             value = String(value);
11044             if(value.length == 0){
11045                 return false;
11046             }
11047             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11048         }
11049         return function(r){
11050             return value.test(r.data[property]);
11051         };
11052     },
11053
11054     /**
11055      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11056      * @param {String} property A field on your records
11057      * @param {Number} start The record index to start at (defaults to 0)
11058      * @param {Number} end The last record index to include (defaults to length - 1)
11059      * @return {Number} The sum
11060      */
11061     sum : function(property, start, end){
11062         var rs = this.data.items, v = 0;
11063         start = start || 0;
11064         end = (end || end === 0) ? end : rs.length-1;
11065
11066         for(var i = start; i <= end; i++){
11067             v += (rs[i].data[property] || 0);
11068         }
11069         return v;
11070     },
11071
11072     /**
11073      * Filter the records by a specified property.
11074      * @param {String} field A field on your records
11075      * @param {String/RegExp} value Either a string that the field
11076      * should start with or a RegExp to test against the field
11077      * @param {Boolean} anyMatch True to match any part not just the beginning
11078      */
11079     filter : function(property, value, anyMatch){
11080         var fn = this.createFilterFn(property, value, anyMatch);
11081         return fn ? this.filterBy(fn) : this.clearFilter();
11082     },
11083
11084     /**
11085      * Filter by a function. The specified function will be called with each
11086      * record in this data source. If the function returns true the record is included,
11087      * otherwise it is filtered.
11088      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11089      * @param {Object} scope (optional) The scope of the function (defaults to this)
11090      */
11091     filterBy : function(fn, scope){
11092         this.snapshot = this.snapshot || this.data;
11093         this.data = this.queryBy(fn, scope||this);
11094         this.fireEvent("datachanged", this);
11095     },
11096
11097     /**
11098      * Query the records by a specified property.
11099      * @param {String} field A field on your records
11100      * @param {String/RegExp} value Either a string that the field
11101      * should start with or a RegExp to test against the field
11102      * @param {Boolean} anyMatch True to match any part not just the beginning
11103      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11104      */
11105     query : function(property, value, anyMatch){
11106         var fn = this.createFilterFn(property, value, anyMatch);
11107         return fn ? this.queryBy(fn) : this.data.clone();
11108     },
11109
11110     /**
11111      * Query by a function. The specified function will be called with each
11112      * record in this data source. If the function returns true the record is included
11113      * in the results.
11114      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11115      * @param {Object} scope (optional) The scope of the function (defaults to this)
11116       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11117      **/
11118     queryBy : function(fn, scope){
11119         var data = this.snapshot || this.data;
11120         return data.filterBy(fn, scope||this);
11121     },
11122
11123     /**
11124      * Collects unique values for a particular dataIndex from this store.
11125      * @param {String} dataIndex The property to collect
11126      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11127      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11128      * @return {Array} An array of the unique values
11129      **/
11130     collect : function(dataIndex, allowNull, bypassFilter){
11131         var d = (bypassFilter === true && this.snapshot) ?
11132                 this.snapshot.items : this.data.items;
11133         var v, sv, r = [], l = {};
11134         for(var i = 0, len = d.length; i < len; i++){
11135             v = d[i].data[dataIndex];
11136             sv = String(v);
11137             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11138                 l[sv] = true;
11139                 r[r.length] = v;
11140             }
11141         }
11142         return r;
11143     },
11144
11145     /**
11146      * Revert to a view of the Record cache with no filtering applied.
11147      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11148      */
11149     clearFilter : function(suppressEvent){
11150         if(this.snapshot && this.snapshot != this.data){
11151             this.data = this.snapshot;
11152             delete this.snapshot;
11153             if(suppressEvent !== true){
11154                 this.fireEvent("datachanged", this);
11155             }
11156         }
11157     },
11158
11159     // private
11160     afterEdit : function(record){
11161         if(this.modified.indexOf(record) == -1){
11162             this.modified.push(record);
11163         }
11164         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11165     },
11166     
11167     // private
11168     afterReject : function(record){
11169         this.modified.remove(record);
11170         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11171     },
11172
11173     // private
11174     afterCommit : function(record){
11175         this.modified.remove(record);
11176         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11177     },
11178
11179     /**
11180      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11181      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11182      */
11183     commitChanges : function(){
11184         var m = this.modified.slice(0);
11185         this.modified = [];
11186         for(var i = 0, len = m.length; i < len; i++){
11187             m[i].commit();
11188         }
11189     },
11190
11191     /**
11192      * Cancel outstanding changes on all changed records.
11193      */
11194     rejectChanges : function(){
11195         var m = this.modified.slice(0);
11196         this.modified = [];
11197         for(var i = 0, len = m.length; i < len; i++){
11198             m[i].reject();
11199         }
11200     },
11201
11202     onMetaChange : function(meta, rtype, o){
11203         this.recordType = rtype;
11204         this.fields = rtype.prototype.fields;
11205         delete this.snapshot;
11206         this.sortInfo = meta.sortInfo || this.sortInfo;
11207         this.modified = [];
11208         this.fireEvent('metachange', this, this.reader.meta);
11209     },
11210     
11211     moveIndex : function(data, type)
11212     {
11213         var index = this.indexOf(data);
11214         
11215         var newIndex = index + type;
11216         
11217         this.remove(data);
11218         
11219         this.insert(newIndex, data);
11220         
11221     }
11222 });/*
11223  * Based on:
11224  * Ext JS Library 1.1.1
11225  * Copyright(c) 2006-2007, Ext JS, LLC.
11226  *
11227  * Originally Released Under LGPL - original licence link has changed is not relivant.
11228  *
11229  * Fork - LGPL
11230  * <script type="text/javascript">
11231  */
11232
11233 /**
11234  * @class Roo.data.SimpleStore
11235  * @extends Roo.data.Store
11236  * Small helper class to make creating Stores from Array data easier.
11237  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11238  * @cfg {Array} fields An array of field definition objects, or field name strings.
11239  * @cfg {Array} data The multi-dimensional array of data
11240  * @constructor
11241  * @param {Object} config
11242  */
11243 Roo.data.SimpleStore = function(config){
11244     Roo.data.SimpleStore.superclass.constructor.call(this, {
11245         isLocal : true,
11246         reader: new Roo.data.ArrayReader({
11247                 id: config.id
11248             },
11249             Roo.data.Record.create(config.fields)
11250         ),
11251         proxy : new Roo.data.MemoryProxy(config.data)
11252     });
11253     this.load();
11254 };
11255 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11256  * Based on:
11257  * Ext JS Library 1.1.1
11258  * Copyright(c) 2006-2007, Ext JS, LLC.
11259  *
11260  * Originally Released Under LGPL - original licence link has changed is not relivant.
11261  *
11262  * Fork - LGPL
11263  * <script type="text/javascript">
11264  */
11265
11266 /**
11267 /**
11268  * @extends Roo.data.Store
11269  * @class Roo.data.JsonStore
11270  * Small helper class to make creating Stores for JSON data easier. <br/>
11271 <pre><code>
11272 var store = new Roo.data.JsonStore({
11273     url: 'get-images.php',
11274     root: 'images',
11275     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11276 });
11277 </code></pre>
11278  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11279  * JsonReader and HttpProxy (unless inline data is provided).</b>
11280  * @cfg {Array} fields An array of field definition objects, or field name strings.
11281  * @constructor
11282  * @param {Object} config
11283  */
11284 Roo.data.JsonStore = function(c){
11285     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11286         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11287         reader: new Roo.data.JsonReader(c, c.fields)
11288     }));
11289 };
11290 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11291  * Based on:
11292  * Ext JS Library 1.1.1
11293  * Copyright(c) 2006-2007, Ext JS, LLC.
11294  *
11295  * Originally Released Under LGPL - original licence link has changed is not relivant.
11296  *
11297  * Fork - LGPL
11298  * <script type="text/javascript">
11299  */
11300
11301  
11302 Roo.data.Field = function(config){
11303     if(typeof config == "string"){
11304         config = {name: config};
11305     }
11306     Roo.apply(this, config);
11307     
11308     if(!this.type){
11309         this.type = "auto";
11310     }
11311     
11312     var st = Roo.data.SortTypes;
11313     // named sortTypes are supported, here we look them up
11314     if(typeof this.sortType == "string"){
11315         this.sortType = st[this.sortType];
11316     }
11317     
11318     // set default sortType for strings and dates
11319     if(!this.sortType){
11320         switch(this.type){
11321             case "string":
11322                 this.sortType = st.asUCString;
11323                 break;
11324             case "date":
11325                 this.sortType = st.asDate;
11326                 break;
11327             default:
11328                 this.sortType = st.none;
11329         }
11330     }
11331
11332     // define once
11333     var stripRe = /[\$,%]/g;
11334
11335     // prebuilt conversion function for this field, instead of
11336     // switching every time we're reading a value
11337     if(!this.convert){
11338         var cv, dateFormat = this.dateFormat;
11339         switch(this.type){
11340             case "":
11341             case "auto":
11342             case undefined:
11343                 cv = function(v){ return v; };
11344                 break;
11345             case "string":
11346                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11347                 break;
11348             case "int":
11349                 cv = function(v){
11350                     return v !== undefined && v !== null && v !== '' ?
11351                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11352                     };
11353                 break;
11354             case "float":
11355                 cv = function(v){
11356                     return v !== undefined && v !== null && v !== '' ?
11357                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11358                     };
11359                 break;
11360             case "bool":
11361             case "boolean":
11362                 cv = function(v){ return v === true || v === "true" || v == 1; };
11363                 break;
11364             case "date":
11365                 cv = function(v){
11366                     if(!v){
11367                         return '';
11368                     }
11369                     if(v instanceof Date){
11370                         return v;
11371                     }
11372                     if(dateFormat){
11373                         if(dateFormat == "timestamp"){
11374                             return new Date(v*1000);
11375                         }
11376                         return Date.parseDate(v, dateFormat);
11377                     }
11378                     var parsed = Date.parse(v);
11379                     return parsed ? new Date(parsed) : null;
11380                 };
11381              break;
11382             
11383         }
11384         this.convert = cv;
11385     }
11386 };
11387
11388 Roo.data.Field.prototype = {
11389     dateFormat: null,
11390     defaultValue: "",
11391     mapping: null,
11392     sortType : null,
11393     sortDir : "ASC"
11394 };/*
11395  * Based on:
11396  * Ext JS Library 1.1.1
11397  * Copyright(c) 2006-2007, Ext JS, LLC.
11398  *
11399  * Originally Released Under LGPL - original licence link has changed is not relivant.
11400  *
11401  * Fork - LGPL
11402  * <script type="text/javascript">
11403  */
11404  
11405 // Base class for reading structured data from a data source.  This class is intended to be
11406 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11407
11408 /**
11409  * @class Roo.data.DataReader
11410  * Base class for reading structured data from a data source.  This class is intended to be
11411  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11412  */
11413
11414 Roo.data.DataReader = function(meta, recordType){
11415     
11416     this.meta = meta;
11417     
11418     this.recordType = recordType instanceof Array ? 
11419         Roo.data.Record.create(recordType) : recordType;
11420 };
11421
11422 Roo.data.DataReader.prototype = {
11423      /**
11424      * Create an empty record
11425      * @param {Object} data (optional) - overlay some values
11426      * @return {Roo.data.Record} record created.
11427      */
11428     newRow :  function(d) {
11429         var da =  {};
11430         this.recordType.prototype.fields.each(function(c) {
11431             switch( c.type) {
11432                 case 'int' : da[c.name] = 0; break;
11433                 case 'date' : da[c.name] = new Date(); break;
11434                 case 'float' : da[c.name] = 0.0; break;
11435                 case 'boolean' : da[c.name] = false; break;
11436                 default : da[c.name] = ""; break;
11437             }
11438             
11439         });
11440         return new this.recordType(Roo.apply(da, d));
11441     }
11442     
11443 };/*
11444  * Based on:
11445  * Ext JS Library 1.1.1
11446  * Copyright(c) 2006-2007, Ext JS, LLC.
11447  *
11448  * Originally Released Under LGPL - original licence link has changed is not relivant.
11449  *
11450  * Fork - LGPL
11451  * <script type="text/javascript">
11452  */
11453
11454 /**
11455  * @class Roo.data.DataProxy
11456  * @extends Roo.data.Observable
11457  * This class is an abstract base class for implementations which provide retrieval of
11458  * unformatted data objects.<br>
11459  * <p>
11460  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11461  * (of the appropriate type which knows how to parse the data object) to provide a block of
11462  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11463  * <p>
11464  * Custom implementations must implement the load method as described in
11465  * {@link Roo.data.HttpProxy#load}.
11466  */
11467 Roo.data.DataProxy = function(){
11468     this.addEvents({
11469         /**
11470          * @event beforeload
11471          * Fires before a network request is made to retrieve a data object.
11472          * @param {Object} This DataProxy object.
11473          * @param {Object} params The params parameter to the load function.
11474          */
11475         beforeload : true,
11476         /**
11477          * @event load
11478          * Fires before the load method's callback is called.
11479          * @param {Object} This DataProxy object.
11480          * @param {Object} o The data object.
11481          * @param {Object} arg The callback argument object passed to the load function.
11482          */
11483         load : true,
11484         /**
11485          * @event loadexception
11486          * Fires if an Exception occurs during data retrieval.
11487          * @param {Object} This DataProxy object.
11488          * @param {Object} o The data object.
11489          * @param {Object} arg The callback argument object passed to the load function.
11490          * @param {Object} e The Exception.
11491          */
11492         loadexception : true
11493     });
11494     Roo.data.DataProxy.superclass.constructor.call(this);
11495 };
11496
11497 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11498
11499     /**
11500      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11501      */
11502 /*
11503  * Based on:
11504  * Ext JS Library 1.1.1
11505  * Copyright(c) 2006-2007, Ext JS, LLC.
11506  *
11507  * Originally Released Under LGPL - original licence link has changed is not relivant.
11508  *
11509  * Fork - LGPL
11510  * <script type="text/javascript">
11511  */
11512 /**
11513  * @class Roo.data.MemoryProxy
11514  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11515  * to the Reader when its load method is called.
11516  * @constructor
11517  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11518  */
11519 Roo.data.MemoryProxy = function(data){
11520     if (data.data) {
11521         data = data.data;
11522     }
11523     Roo.data.MemoryProxy.superclass.constructor.call(this);
11524     this.data = data;
11525 };
11526
11527 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11528     
11529     /**
11530      * Load data from the requested source (in this case an in-memory
11531      * data object passed to the constructor), read the data object into
11532      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11533      * process that block using the passed callback.
11534      * @param {Object} params This parameter is not used by the MemoryProxy class.
11535      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11536      * object into a block of Roo.data.Records.
11537      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11538      * The function must be passed <ul>
11539      * <li>The Record block object</li>
11540      * <li>The "arg" argument from the load function</li>
11541      * <li>A boolean success indicator</li>
11542      * </ul>
11543      * @param {Object} scope The scope in which to call the callback
11544      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11545      */
11546     load : function(params, reader, callback, scope, arg){
11547         params = params || {};
11548         var result;
11549         try {
11550             result = reader.readRecords(this.data);
11551         }catch(e){
11552             this.fireEvent("loadexception", this, arg, null, e);
11553             callback.call(scope, null, arg, false);
11554             return;
11555         }
11556         callback.call(scope, result, arg, true);
11557     },
11558     
11559     // private
11560     update : function(params, records){
11561         
11562     }
11563 });/*
11564  * Based on:
11565  * Ext JS Library 1.1.1
11566  * Copyright(c) 2006-2007, Ext JS, LLC.
11567  *
11568  * Originally Released Under LGPL - original licence link has changed is not relivant.
11569  *
11570  * Fork - LGPL
11571  * <script type="text/javascript">
11572  */
11573 /**
11574  * @class Roo.data.HttpProxy
11575  * @extends Roo.data.DataProxy
11576  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11577  * configured to reference a certain URL.<br><br>
11578  * <p>
11579  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11580  * from which the running page was served.<br><br>
11581  * <p>
11582  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11583  * <p>
11584  * Be aware that to enable the browser to parse an XML document, the server must set
11585  * the Content-Type header in the HTTP response to "text/xml".
11586  * @constructor
11587  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11588  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11589  * will be used to make the request.
11590  */
11591 Roo.data.HttpProxy = function(conn){
11592     Roo.data.HttpProxy.superclass.constructor.call(this);
11593     // is conn a conn config or a real conn?
11594     this.conn = conn;
11595     this.useAjax = !conn || !conn.events;
11596   
11597 };
11598
11599 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11600     // thse are take from connection...
11601     
11602     /**
11603      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11604      */
11605     /**
11606      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      */
11609     /**
11610      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11611      *  to each request made by this object. (defaults to undefined)
11612      */
11613     /**
11614      * @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)
11615      */
11616     /**
11617      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11618      */
11619      /**
11620      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11621      * @type Boolean
11622      */
11623   
11624
11625     /**
11626      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11627      * @type Boolean
11628      */
11629     /**
11630      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11631      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11632      * a finer-grained basis than the DataProxy events.
11633      */
11634     getConnection : function(){
11635         return this.useAjax ? Roo.Ajax : this.conn;
11636     },
11637
11638     /**
11639      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11640      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11641      * process that block using the passed callback.
11642      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11643      * for the request to the remote server.
11644      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11645      * object into a block of Roo.data.Records.
11646      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11647      * The function must be passed <ul>
11648      * <li>The Record block object</li>
11649      * <li>The "arg" argument from the load function</li>
11650      * <li>A boolean success indicator</li>
11651      * </ul>
11652      * @param {Object} scope The scope in which to call the callback
11653      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11654      */
11655     load : function(params, reader, callback, scope, arg){
11656         if(this.fireEvent("beforeload", this, params) !== false){
11657             var  o = {
11658                 params : params || {},
11659                 request: {
11660                     callback : callback,
11661                     scope : scope,
11662                     arg : arg
11663                 },
11664                 reader: reader,
11665                 callback : this.loadResponse,
11666                 scope: this
11667             };
11668             if(this.useAjax){
11669                 Roo.applyIf(o, this.conn);
11670                 if(this.activeRequest){
11671                     Roo.Ajax.abort(this.activeRequest);
11672                 }
11673                 this.activeRequest = Roo.Ajax.request(o);
11674             }else{
11675                 this.conn.request(o);
11676             }
11677         }else{
11678             callback.call(scope||this, null, arg, false);
11679         }
11680     },
11681
11682     // private
11683     loadResponse : function(o, success, response){
11684         delete this.activeRequest;
11685         if(!success){
11686             this.fireEvent("loadexception", this, o, response);
11687             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11688             return;
11689         }
11690         var result;
11691         try {
11692             result = o.reader.read(response);
11693         }catch(e){
11694             this.fireEvent("loadexception", this, o, response, e);
11695             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11696             return;
11697         }
11698         
11699         this.fireEvent("load", this, o, o.request.arg);
11700         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11701     },
11702
11703     // private
11704     update : function(dataSet){
11705
11706     },
11707
11708     // private
11709     updateResponse : function(dataSet){
11710
11711     }
11712 });/*
11713  * Based on:
11714  * Ext JS Library 1.1.1
11715  * Copyright(c) 2006-2007, Ext JS, LLC.
11716  *
11717  * Originally Released Under LGPL - original licence link has changed is not relivant.
11718  *
11719  * Fork - LGPL
11720  * <script type="text/javascript">
11721  */
11722
11723 /**
11724  * @class Roo.data.ScriptTagProxy
11725  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11726  * other than the originating domain of the running page.<br><br>
11727  * <p>
11728  * <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
11729  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11730  * <p>
11731  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11732  * source code that is used as the source inside a &lt;script> tag.<br><br>
11733  * <p>
11734  * In order for the browser to process the returned data, the server must wrap the data object
11735  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11736  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11737  * depending on whether the callback name was passed:
11738  * <p>
11739  * <pre><code>
11740 boolean scriptTag = false;
11741 String cb = request.getParameter("callback");
11742 if (cb != null) {
11743     scriptTag = true;
11744     response.setContentType("text/javascript");
11745 } else {
11746     response.setContentType("application/x-json");
11747 }
11748 Writer out = response.getWriter();
11749 if (scriptTag) {
11750     out.write(cb + "(");
11751 }
11752 out.print(dataBlock.toJsonString());
11753 if (scriptTag) {
11754     out.write(");");
11755 }
11756 </pre></code>
11757  *
11758  * @constructor
11759  * @param {Object} config A configuration object.
11760  */
11761 Roo.data.ScriptTagProxy = function(config){
11762     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11763     Roo.apply(this, config);
11764     this.head = document.getElementsByTagName("head")[0];
11765 };
11766
11767 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11768
11769 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11770     /**
11771      * @cfg {String} url The URL from which to request the data object.
11772      */
11773     /**
11774      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11775      */
11776     timeout : 30000,
11777     /**
11778      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11779      * the server the name of the callback function set up by the load call to process the returned data object.
11780      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11781      * javascript output which calls this named function passing the data object as its only parameter.
11782      */
11783     callbackParam : "callback",
11784     /**
11785      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11786      * name to the request.
11787      */
11788     nocache : true,
11789
11790     /**
11791      * Load data from the configured URL, read the data object into
11792      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11793      * process that block using the passed callback.
11794      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11795      * for the request to the remote server.
11796      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11797      * object into a block of Roo.data.Records.
11798      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11799      * The function must be passed <ul>
11800      * <li>The Record block object</li>
11801      * <li>The "arg" argument from the load function</li>
11802      * <li>A boolean success indicator</li>
11803      * </ul>
11804      * @param {Object} scope The scope in which to call the callback
11805      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11806      */
11807     load : function(params, reader, callback, scope, arg){
11808         if(this.fireEvent("beforeload", this, params) !== false){
11809
11810             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11811
11812             var url = this.url;
11813             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11814             if(this.nocache){
11815                 url += "&_dc=" + (new Date().getTime());
11816             }
11817             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11818             var trans = {
11819                 id : transId,
11820                 cb : "stcCallback"+transId,
11821                 scriptId : "stcScript"+transId,
11822                 params : params,
11823                 arg : arg,
11824                 url : url,
11825                 callback : callback,
11826                 scope : scope,
11827                 reader : reader
11828             };
11829             var conn = this;
11830
11831             window[trans.cb] = function(o){
11832                 conn.handleResponse(o, trans);
11833             };
11834
11835             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11836
11837             if(this.autoAbort !== false){
11838                 this.abort();
11839             }
11840
11841             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11842
11843             var script = document.createElement("script");
11844             script.setAttribute("src", url);
11845             script.setAttribute("type", "text/javascript");
11846             script.setAttribute("id", trans.scriptId);
11847             this.head.appendChild(script);
11848
11849             this.trans = trans;
11850         }else{
11851             callback.call(scope||this, null, arg, false);
11852         }
11853     },
11854
11855     // private
11856     isLoading : function(){
11857         return this.trans ? true : false;
11858     },
11859
11860     /**
11861      * Abort the current server request.
11862      */
11863     abort : function(){
11864         if(this.isLoading()){
11865             this.destroyTrans(this.trans);
11866         }
11867     },
11868
11869     // private
11870     destroyTrans : function(trans, isLoaded){
11871         this.head.removeChild(document.getElementById(trans.scriptId));
11872         clearTimeout(trans.timeoutId);
11873         if(isLoaded){
11874             window[trans.cb] = undefined;
11875             try{
11876                 delete window[trans.cb];
11877             }catch(e){}
11878         }else{
11879             // if hasn't been loaded, wait for load to remove it to prevent script error
11880             window[trans.cb] = function(){
11881                 window[trans.cb] = undefined;
11882                 try{
11883                     delete window[trans.cb];
11884                 }catch(e){}
11885             };
11886         }
11887     },
11888
11889     // private
11890     handleResponse : function(o, trans){
11891         this.trans = false;
11892         this.destroyTrans(trans, true);
11893         var result;
11894         try {
11895             result = trans.reader.readRecords(o);
11896         }catch(e){
11897             this.fireEvent("loadexception", this, o, trans.arg, e);
11898             trans.callback.call(trans.scope||window, null, trans.arg, false);
11899             return;
11900         }
11901         this.fireEvent("load", this, o, trans.arg);
11902         trans.callback.call(trans.scope||window, result, trans.arg, true);
11903     },
11904
11905     // private
11906     handleFailure : function(trans){
11907         this.trans = false;
11908         this.destroyTrans(trans, false);
11909         this.fireEvent("loadexception", this, null, trans.arg);
11910         trans.callback.call(trans.scope||window, null, trans.arg, false);
11911     }
11912 });/*
11913  * Based on:
11914  * Ext JS Library 1.1.1
11915  * Copyright(c) 2006-2007, Ext JS, LLC.
11916  *
11917  * Originally Released Under LGPL - original licence link has changed is not relivant.
11918  *
11919  * Fork - LGPL
11920  * <script type="text/javascript">
11921  */
11922
11923 /**
11924  * @class Roo.data.JsonReader
11925  * @extends Roo.data.DataReader
11926  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11927  * based on mappings in a provided Roo.data.Record constructor.
11928  * 
11929  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11930  * in the reply previously. 
11931  * 
11932  * <p>
11933  * Example code:
11934  * <pre><code>
11935 var RecordDef = Roo.data.Record.create([
11936     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11937     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11938 ]);
11939 var myReader = new Roo.data.JsonReader({
11940     totalProperty: "results",    // The property which contains the total dataset size (optional)
11941     root: "rows",                // The property which contains an Array of row objects
11942     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11943 }, RecordDef);
11944 </code></pre>
11945  * <p>
11946  * This would consume a JSON file like this:
11947  * <pre><code>
11948 { 'results': 2, 'rows': [
11949     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11950     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11951 }
11952 </code></pre>
11953  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11954  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11955  * paged from the remote server.
11956  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11957  * @cfg {String} root name of the property which contains the Array of row objects.
11958  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11959  * @cfg {Array} fields Array of field definition objects
11960  * @constructor
11961  * Create a new JsonReader
11962  * @param {Object} meta Metadata configuration options
11963  * @param {Object} recordType Either an Array of field definition objects,
11964  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11965  */
11966 Roo.data.JsonReader = function(meta, recordType){
11967     
11968     meta = meta || {};
11969     // set some defaults:
11970     Roo.applyIf(meta, {
11971         totalProperty: 'total',
11972         successProperty : 'success',
11973         root : 'data',
11974         id : 'id'
11975     });
11976     
11977     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11978 };
11979 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11980     
11981     /**
11982      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11983      * Used by Store query builder to append _requestMeta to params.
11984      * 
11985      */
11986     metaFromRemote : false,
11987     /**
11988      * This method is only used by a DataProxy which has retrieved data from a remote server.
11989      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11990      * @return {Object} data A data block which is used by an Roo.data.Store object as
11991      * a cache of Roo.data.Records.
11992      */
11993     read : function(response){
11994         var json = response.responseText;
11995        
11996         var o = /* eval:var:o */ eval("("+json+")");
11997         if(!o) {
11998             throw {message: "JsonReader.read: Json object not found"};
11999         }
12000         
12001         if(o.metaData){
12002             
12003             delete this.ef;
12004             this.metaFromRemote = true;
12005             this.meta = o.metaData;
12006             this.recordType = Roo.data.Record.create(o.metaData.fields);
12007             this.onMetaChange(this.meta, this.recordType, o);
12008         }
12009         return this.readRecords(o);
12010     },
12011
12012     // private function a store will implement
12013     onMetaChange : function(meta, recordType, o){
12014
12015     },
12016
12017     /**
12018          * @ignore
12019          */
12020     simpleAccess: function(obj, subsc) {
12021         return obj[subsc];
12022     },
12023
12024         /**
12025          * @ignore
12026          */
12027     getJsonAccessor: function(){
12028         var re = /[\[\.]/;
12029         return function(expr) {
12030             try {
12031                 return(re.test(expr))
12032                     ? new Function("obj", "return obj." + expr)
12033                     : function(obj){
12034                         return obj[expr];
12035                     };
12036             } catch(e){}
12037             return Roo.emptyFn;
12038         };
12039     }(),
12040
12041     /**
12042      * Create a data block containing Roo.data.Records from an XML document.
12043      * @param {Object} o An object which contains an Array of row objects in the property specified
12044      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12045      * which contains the total size of the dataset.
12046      * @return {Object} data A data block which is used by an Roo.data.Store object as
12047      * a cache of Roo.data.Records.
12048      */
12049     readRecords : function(o){
12050         /**
12051          * After any data loads, the raw JSON data is available for further custom processing.
12052          * @type Object
12053          */
12054         this.o = o;
12055         var s = this.meta, Record = this.recordType,
12056             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12057
12058 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12059         if (!this.ef) {
12060             if(s.totalProperty) {
12061                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12062                 }
12063                 if(s.successProperty) {
12064                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12065                 }
12066                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12067                 if (s.id) {
12068                         var g = this.getJsonAccessor(s.id);
12069                         this.getId = function(rec) {
12070                                 var r = g(rec);  
12071                                 return (r === undefined || r === "") ? null : r;
12072                         };
12073                 } else {
12074                         this.getId = function(){return null;};
12075                 }
12076             this.ef = [];
12077             for(var jj = 0; jj < fl; jj++){
12078                 f = fi[jj];
12079                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12080                 this.ef[jj] = this.getJsonAccessor(map);
12081             }
12082         }
12083
12084         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12085         if(s.totalProperty){
12086             var vt = parseInt(this.getTotal(o), 10);
12087             if(!isNaN(vt)){
12088                 totalRecords = vt;
12089             }
12090         }
12091         if(s.successProperty){
12092             var vs = this.getSuccess(o);
12093             if(vs === false || vs === 'false'){
12094                 success = false;
12095             }
12096         }
12097         var records = [];
12098         for(var i = 0; i < c; i++){
12099                 var n = root[i];
12100             var values = {};
12101             var id = this.getId(n);
12102             for(var j = 0; j < fl; j++){
12103                 f = fi[j];
12104             var v = this.ef[j](n);
12105             if (!f.convert) {
12106                 Roo.log('missing convert for ' + f.name);
12107                 Roo.log(f);
12108                 continue;
12109             }
12110             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12111             }
12112             var record = new Record(values, id);
12113             record.json = n;
12114             records[i] = record;
12115         }
12116         return {
12117             raw : o,
12118             success : success,
12119             records : records,
12120             totalRecords : totalRecords
12121         };
12122     }
12123 });/*
12124  * Based on:
12125  * Ext JS Library 1.1.1
12126  * Copyright(c) 2006-2007, Ext JS, LLC.
12127  *
12128  * Originally Released Under LGPL - original licence link has changed is not relivant.
12129  *
12130  * Fork - LGPL
12131  * <script type="text/javascript">
12132  */
12133
12134 /**
12135  * @class Roo.data.ArrayReader
12136  * @extends Roo.data.DataReader
12137  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12138  * Each element of that Array represents a row of data fields. The
12139  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12140  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12141  * <p>
12142  * Example code:.
12143  * <pre><code>
12144 var RecordDef = Roo.data.Record.create([
12145     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12146     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12147 ]);
12148 var myReader = new Roo.data.ArrayReader({
12149     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12150 }, RecordDef);
12151 </code></pre>
12152  * <p>
12153  * This would consume an Array like this:
12154  * <pre><code>
12155 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12156   </code></pre>
12157  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12158  * @constructor
12159  * Create a new JsonReader
12160  * @param {Object} meta Metadata configuration options.
12161  * @param {Object} recordType Either an Array of field definition objects
12162  * as specified to {@link Roo.data.Record#create},
12163  * or an {@link Roo.data.Record} object
12164  * created using {@link Roo.data.Record#create}.
12165  */
12166 Roo.data.ArrayReader = function(meta, recordType){
12167     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12168 };
12169
12170 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12171     /**
12172      * Create a data block containing Roo.data.Records from an XML document.
12173      * @param {Object} o An Array of row objects which represents the dataset.
12174      * @return {Object} data A data block which is used by an Roo.data.Store object as
12175      * a cache of Roo.data.Records.
12176      */
12177     readRecords : function(o){
12178         var sid = this.meta ? this.meta.id : null;
12179         var recordType = this.recordType, fields = recordType.prototype.fields;
12180         var records = [];
12181         var root = o;
12182             for(var i = 0; i < root.length; i++){
12183                     var n = root[i];
12184                 var values = {};
12185                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12186                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12187                 var f = fields.items[j];
12188                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12189                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12190                 v = f.convert(v);
12191                 values[f.name] = v;
12192             }
12193                 var record = new recordType(values, id);
12194                 record.json = n;
12195                 records[records.length] = record;
12196             }
12197             return {
12198                 records : records,
12199                 totalRecords : records.length
12200             };
12201     }
12202 });/*
12203  * - LGPL
12204  * * 
12205  */
12206
12207 /**
12208  * @class Roo.bootstrap.ComboBox
12209  * @extends Roo.bootstrap.TriggerField
12210  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12211  * @cfg {Boolean} append (true|false) default false
12212  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12213  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12214  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12215  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12216  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12217  * @cfg {Boolean} animate default true
12218  * @cfg {Boolean} emptyResultText only for touch device
12219  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12220  * @constructor
12221  * Create a new ComboBox.
12222  * @param {Object} config Configuration options
12223  */
12224 Roo.bootstrap.ComboBox = function(config){
12225     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12226     this.addEvents({
12227         /**
12228          * @event expand
12229          * Fires when the dropdown list is expanded
12230              * @param {Roo.bootstrap.ComboBox} combo This combo box
12231              */
12232         'expand' : true,
12233         /**
12234          * @event collapse
12235          * Fires when the dropdown list is collapsed
12236              * @param {Roo.bootstrap.ComboBox} combo This combo box
12237              */
12238         'collapse' : true,
12239         /**
12240          * @event beforeselect
12241          * Fires before a list item is selected. Return false to cancel the selection.
12242              * @param {Roo.bootstrap.ComboBox} combo This combo box
12243              * @param {Roo.data.Record} record The data record returned from the underlying store
12244              * @param {Number} index The index of the selected item in the dropdown list
12245              */
12246         'beforeselect' : true,
12247         /**
12248          * @event select
12249          * Fires when a list item is selected
12250              * @param {Roo.bootstrap.ComboBox} combo This combo box
12251              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12252              * @param {Number} index The index of the selected item in the dropdown list
12253              */
12254         'select' : true,
12255         /**
12256          * @event beforequery
12257          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12258          * The event object passed has these properties:
12259              * @param {Roo.bootstrap.ComboBox} combo This combo box
12260              * @param {String} query The query
12261              * @param {Boolean} forceAll true to force "all" query
12262              * @param {Boolean} cancel true to cancel the query
12263              * @param {Object} e The query event object
12264              */
12265         'beforequery': true,
12266          /**
12267          * @event add
12268          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12269              * @param {Roo.bootstrap.ComboBox} combo This combo box
12270              */
12271         'add' : true,
12272         /**
12273          * @event edit
12274          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12275              * @param {Roo.bootstrap.ComboBox} combo This combo box
12276              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12277              */
12278         'edit' : true,
12279         /**
12280          * @event remove
12281          * Fires when the remove value from the combobox array
12282              * @param {Roo.bootstrap.ComboBox} combo This combo box
12283              */
12284         'remove' : true,
12285         /**
12286          * @event afterremove
12287          * Fires when the remove value from the combobox array
12288              * @param {Roo.bootstrap.ComboBox} combo This combo box
12289              */
12290         'afterremove' : true,
12291         /**
12292          * @event specialfilter
12293          * Fires when specialfilter
12294             * @param {Roo.bootstrap.ComboBox} combo This combo box
12295             */
12296         'specialfilter' : true,
12297         /**
12298          * @event tick
12299          * Fires when tick the element
12300             * @param {Roo.bootstrap.ComboBox} combo This combo box
12301             */
12302         'tick' : true,
12303         /**
12304          * @event touchviewdisplay
12305          * Fires when touch view require special display (default is using displayField)
12306             * @param {Roo.bootstrap.ComboBox} combo This combo box
12307             * @param {Object} cfg set html .
12308             */
12309         'touchviewdisplay' : true
12310         
12311     });
12312     
12313     this.item = [];
12314     this.tickItems = [];
12315     
12316     this.selectedIndex = -1;
12317     if(this.mode == 'local'){
12318         if(config.queryDelay === undefined){
12319             this.queryDelay = 10;
12320         }
12321         if(config.minChars === undefined){
12322             this.minChars = 0;
12323         }
12324     }
12325 };
12326
12327 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12328      
12329     /**
12330      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12331      * rendering into an Roo.Editor, defaults to false)
12332      */
12333     /**
12334      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12335      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12336      */
12337     /**
12338      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12339      */
12340     /**
12341      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12342      * the dropdown list (defaults to undefined, with no header element)
12343      */
12344
12345      /**
12346      * @cfg {String/Roo.Template} tpl The template to use to render the output
12347      */
12348      
12349      /**
12350      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12351      */
12352     listWidth: undefined,
12353     /**
12354      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12355      * mode = 'remote' or 'text' if mode = 'local')
12356      */
12357     displayField: undefined,
12358     
12359     /**
12360      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12361      * mode = 'remote' or 'value' if mode = 'local'). 
12362      * Note: use of a valueField requires the user make a selection
12363      * in order for a value to be mapped.
12364      */
12365     valueField: undefined,
12366     /**
12367      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12368      */
12369     modalTitle : '',
12370     
12371     /**
12372      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12373      * field's data value (defaults to the underlying DOM element's name)
12374      */
12375     hiddenName: undefined,
12376     /**
12377      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12378      */
12379     listClass: '',
12380     /**
12381      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12382      */
12383     selectedClass: 'active',
12384     
12385     /**
12386      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12387      */
12388     shadow:'sides',
12389     /**
12390      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12391      * anchor positions (defaults to 'tl-bl')
12392      */
12393     listAlign: 'tl-bl?',
12394     /**
12395      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12396      */
12397     maxHeight: 300,
12398     /**
12399      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12400      * query specified by the allQuery config option (defaults to 'query')
12401      */
12402     triggerAction: 'query',
12403     /**
12404      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12405      * (defaults to 4, does not apply if editable = false)
12406      */
12407     minChars : 4,
12408     /**
12409      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12410      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12411      */
12412     typeAhead: false,
12413     /**
12414      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12415      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12416      */
12417     queryDelay: 500,
12418     /**
12419      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12420      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12421      */
12422     pageSize: 0,
12423     /**
12424      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12425      * when editable = true (defaults to false)
12426      */
12427     selectOnFocus:false,
12428     /**
12429      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12430      */
12431     queryParam: 'query',
12432     /**
12433      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12434      * when mode = 'remote' (defaults to 'Loading...')
12435      */
12436     loadingText: 'Loading...',
12437     /**
12438      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12439      */
12440     resizable: false,
12441     /**
12442      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12443      */
12444     handleHeight : 8,
12445     /**
12446      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12447      * traditional select (defaults to true)
12448      */
12449     editable: true,
12450     /**
12451      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12452      */
12453     allQuery: '',
12454     /**
12455      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12456      */
12457     mode: 'remote',
12458     /**
12459      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12460      * listWidth has a higher value)
12461      */
12462     minListWidth : 70,
12463     /**
12464      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12465      * allow the user to set arbitrary text into the field (defaults to false)
12466      */
12467     forceSelection:false,
12468     /**
12469      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12470      * if typeAhead = true (defaults to 250)
12471      */
12472     typeAheadDelay : 250,
12473     /**
12474      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12475      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12476      */
12477     valueNotFoundText : undefined,
12478     /**
12479      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12480      */
12481     blockFocus : false,
12482     
12483     /**
12484      * @cfg {Boolean} disableClear Disable showing of clear button.
12485      */
12486     disableClear : false,
12487     /**
12488      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12489      */
12490     alwaysQuery : false,
12491     
12492     /**
12493      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12494      */
12495     multiple : false,
12496     
12497     /**
12498      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12499      */
12500     invalidClass : "has-warning",
12501     
12502     /**
12503      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12504      */
12505     validClass : "has-success",
12506     
12507     /**
12508      * @cfg {Boolean} specialFilter (true|false) special filter default false
12509      */
12510     specialFilter : false,
12511     
12512     /**
12513      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12514      */
12515     mobileTouchView : true,
12516     
12517     /**
12518      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12519      */
12520     useNativeIOS : false,
12521     
12522     ios_options : false,
12523     
12524     //private
12525     addicon : false,
12526     editicon: false,
12527     
12528     page: 0,
12529     hasQuery: false,
12530     append: false,
12531     loadNext: false,
12532     autoFocus : true,
12533     tickable : false,
12534     btnPosition : 'right',
12535     triggerList : true,
12536     showToggleBtn : true,
12537     animate : true,
12538     emptyResultText: 'Empty',
12539     triggerText : 'Select',
12540     
12541     // element that contains real text value.. (when hidden is used..)
12542     
12543     getAutoCreate : function()
12544     {
12545         var cfg = false;
12546         
12547         /*
12548          * Render classic select for iso
12549          */
12550         
12551         if(Roo.isIOS && this.useNativeIOS){
12552             cfg = this.getAutoCreateNativeIOS();
12553             return cfg;
12554         }
12555         
12556         /*
12557          * Touch Devices
12558          */
12559         
12560         if(Roo.isTouch && this.mobileTouchView){
12561             cfg = this.getAutoCreateTouchView();
12562             return cfg;;
12563         }
12564         
12565         /*
12566          *  Normal ComboBox
12567          */
12568         if(!this.tickable){
12569             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12570             if(this.name == 'info_year_invest_id_display_name'){
12571                 Roo.log('cfg.................................................');
12572                 Roo.log(cfg);
12573             }
12574             return cfg;
12575         }
12576         
12577         /*
12578          *  ComboBox with tickable selections
12579          */
12580              
12581         var align = this.labelAlign || this.parentLabelAlign();
12582         
12583         cfg = {
12584             cls : 'form-group roo-combobox-tickable' //input-group
12585         };
12586         
12587         var btn_text_select = '';
12588         var btn_text_done = '';
12589         var btn_text_cancel = '';
12590         
12591         if (this.btn_text_show) {
12592             btn_text_select = 'Select';
12593             btn_text_done = 'Done';
12594             btn_text_cancel = 'Cancel'; 
12595         }
12596         
12597         var buttons = {
12598             tag : 'div',
12599             cls : 'tickable-buttons',
12600             cn : [
12601                 {
12602                     tag : 'button',
12603                     type : 'button',
12604                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12605                     //html : this.triggerText
12606                     html: btn_text_select
12607                 },
12608                 {
12609                     tag : 'button',
12610                     type : 'button',
12611                     name : 'ok',
12612                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12613                     //html : 'Done'
12614                     html: btn_text_done
12615                 },
12616                 {
12617                     tag : 'button',
12618                     type : 'button',
12619                     name : 'cancel',
12620                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12621                     //html : 'Cancel'
12622                     html: btn_text_cancel
12623                 }
12624             ]
12625         };
12626         
12627         if(this.editable){
12628             buttons.cn.unshift({
12629                 tag: 'input',
12630                 cls: 'roo-select2-search-field-input'
12631             });
12632         }
12633         
12634         var _this = this;
12635         
12636         Roo.each(buttons.cn, function(c){
12637             if (_this.size) {
12638                 c.cls += ' btn-' + _this.size;
12639             }
12640
12641             if (_this.disabled) {
12642                 c.disabled = true;
12643             }
12644         });
12645         
12646         var box = {
12647             tag: 'div',
12648             cn: [
12649                 {
12650                     tag: 'input',
12651                     type : 'hidden',
12652                     cls: 'form-hidden-field'
12653                 },
12654                 {
12655                     tag: 'ul',
12656                     cls: 'roo-select2-choices',
12657                     cn:[
12658                         {
12659                             tag: 'li',
12660                             cls: 'roo-select2-search-field',
12661                             cn: [
12662
12663                                 buttons
12664                             ]
12665                         }
12666                     ]
12667                 }
12668             ]
12669         };
12670         
12671         var combobox = {
12672             cls: 'roo-select2-container input-group roo-select2-container-multi',
12673             cn: [
12674                 box
12675 //                {
12676 //                    tag: 'ul',
12677 //                    cls: 'typeahead typeahead-long dropdown-menu',
12678 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12679 //                }
12680             ]
12681         };
12682         
12683         if(this.hasFeedback && !this.allowBlank){
12684             
12685             var feedback = {
12686                 tag: 'span',
12687                 cls: 'glyphicon form-control-feedback'
12688             };
12689
12690             combobox.cn.push(feedback);
12691         }
12692         
12693         if (align ==='left' && this.fieldLabel.length) {
12694             
12695             cfg.cn = [
12696                 {
12697                     tag : 'i',
12698                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12699                     tooltip : 'This field is required'
12700                 },
12701                 {
12702                     tag: 'label',
12703                     'for' :  id,
12704                     cls : 'control-label',
12705                     html : this.fieldLabel
12706
12707                 },
12708                 {
12709                     cls : "", 
12710                     cn: [
12711                         combobox
12712                     ]
12713                 }
12714
12715             ];
12716             
12717             var labelCfg = cfg.cn[1];
12718             var contentCfg = cfg.cn[2];
12719             
12720
12721             if(this.indicatorpos == 'right'){
12722                 
12723                 cfg.cn = [
12724                     {
12725                         tag: 'label',
12726                         'for' :  id,
12727                         cls : 'control-label',
12728                         html : this.fieldLabel
12729
12730                     },
12731                     {
12732                         tag : 'i',
12733                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12734                         tooltip : 'This field is required'
12735                     },
12736                     {
12737                         cls : "",
12738                         cn: [
12739                             combobox
12740                         ]
12741                     }
12742
12743                 ];
12744                 
12745                 labelCfg = cfg.cn[0];
12746                 contentCfg = cfg.cn[2];
12747             
12748             }
12749             
12750             if(this.labelWidth > 12){
12751                 labelCfg.style = "width: " + this.labelWidth + 'px';
12752             }
12753             
12754             if(this.labelWidth < 13 && this.labelmd == 0){
12755                 this.labelmd = this.labelWidth;
12756             }
12757             
12758             if(this.labellg > 0){
12759                 labelCfg.cls += ' col-lg-' + this.labellg;
12760                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12761             }
12762             
12763             if(this.labelmd > 0){
12764                 labelCfg.cls += ' col-md-' + this.labelmd;
12765                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12766             }
12767             
12768             if(this.labelsm > 0){
12769                 labelCfg.cls += ' col-sm-' + this.labelsm;
12770                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12771             }
12772             
12773             if(this.labelxs > 0){
12774                 labelCfg.cls += ' col-xs-' + this.labelxs;
12775                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12776             }
12777                 
12778                 
12779         } else if ( this.fieldLabel.length) {
12780 //                Roo.log(" label");
12781                  cfg.cn = [
12782                     {
12783                         tag : 'i',
12784                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12785                         tooltip : 'This field is required'
12786                     },
12787                     {
12788                         tag: 'label',
12789                         //cls : 'input-group-addon',
12790                         html : this.fieldLabel
12791                         
12792                     },
12793                     
12794                     combobox
12795                     
12796                 ];
12797                 
12798                 if(this.indicatorpos == 'right'){
12799                     
12800                     cfg.cn = [
12801                         {
12802                             tag: 'label',
12803                             //cls : 'input-group-addon',
12804                             html : this.fieldLabel
12805
12806                         },
12807                         
12808                         {
12809                             tag : 'i',
12810                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12811                             tooltip : 'This field is required'
12812                         },
12813                         
12814                         combobox
12815
12816                     ];
12817                 
12818                 }
12819
12820         } else {
12821             
12822 //                Roo.log(" no label && no align");
12823                 cfg = combobox
12824                      
12825                 
12826         }
12827          
12828         var settings=this;
12829         ['xs','sm','md','lg'].map(function(size){
12830             if (settings[size]) {
12831                 cfg.cls += ' col-' + size + '-' + settings[size];
12832             }
12833         });
12834         
12835         return cfg;
12836         
12837     },
12838     
12839     _initEventsCalled : false,
12840     
12841     // private
12842     initEvents: function()
12843     {   
12844         if (this._initEventsCalled) { // as we call render... prevent looping...
12845             return;
12846         }
12847         this._initEventsCalled = true;
12848         
12849         if (!this.store) {
12850             throw "can not find store for combo";
12851         }
12852         
12853         this.store = Roo.factory(this.store, Roo.data);
12854         
12855         // if we are building from html. then this element is so complex, that we can not really
12856         // use the rendered HTML.
12857         // so we have to trash and replace the previous code.
12858         if (Roo.XComponent.build_from_html) {
12859             
12860             // remove this element....
12861             var e = this.el.dom, k=0;
12862             while (e ) { e = e.previousSibling;  ++k;}
12863
12864             this.el.remove();
12865             
12866             this.el=false;
12867             this.rendered = false;
12868             
12869             this.render(this.parent().getChildContainer(true), k);
12870             
12871             
12872             
12873         }
12874         
12875         if(Roo.isIOS && this.useNativeIOS){
12876             this.initIOSView();
12877             return;
12878         }
12879         
12880         /*
12881          * Touch Devices
12882          */
12883         
12884         if(Roo.isTouch && this.mobileTouchView){
12885             this.initTouchView();
12886             return;
12887         }
12888         
12889         if(this.tickable){
12890             this.initTickableEvents();
12891             return;
12892         }
12893         
12894         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12895         
12896         if(this.hiddenName){
12897             
12898             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12899             
12900             this.hiddenField.dom.value =
12901                 this.hiddenValue !== undefined ? this.hiddenValue :
12902                 this.value !== undefined ? this.value : '';
12903
12904             // prevent input submission
12905             this.el.dom.removeAttribute('name');
12906             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12907              
12908              
12909         }
12910         //if(Roo.isGecko){
12911         //    this.el.dom.setAttribute('autocomplete', 'off');
12912         //}
12913         
12914         var cls = 'x-combo-list';
12915         
12916         //this.list = new Roo.Layer({
12917         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12918         //});
12919         
12920         var _this = this;
12921         
12922         (function(){
12923             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12924             _this.list.setWidth(lw);
12925         }).defer(100);
12926         
12927         this.list.on('mouseover', this.onViewOver, this);
12928         this.list.on('mousemove', this.onViewMove, this);
12929         
12930         this.list.on('scroll', this.onViewScroll, this);
12931         
12932         /*
12933         this.list.swallowEvent('mousewheel');
12934         this.assetHeight = 0;
12935
12936         if(this.title){
12937             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12938             this.assetHeight += this.header.getHeight();
12939         }
12940
12941         this.innerList = this.list.createChild({cls:cls+'-inner'});
12942         this.innerList.on('mouseover', this.onViewOver, this);
12943         this.innerList.on('mousemove', this.onViewMove, this);
12944         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12945         
12946         if(this.allowBlank && !this.pageSize && !this.disableClear){
12947             this.footer = this.list.createChild({cls:cls+'-ft'});
12948             this.pageTb = new Roo.Toolbar(this.footer);
12949            
12950         }
12951         if(this.pageSize){
12952             this.footer = this.list.createChild({cls:cls+'-ft'});
12953             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12954                     {pageSize: this.pageSize});
12955             
12956         }
12957         
12958         if (this.pageTb && this.allowBlank && !this.disableClear) {
12959             var _this = this;
12960             this.pageTb.add(new Roo.Toolbar.Fill(), {
12961                 cls: 'x-btn-icon x-btn-clear',
12962                 text: '&#160;',
12963                 handler: function()
12964                 {
12965                     _this.collapse();
12966                     _this.clearValue();
12967                     _this.onSelect(false, -1);
12968                 }
12969             });
12970         }
12971         if (this.footer) {
12972             this.assetHeight += this.footer.getHeight();
12973         }
12974         */
12975             
12976         if(!this.tpl){
12977             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12978         }
12979
12980         this.view = new Roo.View(this.list, this.tpl, {
12981             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12982         });
12983         //this.view.wrapEl.setDisplayed(false);
12984         this.view.on('click', this.onViewClick, this);
12985         
12986         
12987         
12988         this.store.on('beforeload', this.onBeforeLoad, this);
12989         this.store.on('load', this.onLoad, this);
12990         this.store.on('loadexception', this.onLoadException, this);
12991         /*
12992         if(this.resizable){
12993             this.resizer = new Roo.Resizable(this.list,  {
12994                pinned:true, handles:'se'
12995             });
12996             this.resizer.on('resize', function(r, w, h){
12997                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12998                 this.listWidth = w;
12999                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13000                 this.restrictHeight();
13001             }, this);
13002             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13003         }
13004         */
13005         if(!this.editable){
13006             this.editable = true;
13007             this.setEditable(false);
13008         }
13009         
13010         /*
13011         
13012         if (typeof(this.events.add.listeners) != 'undefined') {
13013             
13014             this.addicon = this.wrap.createChild(
13015                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13016        
13017             this.addicon.on('click', function(e) {
13018                 this.fireEvent('add', this);
13019             }, this);
13020         }
13021         if (typeof(this.events.edit.listeners) != 'undefined') {
13022             
13023             this.editicon = this.wrap.createChild(
13024                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13025             if (this.addicon) {
13026                 this.editicon.setStyle('margin-left', '40px');
13027             }
13028             this.editicon.on('click', function(e) {
13029                 
13030                 // we fire even  if inothing is selected..
13031                 this.fireEvent('edit', this, this.lastData );
13032                 
13033             }, this);
13034         }
13035         */
13036         
13037         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13038             "up" : function(e){
13039                 this.inKeyMode = true;
13040                 this.selectPrev();
13041             },
13042
13043             "down" : function(e){
13044                 if(!this.isExpanded()){
13045                     this.onTriggerClick();
13046                 }else{
13047                     this.inKeyMode = true;
13048                     this.selectNext();
13049                 }
13050             },
13051
13052             "enter" : function(e){
13053 //                this.onViewClick();
13054                 //return true;
13055                 this.collapse();
13056                 
13057                 if(this.fireEvent("specialkey", this, e)){
13058                     this.onViewClick(false);
13059                 }
13060                 
13061                 return true;
13062             },
13063
13064             "esc" : function(e){
13065                 this.collapse();
13066             },
13067
13068             "tab" : function(e){
13069                 this.collapse();
13070                 
13071                 if(this.fireEvent("specialkey", this, e)){
13072                     this.onViewClick(false);
13073                 }
13074                 
13075                 return true;
13076             },
13077
13078             scope : this,
13079
13080             doRelay : function(foo, bar, hname){
13081                 if(hname == 'down' || this.scope.isExpanded()){
13082                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13083                 }
13084                 return true;
13085             },
13086
13087             forceKeyDown: true
13088         });
13089         
13090         
13091         this.queryDelay = Math.max(this.queryDelay || 10,
13092                 this.mode == 'local' ? 10 : 250);
13093         
13094         
13095         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13096         
13097         if(this.typeAhead){
13098             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13099         }
13100         if(this.editable !== false){
13101             this.inputEl().on("keyup", this.onKeyUp, this);
13102         }
13103         if(this.forceSelection){
13104             this.inputEl().on('blur', this.doForce, this);
13105         }
13106         
13107         if(this.multiple){
13108             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13109             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13110         }
13111     },
13112     
13113     initTickableEvents: function()
13114     {   
13115         this.createList();
13116         
13117         if(this.hiddenName){
13118             
13119             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13120             
13121             this.hiddenField.dom.value =
13122                 this.hiddenValue !== undefined ? this.hiddenValue :
13123                 this.value !== undefined ? this.value : '';
13124
13125             // prevent input submission
13126             this.el.dom.removeAttribute('name');
13127             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13128              
13129              
13130         }
13131         
13132 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13133         
13134         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13135         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13136         if(this.triggerList){
13137             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13138         }
13139          
13140         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13141         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13142         
13143         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13144         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13145         
13146         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13147         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13148         
13149         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13150         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13151         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13152         
13153         this.okBtn.hide();
13154         this.cancelBtn.hide();
13155         
13156         var _this = this;
13157         
13158         (function(){
13159             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13160             _this.list.setWidth(lw);
13161         }).defer(100);
13162         
13163         this.list.on('mouseover', this.onViewOver, this);
13164         this.list.on('mousemove', this.onViewMove, this);
13165         
13166         this.list.on('scroll', this.onViewScroll, this);
13167         
13168         if(!this.tpl){
13169             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13170         }
13171
13172         this.view = new Roo.View(this.list, this.tpl, {
13173             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13174         });
13175         
13176         //this.view.wrapEl.setDisplayed(false);
13177         this.view.on('click', this.onViewClick, this);
13178         
13179         
13180         
13181         this.store.on('beforeload', this.onBeforeLoad, this);
13182         this.store.on('load', this.onLoad, this);
13183         this.store.on('loadexception', this.onLoadException, this);
13184         
13185         if(this.editable){
13186             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13187                 "up" : function(e){
13188                     this.inKeyMode = true;
13189                     this.selectPrev();
13190                 },
13191
13192                 "down" : function(e){
13193                     this.inKeyMode = true;
13194                     this.selectNext();
13195                 },
13196
13197                 "enter" : function(e){
13198                     if(this.fireEvent("specialkey", this, e)){
13199                         this.onViewClick(false);
13200                     }
13201                     
13202                     return true;
13203                 },
13204
13205                 "esc" : function(e){
13206                     this.onTickableFooterButtonClick(e, false, false);
13207                 },
13208
13209                 "tab" : function(e){
13210                     this.fireEvent("specialkey", this, e);
13211                     
13212                     this.onTickableFooterButtonClick(e, false, false);
13213                     
13214                     return true;
13215                 },
13216
13217                 scope : this,
13218
13219                 doRelay : function(e, fn, key){
13220                     if(this.scope.isExpanded()){
13221                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13222                     }
13223                     return true;
13224                 },
13225
13226                 forceKeyDown: true
13227             });
13228         }
13229         
13230         this.queryDelay = Math.max(this.queryDelay || 10,
13231                 this.mode == 'local' ? 10 : 250);
13232         
13233         
13234         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13235         
13236         if(this.typeAhead){
13237             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13238         }
13239         
13240         if(this.editable !== false){
13241             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13242         }
13243         
13244     },
13245
13246     onDestroy : function(){
13247         if(this.view){
13248             this.view.setStore(null);
13249             this.view.el.removeAllListeners();
13250             this.view.el.remove();
13251             this.view.purgeListeners();
13252         }
13253         if(this.list){
13254             this.list.dom.innerHTML  = '';
13255         }
13256         
13257         if(this.store){
13258             this.store.un('beforeload', this.onBeforeLoad, this);
13259             this.store.un('load', this.onLoad, this);
13260             this.store.un('loadexception', this.onLoadException, this);
13261         }
13262         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13263     },
13264
13265     // private
13266     fireKey : function(e){
13267         if(e.isNavKeyPress() && !this.list.isVisible()){
13268             this.fireEvent("specialkey", this, e);
13269         }
13270     },
13271
13272     // private
13273     onResize: function(w, h){
13274 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13275 //        
13276 //        if(typeof w != 'number'){
13277 //            // we do not handle it!?!?
13278 //            return;
13279 //        }
13280 //        var tw = this.trigger.getWidth();
13281 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13282 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13283 //        var x = w - tw;
13284 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13285 //            
13286 //        //this.trigger.setStyle('left', x+'px');
13287 //        
13288 //        if(this.list && this.listWidth === undefined){
13289 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13290 //            this.list.setWidth(lw);
13291 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13292 //        }
13293         
13294     
13295         
13296     },
13297
13298     /**
13299      * Allow or prevent the user from directly editing the field text.  If false is passed,
13300      * the user will only be able to select from the items defined in the dropdown list.  This method
13301      * is the runtime equivalent of setting the 'editable' config option at config time.
13302      * @param {Boolean} value True to allow the user to directly edit the field text
13303      */
13304     setEditable : function(value){
13305         if(value == this.editable){
13306             return;
13307         }
13308         this.editable = value;
13309         if(!value){
13310             this.inputEl().dom.setAttribute('readOnly', true);
13311             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13312             this.inputEl().addClass('x-combo-noedit');
13313         }else{
13314             this.inputEl().dom.setAttribute('readOnly', false);
13315             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13316             this.inputEl().removeClass('x-combo-noedit');
13317         }
13318     },
13319
13320     // private
13321     
13322     onBeforeLoad : function(combo,opts){
13323         if(!this.hasFocus){
13324             return;
13325         }
13326          if (!opts.add) {
13327             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13328          }
13329         this.restrictHeight();
13330         this.selectedIndex = -1;
13331     },
13332
13333     // private
13334     onLoad : function(){
13335         
13336         this.hasQuery = false;
13337         
13338         if(!this.hasFocus){
13339             return;
13340         }
13341         
13342         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13343             this.loading.hide();
13344         }
13345              
13346         if(this.store.getCount() > 0){
13347             this.expand();
13348             this.restrictHeight();
13349             if(this.lastQuery == this.allQuery){
13350                 if(this.editable && !this.tickable){
13351                     this.inputEl().dom.select();
13352                 }
13353                 
13354                 if(
13355                     !this.selectByValue(this.value, true) &&
13356                     this.autoFocus && 
13357                     (
13358                         !this.store.lastOptions ||
13359                         typeof(this.store.lastOptions.add) == 'undefined' || 
13360                         this.store.lastOptions.add != true
13361                     )
13362                 ){
13363                     this.select(0, true);
13364                 }
13365             }else{
13366                 if(this.autoFocus){
13367                     this.selectNext();
13368                 }
13369                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13370                     this.taTask.delay(this.typeAheadDelay);
13371                 }
13372             }
13373         }else{
13374             this.onEmptyResults();
13375         }
13376         
13377         //this.el.focus();
13378     },
13379     // private
13380     onLoadException : function()
13381     {
13382         this.hasQuery = false;
13383         
13384         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13385             this.loading.hide();
13386         }
13387         
13388         if(this.tickable && this.editable){
13389             return;
13390         }
13391         
13392         this.collapse();
13393         // only causes errors at present
13394         //Roo.log(this.store.reader.jsonData);
13395         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13396             // fixme
13397             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13398         //}
13399         
13400         
13401     },
13402     // private
13403     onTypeAhead : function(){
13404         if(this.store.getCount() > 0){
13405             var r = this.store.getAt(0);
13406             var newValue = r.data[this.displayField];
13407             var len = newValue.length;
13408             var selStart = this.getRawValue().length;
13409             
13410             if(selStart != len){
13411                 this.setRawValue(newValue);
13412                 this.selectText(selStart, newValue.length);
13413             }
13414         }
13415     },
13416
13417     // private
13418     onSelect : function(record, index){
13419         
13420         if(this.fireEvent('beforeselect', this, record, index) !== false){
13421         
13422             this.setFromData(index > -1 ? record.data : false);
13423             
13424             this.collapse();
13425             this.fireEvent('select', this, record, index);
13426         }
13427     },
13428
13429     /**
13430      * Returns the currently selected field value or empty string if no value is set.
13431      * @return {String} value The selected value
13432      */
13433     getValue : function()
13434     {
13435         if(Roo.isIOS && this.useNativeIOS){
13436             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13437         }
13438         
13439         if(this.multiple){
13440             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13441         }
13442         
13443         if(this.valueField){
13444             return typeof this.value != 'undefined' ? this.value : '';
13445         }else{
13446             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13447         }
13448     },
13449     
13450     getRawValue : function()
13451     {
13452         if(Roo.isIOS && this.useNativeIOS){
13453             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13454         }
13455         
13456         var v = this.inputEl().getValue();
13457         
13458         return v;
13459     },
13460
13461     /**
13462      * Clears any text/value currently set in the field
13463      */
13464     clearValue : function(){
13465         
13466         if(this.hiddenField){
13467             this.hiddenField.dom.value = '';
13468         }
13469         this.value = '';
13470         this.setRawValue('');
13471         this.lastSelectionText = '';
13472         this.lastData = false;
13473         
13474         var close = this.closeTriggerEl();
13475         
13476         if(close){
13477             close.hide();
13478         }
13479         
13480         this.validate();
13481         
13482     },
13483
13484     /**
13485      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13486      * will be displayed in the field.  If the value does not match the data value of an existing item,
13487      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13488      * Otherwise the field will be blank (although the value will still be set).
13489      * @param {String} value The value to match
13490      */
13491     setValue : function(v)
13492     {
13493         if(Roo.isIOS && this.useNativeIOS){
13494             this.setIOSValue(v);
13495             return;
13496         }
13497         
13498         if(this.multiple){
13499             this.syncValue();
13500             return;
13501         }
13502         
13503         var text = v;
13504         if(this.valueField){
13505             var r = this.findRecord(this.valueField, v);
13506             if(r){
13507                 text = r.data[this.displayField];
13508             }else if(this.valueNotFoundText !== undefined){
13509                 text = this.valueNotFoundText;
13510             }
13511         }
13512         this.lastSelectionText = text;
13513         if(this.hiddenField){
13514             this.hiddenField.dom.value = v;
13515         }
13516         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13517         this.value = v;
13518         
13519         var close = this.closeTriggerEl();
13520         
13521         if(close){
13522             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13523         }
13524         
13525         this.validate();
13526     },
13527     /**
13528      * @property {Object} the last set data for the element
13529      */
13530     
13531     lastData : false,
13532     /**
13533      * Sets the value of the field based on a object which is related to the record format for the store.
13534      * @param {Object} value the value to set as. or false on reset?
13535      */
13536     setFromData : function(o){
13537         
13538         if(this.multiple){
13539             this.addItem(o);
13540             return;
13541         }
13542             
13543         var dv = ''; // display value
13544         var vv = ''; // value value..
13545         this.lastData = o;
13546         if (this.displayField) {
13547             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13548         } else {
13549             // this is an error condition!!!
13550             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13551         }
13552         
13553         if(this.valueField){
13554             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13555         }
13556         
13557         var close = this.closeTriggerEl();
13558         
13559         if(close){
13560             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13561         }
13562         
13563         if(this.hiddenField){
13564             this.hiddenField.dom.value = vv;
13565             
13566             this.lastSelectionText = dv;
13567             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13568             this.value = vv;
13569             return;
13570         }
13571         // no hidden field.. - we store the value in 'value', but still display
13572         // display field!!!!
13573         this.lastSelectionText = dv;
13574         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13575         this.value = vv;
13576         
13577         
13578         
13579     },
13580     // private
13581     reset : function(){
13582         // overridden so that last data is reset..
13583         
13584         if(this.multiple){
13585             this.clearItem();
13586             return;
13587         }
13588         
13589         this.setValue(this.originalValue);
13590         //this.clearInvalid();
13591         this.lastData = false;
13592         if (this.view) {
13593             this.view.clearSelections();
13594         }
13595         
13596         this.validate();
13597     },
13598     // private
13599     findRecord : function(prop, value){
13600         var record;
13601         if(this.store.getCount() > 0){
13602             this.store.each(function(r){
13603                 if(r.data[prop] == value){
13604                     record = r;
13605                     return false;
13606                 }
13607                 return true;
13608             });
13609         }
13610         return record;
13611     },
13612     
13613     getName: function()
13614     {
13615         // returns hidden if it's set..
13616         if (!this.rendered) {return ''};
13617         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13618         
13619     },
13620     // private
13621     onViewMove : function(e, t){
13622         this.inKeyMode = false;
13623     },
13624
13625     // private
13626     onViewOver : function(e, t){
13627         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13628             return;
13629         }
13630         var item = this.view.findItemFromChild(t);
13631         
13632         if(item){
13633             var index = this.view.indexOf(item);
13634             this.select(index, false);
13635         }
13636     },
13637
13638     // private
13639     onViewClick : function(view, doFocus, el, e)
13640     {
13641         var index = this.view.getSelectedIndexes()[0];
13642         
13643         var r = this.store.getAt(index);
13644         
13645         if(this.tickable){
13646             
13647             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13648                 return;
13649             }
13650             
13651             var rm = false;
13652             var _this = this;
13653             
13654             Roo.each(this.tickItems, function(v,k){
13655                 
13656                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13657                     Roo.log(v);
13658                     _this.tickItems.splice(k, 1);
13659                     
13660                     if(typeof(e) == 'undefined' && view == false){
13661                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13662                     }
13663                     
13664                     rm = true;
13665                     return;
13666                 }
13667             });
13668             
13669             if(rm){
13670                 return;
13671             }
13672             
13673             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13674                 this.tickItems.push(r.data);
13675             }
13676             
13677             if(typeof(e) == 'undefined' && view == false){
13678                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13679             }
13680                     
13681             return;
13682         }
13683         
13684         if(r){
13685             this.onSelect(r, index);
13686         }
13687         if(doFocus !== false && !this.blockFocus){
13688             this.inputEl().focus();
13689         }
13690     },
13691
13692     // private
13693     restrictHeight : function(){
13694         //this.innerList.dom.style.height = '';
13695         //var inner = this.innerList.dom;
13696         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13697         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13698         //this.list.beginUpdate();
13699         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13700         this.list.alignTo(this.inputEl(), this.listAlign);
13701         this.list.alignTo(this.inputEl(), this.listAlign);
13702         //this.list.endUpdate();
13703     },
13704
13705     // private
13706     onEmptyResults : function(){
13707         
13708         if(this.tickable && this.editable){
13709             this.restrictHeight();
13710             return;
13711         }
13712         
13713         this.collapse();
13714     },
13715
13716     /**
13717      * Returns true if the dropdown list is expanded, else false.
13718      */
13719     isExpanded : function(){
13720         return this.list.isVisible();
13721     },
13722
13723     /**
13724      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13725      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13726      * @param {String} value The data value of the item to select
13727      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13728      * selected item if it is not currently in view (defaults to true)
13729      * @return {Boolean} True if the value matched an item in the list, else false
13730      */
13731     selectByValue : function(v, scrollIntoView){
13732         if(v !== undefined && v !== null){
13733             var r = this.findRecord(this.valueField || this.displayField, v);
13734             if(r){
13735                 this.select(this.store.indexOf(r), scrollIntoView);
13736                 return true;
13737             }
13738         }
13739         return false;
13740     },
13741
13742     /**
13743      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13744      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13745      * @param {Number} index The zero-based index of the list item to select
13746      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13747      * selected item if it is not currently in view (defaults to true)
13748      */
13749     select : function(index, scrollIntoView){
13750         this.selectedIndex = index;
13751         this.view.select(index);
13752         if(scrollIntoView !== false){
13753             var el = this.view.getNode(index);
13754             /*
13755              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13756              */
13757             if(el){
13758                 this.list.scrollChildIntoView(el, false);
13759             }
13760         }
13761     },
13762
13763     // private
13764     selectNext : function(){
13765         var ct = this.store.getCount();
13766         if(ct > 0){
13767             if(this.selectedIndex == -1){
13768                 this.select(0);
13769             }else if(this.selectedIndex < ct-1){
13770                 this.select(this.selectedIndex+1);
13771             }
13772         }
13773     },
13774
13775     // private
13776     selectPrev : function(){
13777         var ct = this.store.getCount();
13778         if(ct > 0){
13779             if(this.selectedIndex == -1){
13780                 this.select(0);
13781             }else if(this.selectedIndex != 0){
13782                 this.select(this.selectedIndex-1);
13783             }
13784         }
13785     },
13786
13787     // private
13788     onKeyUp : function(e){
13789         if(this.editable !== false && !e.isSpecialKey()){
13790             this.lastKey = e.getKey();
13791             this.dqTask.delay(this.queryDelay);
13792         }
13793     },
13794
13795     // private
13796     validateBlur : function(){
13797         return !this.list || !this.list.isVisible();   
13798     },
13799
13800     // private
13801     initQuery : function(){
13802         
13803         var v = this.getRawValue();
13804         
13805         if(this.tickable && this.editable){
13806             v = this.tickableInputEl().getValue();
13807         }
13808         
13809         this.doQuery(v);
13810     },
13811
13812     // private
13813     doForce : function(){
13814         if(this.inputEl().dom.value.length > 0){
13815             this.inputEl().dom.value =
13816                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13817              
13818         }
13819     },
13820
13821     /**
13822      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13823      * query allowing the query action to be canceled if needed.
13824      * @param {String} query The SQL query to execute
13825      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13826      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13827      * saved in the current store (defaults to false)
13828      */
13829     doQuery : function(q, forceAll){
13830         
13831         if(q === undefined || q === null){
13832             q = '';
13833         }
13834         var qe = {
13835             query: q,
13836             forceAll: forceAll,
13837             combo: this,
13838             cancel:false
13839         };
13840         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13841             return false;
13842         }
13843         q = qe.query;
13844         
13845         forceAll = qe.forceAll;
13846         if(forceAll === true || (q.length >= this.minChars)){
13847             
13848             this.hasQuery = true;
13849             
13850             if(this.lastQuery != q || this.alwaysQuery){
13851                 this.lastQuery = q;
13852                 if(this.mode == 'local'){
13853                     this.selectedIndex = -1;
13854                     if(forceAll){
13855                         this.store.clearFilter();
13856                     }else{
13857                         
13858                         if(this.specialFilter){
13859                             this.fireEvent('specialfilter', this);
13860                             this.onLoad();
13861                             return;
13862                         }
13863                         
13864                         this.store.filter(this.displayField, q);
13865                     }
13866                     
13867                     this.store.fireEvent("datachanged", this.store);
13868                     
13869                     this.onLoad();
13870                     
13871                     
13872                 }else{
13873                     
13874                     this.store.baseParams[this.queryParam] = q;
13875                     
13876                     var options = {params : this.getParams(q)};
13877                     
13878                     if(this.loadNext){
13879                         options.add = true;
13880                         options.params.start = this.page * this.pageSize;
13881                     }
13882                     
13883                     this.store.load(options);
13884                     
13885                     /*
13886                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13887                      *  we should expand the list on onLoad
13888                      *  so command out it
13889                      */
13890 //                    this.expand();
13891                 }
13892             }else{
13893                 this.selectedIndex = -1;
13894                 this.onLoad();   
13895             }
13896         }
13897         
13898         this.loadNext = false;
13899     },
13900     
13901     // private
13902     getParams : function(q){
13903         var p = {};
13904         //p[this.queryParam] = q;
13905         
13906         if(this.pageSize){
13907             p.start = 0;
13908             p.limit = this.pageSize;
13909         }
13910         return p;
13911     },
13912
13913     /**
13914      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13915      */
13916     collapse : function(){
13917         if(!this.isExpanded()){
13918             return;
13919         }
13920         
13921         this.list.hide();
13922         
13923         this.hasFocus = false;
13924         
13925         if(this.tickable){
13926             this.okBtn.hide();
13927             this.cancelBtn.hide();
13928             this.trigger.show();
13929             
13930             if(this.editable){
13931                 this.tickableInputEl().dom.value = '';
13932                 this.tickableInputEl().blur();
13933             }
13934             
13935         }
13936         
13937         Roo.get(document).un('mousedown', this.collapseIf, this);
13938         Roo.get(document).un('mousewheel', this.collapseIf, this);
13939         if (!this.editable) {
13940             Roo.get(document).un('keydown', this.listKeyPress, this);
13941         }
13942         this.fireEvent('collapse', this);
13943         
13944         this.validate();
13945     },
13946
13947     // private
13948     collapseIf : function(e){
13949         var in_combo  = e.within(this.el);
13950         var in_list =  e.within(this.list);
13951         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13952         
13953         if (in_combo || in_list || is_list) {
13954             //e.stopPropagation();
13955             return;
13956         }
13957         
13958         if(this.tickable){
13959             this.onTickableFooterButtonClick(e, false, false);
13960         }
13961
13962         this.collapse();
13963         
13964     },
13965
13966     /**
13967      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13968      */
13969     expand : function(){
13970        
13971         if(this.isExpanded() || !this.hasFocus){
13972             return;
13973         }
13974         
13975         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13976         this.list.setWidth(lw);
13977         
13978         Roo.log('expand');
13979         
13980         this.list.show();
13981         
13982         this.restrictHeight();
13983         
13984         if(this.tickable){
13985             
13986             this.tickItems = Roo.apply([], this.item);
13987             
13988             this.okBtn.show();
13989             this.cancelBtn.show();
13990             this.trigger.hide();
13991             
13992             if(this.editable){
13993                 this.tickableInputEl().focus();
13994             }
13995             
13996         }
13997         
13998         Roo.get(document).on('mousedown', this.collapseIf, this);
13999         Roo.get(document).on('mousewheel', this.collapseIf, this);
14000         if (!this.editable) {
14001             Roo.get(document).on('keydown', this.listKeyPress, this);
14002         }
14003         
14004         this.fireEvent('expand', this);
14005     },
14006
14007     // private
14008     // Implements the default empty TriggerField.onTriggerClick function
14009     onTriggerClick : function(e)
14010     {
14011         Roo.log('trigger click');
14012         
14013         if(this.disabled || !this.triggerList){
14014             return;
14015         }
14016         
14017         this.page = 0;
14018         this.loadNext = false;
14019         
14020         if(this.isExpanded()){
14021             this.collapse();
14022             if (!this.blockFocus) {
14023                 this.inputEl().focus();
14024             }
14025             
14026         }else {
14027             this.hasFocus = true;
14028             if(this.triggerAction == 'all') {
14029                 this.doQuery(this.allQuery, true);
14030             } else {
14031                 this.doQuery(this.getRawValue());
14032             }
14033             if (!this.blockFocus) {
14034                 this.inputEl().focus();
14035             }
14036         }
14037     },
14038     
14039     onTickableTriggerClick : function(e)
14040     {
14041         if(this.disabled){
14042             return;
14043         }
14044         
14045         this.page = 0;
14046         this.loadNext = false;
14047         this.hasFocus = true;
14048         
14049         if(this.triggerAction == 'all') {
14050             this.doQuery(this.allQuery, true);
14051         } else {
14052             this.doQuery(this.getRawValue());
14053         }
14054     },
14055     
14056     onSearchFieldClick : function(e)
14057     {
14058         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14059             this.onTickableFooterButtonClick(e, false, false);
14060             return;
14061         }
14062         
14063         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14064             return;
14065         }
14066         
14067         this.page = 0;
14068         this.loadNext = false;
14069         this.hasFocus = true;
14070         
14071         if(this.triggerAction == 'all') {
14072             this.doQuery(this.allQuery, true);
14073         } else {
14074             this.doQuery(this.getRawValue());
14075         }
14076     },
14077     
14078     listKeyPress : function(e)
14079     {
14080         //Roo.log('listkeypress');
14081         // scroll to first matching element based on key pres..
14082         if (e.isSpecialKey()) {
14083             return false;
14084         }
14085         var k = String.fromCharCode(e.getKey()).toUpperCase();
14086         //Roo.log(k);
14087         var match  = false;
14088         var csel = this.view.getSelectedNodes();
14089         var cselitem = false;
14090         if (csel.length) {
14091             var ix = this.view.indexOf(csel[0]);
14092             cselitem  = this.store.getAt(ix);
14093             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14094                 cselitem = false;
14095             }
14096             
14097         }
14098         
14099         this.store.each(function(v) { 
14100             if (cselitem) {
14101                 // start at existing selection.
14102                 if (cselitem.id == v.id) {
14103                     cselitem = false;
14104                 }
14105                 return true;
14106             }
14107                 
14108             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14109                 match = this.store.indexOf(v);
14110                 return false;
14111             }
14112             return true;
14113         }, this);
14114         
14115         if (match === false) {
14116             return true; // no more action?
14117         }
14118         // scroll to?
14119         this.view.select(match);
14120         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14121         sn.scrollIntoView(sn.dom.parentNode, false);
14122     },
14123     
14124     onViewScroll : function(e, t){
14125         
14126         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){
14127             return;
14128         }
14129         
14130         this.hasQuery = true;
14131         
14132         this.loading = this.list.select('.loading', true).first();
14133         
14134         if(this.loading === null){
14135             this.list.createChild({
14136                 tag: 'div',
14137                 cls: 'loading roo-select2-more-results roo-select2-active',
14138                 html: 'Loading more results...'
14139             });
14140             
14141             this.loading = this.list.select('.loading', true).first();
14142             
14143             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14144             
14145             this.loading.hide();
14146         }
14147         
14148         this.loading.show();
14149         
14150         var _combo = this;
14151         
14152         this.page++;
14153         this.loadNext = true;
14154         
14155         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14156         
14157         return;
14158     },
14159     
14160     addItem : function(o)
14161     {   
14162         var dv = ''; // display value
14163         
14164         if (this.displayField) {
14165             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14166         } else {
14167             // this is an error condition!!!
14168             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14169         }
14170         
14171         if(!dv.length){
14172             return;
14173         }
14174         
14175         var choice = this.choices.createChild({
14176             tag: 'li',
14177             cls: 'roo-select2-search-choice',
14178             cn: [
14179                 {
14180                     tag: 'div',
14181                     html: dv
14182                 },
14183                 {
14184                     tag: 'a',
14185                     href: '#',
14186                     cls: 'roo-select2-search-choice-close',
14187                     tabindex: '-1'
14188                 }
14189             ]
14190             
14191         }, this.searchField);
14192         
14193         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14194         
14195         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14196         
14197         this.item.push(o);
14198         
14199         this.lastData = o;
14200         
14201         this.syncValue();
14202         
14203         this.inputEl().dom.value = '';
14204         
14205         this.validate();
14206     },
14207     
14208     onRemoveItem : function(e, _self, o)
14209     {
14210         e.preventDefault();
14211         
14212         this.lastItem = Roo.apply([], this.item);
14213         
14214         var index = this.item.indexOf(o.data) * 1;
14215         
14216         if( index < 0){
14217             Roo.log('not this item?!');
14218             return;
14219         }
14220         
14221         this.item.splice(index, 1);
14222         o.item.remove();
14223         
14224         this.syncValue();
14225         
14226         this.fireEvent('remove', this, e);
14227         
14228         this.validate();
14229         
14230     },
14231     
14232     syncValue : function()
14233     {
14234         if(!this.item.length){
14235             this.clearValue();
14236             return;
14237         }
14238             
14239         var value = [];
14240         var _this = this;
14241         Roo.each(this.item, function(i){
14242             if(_this.valueField){
14243                 value.push(i[_this.valueField]);
14244                 return;
14245             }
14246
14247             value.push(i);
14248         });
14249
14250         this.value = value.join(',');
14251
14252         if(this.hiddenField){
14253             this.hiddenField.dom.value = this.value;
14254         }
14255         
14256         this.store.fireEvent("datachanged", this.store);
14257         
14258         this.validate();
14259     },
14260     
14261     clearItem : function()
14262     {
14263         if(!this.multiple){
14264             return;
14265         }
14266         
14267         this.item = [];
14268         
14269         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14270            c.remove();
14271         });
14272         
14273         this.syncValue();
14274         
14275         this.validate();
14276         
14277         if(this.tickable && !Roo.isTouch){
14278             this.view.refresh();
14279         }
14280     },
14281     
14282     inputEl: function ()
14283     {
14284         if(Roo.isIOS && this.useNativeIOS){
14285             return this.el.select('select.roo-ios-select', true).first();
14286         }
14287         
14288         if(Roo.isTouch && this.mobileTouchView){
14289             return this.el.select('input.form-control',true).first();
14290         }
14291         
14292         if(this.tickable){
14293             return this.searchField;
14294         }
14295         
14296         return this.el.select('input.form-control',true).first();
14297     },
14298     
14299     onTickableFooterButtonClick : function(e, btn, el)
14300     {
14301         e.preventDefault();
14302         
14303         this.lastItem = Roo.apply([], this.item);
14304         
14305         if(btn && btn.name == 'cancel'){
14306             this.tickItems = Roo.apply([], this.item);
14307             this.collapse();
14308             return;
14309         }
14310         
14311         this.clearItem();
14312         
14313         var _this = this;
14314         
14315         Roo.each(this.tickItems, function(o){
14316             _this.addItem(o);
14317         });
14318         
14319         this.collapse();
14320         
14321     },
14322     
14323     validate : function()
14324     {
14325         var v = this.getRawValue();
14326         
14327         if(this.multiple){
14328             v = this.getValue();
14329         }
14330         
14331         if(this.disabled || this.allowBlank || v.length){
14332             this.markValid();
14333             return true;
14334         }
14335         
14336         this.markInvalid();
14337         return false;
14338     },
14339     
14340     tickableInputEl : function()
14341     {
14342         if(!this.tickable || !this.editable){
14343             return this.inputEl();
14344         }
14345         
14346         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14347     },
14348     
14349     
14350     getAutoCreateTouchView : function()
14351     {
14352         var id = Roo.id();
14353         
14354         var cfg = {
14355             cls: 'form-group' //input-group
14356         };
14357         
14358         var input =  {
14359             tag: 'input',
14360             id : id,
14361             type : this.inputType,
14362             cls : 'form-control x-combo-noedit',
14363             autocomplete: 'new-password',
14364             placeholder : this.placeholder || '',
14365             readonly : true
14366         };
14367         
14368         if (this.name) {
14369             input.name = this.name;
14370         }
14371         
14372         if (this.size) {
14373             input.cls += ' input-' + this.size;
14374         }
14375         
14376         if (this.disabled) {
14377             input.disabled = true;
14378         }
14379         
14380         var inputblock = {
14381             cls : '',
14382             cn : [
14383                 input
14384             ]
14385         };
14386         
14387         if(this.before){
14388             inputblock.cls += ' input-group';
14389             
14390             inputblock.cn.unshift({
14391                 tag :'span',
14392                 cls : 'input-group-addon',
14393                 html : this.before
14394             });
14395         }
14396         
14397         if(this.removable && !this.multiple){
14398             inputblock.cls += ' roo-removable';
14399             
14400             inputblock.cn.push({
14401                 tag: 'button',
14402                 html : 'x',
14403                 cls : 'roo-combo-removable-btn close'
14404             });
14405         }
14406
14407         if(this.hasFeedback && !this.allowBlank){
14408             
14409             inputblock.cls += ' has-feedback';
14410             
14411             inputblock.cn.push({
14412                 tag: 'span',
14413                 cls: 'glyphicon form-control-feedback'
14414             });
14415             
14416         }
14417         
14418         if (this.after) {
14419             
14420             inputblock.cls += (this.before) ? '' : ' input-group';
14421             
14422             inputblock.cn.push({
14423                 tag :'span',
14424                 cls : 'input-group-addon',
14425                 html : this.after
14426             });
14427         }
14428
14429         var box = {
14430             tag: 'div',
14431             cn: [
14432                 {
14433                     tag: 'input',
14434                     type : 'hidden',
14435                     cls: 'form-hidden-field'
14436                 },
14437                 inputblock
14438             ]
14439             
14440         };
14441         
14442         if(this.multiple){
14443             box = {
14444                 tag: 'div',
14445                 cn: [
14446                     {
14447                         tag: 'input',
14448                         type : 'hidden',
14449                         cls: 'form-hidden-field'
14450                     },
14451                     {
14452                         tag: 'ul',
14453                         cls: 'roo-select2-choices',
14454                         cn:[
14455                             {
14456                                 tag: 'li',
14457                                 cls: 'roo-select2-search-field',
14458                                 cn: [
14459
14460                                     inputblock
14461                                 ]
14462                             }
14463                         ]
14464                     }
14465                 ]
14466             }
14467         };
14468         
14469         var combobox = {
14470             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14471             cn: [
14472                 box
14473             ]
14474         };
14475         
14476         if(!this.multiple && this.showToggleBtn){
14477             
14478             var caret = {
14479                         tag: 'span',
14480                         cls: 'caret'
14481             };
14482             
14483             if (this.caret != false) {
14484                 caret = {
14485                      tag: 'i',
14486                      cls: 'fa fa-' + this.caret
14487                 };
14488                 
14489             }
14490             
14491             combobox.cn.push({
14492                 tag :'span',
14493                 cls : 'input-group-addon btn dropdown-toggle',
14494                 cn : [
14495                     caret,
14496                     {
14497                         tag: 'span',
14498                         cls: 'combobox-clear',
14499                         cn  : [
14500                             {
14501                                 tag : 'i',
14502                                 cls: 'icon-remove'
14503                             }
14504                         ]
14505                     }
14506                 ]
14507
14508             })
14509         }
14510         
14511         if(this.multiple){
14512             combobox.cls += ' roo-select2-container-multi';
14513         }
14514         
14515         var align = this.labelAlign || this.parentLabelAlign();
14516         
14517         if (align ==='left' && this.fieldLabel.length) {
14518             
14519             cfg.cn = [
14520                 {
14521                    tag : 'i',
14522                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14523                    tooltip : 'This field is required'
14524                 },
14525                 {
14526                     tag: 'label',
14527                     cls : 'control-label',
14528                     html : this.fieldLabel
14529
14530                 },
14531                 {
14532                     cls : '', 
14533                     cn: [
14534                         combobox
14535                     ]
14536                 }
14537             ];
14538             
14539             var labelCfg = cfg.cn[1];
14540             var contentCfg = cfg.cn[2];
14541             
14542
14543             if(this.indicatorpos == 'right'){
14544                 cfg.cn = [
14545                     {
14546                         tag: 'label',
14547                         cls : 'control-label',
14548                         html : this.fieldLabel
14549
14550                     },
14551                     {
14552                        tag : 'i',
14553                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14554                        tooltip : 'This field is required'
14555                     },
14556                     {
14557                         cls : '', 
14558                         cn: [
14559                             combobox
14560                         ]
14561                     }
14562                 ];
14563             }
14564             
14565             labelCfg = cfg.cn[0];
14566             contentCfg = cfg.cn[2];
14567             
14568             if(this.labelWidth > 12){
14569                 labelCfg.style = "width: " + this.labelWidth + 'px';
14570             }
14571             
14572             if(this.labelWidth < 13 && this.labelmd == 0){
14573                 this.labelmd = this.labelWidth;
14574             }
14575             
14576             if(this.labellg > 0){
14577                 labelCfg.cls += ' col-lg-' + this.labellg;
14578                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14579             }
14580             
14581             if(this.labelmd > 0){
14582                 labelCfg.cls += ' col-md-' + this.labelmd;
14583                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14584             }
14585             
14586             if(this.labelsm > 0){
14587                 labelCfg.cls += ' col-sm-' + this.labelsm;
14588                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14589             }
14590             
14591             if(this.labelxs > 0){
14592                 labelCfg.cls += ' col-xs-' + this.labelxs;
14593                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14594             }
14595                 
14596                 
14597         } else if ( this.fieldLabel.length) {
14598             cfg.cn = [
14599                 {
14600                    tag : 'i',
14601                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14602                    tooltip : 'This field is required'
14603                 },
14604                 {
14605                     tag: 'label',
14606                     cls : 'control-label',
14607                     html : this.fieldLabel
14608
14609                 },
14610                 {
14611                     cls : '', 
14612                     cn: [
14613                         combobox
14614                     ]
14615                 }
14616             ];
14617             
14618             if(this.indicatorpos == 'right'){
14619                 cfg.cn = [
14620                     {
14621                         tag: 'label',
14622                         cls : 'control-label',
14623                         html : this.fieldLabel
14624
14625                     },
14626                     {
14627                        tag : 'i',
14628                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14629                        tooltip : 'This field is required'
14630                     },
14631                     {
14632                         cls : '', 
14633                         cn: [
14634                             combobox
14635                         ]
14636                     }
14637                 ];
14638             }
14639         } else {
14640             cfg.cn = combobox;    
14641         }
14642         
14643         
14644         var settings = this;
14645         
14646         ['xs','sm','md','lg'].map(function(size){
14647             if (settings[size]) {
14648                 cfg.cls += ' col-' + size + '-' + settings[size];
14649             }
14650         });
14651         
14652         return cfg;
14653     },
14654     
14655     initTouchView : function()
14656     {
14657         this.renderTouchView();
14658         
14659         this.touchViewEl.on('scroll', function(){
14660             this.el.dom.scrollTop = 0;
14661         }, this);
14662         
14663         this.originalValue = this.getValue();
14664         
14665         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14666         
14667         this.inputEl().on("click", this.showTouchView, this);
14668         if (this.triggerEl) {
14669             this.triggerEl.on("click", this.showTouchView, this);
14670         }
14671         
14672         
14673         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14674         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14675         
14676         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14677         
14678         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14679         this.store.on('load', this.onTouchViewLoad, this);
14680         this.store.on('loadexception', this.onTouchViewLoadException, this);
14681         
14682         if(this.hiddenName){
14683             
14684             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14685             
14686             this.hiddenField.dom.value =
14687                 this.hiddenValue !== undefined ? this.hiddenValue :
14688                 this.value !== undefined ? this.value : '';
14689         
14690             this.el.dom.removeAttribute('name');
14691             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14692         }
14693         
14694         if(this.multiple){
14695             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14696             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14697         }
14698         
14699         if(this.removable && !this.multiple){
14700             var close = this.closeTriggerEl();
14701             if(close){
14702                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14703                 close.on('click', this.removeBtnClick, this, close);
14704             }
14705         }
14706         /*
14707          * fix the bug in Safari iOS8
14708          */
14709         this.inputEl().on("focus", function(e){
14710             document.activeElement.blur();
14711         }, this);
14712         
14713         return;
14714         
14715         
14716     },
14717     
14718     renderTouchView : function()
14719     {
14720         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14721         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14722         
14723         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14724         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14725         
14726         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14727         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14728         this.touchViewBodyEl.setStyle('overflow', 'auto');
14729         
14730         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14731         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14732         
14733         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14734         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14735         
14736     },
14737     
14738     showTouchView : function()
14739     {
14740         if(this.disabled){
14741             return;
14742         }
14743         
14744         this.touchViewHeaderEl.hide();
14745
14746         if(this.modalTitle.length){
14747             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14748             this.touchViewHeaderEl.show();
14749         }
14750
14751         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14752         this.touchViewEl.show();
14753
14754         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14755         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14756                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14757
14758         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14759
14760         if(this.modalTitle.length){
14761             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14762         }
14763         
14764         this.touchViewBodyEl.setHeight(bodyHeight);
14765
14766         if(this.animate){
14767             var _this = this;
14768             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14769         }else{
14770             this.touchViewEl.addClass('in');
14771         }
14772
14773         this.doTouchViewQuery();
14774         
14775     },
14776     
14777     hideTouchView : function()
14778     {
14779         this.touchViewEl.removeClass('in');
14780
14781         if(this.animate){
14782             var _this = this;
14783             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14784         }else{
14785             this.touchViewEl.setStyle('display', 'none');
14786         }
14787         
14788     },
14789     
14790     setTouchViewValue : function()
14791     {
14792         if(this.multiple){
14793             this.clearItem();
14794         
14795             var _this = this;
14796
14797             Roo.each(this.tickItems, function(o){
14798                 this.addItem(o);
14799             }, this);
14800         }
14801         
14802         this.hideTouchView();
14803     },
14804     
14805     doTouchViewQuery : function()
14806     {
14807         var qe = {
14808             query: '',
14809             forceAll: true,
14810             combo: this,
14811             cancel:false
14812         };
14813         
14814         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14815             return false;
14816         }
14817         
14818         if(!this.alwaysQuery || this.mode == 'local'){
14819             this.onTouchViewLoad();
14820             return;
14821         }
14822         
14823         this.store.load();
14824     },
14825     
14826     onTouchViewBeforeLoad : function(combo,opts)
14827     {
14828         return;
14829     },
14830
14831     // private
14832     onTouchViewLoad : function()
14833     {
14834         if(this.store.getCount() < 1){
14835             this.onTouchViewEmptyResults();
14836             return;
14837         }
14838         
14839         this.clearTouchView();
14840         
14841         var rawValue = this.getRawValue();
14842         
14843         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14844         
14845         this.tickItems = [];
14846         
14847         this.store.data.each(function(d, rowIndex){
14848             var row = this.touchViewListGroup.createChild(template);
14849             
14850             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14851                 row.addClass(d.data.cls);
14852             }
14853             
14854             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14855                 var cfg = {
14856                     data : d.data,
14857                     html : d.data[this.displayField]
14858                 };
14859                 
14860                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14861                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14862                 }
14863             }
14864             row.removeClass('selected');
14865             if(!this.multiple && this.valueField &&
14866                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14867             {
14868                 // radio buttons..
14869                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14870                 row.addClass('selected');
14871             }
14872             
14873             if(this.multiple && this.valueField &&
14874                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14875             {
14876                 
14877                 // checkboxes...
14878                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14879                 this.tickItems.push(d.data);
14880             }
14881             
14882             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14883             
14884         }, this);
14885         
14886         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14887         
14888         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14889
14890         if(this.modalTitle.length){
14891             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14892         }
14893
14894         var listHeight = this.touchViewListGroup.getHeight();
14895         
14896         var _this = this;
14897         
14898         if(firstChecked && listHeight > bodyHeight){
14899             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14900         }
14901         
14902     },
14903     
14904     onTouchViewLoadException : function()
14905     {
14906         this.hideTouchView();
14907     },
14908     
14909     onTouchViewEmptyResults : function()
14910     {
14911         this.clearTouchView();
14912         
14913         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14914         
14915         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14916         
14917     },
14918     
14919     clearTouchView : function()
14920     {
14921         this.touchViewListGroup.dom.innerHTML = '';
14922     },
14923     
14924     onTouchViewClick : function(e, el, o)
14925     {
14926         e.preventDefault();
14927         
14928         var row = o.row;
14929         var rowIndex = o.rowIndex;
14930         
14931         var r = this.store.getAt(rowIndex);
14932         
14933         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14934             
14935             if(!this.multiple){
14936                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14937                     c.dom.removeAttribute('checked');
14938                 }, this);
14939
14940                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14941
14942                 this.setFromData(r.data);
14943
14944                 var close = this.closeTriggerEl();
14945
14946                 if(close){
14947                     close.show();
14948                 }
14949
14950                 this.hideTouchView();
14951
14952                 this.fireEvent('select', this, r, rowIndex);
14953
14954                 return;
14955             }
14956
14957             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14958                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14959                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14960                 return;
14961             }
14962
14963             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14964             this.addItem(r.data);
14965             this.tickItems.push(r.data);
14966         }
14967     },
14968     
14969     getAutoCreateNativeIOS : function()
14970     {
14971         var cfg = {
14972             cls: 'form-group' //input-group,
14973         };
14974         
14975         var combobox =  {
14976             tag: 'select',
14977             cls : 'roo-ios-select'
14978         };
14979         
14980         if (this.name) {
14981             combobox.name = this.name;
14982         }
14983         
14984         if (this.disabled) {
14985             combobox.disabled = true;
14986         }
14987         
14988         var settings = this;
14989         
14990         ['xs','sm','md','lg'].map(function(size){
14991             if (settings[size]) {
14992                 cfg.cls += ' col-' + size + '-' + settings[size];
14993             }
14994         });
14995         
14996         cfg.cn = combobox;
14997         
14998         return cfg;
14999         
15000     },
15001     
15002     initIOSView : function()
15003     {
15004         this.store.on('load', this.onIOSViewLoad, this);
15005         
15006         return;
15007     },
15008     
15009     onIOSViewLoad : function()
15010     {
15011         if(this.store.getCount() < 1){
15012             return;
15013         }
15014         
15015         this.clearIOSView();
15016         
15017         if(this.allowBlank) {
15018             
15019             var default_text = '-- SELECT --';
15020             
15021             var opt = this.inputEl().createChild({
15022                 tag: 'option',
15023                 value : 0,
15024                 html : default_text
15025             });
15026             
15027             var o = {};
15028             o[this.valueField] = 0;
15029             o[this.displayField] = default_text;
15030             
15031             this.ios_options.push({
15032                 data : o,
15033                 el : opt
15034             });
15035             
15036         }
15037         
15038         this.store.data.each(function(d, rowIndex){
15039             
15040             var html = '';
15041             
15042             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15043                 html = d.data[this.displayField];
15044             }
15045             
15046             var value = '';
15047             
15048             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15049                 value = d.data[this.valueField];
15050             }
15051             
15052             var option = {
15053                 tag: 'option',
15054                 value : value,
15055                 html : html
15056             };
15057             
15058             if(this.value == d.data[this.valueField]){
15059                 option['selected'] = true;
15060             }
15061             
15062             var opt = this.inputEl().createChild(option);
15063             
15064             this.ios_options.push({
15065                 data : d.data,
15066                 el : opt
15067             });
15068             
15069         }, this);
15070         
15071         this.inputEl().on('change', function(){
15072            this.fireEvent('select', this);
15073         }, this);
15074         
15075     },
15076     
15077     clearIOSView: function()
15078     {
15079         this.inputEl().dom.innerHTML = '';
15080         
15081         this.ios_options = [];
15082     },
15083     
15084     setIOSValue: function(v)
15085     {
15086         this.value = v;
15087         
15088         if(!this.ios_options){
15089             return;
15090         }
15091         
15092         Roo.each(this.ios_options, function(opts){
15093            
15094            opts.el.dom.removeAttribute('selected');
15095            
15096            if(opts.data[this.valueField] != v){
15097                return;
15098            }
15099            
15100            opts.el.dom.setAttribute('selected', true);
15101            
15102         }, this);
15103     }
15104
15105     /** 
15106     * @cfg {Boolean} grow 
15107     * @hide 
15108     */
15109     /** 
15110     * @cfg {Number} growMin 
15111     * @hide 
15112     */
15113     /** 
15114     * @cfg {Number} growMax 
15115     * @hide 
15116     */
15117     /**
15118      * @hide
15119      * @method autoSize
15120      */
15121 });
15122
15123 Roo.apply(Roo.bootstrap.ComboBox,  {
15124     
15125     header : {
15126         tag: 'div',
15127         cls: 'modal-header',
15128         cn: [
15129             {
15130                 tag: 'h4',
15131                 cls: 'modal-title'
15132             }
15133         ]
15134     },
15135     
15136     body : {
15137         tag: 'div',
15138         cls: 'modal-body',
15139         cn: [
15140             {
15141                 tag: 'ul',
15142                 cls: 'list-group'
15143             }
15144         ]
15145     },
15146     
15147     listItemRadio : {
15148         tag: 'li',
15149         cls: 'list-group-item',
15150         cn: [
15151             {
15152                 tag: 'span',
15153                 cls: 'roo-combobox-list-group-item-value'
15154             },
15155             {
15156                 tag: 'div',
15157                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15158                 cn: [
15159                     {
15160                         tag: 'input',
15161                         type: 'radio'
15162                     },
15163                     {
15164                         tag: 'label'
15165                     }
15166                 ]
15167             }
15168         ]
15169     },
15170     
15171     listItemCheckbox : {
15172         tag: 'li',
15173         cls: 'list-group-item',
15174         cn: [
15175             {
15176                 tag: 'span',
15177                 cls: 'roo-combobox-list-group-item-value'
15178             },
15179             {
15180                 tag: 'div',
15181                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15182                 cn: [
15183                     {
15184                         tag: 'input',
15185                         type: 'checkbox'
15186                     },
15187                     {
15188                         tag: 'label'
15189                     }
15190                 ]
15191             }
15192         ]
15193     },
15194     
15195     emptyResult : {
15196         tag: 'div',
15197         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15198     },
15199     
15200     footer : {
15201         tag: 'div',
15202         cls: 'modal-footer',
15203         cn: [
15204             {
15205                 tag: 'div',
15206                 cls: 'row',
15207                 cn: [
15208                     {
15209                         tag: 'div',
15210                         cls: 'col-xs-6 text-left',
15211                         cn: {
15212                             tag: 'button',
15213                             cls: 'btn btn-danger roo-touch-view-cancel',
15214                             html: 'Cancel'
15215                         }
15216                     },
15217                     {
15218                         tag: 'div',
15219                         cls: 'col-xs-6 text-right',
15220                         cn: {
15221                             tag: 'button',
15222                             cls: 'btn btn-success roo-touch-view-ok',
15223                             html: 'OK'
15224                         }
15225                     }
15226                 ]
15227             }
15228         ]
15229         
15230     }
15231 });
15232
15233 Roo.apply(Roo.bootstrap.ComboBox,  {
15234     
15235     touchViewTemplate : {
15236         tag: 'div',
15237         cls: 'modal fade roo-combobox-touch-view',
15238         cn: [
15239             {
15240                 tag: 'div',
15241                 cls: 'modal-dialog',
15242                 style : 'position:fixed', // we have to fix position....
15243                 cn: [
15244                     {
15245                         tag: 'div',
15246                         cls: 'modal-content',
15247                         cn: [
15248                             Roo.bootstrap.ComboBox.header,
15249                             Roo.bootstrap.ComboBox.body,
15250                             Roo.bootstrap.ComboBox.footer
15251                         ]
15252                     }
15253                 ]
15254             }
15255         ]
15256     }
15257 });/*
15258  * Based on:
15259  * Ext JS Library 1.1.1
15260  * Copyright(c) 2006-2007, Ext JS, LLC.
15261  *
15262  * Originally Released Under LGPL - original licence link has changed is not relivant.
15263  *
15264  * Fork - LGPL
15265  * <script type="text/javascript">
15266  */
15267
15268 /**
15269  * @class Roo.View
15270  * @extends Roo.util.Observable
15271  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15272  * This class also supports single and multi selection modes. <br>
15273  * Create a data model bound view:
15274  <pre><code>
15275  var store = new Roo.data.Store(...);
15276
15277  var view = new Roo.View({
15278     el : "my-element",
15279     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15280  
15281     singleSelect: true,
15282     selectedClass: "ydataview-selected",
15283     store: store
15284  });
15285
15286  // listen for node click?
15287  view.on("click", function(vw, index, node, e){
15288  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15289  });
15290
15291  // load XML data
15292  dataModel.load("foobar.xml");
15293  </code></pre>
15294  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15295  * <br><br>
15296  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15297  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15298  * 
15299  * Note: old style constructor is still suported (container, template, config)
15300  * 
15301  * @constructor
15302  * Create a new View
15303  * @param {Object} config The config object
15304  * 
15305  */
15306 Roo.View = function(config, depreciated_tpl, depreciated_config){
15307     
15308     this.parent = false;
15309     
15310     if (typeof(depreciated_tpl) == 'undefined') {
15311         // new way.. - universal constructor.
15312         Roo.apply(this, config);
15313         this.el  = Roo.get(this.el);
15314     } else {
15315         // old format..
15316         this.el  = Roo.get(config);
15317         this.tpl = depreciated_tpl;
15318         Roo.apply(this, depreciated_config);
15319     }
15320     this.wrapEl  = this.el.wrap().wrap();
15321     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15322     
15323     
15324     if(typeof(this.tpl) == "string"){
15325         this.tpl = new Roo.Template(this.tpl);
15326     } else {
15327         // support xtype ctors..
15328         this.tpl = new Roo.factory(this.tpl, Roo);
15329     }
15330     
15331     
15332     this.tpl.compile();
15333     
15334     /** @private */
15335     this.addEvents({
15336         /**
15337          * @event beforeclick
15338          * Fires before a click is processed. Returns false to cancel the default action.
15339          * @param {Roo.View} this
15340          * @param {Number} index The index of the target node
15341          * @param {HTMLElement} node The target node
15342          * @param {Roo.EventObject} e The raw event object
15343          */
15344             "beforeclick" : true,
15345         /**
15346          * @event click
15347          * Fires when a template node is clicked.
15348          * @param {Roo.View} this
15349          * @param {Number} index The index of the target node
15350          * @param {HTMLElement} node The target node
15351          * @param {Roo.EventObject} e The raw event object
15352          */
15353             "click" : true,
15354         /**
15355          * @event dblclick
15356          * Fires when a template node is double clicked.
15357          * @param {Roo.View} this
15358          * @param {Number} index The index of the target node
15359          * @param {HTMLElement} node The target node
15360          * @param {Roo.EventObject} e The raw event object
15361          */
15362             "dblclick" : true,
15363         /**
15364          * @event contextmenu
15365          * Fires when a template node is right clicked.
15366          * @param {Roo.View} this
15367          * @param {Number} index The index of the target node
15368          * @param {HTMLElement} node The target node
15369          * @param {Roo.EventObject} e The raw event object
15370          */
15371             "contextmenu" : true,
15372         /**
15373          * @event selectionchange
15374          * Fires when the selected nodes change.
15375          * @param {Roo.View} this
15376          * @param {Array} selections Array of the selected nodes
15377          */
15378             "selectionchange" : true,
15379     
15380         /**
15381          * @event beforeselect
15382          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15383          * @param {Roo.View} this
15384          * @param {HTMLElement} node The node to be selected
15385          * @param {Array} selections Array of currently selected nodes
15386          */
15387             "beforeselect" : true,
15388         /**
15389          * @event preparedata
15390          * Fires on every row to render, to allow you to change the data.
15391          * @param {Roo.View} this
15392          * @param {Object} data to be rendered (change this)
15393          */
15394           "preparedata" : true
15395           
15396           
15397         });
15398
15399
15400
15401     this.el.on({
15402         "click": this.onClick,
15403         "dblclick": this.onDblClick,
15404         "contextmenu": this.onContextMenu,
15405         scope:this
15406     });
15407
15408     this.selections = [];
15409     this.nodes = [];
15410     this.cmp = new Roo.CompositeElementLite([]);
15411     if(this.store){
15412         this.store = Roo.factory(this.store, Roo.data);
15413         this.setStore(this.store, true);
15414     }
15415     
15416     if ( this.footer && this.footer.xtype) {
15417            
15418          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15419         
15420         this.footer.dataSource = this.store;
15421         this.footer.container = fctr;
15422         this.footer = Roo.factory(this.footer, Roo);
15423         fctr.insertFirst(this.el);
15424         
15425         // this is a bit insane - as the paging toolbar seems to detach the el..
15426 //        dom.parentNode.parentNode.parentNode
15427          // they get detached?
15428     }
15429     
15430     
15431     Roo.View.superclass.constructor.call(this);
15432     
15433     
15434 };
15435
15436 Roo.extend(Roo.View, Roo.util.Observable, {
15437     
15438      /**
15439      * @cfg {Roo.data.Store} store Data store to load data from.
15440      */
15441     store : false,
15442     
15443     /**
15444      * @cfg {String|Roo.Element} el The container element.
15445      */
15446     el : '',
15447     
15448     /**
15449      * @cfg {String|Roo.Template} tpl The template used by this View 
15450      */
15451     tpl : false,
15452     /**
15453      * @cfg {String} dataName the named area of the template to use as the data area
15454      *                          Works with domtemplates roo-name="name"
15455      */
15456     dataName: false,
15457     /**
15458      * @cfg {String} selectedClass The css class to add to selected nodes
15459      */
15460     selectedClass : "x-view-selected",
15461      /**
15462      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15463      */
15464     emptyText : "",
15465     
15466     /**
15467      * @cfg {String} text to display on mask (default Loading)
15468      */
15469     mask : false,
15470     /**
15471      * @cfg {Boolean} multiSelect Allow multiple selection
15472      */
15473     multiSelect : false,
15474     /**
15475      * @cfg {Boolean} singleSelect Allow single selection
15476      */
15477     singleSelect:  false,
15478     
15479     /**
15480      * @cfg {Boolean} toggleSelect - selecting 
15481      */
15482     toggleSelect : false,
15483     
15484     /**
15485      * @cfg {Boolean} tickable - selecting 
15486      */
15487     tickable : false,
15488     
15489     /**
15490      * Returns the element this view is bound to.
15491      * @return {Roo.Element}
15492      */
15493     getEl : function(){
15494         return this.wrapEl;
15495     },
15496     
15497     
15498
15499     /**
15500      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15501      */
15502     refresh : function(){
15503         //Roo.log('refresh');
15504         var t = this.tpl;
15505         
15506         // if we are using something like 'domtemplate', then
15507         // the what gets used is:
15508         // t.applySubtemplate(NAME, data, wrapping data..)
15509         // the outer template then get' applied with
15510         //     the store 'extra data'
15511         // and the body get's added to the
15512         //      roo-name="data" node?
15513         //      <span class='roo-tpl-{name}'></span> ?????
15514         
15515         
15516         
15517         this.clearSelections();
15518         this.el.update("");
15519         var html = [];
15520         var records = this.store.getRange();
15521         if(records.length < 1) {
15522             
15523             // is this valid??  = should it render a template??
15524             
15525             this.el.update(this.emptyText);
15526             return;
15527         }
15528         var el = this.el;
15529         if (this.dataName) {
15530             this.el.update(t.apply(this.store.meta)); //????
15531             el = this.el.child('.roo-tpl-' + this.dataName);
15532         }
15533         
15534         for(var i = 0, len = records.length; i < len; i++){
15535             var data = this.prepareData(records[i].data, i, records[i]);
15536             this.fireEvent("preparedata", this, data, i, records[i]);
15537             
15538             var d = Roo.apply({}, data);
15539             
15540             if(this.tickable){
15541                 Roo.apply(d, {'roo-id' : Roo.id()});
15542                 
15543                 var _this = this;
15544             
15545                 Roo.each(this.parent.item, function(item){
15546                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15547                         return;
15548                     }
15549                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15550                 });
15551             }
15552             
15553             html[html.length] = Roo.util.Format.trim(
15554                 this.dataName ?
15555                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15556                     t.apply(d)
15557             );
15558         }
15559         
15560         
15561         
15562         el.update(html.join(""));
15563         this.nodes = el.dom.childNodes;
15564         this.updateIndexes(0);
15565     },
15566     
15567
15568     /**
15569      * Function to override to reformat the data that is sent to
15570      * the template for each node.
15571      * DEPRICATED - use the preparedata event handler.
15572      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15573      * a JSON object for an UpdateManager bound view).
15574      */
15575     prepareData : function(data, index, record)
15576     {
15577         this.fireEvent("preparedata", this, data, index, record);
15578         return data;
15579     },
15580
15581     onUpdate : function(ds, record){
15582         // Roo.log('on update');   
15583         this.clearSelections();
15584         var index = this.store.indexOf(record);
15585         var n = this.nodes[index];
15586         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15587         n.parentNode.removeChild(n);
15588         this.updateIndexes(index, index);
15589     },
15590
15591     
15592     
15593 // --------- FIXME     
15594     onAdd : function(ds, records, index)
15595     {
15596         //Roo.log(['on Add', ds, records, index] );        
15597         this.clearSelections();
15598         if(this.nodes.length == 0){
15599             this.refresh();
15600             return;
15601         }
15602         var n = this.nodes[index];
15603         for(var i = 0, len = records.length; i < len; i++){
15604             var d = this.prepareData(records[i].data, i, records[i]);
15605             if(n){
15606                 this.tpl.insertBefore(n, d);
15607             }else{
15608                 
15609                 this.tpl.append(this.el, d);
15610             }
15611         }
15612         this.updateIndexes(index);
15613     },
15614
15615     onRemove : function(ds, record, index){
15616        // Roo.log('onRemove');
15617         this.clearSelections();
15618         var el = this.dataName  ?
15619             this.el.child('.roo-tpl-' + this.dataName) :
15620             this.el; 
15621         
15622         el.dom.removeChild(this.nodes[index]);
15623         this.updateIndexes(index);
15624     },
15625
15626     /**
15627      * Refresh an individual node.
15628      * @param {Number} index
15629      */
15630     refreshNode : function(index){
15631         this.onUpdate(this.store, this.store.getAt(index));
15632     },
15633
15634     updateIndexes : function(startIndex, endIndex){
15635         var ns = this.nodes;
15636         startIndex = startIndex || 0;
15637         endIndex = endIndex || ns.length - 1;
15638         for(var i = startIndex; i <= endIndex; i++){
15639             ns[i].nodeIndex = i;
15640         }
15641     },
15642
15643     /**
15644      * Changes the data store this view uses and refresh the view.
15645      * @param {Store} store
15646      */
15647     setStore : function(store, initial){
15648         if(!initial && this.store){
15649             this.store.un("datachanged", this.refresh);
15650             this.store.un("add", this.onAdd);
15651             this.store.un("remove", this.onRemove);
15652             this.store.un("update", this.onUpdate);
15653             this.store.un("clear", this.refresh);
15654             this.store.un("beforeload", this.onBeforeLoad);
15655             this.store.un("load", this.onLoad);
15656             this.store.un("loadexception", this.onLoad);
15657         }
15658         if(store){
15659           
15660             store.on("datachanged", this.refresh, this);
15661             store.on("add", this.onAdd, this);
15662             store.on("remove", this.onRemove, this);
15663             store.on("update", this.onUpdate, this);
15664             store.on("clear", this.refresh, this);
15665             store.on("beforeload", this.onBeforeLoad, this);
15666             store.on("load", this.onLoad, this);
15667             store.on("loadexception", this.onLoad, this);
15668         }
15669         
15670         if(store){
15671             this.refresh();
15672         }
15673     },
15674     /**
15675      * onbeforeLoad - masks the loading area.
15676      *
15677      */
15678     onBeforeLoad : function(store,opts)
15679     {
15680          //Roo.log('onBeforeLoad');   
15681         if (!opts.add) {
15682             this.el.update("");
15683         }
15684         this.el.mask(this.mask ? this.mask : "Loading" ); 
15685     },
15686     onLoad : function ()
15687     {
15688         this.el.unmask();
15689     },
15690     
15691
15692     /**
15693      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15694      * @param {HTMLElement} node
15695      * @return {HTMLElement} The template node
15696      */
15697     findItemFromChild : function(node){
15698         var el = this.dataName  ?
15699             this.el.child('.roo-tpl-' + this.dataName,true) :
15700             this.el.dom; 
15701         
15702         if(!node || node.parentNode == el){
15703                     return node;
15704             }
15705             var p = node.parentNode;
15706             while(p && p != el){
15707             if(p.parentNode == el){
15708                 return p;
15709             }
15710             p = p.parentNode;
15711         }
15712             return null;
15713     },
15714
15715     /** @ignore */
15716     onClick : function(e){
15717         var item = this.findItemFromChild(e.getTarget());
15718         if(item){
15719             var index = this.indexOf(item);
15720             if(this.onItemClick(item, index, e) !== false){
15721                 this.fireEvent("click", this, index, item, e);
15722             }
15723         }else{
15724             this.clearSelections();
15725         }
15726     },
15727
15728     /** @ignore */
15729     onContextMenu : function(e){
15730         var item = this.findItemFromChild(e.getTarget());
15731         if(item){
15732             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15733         }
15734     },
15735
15736     /** @ignore */
15737     onDblClick : function(e){
15738         var item = this.findItemFromChild(e.getTarget());
15739         if(item){
15740             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15741         }
15742     },
15743
15744     onItemClick : function(item, index, e)
15745     {
15746         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15747             return false;
15748         }
15749         if (this.toggleSelect) {
15750             var m = this.isSelected(item) ? 'unselect' : 'select';
15751             //Roo.log(m);
15752             var _t = this;
15753             _t[m](item, true, false);
15754             return true;
15755         }
15756         if(this.multiSelect || this.singleSelect){
15757             if(this.multiSelect && e.shiftKey && this.lastSelection){
15758                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15759             }else{
15760                 this.select(item, this.multiSelect && e.ctrlKey);
15761                 this.lastSelection = item;
15762             }
15763             
15764             if(!this.tickable){
15765                 e.preventDefault();
15766             }
15767             
15768         }
15769         return true;
15770     },
15771
15772     /**
15773      * Get the number of selected nodes.
15774      * @return {Number}
15775      */
15776     getSelectionCount : function(){
15777         return this.selections.length;
15778     },
15779
15780     /**
15781      * Get the currently selected nodes.
15782      * @return {Array} An array of HTMLElements
15783      */
15784     getSelectedNodes : function(){
15785         return this.selections;
15786     },
15787
15788     /**
15789      * Get the indexes of the selected nodes.
15790      * @return {Array}
15791      */
15792     getSelectedIndexes : function(){
15793         var indexes = [], s = this.selections;
15794         for(var i = 0, len = s.length; i < len; i++){
15795             indexes.push(s[i].nodeIndex);
15796         }
15797         return indexes;
15798     },
15799
15800     /**
15801      * Clear all selections
15802      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15803      */
15804     clearSelections : function(suppressEvent){
15805         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15806             this.cmp.elements = this.selections;
15807             this.cmp.removeClass(this.selectedClass);
15808             this.selections = [];
15809             if(!suppressEvent){
15810                 this.fireEvent("selectionchange", this, this.selections);
15811             }
15812         }
15813     },
15814
15815     /**
15816      * Returns true if the passed node is selected
15817      * @param {HTMLElement/Number} node The node or node index
15818      * @return {Boolean}
15819      */
15820     isSelected : function(node){
15821         var s = this.selections;
15822         if(s.length < 1){
15823             return false;
15824         }
15825         node = this.getNode(node);
15826         return s.indexOf(node) !== -1;
15827     },
15828
15829     /**
15830      * Selects nodes.
15831      * @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
15832      * @param {Boolean} keepExisting (optional) true to keep existing selections
15833      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15834      */
15835     select : function(nodeInfo, keepExisting, suppressEvent){
15836         if(nodeInfo instanceof Array){
15837             if(!keepExisting){
15838                 this.clearSelections(true);
15839             }
15840             for(var i = 0, len = nodeInfo.length; i < len; i++){
15841                 this.select(nodeInfo[i], true, true);
15842             }
15843             return;
15844         } 
15845         var node = this.getNode(nodeInfo);
15846         if(!node || this.isSelected(node)){
15847             return; // already selected.
15848         }
15849         if(!keepExisting){
15850             this.clearSelections(true);
15851         }
15852         
15853         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15854             Roo.fly(node).addClass(this.selectedClass);
15855             this.selections.push(node);
15856             if(!suppressEvent){
15857                 this.fireEvent("selectionchange", this, this.selections);
15858             }
15859         }
15860         
15861         
15862     },
15863       /**
15864      * Unselects nodes.
15865      * @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
15866      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15867      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15868      */
15869     unselect : function(nodeInfo, keepExisting, suppressEvent)
15870     {
15871         if(nodeInfo instanceof Array){
15872             Roo.each(this.selections, function(s) {
15873                 this.unselect(s, nodeInfo);
15874             }, this);
15875             return;
15876         }
15877         var node = this.getNode(nodeInfo);
15878         if(!node || !this.isSelected(node)){
15879             //Roo.log("not selected");
15880             return; // not selected.
15881         }
15882         // fireevent???
15883         var ns = [];
15884         Roo.each(this.selections, function(s) {
15885             if (s == node ) {
15886                 Roo.fly(node).removeClass(this.selectedClass);
15887
15888                 return;
15889             }
15890             ns.push(s);
15891         },this);
15892         
15893         this.selections= ns;
15894         this.fireEvent("selectionchange", this, this.selections);
15895     },
15896
15897     /**
15898      * Gets a template node.
15899      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15900      * @return {HTMLElement} The node or null if it wasn't found
15901      */
15902     getNode : function(nodeInfo){
15903         if(typeof nodeInfo == "string"){
15904             return document.getElementById(nodeInfo);
15905         }else if(typeof nodeInfo == "number"){
15906             return this.nodes[nodeInfo];
15907         }
15908         return nodeInfo;
15909     },
15910
15911     /**
15912      * Gets a range template nodes.
15913      * @param {Number} startIndex
15914      * @param {Number} endIndex
15915      * @return {Array} An array of nodes
15916      */
15917     getNodes : function(start, end){
15918         var ns = this.nodes;
15919         start = start || 0;
15920         end = typeof end == "undefined" ? ns.length - 1 : end;
15921         var nodes = [];
15922         if(start <= end){
15923             for(var i = start; i <= end; i++){
15924                 nodes.push(ns[i]);
15925             }
15926         } else{
15927             for(var i = start; i >= end; i--){
15928                 nodes.push(ns[i]);
15929             }
15930         }
15931         return nodes;
15932     },
15933
15934     /**
15935      * Finds the index of the passed node
15936      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15937      * @return {Number} The index of the node or -1
15938      */
15939     indexOf : function(node){
15940         node = this.getNode(node);
15941         if(typeof node.nodeIndex == "number"){
15942             return node.nodeIndex;
15943         }
15944         var ns = this.nodes;
15945         for(var i = 0, len = ns.length; i < len; i++){
15946             if(ns[i] == node){
15947                 return i;
15948             }
15949         }
15950         return -1;
15951     }
15952 });
15953 /*
15954  * - LGPL
15955  *
15956  * based on jquery fullcalendar
15957  * 
15958  */
15959
15960 Roo.bootstrap = Roo.bootstrap || {};
15961 /**
15962  * @class Roo.bootstrap.Calendar
15963  * @extends Roo.bootstrap.Component
15964  * Bootstrap Calendar class
15965  * @cfg {Boolean} loadMask (true|false) default false
15966  * @cfg {Object} header generate the user specific header of the calendar, default false
15967
15968  * @constructor
15969  * Create a new Container
15970  * @param {Object} config The config object
15971  */
15972
15973
15974
15975 Roo.bootstrap.Calendar = function(config){
15976     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15977      this.addEvents({
15978         /**
15979              * @event select
15980              * Fires when a date is selected
15981              * @param {DatePicker} this
15982              * @param {Date} date The selected date
15983              */
15984         'select': true,
15985         /**
15986              * @event monthchange
15987              * Fires when the displayed month changes 
15988              * @param {DatePicker} this
15989              * @param {Date} date The selected month
15990              */
15991         'monthchange': true,
15992         /**
15993              * @event evententer
15994              * Fires when mouse over an event
15995              * @param {Calendar} this
15996              * @param {event} Event
15997              */
15998         'evententer': true,
15999         /**
16000              * @event eventleave
16001              * Fires when the mouse leaves an
16002              * @param {Calendar} this
16003              * @param {event}
16004              */
16005         'eventleave': true,
16006         /**
16007              * @event eventclick
16008              * Fires when the mouse click an
16009              * @param {Calendar} this
16010              * @param {event}
16011              */
16012         'eventclick': true
16013         
16014     });
16015
16016 };
16017
16018 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16019     
16020      /**
16021      * @cfg {Number} startDay
16022      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16023      */
16024     startDay : 0,
16025     
16026     loadMask : false,
16027     
16028     header : false,
16029       
16030     getAutoCreate : function(){
16031         
16032         
16033         var fc_button = function(name, corner, style, content ) {
16034             return Roo.apply({},{
16035                 tag : 'span',
16036                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16037                          (corner.length ?
16038                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16039                             ''
16040                         ),
16041                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16042                 unselectable: 'on'
16043             });
16044         };
16045         
16046         var header = {};
16047         
16048         if(!this.header){
16049             header = {
16050                 tag : 'table',
16051                 cls : 'fc-header',
16052                 style : 'width:100%',
16053                 cn : [
16054                     {
16055                         tag: 'tr',
16056                         cn : [
16057                             {
16058                                 tag : 'td',
16059                                 cls : 'fc-header-left',
16060                                 cn : [
16061                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16062                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16063                                     { tag: 'span', cls: 'fc-header-space' },
16064                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16065
16066
16067                                 ]
16068                             },
16069
16070                             {
16071                                 tag : 'td',
16072                                 cls : 'fc-header-center',
16073                                 cn : [
16074                                     {
16075                                         tag: 'span',
16076                                         cls: 'fc-header-title',
16077                                         cn : {
16078                                             tag: 'H2',
16079                                             html : 'month / year'
16080                                         }
16081                                     }
16082
16083                                 ]
16084                             },
16085                             {
16086                                 tag : 'td',
16087                                 cls : 'fc-header-right',
16088                                 cn : [
16089                               /*      fc_button('month', 'left', '', 'month' ),
16090                                     fc_button('week', '', '', 'week' ),
16091                                     fc_button('day', 'right', '', 'day' )
16092                                 */    
16093
16094                                 ]
16095                             }
16096
16097                         ]
16098                     }
16099                 ]
16100             };
16101         }
16102         
16103         header = this.header;
16104         
16105        
16106         var cal_heads = function() {
16107             var ret = [];
16108             // fixme - handle this.
16109             
16110             for (var i =0; i < Date.dayNames.length; i++) {
16111                 var d = Date.dayNames[i];
16112                 ret.push({
16113                     tag: 'th',
16114                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16115                     html : d.substring(0,3)
16116                 });
16117                 
16118             }
16119             ret[0].cls += ' fc-first';
16120             ret[6].cls += ' fc-last';
16121             return ret;
16122         };
16123         var cal_cell = function(n) {
16124             return  {
16125                 tag: 'td',
16126                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16127                 cn : [
16128                     {
16129                         cn : [
16130                             {
16131                                 cls: 'fc-day-number',
16132                                 html: 'D'
16133                             },
16134                             {
16135                                 cls: 'fc-day-content',
16136                              
16137                                 cn : [
16138                                      {
16139                                         style: 'position: relative;' // height: 17px;
16140                                     }
16141                                 ]
16142                             }
16143                             
16144                             
16145                         ]
16146                     }
16147                 ]
16148                 
16149             }
16150         };
16151         var cal_rows = function() {
16152             
16153             var ret = [];
16154             for (var r = 0; r < 6; r++) {
16155                 var row= {
16156                     tag : 'tr',
16157                     cls : 'fc-week',
16158                     cn : []
16159                 };
16160                 
16161                 for (var i =0; i < Date.dayNames.length; i++) {
16162                     var d = Date.dayNames[i];
16163                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16164
16165                 }
16166                 row.cn[0].cls+=' fc-first';
16167                 row.cn[0].cn[0].style = 'min-height:90px';
16168                 row.cn[6].cls+=' fc-last';
16169                 ret.push(row);
16170                 
16171             }
16172             ret[0].cls += ' fc-first';
16173             ret[4].cls += ' fc-prev-last';
16174             ret[5].cls += ' fc-last';
16175             return ret;
16176             
16177         };
16178         
16179         var cal_table = {
16180             tag: 'table',
16181             cls: 'fc-border-separate',
16182             style : 'width:100%',
16183             cellspacing  : 0,
16184             cn : [
16185                 { 
16186                     tag: 'thead',
16187                     cn : [
16188                         { 
16189                             tag: 'tr',
16190                             cls : 'fc-first fc-last',
16191                             cn : cal_heads()
16192                         }
16193                     ]
16194                 },
16195                 { 
16196                     tag: 'tbody',
16197                     cn : cal_rows()
16198                 }
16199                   
16200             ]
16201         };
16202          
16203          var cfg = {
16204             cls : 'fc fc-ltr',
16205             cn : [
16206                 header,
16207                 {
16208                     cls : 'fc-content',
16209                     style : "position: relative;",
16210                     cn : [
16211                         {
16212                             cls : 'fc-view fc-view-month fc-grid',
16213                             style : 'position: relative',
16214                             unselectable : 'on',
16215                             cn : [
16216                                 {
16217                                     cls : 'fc-event-container',
16218                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16219                                 },
16220                                 cal_table
16221                             ]
16222                         }
16223                     ]
16224     
16225                 }
16226            ] 
16227             
16228         };
16229         
16230          
16231         
16232         return cfg;
16233     },
16234     
16235     
16236     initEvents : function()
16237     {
16238         if(!this.store){
16239             throw "can not find store for calendar";
16240         }
16241         
16242         var mark = {
16243             tag: "div",
16244             cls:"x-dlg-mask",
16245             style: "text-align:center",
16246             cn: [
16247                 {
16248                     tag: "div",
16249                     style: "background-color:white;width:50%;margin:250 auto",
16250                     cn: [
16251                         {
16252                             tag: "img",
16253                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16254                         },
16255                         {
16256                             tag: "span",
16257                             html: "Loading"
16258                         }
16259                         
16260                     ]
16261                 }
16262             ]
16263         };
16264         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16265         
16266         var size = this.el.select('.fc-content', true).first().getSize();
16267         this.maskEl.setSize(size.width, size.height);
16268         this.maskEl.enableDisplayMode("block");
16269         if(!this.loadMask){
16270             this.maskEl.hide();
16271         }
16272         
16273         this.store = Roo.factory(this.store, Roo.data);
16274         this.store.on('load', this.onLoad, this);
16275         this.store.on('beforeload', this.onBeforeLoad, this);
16276         
16277         this.resize();
16278         
16279         this.cells = this.el.select('.fc-day',true);
16280         //Roo.log(this.cells);
16281         this.textNodes = this.el.query('.fc-day-number');
16282         this.cells.addClassOnOver('fc-state-hover');
16283         
16284         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16285         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16286         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16287         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16288         
16289         this.on('monthchange', this.onMonthChange, this);
16290         
16291         this.update(new Date().clearTime());
16292     },
16293     
16294     resize : function() {
16295         var sz  = this.el.getSize();
16296         
16297         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16298         this.el.select('.fc-day-content div',true).setHeight(34);
16299     },
16300     
16301     
16302     // private
16303     showPrevMonth : function(e){
16304         this.update(this.activeDate.add("mo", -1));
16305     },
16306     showToday : function(e){
16307         this.update(new Date().clearTime());
16308     },
16309     // private
16310     showNextMonth : function(e){
16311         this.update(this.activeDate.add("mo", 1));
16312     },
16313
16314     // private
16315     showPrevYear : function(){
16316         this.update(this.activeDate.add("y", -1));
16317     },
16318
16319     // private
16320     showNextYear : function(){
16321         this.update(this.activeDate.add("y", 1));
16322     },
16323
16324     
16325    // private
16326     update : function(date)
16327     {
16328         var vd = this.activeDate;
16329         this.activeDate = date;
16330 //        if(vd && this.el){
16331 //            var t = date.getTime();
16332 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16333 //                Roo.log('using add remove');
16334 //                
16335 //                this.fireEvent('monthchange', this, date);
16336 //                
16337 //                this.cells.removeClass("fc-state-highlight");
16338 //                this.cells.each(function(c){
16339 //                   if(c.dateValue == t){
16340 //                       c.addClass("fc-state-highlight");
16341 //                       setTimeout(function(){
16342 //                            try{c.dom.firstChild.focus();}catch(e){}
16343 //                       }, 50);
16344 //                       return false;
16345 //                   }
16346 //                   return true;
16347 //                });
16348 //                return;
16349 //            }
16350 //        }
16351         
16352         var days = date.getDaysInMonth();
16353         
16354         var firstOfMonth = date.getFirstDateOfMonth();
16355         var startingPos = firstOfMonth.getDay()-this.startDay;
16356         
16357         if(startingPos < this.startDay){
16358             startingPos += 7;
16359         }
16360         
16361         var pm = date.add(Date.MONTH, -1);
16362         var prevStart = pm.getDaysInMonth()-startingPos;
16363 //        
16364         this.cells = this.el.select('.fc-day',true);
16365         this.textNodes = this.el.query('.fc-day-number');
16366         this.cells.addClassOnOver('fc-state-hover');
16367         
16368         var cells = this.cells.elements;
16369         var textEls = this.textNodes;
16370         
16371         Roo.each(cells, function(cell){
16372             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16373         });
16374         
16375         days += startingPos;
16376
16377         // convert everything to numbers so it's fast
16378         var day = 86400000;
16379         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16380         //Roo.log(d);
16381         //Roo.log(pm);
16382         //Roo.log(prevStart);
16383         
16384         var today = new Date().clearTime().getTime();
16385         var sel = date.clearTime().getTime();
16386         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16387         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16388         var ddMatch = this.disabledDatesRE;
16389         var ddText = this.disabledDatesText;
16390         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16391         var ddaysText = this.disabledDaysText;
16392         var format = this.format;
16393         
16394         var setCellClass = function(cal, cell){
16395             cell.row = 0;
16396             cell.events = [];
16397             cell.more = [];
16398             //Roo.log('set Cell Class');
16399             cell.title = "";
16400             var t = d.getTime();
16401             
16402             //Roo.log(d);
16403             
16404             cell.dateValue = t;
16405             if(t == today){
16406                 cell.className += " fc-today";
16407                 cell.className += " fc-state-highlight";
16408                 cell.title = cal.todayText;
16409             }
16410             if(t == sel){
16411                 // disable highlight in other month..
16412                 //cell.className += " fc-state-highlight";
16413                 
16414             }
16415             // disabling
16416             if(t < min) {
16417                 cell.className = " fc-state-disabled";
16418                 cell.title = cal.minText;
16419                 return;
16420             }
16421             if(t > max) {
16422                 cell.className = " fc-state-disabled";
16423                 cell.title = cal.maxText;
16424                 return;
16425             }
16426             if(ddays){
16427                 if(ddays.indexOf(d.getDay()) != -1){
16428                     cell.title = ddaysText;
16429                     cell.className = " fc-state-disabled";
16430                 }
16431             }
16432             if(ddMatch && format){
16433                 var fvalue = d.dateFormat(format);
16434                 if(ddMatch.test(fvalue)){
16435                     cell.title = ddText.replace("%0", fvalue);
16436                     cell.className = " fc-state-disabled";
16437                 }
16438             }
16439             
16440             if (!cell.initialClassName) {
16441                 cell.initialClassName = cell.dom.className;
16442             }
16443             
16444             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16445         };
16446
16447         var i = 0;
16448         
16449         for(; i < startingPos; i++) {
16450             textEls[i].innerHTML = (++prevStart);
16451             d.setDate(d.getDate()+1);
16452             
16453             cells[i].className = "fc-past fc-other-month";
16454             setCellClass(this, cells[i]);
16455         }
16456         
16457         var intDay = 0;
16458         
16459         for(; i < days; i++){
16460             intDay = i - startingPos + 1;
16461             textEls[i].innerHTML = (intDay);
16462             d.setDate(d.getDate()+1);
16463             
16464             cells[i].className = ''; // "x-date-active";
16465             setCellClass(this, cells[i]);
16466         }
16467         var extraDays = 0;
16468         
16469         for(; i < 42; i++) {
16470             textEls[i].innerHTML = (++extraDays);
16471             d.setDate(d.getDate()+1);
16472             
16473             cells[i].className = "fc-future fc-other-month";
16474             setCellClass(this, cells[i]);
16475         }
16476         
16477         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16478         
16479         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16480         
16481         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16482         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16483         
16484         if(totalRows != 6){
16485             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16486             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16487         }
16488         
16489         this.fireEvent('monthchange', this, date);
16490         
16491         
16492         /*
16493         if(!this.internalRender){
16494             var main = this.el.dom.firstChild;
16495             var w = main.offsetWidth;
16496             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16497             Roo.fly(main).setWidth(w);
16498             this.internalRender = true;
16499             // opera does not respect the auto grow header center column
16500             // then, after it gets a width opera refuses to recalculate
16501             // without a second pass
16502             if(Roo.isOpera && !this.secondPass){
16503                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16504                 this.secondPass = true;
16505                 this.update.defer(10, this, [date]);
16506             }
16507         }
16508         */
16509         
16510     },
16511     
16512     findCell : function(dt) {
16513         dt = dt.clearTime().getTime();
16514         var ret = false;
16515         this.cells.each(function(c){
16516             //Roo.log("check " +c.dateValue + '?=' + dt);
16517             if(c.dateValue == dt){
16518                 ret = c;
16519                 return false;
16520             }
16521             return true;
16522         });
16523         
16524         return ret;
16525     },
16526     
16527     findCells : function(ev) {
16528         var s = ev.start.clone().clearTime().getTime();
16529        // Roo.log(s);
16530         var e= ev.end.clone().clearTime().getTime();
16531        // Roo.log(e);
16532         var ret = [];
16533         this.cells.each(function(c){
16534              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16535             
16536             if(c.dateValue > e){
16537                 return ;
16538             }
16539             if(c.dateValue < s){
16540                 return ;
16541             }
16542             ret.push(c);
16543         });
16544         
16545         return ret;    
16546     },
16547     
16548 //    findBestRow: function(cells)
16549 //    {
16550 //        var ret = 0;
16551 //        
16552 //        for (var i =0 ; i < cells.length;i++) {
16553 //            ret  = Math.max(cells[i].rows || 0,ret);
16554 //        }
16555 //        return ret;
16556 //        
16557 //    },
16558     
16559     
16560     addItem : function(ev)
16561     {
16562         // look for vertical location slot in
16563         var cells = this.findCells(ev);
16564         
16565 //        ev.row = this.findBestRow(cells);
16566         
16567         // work out the location.
16568         
16569         var crow = false;
16570         var rows = [];
16571         for(var i =0; i < cells.length; i++) {
16572             
16573             cells[i].row = cells[0].row;
16574             
16575             if(i == 0){
16576                 cells[i].row = cells[i].row + 1;
16577             }
16578             
16579             if (!crow) {
16580                 crow = {
16581                     start : cells[i],
16582                     end :  cells[i]
16583                 };
16584                 continue;
16585             }
16586             if (crow.start.getY() == cells[i].getY()) {
16587                 // on same row.
16588                 crow.end = cells[i];
16589                 continue;
16590             }
16591             // different row.
16592             rows.push(crow);
16593             crow = {
16594                 start: cells[i],
16595                 end : cells[i]
16596             };
16597             
16598         }
16599         
16600         rows.push(crow);
16601         ev.els = [];
16602         ev.rows = rows;
16603         ev.cells = cells;
16604         
16605         cells[0].events.push(ev);
16606         
16607         this.calevents.push(ev);
16608     },
16609     
16610     clearEvents: function() {
16611         
16612         if(!this.calevents){
16613             return;
16614         }
16615         
16616         Roo.each(this.cells.elements, function(c){
16617             c.row = 0;
16618             c.events = [];
16619             c.more = [];
16620         });
16621         
16622         Roo.each(this.calevents, function(e) {
16623             Roo.each(e.els, function(el) {
16624                 el.un('mouseenter' ,this.onEventEnter, this);
16625                 el.un('mouseleave' ,this.onEventLeave, this);
16626                 el.remove();
16627             },this);
16628         },this);
16629         
16630         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16631             e.remove();
16632         });
16633         
16634     },
16635     
16636     renderEvents: function()
16637     {   
16638         var _this = this;
16639         
16640         this.cells.each(function(c) {
16641             
16642             if(c.row < 5){
16643                 return;
16644             }
16645             
16646             var ev = c.events;
16647             
16648             var r = 4;
16649             if(c.row != c.events.length){
16650                 r = 4 - (4 - (c.row - c.events.length));
16651             }
16652             
16653             c.events = ev.slice(0, r);
16654             c.more = ev.slice(r);
16655             
16656             if(c.more.length && c.more.length == 1){
16657                 c.events.push(c.more.pop());
16658             }
16659             
16660             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16661             
16662         });
16663             
16664         this.cells.each(function(c) {
16665             
16666             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16667             
16668             
16669             for (var e = 0; e < c.events.length; e++){
16670                 var ev = c.events[e];
16671                 var rows = ev.rows;
16672                 
16673                 for(var i = 0; i < rows.length; i++) {
16674                 
16675                     // how many rows should it span..
16676
16677                     var  cfg = {
16678                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16679                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16680
16681                         unselectable : "on",
16682                         cn : [
16683                             {
16684                                 cls: 'fc-event-inner',
16685                                 cn : [
16686     //                                {
16687     //                                  tag:'span',
16688     //                                  cls: 'fc-event-time',
16689     //                                  html : cells.length > 1 ? '' : ev.time
16690     //                                },
16691                                     {
16692                                       tag:'span',
16693                                       cls: 'fc-event-title',
16694                                       html : String.format('{0}', ev.title)
16695                                     }
16696
16697
16698                                 ]
16699                             },
16700                             {
16701                                 cls: 'ui-resizable-handle ui-resizable-e',
16702                                 html : '&nbsp;&nbsp;&nbsp'
16703                             }
16704
16705                         ]
16706                     };
16707
16708                     if (i == 0) {
16709                         cfg.cls += ' fc-event-start';
16710                     }
16711                     if ((i+1) == rows.length) {
16712                         cfg.cls += ' fc-event-end';
16713                     }
16714
16715                     var ctr = _this.el.select('.fc-event-container',true).first();
16716                     var cg = ctr.createChild(cfg);
16717
16718                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16719                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16720
16721                     var r = (c.more.length) ? 1 : 0;
16722                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16723                     cg.setWidth(ebox.right - sbox.x -2);
16724
16725                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16726                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16727                     cg.on('click', _this.onEventClick, _this, ev);
16728
16729                     ev.els.push(cg);
16730                     
16731                 }
16732                 
16733             }
16734             
16735             
16736             if(c.more.length){
16737                 var  cfg = {
16738                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16739                     style : 'position: absolute',
16740                     unselectable : "on",
16741                     cn : [
16742                         {
16743                             cls: 'fc-event-inner',
16744                             cn : [
16745                                 {
16746                                   tag:'span',
16747                                   cls: 'fc-event-title',
16748                                   html : 'More'
16749                                 }
16750
16751
16752                             ]
16753                         },
16754                         {
16755                             cls: 'ui-resizable-handle ui-resizable-e',
16756                             html : '&nbsp;&nbsp;&nbsp'
16757                         }
16758
16759                     ]
16760                 };
16761
16762                 var ctr = _this.el.select('.fc-event-container',true).first();
16763                 var cg = ctr.createChild(cfg);
16764
16765                 var sbox = c.select('.fc-day-content',true).first().getBox();
16766                 var ebox = c.select('.fc-day-content',true).first().getBox();
16767                 //Roo.log(cg);
16768                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16769                 cg.setWidth(ebox.right - sbox.x -2);
16770
16771                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16772                 
16773             }
16774             
16775         });
16776         
16777         
16778         
16779     },
16780     
16781     onEventEnter: function (e, el,event,d) {
16782         this.fireEvent('evententer', this, el, event);
16783     },
16784     
16785     onEventLeave: function (e, el,event,d) {
16786         this.fireEvent('eventleave', this, el, event);
16787     },
16788     
16789     onEventClick: function (e, el,event,d) {
16790         this.fireEvent('eventclick', this, el, event);
16791     },
16792     
16793     onMonthChange: function () {
16794         this.store.load();
16795     },
16796     
16797     onMoreEventClick: function(e, el, more)
16798     {
16799         var _this = this;
16800         
16801         this.calpopover.placement = 'right';
16802         this.calpopover.setTitle('More');
16803         
16804         this.calpopover.setContent('');
16805         
16806         var ctr = this.calpopover.el.select('.popover-content', true).first();
16807         
16808         Roo.each(more, function(m){
16809             var cfg = {
16810                 cls : 'fc-event-hori fc-event-draggable',
16811                 html : m.title
16812             };
16813             var cg = ctr.createChild(cfg);
16814             
16815             cg.on('click', _this.onEventClick, _this, m);
16816         });
16817         
16818         this.calpopover.show(el);
16819         
16820         
16821     },
16822     
16823     onLoad: function () 
16824     {   
16825         this.calevents = [];
16826         var cal = this;
16827         
16828         if(this.store.getCount() > 0){
16829             this.store.data.each(function(d){
16830                cal.addItem({
16831                     id : d.data.id,
16832                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16833                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16834                     time : d.data.start_time,
16835                     title : d.data.title,
16836                     description : d.data.description,
16837                     venue : d.data.venue
16838                 });
16839             });
16840         }
16841         
16842         this.renderEvents();
16843         
16844         if(this.calevents.length && this.loadMask){
16845             this.maskEl.hide();
16846         }
16847     },
16848     
16849     onBeforeLoad: function()
16850     {
16851         this.clearEvents();
16852         if(this.loadMask){
16853             this.maskEl.show();
16854         }
16855     }
16856 });
16857
16858  
16859  /*
16860  * - LGPL
16861  *
16862  * element
16863  * 
16864  */
16865
16866 /**
16867  * @class Roo.bootstrap.Popover
16868  * @extends Roo.bootstrap.Component
16869  * Bootstrap Popover class
16870  * @cfg {String} html contents of the popover   (or false to use children..)
16871  * @cfg {String} title of popover (or false to hide)
16872  * @cfg {String} placement how it is placed
16873  * @cfg {String} trigger click || hover (or false to trigger manually)
16874  * @cfg {String} over what (parent or false to trigger manually.)
16875  * @cfg {Number} delay - delay before showing
16876  
16877  * @constructor
16878  * Create a new Popover
16879  * @param {Object} config The config object
16880  */
16881
16882 Roo.bootstrap.Popover = function(config){
16883     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16884     
16885     this.addEvents({
16886         // raw events
16887          /**
16888          * @event show
16889          * After the popover show
16890          * 
16891          * @param {Roo.bootstrap.Popover} this
16892          */
16893         "show" : true,
16894         /**
16895          * @event hide
16896          * After the popover hide
16897          * 
16898          * @param {Roo.bootstrap.Popover} this
16899          */
16900         "hide" : true
16901     });
16902 };
16903
16904 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16905     
16906     title: 'Fill in a title',
16907     html: false,
16908     
16909     placement : 'right',
16910     trigger : 'hover', // hover
16911     
16912     delay : 0,
16913     
16914     over: 'parent',
16915     
16916     can_build_overlaid : false,
16917     
16918     getChildContainer : function()
16919     {
16920         return this.el.select('.popover-content',true).first();
16921     },
16922     
16923     getAutoCreate : function(){
16924          
16925         var cfg = {
16926            cls : 'popover roo-dynamic',
16927            style: 'display:block',
16928            cn : [
16929                 {
16930                     cls : 'arrow'
16931                 },
16932                 {
16933                     cls : 'popover-inner',
16934                     cn : [
16935                         {
16936                             tag: 'h3',
16937                             cls: 'popover-title',
16938                             html : this.title
16939                         },
16940                         {
16941                             cls : 'popover-content',
16942                             html : this.html
16943                         }
16944                     ]
16945                     
16946                 }
16947            ]
16948         };
16949         
16950         return cfg;
16951     },
16952     setTitle: function(str)
16953     {
16954         this.title = str;
16955         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16956     },
16957     setContent: function(str)
16958     {
16959         this.html = str;
16960         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16961     },
16962     // as it get's added to the bottom of the page.
16963     onRender : function(ct, position)
16964     {
16965         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16966         if(!this.el){
16967             var cfg = Roo.apply({},  this.getAutoCreate());
16968             cfg.id = Roo.id();
16969             
16970             if (this.cls) {
16971                 cfg.cls += ' ' + this.cls;
16972             }
16973             if (this.style) {
16974                 cfg.style = this.style;
16975             }
16976             //Roo.log("adding to ");
16977             this.el = Roo.get(document.body).createChild(cfg, position);
16978 //            Roo.log(this.el);
16979         }
16980         this.initEvents();
16981     },
16982     
16983     initEvents : function()
16984     {
16985         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16986         this.el.enableDisplayMode('block');
16987         this.el.hide();
16988         if (this.over === false) {
16989             return; 
16990         }
16991         if (this.triggers === false) {
16992             return;
16993         }
16994         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16995         var triggers = this.trigger ? this.trigger.split(' ') : [];
16996         Roo.each(triggers, function(trigger) {
16997         
16998             if (trigger == 'click') {
16999                 on_el.on('click', this.toggle, this);
17000             } else if (trigger != 'manual') {
17001                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17002                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17003       
17004                 on_el.on(eventIn  ,this.enter, this);
17005                 on_el.on(eventOut, this.leave, this);
17006             }
17007         }, this);
17008         
17009     },
17010     
17011     
17012     // private
17013     timeout : null,
17014     hoverState : null,
17015     
17016     toggle : function () {
17017         this.hoverState == 'in' ? this.leave() : this.enter();
17018     },
17019     
17020     enter : function () {
17021         
17022         clearTimeout(this.timeout);
17023     
17024         this.hoverState = 'in';
17025     
17026         if (!this.delay || !this.delay.show) {
17027             this.show();
17028             return;
17029         }
17030         var _t = this;
17031         this.timeout = setTimeout(function () {
17032             if (_t.hoverState == 'in') {
17033                 _t.show();
17034             }
17035         }, this.delay.show)
17036     },
17037     
17038     leave : function() {
17039         clearTimeout(this.timeout);
17040     
17041         this.hoverState = 'out';
17042     
17043         if (!this.delay || !this.delay.hide) {
17044             this.hide();
17045             return;
17046         }
17047         var _t = this;
17048         this.timeout = setTimeout(function () {
17049             if (_t.hoverState == 'out') {
17050                 _t.hide();
17051             }
17052         }, this.delay.hide)
17053     },
17054     
17055     show : function (on_el)
17056     {
17057         if (!on_el) {
17058             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17059         }
17060         
17061         // set content.
17062         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17063         if (this.html !== false) {
17064             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17065         }
17066         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17067         if (!this.title.length) {
17068             this.el.select('.popover-title',true).hide();
17069         }
17070         
17071         var placement = typeof this.placement == 'function' ?
17072             this.placement.call(this, this.el, on_el) :
17073             this.placement;
17074             
17075         var autoToken = /\s?auto?\s?/i;
17076         var autoPlace = autoToken.test(placement);
17077         if (autoPlace) {
17078             placement = placement.replace(autoToken, '') || 'top';
17079         }
17080         
17081         //this.el.detach()
17082         //this.el.setXY([0,0]);
17083         this.el.show();
17084         this.el.dom.style.display='block';
17085         this.el.addClass(placement);
17086         
17087         //this.el.appendTo(on_el);
17088         
17089         var p = this.getPosition();
17090         var box = this.el.getBox();
17091         
17092         if (autoPlace) {
17093             // fixme..
17094         }
17095         var align = Roo.bootstrap.Popover.alignment[placement];
17096         this.el.alignTo(on_el, align[0],align[1]);
17097         //var arrow = this.el.select('.arrow',true).first();
17098         //arrow.set(align[2], 
17099         
17100         this.el.addClass('in');
17101         
17102         
17103         if (this.el.hasClass('fade')) {
17104             // fade it?
17105         }
17106         
17107         this.hoverState = 'in';
17108         
17109         this.fireEvent('show', this);
17110         
17111     },
17112     hide : function()
17113     {
17114         this.el.setXY([0,0]);
17115         this.el.removeClass('in');
17116         this.el.hide();
17117         this.hoverState = null;
17118         
17119         this.fireEvent('hide', this);
17120     }
17121     
17122 });
17123
17124 Roo.bootstrap.Popover.alignment = {
17125     'left' : ['r-l', [-10,0], 'right'],
17126     'right' : ['l-r', [10,0], 'left'],
17127     'bottom' : ['t-b', [0,10], 'top'],
17128     'top' : [ 'b-t', [0,-10], 'bottom']
17129 };
17130
17131  /*
17132  * - LGPL
17133  *
17134  * Progress
17135  * 
17136  */
17137
17138 /**
17139  * @class Roo.bootstrap.Progress
17140  * @extends Roo.bootstrap.Component
17141  * Bootstrap Progress class
17142  * @cfg {Boolean} striped striped of the progress bar
17143  * @cfg {Boolean} active animated of the progress bar
17144  * 
17145  * 
17146  * @constructor
17147  * Create a new Progress
17148  * @param {Object} config The config object
17149  */
17150
17151 Roo.bootstrap.Progress = function(config){
17152     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17153 };
17154
17155 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17156     
17157     striped : false,
17158     active: false,
17159     
17160     getAutoCreate : function(){
17161         var cfg = {
17162             tag: 'div',
17163             cls: 'progress'
17164         };
17165         
17166         
17167         if(this.striped){
17168             cfg.cls += ' progress-striped';
17169         }
17170       
17171         if(this.active){
17172             cfg.cls += ' active';
17173         }
17174         
17175         
17176         return cfg;
17177     }
17178    
17179 });
17180
17181  
17182
17183  /*
17184  * - LGPL
17185  *
17186  * ProgressBar
17187  * 
17188  */
17189
17190 /**
17191  * @class Roo.bootstrap.ProgressBar
17192  * @extends Roo.bootstrap.Component
17193  * Bootstrap ProgressBar class
17194  * @cfg {Number} aria_valuenow aria-value now
17195  * @cfg {Number} aria_valuemin aria-value min
17196  * @cfg {Number} aria_valuemax aria-value max
17197  * @cfg {String} label label for the progress bar
17198  * @cfg {String} panel (success | info | warning | danger )
17199  * @cfg {String} role role of the progress bar
17200  * @cfg {String} sr_only text
17201  * 
17202  * 
17203  * @constructor
17204  * Create a new ProgressBar
17205  * @param {Object} config The config object
17206  */
17207
17208 Roo.bootstrap.ProgressBar = function(config){
17209     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17210 };
17211
17212 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17213     
17214     aria_valuenow : 0,
17215     aria_valuemin : 0,
17216     aria_valuemax : 100,
17217     label : false,
17218     panel : false,
17219     role : false,
17220     sr_only: false,
17221     
17222     getAutoCreate : function()
17223     {
17224         
17225         var cfg = {
17226             tag: 'div',
17227             cls: 'progress-bar',
17228             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17229         };
17230         
17231         if(this.sr_only){
17232             cfg.cn = {
17233                 tag: 'span',
17234                 cls: 'sr-only',
17235                 html: this.sr_only
17236             }
17237         }
17238         
17239         if(this.role){
17240             cfg.role = this.role;
17241         }
17242         
17243         if(this.aria_valuenow){
17244             cfg['aria-valuenow'] = this.aria_valuenow;
17245         }
17246         
17247         if(this.aria_valuemin){
17248             cfg['aria-valuemin'] = this.aria_valuemin;
17249         }
17250         
17251         if(this.aria_valuemax){
17252             cfg['aria-valuemax'] = this.aria_valuemax;
17253         }
17254         
17255         if(this.label && !this.sr_only){
17256             cfg.html = this.label;
17257         }
17258         
17259         if(this.panel){
17260             cfg.cls += ' progress-bar-' + this.panel;
17261         }
17262         
17263         return cfg;
17264     },
17265     
17266     update : function(aria_valuenow)
17267     {
17268         this.aria_valuenow = aria_valuenow;
17269         
17270         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17271     }
17272    
17273 });
17274
17275  
17276
17277  /*
17278  * - LGPL
17279  *
17280  * column
17281  * 
17282  */
17283
17284 /**
17285  * @class Roo.bootstrap.TabGroup
17286  * @extends Roo.bootstrap.Column
17287  * Bootstrap Column class
17288  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17289  * @cfg {Boolean} carousel true to make the group behave like a carousel
17290  * @cfg {Boolean} bullets show bullets for the panels
17291  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17292  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17293  * @cfg {Boolean} showarrow (true|false) show arrow default true
17294  * 
17295  * @constructor
17296  * Create a new TabGroup
17297  * @param {Object} config The config object
17298  */
17299
17300 Roo.bootstrap.TabGroup = function(config){
17301     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17302     if (!this.navId) {
17303         this.navId = Roo.id();
17304     }
17305     this.tabs = [];
17306     Roo.bootstrap.TabGroup.register(this);
17307     
17308 };
17309
17310 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17311     
17312     carousel : false,
17313     transition : false,
17314     bullets : 0,
17315     timer : 0,
17316     autoslide : false,
17317     slideFn : false,
17318     slideOnTouch : false,
17319     showarrow : true,
17320     
17321     getAutoCreate : function()
17322     {
17323         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17324         
17325         cfg.cls += ' tab-content';
17326         
17327         if (this.carousel) {
17328             cfg.cls += ' carousel slide';
17329             
17330             cfg.cn = [{
17331                cls : 'carousel-inner',
17332                cn : []
17333             }];
17334         
17335             if(this.bullets  && !Roo.isTouch){
17336                 
17337                 var bullets = {
17338                     cls : 'carousel-bullets',
17339                     cn : []
17340                 };
17341                
17342                 if(this.bullets_cls){
17343                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17344                 }
17345                 
17346                 bullets.cn.push({
17347                     cls : 'clear'
17348                 });
17349                 
17350                 cfg.cn[0].cn.push(bullets);
17351             }
17352             
17353             if(this.showarrow){
17354                 cfg.cn[0].cn.push({
17355                     tag : 'div',
17356                     class : 'carousel-arrow',
17357                     cn : [
17358                         {
17359                             tag : 'div',
17360                             class : 'carousel-prev',
17361                             cn : [
17362                                 {
17363                                     tag : 'i',
17364                                     class : 'fa fa-chevron-left'
17365                                 }
17366                             ]
17367                         },
17368                         {
17369                             tag : 'div',
17370                             class : 'carousel-next',
17371                             cn : [
17372                                 {
17373                                     tag : 'i',
17374                                     class : 'fa fa-chevron-right'
17375                                 }
17376                             ]
17377                         }
17378                     ]
17379                 });
17380             }
17381             
17382         }
17383         
17384         return cfg;
17385     },
17386     
17387     initEvents:  function()
17388     {
17389 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17390 //            this.el.on("touchstart", this.onTouchStart, this);
17391 //        }
17392         
17393         if(this.autoslide){
17394             var _this = this;
17395             
17396             this.slideFn = window.setInterval(function() {
17397                 _this.showPanelNext();
17398             }, this.timer);
17399         }
17400         
17401         if(this.showarrow){
17402             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17403             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17404         }
17405         
17406         
17407     },
17408     
17409 //    onTouchStart : function(e, el, o)
17410 //    {
17411 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17412 //            return;
17413 //        }
17414 //        
17415 //        this.showPanelNext();
17416 //    },
17417     
17418     
17419     getChildContainer : function()
17420     {
17421         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17422     },
17423     
17424     /**
17425     * register a Navigation item
17426     * @param {Roo.bootstrap.NavItem} the navitem to add
17427     */
17428     register : function(item)
17429     {
17430         this.tabs.push( item);
17431         item.navId = this.navId; // not really needed..
17432         this.addBullet();
17433     
17434     },
17435     
17436     getActivePanel : function()
17437     {
17438         var r = false;
17439         Roo.each(this.tabs, function(t) {
17440             if (t.active) {
17441                 r = t;
17442                 return false;
17443             }
17444             return null;
17445         });
17446         return r;
17447         
17448     },
17449     getPanelByName : function(n)
17450     {
17451         var r = false;
17452         Roo.each(this.tabs, function(t) {
17453             if (t.tabId == n) {
17454                 r = t;
17455                 return false;
17456             }
17457             return null;
17458         });
17459         return r;
17460     },
17461     indexOfPanel : function(p)
17462     {
17463         var r = false;
17464         Roo.each(this.tabs, function(t,i) {
17465             if (t.tabId == p.tabId) {
17466                 r = i;
17467                 return false;
17468             }
17469             return null;
17470         });
17471         return r;
17472     },
17473     /**
17474      * show a specific panel
17475      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17476      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17477      */
17478     showPanel : function (pan)
17479     {
17480         if(this.transition || typeof(pan) == 'undefined'){
17481             Roo.log("waiting for the transitionend");
17482             return;
17483         }
17484         
17485         if (typeof(pan) == 'number') {
17486             pan = this.tabs[pan];
17487         }
17488         
17489         if (typeof(pan) == 'string') {
17490             pan = this.getPanelByName(pan);
17491         }
17492         
17493         var cur = this.getActivePanel();
17494         
17495         if(!pan || !cur){
17496             Roo.log('pan or acitve pan is undefined');
17497             return false;
17498         }
17499         
17500         if (pan.tabId == this.getActivePanel().tabId) {
17501             return true;
17502         }
17503         
17504         if (false === cur.fireEvent('beforedeactivate')) {
17505             return false;
17506         }
17507         
17508         if(this.bullets > 0 && !Roo.isTouch){
17509             this.setActiveBullet(this.indexOfPanel(pan));
17510         }
17511         
17512         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17513             
17514             this.transition = true;
17515             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17516             var lr = dir == 'next' ? 'left' : 'right';
17517             pan.el.addClass(dir); // or prev
17518             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17519             cur.el.addClass(lr); // or right
17520             pan.el.addClass(lr);
17521             
17522             var _this = this;
17523             cur.el.on('transitionend', function() {
17524                 Roo.log("trans end?");
17525                 
17526                 pan.el.removeClass([lr,dir]);
17527                 pan.setActive(true);
17528                 
17529                 cur.el.removeClass([lr]);
17530                 cur.setActive(false);
17531                 
17532                 _this.transition = false;
17533                 
17534             }, this, { single:  true } );
17535             
17536             return true;
17537         }
17538         
17539         cur.setActive(false);
17540         pan.setActive(true);
17541         
17542         return true;
17543         
17544     },
17545     showPanelNext : function()
17546     {
17547         var i = this.indexOfPanel(this.getActivePanel());
17548         
17549         if (i >= this.tabs.length - 1 && !this.autoslide) {
17550             return;
17551         }
17552         
17553         if (i >= this.tabs.length - 1 && this.autoslide) {
17554             i = -1;
17555         }
17556         
17557         this.showPanel(this.tabs[i+1]);
17558     },
17559     
17560     showPanelPrev : function()
17561     {
17562         var i = this.indexOfPanel(this.getActivePanel());
17563         
17564         if (i  < 1 && !this.autoslide) {
17565             return;
17566         }
17567         
17568         if (i < 1 && this.autoslide) {
17569             i = this.tabs.length;
17570         }
17571         
17572         this.showPanel(this.tabs[i-1]);
17573     },
17574     
17575     
17576     addBullet: function()
17577     {
17578         if(!this.bullets || Roo.isTouch){
17579             return;
17580         }
17581         var ctr = this.el.select('.carousel-bullets',true).first();
17582         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17583         var bullet = ctr.createChild({
17584             cls : 'bullet bullet-' + i
17585         },ctr.dom.lastChild);
17586         
17587         
17588         var _this = this;
17589         
17590         bullet.on('click', (function(e, el, o, ii, t){
17591
17592             e.preventDefault();
17593
17594             this.showPanel(ii);
17595
17596             if(this.autoslide && this.slideFn){
17597                 clearInterval(this.slideFn);
17598                 this.slideFn = window.setInterval(function() {
17599                     _this.showPanelNext();
17600                 }, this.timer);
17601             }
17602
17603         }).createDelegate(this, [i, bullet], true));
17604                 
17605         
17606     },
17607      
17608     setActiveBullet : function(i)
17609     {
17610         if(Roo.isTouch){
17611             return;
17612         }
17613         
17614         Roo.each(this.el.select('.bullet', true).elements, function(el){
17615             el.removeClass('selected');
17616         });
17617
17618         var bullet = this.el.select('.bullet-' + i, true).first();
17619         
17620         if(!bullet){
17621             return;
17622         }
17623         
17624         bullet.addClass('selected');
17625     }
17626     
17627     
17628   
17629 });
17630
17631  
17632
17633  
17634  
17635 Roo.apply(Roo.bootstrap.TabGroup, {
17636     
17637     groups: {},
17638      /**
17639     * register a Navigation Group
17640     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17641     */
17642     register : function(navgrp)
17643     {
17644         this.groups[navgrp.navId] = navgrp;
17645         
17646     },
17647     /**
17648     * fetch a Navigation Group based on the navigation ID
17649     * if one does not exist , it will get created.
17650     * @param {string} the navgroup to add
17651     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17652     */
17653     get: function(navId) {
17654         if (typeof(this.groups[navId]) == 'undefined') {
17655             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17656         }
17657         return this.groups[navId] ;
17658     }
17659     
17660     
17661     
17662 });
17663
17664  /*
17665  * - LGPL
17666  *
17667  * TabPanel
17668  * 
17669  */
17670
17671 /**
17672  * @class Roo.bootstrap.TabPanel
17673  * @extends Roo.bootstrap.Component
17674  * Bootstrap TabPanel class
17675  * @cfg {Boolean} active panel active
17676  * @cfg {String} html panel content
17677  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17678  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17679  * @cfg {String} href click to link..
17680  * 
17681  * 
17682  * @constructor
17683  * Create a new TabPanel
17684  * @param {Object} config The config object
17685  */
17686
17687 Roo.bootstrap.TabPanel = function(config){
17688     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17689     this.addEvents({
17690         /**
17691              * @event changed
17692              * Fires when the active status changes
17693              * @param {Roo.bootstrap.TabPanel} this
17694              * @param {Boolean} state the new state
17695             
17696          */
17697         'changed': true,
17698         /**
17699              * @event beforedeactivate
17700              * Fires before a tab is de-activated - can be used to do validation on a form.
17701              * @param {Roo.bootstrap.TabPanel} this
17702              * @return {Boolean} false if there is an error
17703             
17704          */
17705         'beforedeactivate': true
17706      });
17707     
17708     this.tabId = this.tabId || Roo.id();
17709   
17710 };
17711
17712 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17713     
17714     active: false,
17715     html: false,
17716     tabId: false,
17717     navId : false,
17718     href : '',
17719     
17720     getAutoCreate : function(){
17721         var cfg = {
17722             tag: 'div',
17723             // item is needed for carousel - not sure if it has any effect otherwise
17724             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17725             html: this.html || ''
17726         };
17727         
17728         if(this.active){
17729             cfg.cls += ' active';
17730         }
17731         
17732         if(this.tabId){
17733             cfg.tabId = this.tabId;
17734         }
17735         
17736         
17737         return cfg;
17738     },
17739     
17740     initEvents:  function()
17741     {
17742         var p = this.parent();
17743         
17744         this.navId = this.navId || p.navId;
17745         
17746         if (typeof(this.navId) != 'undefined') {
17747             // not really needed.. but just in case.. parent should be a NavGroup.
17748             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17749             
17750             tg.register(this);
17751             
17752             var i = tg.tabs.length - 1;
17753             
17754             if(this.active && tg.bullets > 0 && i < tg.bullets){
17755                 tg.setActiveBullet(i);
17756             }
17757         }
17758         
17759         this.el.on('click', this.onClick, this);
17760         
17761         if(Roo.isTouch){
17762             this.el.on("touchstart", this.onTouchStart, this);
17763             this.el.on("touchmove", this.onTouchMove, this);
17764             this.el.on("touchend", this.onTouchEnd, this);
17765         }
17766         
17767     },
17768     
17769     onRender : function(ct, position)
17770     {
17771         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17772     },
17773     
17774     setActive : function(state)
17775     {
17776         Roo.log("panel - set active " + this.tabId + "=" + state);
17777         
17778         this.active = state;
17779         if (!state) {
17780             this.el.removeClass('active');
17781             
17782         } else  if (!this.el.hasClass('active')) {
17783             this.el.addClass('active');
17784         }
17785         
17786         this.fireEvent('changed', this, state);
17787     },
17788     
17789     onClick : function(e)
17790     {
17791         e.preventDefault();
17792         
17793         if(!this.href.length){
17794             return;
17795         }
17796         
17797         window.location.href = this.href;
17798     },
17799     
17800     startX : 0,
17801     startY : 0,
17802     endX : 0,
17803     endY : 0,
17804     swiping : false,
17805     
17806     onTouchStart : function(e)
17807     {
17808         this.swiping = false;
17809         
17810         this.startX = e.browserEvent.touches[0].clientX;
17811         this.startY = e.browserEvent.touches[0].clientY;
17812     },
17813     
17814     onTouchMove : function(e)
17815     {
17816         this.swiping = true;
17817         
17818         this.endX = e.browserEvent.touches[0].clientX;
17819         this.endY = e.browserEvent.touches[0].clientY;
17820     },
17821     
17822     onTouchEnd : function(e)
17823     {
17824         if(!this.swiping){
17825             this.onClick(e);
17826             return;
17827         }
17828         
17829         var tabGroup = this.parent();
17830         
17831         if(this.endX > this.startX){ // swiping right
17832             tabGroup.showPanelPrev();
17833             return;
17834         }
17835         
17836         if(this.startX > this.endX){ // swiping left
17837             tabGroup.showPanelNext();
17838             return;
17839         }
17840     }
17841     
17842     
17843 });
17844  
17845
17846  
17847
17848  /*
17849  * - LGPL
17850  *
17851  * DateField
17852  * 
17853  */
17854
17855 /**
17856  * @class Roo.bootstrap.DateField
17857  * @extends Roo.bootstrap.Input
17858  * Bootstrap DateField class
17859  * @cfg {Number} weekStart default 0
17860  * @cfg {String} viewMode default empty, (months|years)
17861  * @cfg {String} minViewMode default empty, (months|years)
17862  * @cfg {Number} startDate default -Infinity
17863  * @cfg {Number} endDate default Infinity
17864  * @cfg {Boolean} todayHighlight default false
17865  * @cfg {Boolean} todayBtn default false
17866  * @cfg {Boolean} calendarWeeks default false
17867  * @cfg {Object} daysOfWeekDisabled default empty
17868  * @cfg {Boolean} singleMode default false (true | false)
17869  * 
17870  * @cfg {Boolean} keyboardNavigation default true
17871  * @cfg {String} language default en
17872  * 
17873  * @constructor
17874  * Create a new DateField
17875  * @param {Object} config The config object
17876  */
17877
17878 Roo.bootstrap.DateField = function(config){
17879     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17880      this.addEvents({
17881             /**
17882              * @event show
17883              * Fires when this field show.
17884              * @param {Roo.bootstrap.DateField} this
17885              * @param {Mixed} date The date value
17886              */
17887             show : true,
17888             /**
17889              * @event show
17890              * Fires when this field hide.
17891              * @param {Roo.bootstrap.DateField} this
17892              * @param {Mixed} date The date value
17893              */
17894             hide : true,
17895             /**
17896              * @event select
17897              * Fires when select a date.
17898              * @param {Roo.bootstrap.DateField} this
17899              * @param {Mixed} date The date value
17900              */
17901             select : true,
17902             /**
17903              * @event beforeselect
17904              * Fires when before select a date.
17905              * @param {Roo.bootstrap.DateField} this
17906              * @param {Mixed} date The date value
17907              */
17908             beforeselect : true
17909         });
17910 };
17911
17912 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17913     
17914     /**
17915      * @cfg {String} format
17916      * The default date format string which can be overriden for localization support.  The format must be
17917      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17918      */
17919     format : "m/d/y",
17920     /**
17921      * @cfg {String} altFormats
17922      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17923      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17924      */
17925     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17926     
17927     weekStart : 0,
17928     
17929     viewMode : '',
17930     
17931     minViewMode : '',
17932     
17933     todayHighlight : false,
17934     
17935     todayBtn: false,
17936     
17937     language: 'en',
17938     
17939     keyboardNavigation: true,
17940     
17941     calendarWeeks: false,
17942     
17943     startDate: -Infinity,
17944     
17945     endDate: Infinity,
17946     
17947     daysOfWeekDisabled: [],
17948     
17949     _events: [],
17950     
17951     singleMode : false,
17952     
17953     UTCDate: function()
17954     {
17955         return new Date(Date.UTC.apply(Date, arguments));
17956     },
17957     
17958     UTCToday: function()
17959     {
17960         var today = new Date();
17961         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17962     },
17963     
17964     getDate: function() {
17965             var d = this.getUTCDate();
17966             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17967     },
17968     
17969     getUTCDate: function() {
17970             return this.date;
17971     },
17972     
17973     setDate: function(d) {
17974             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17975     },
17976     
17977     setUTCDate: function(d) {
17978             this.date = d;
17979             this.setValue(this.formatDate(this.date));
17980     },
17981         
17982     onRender: function(ct, position)
17983     {
17984         
17985         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17986         
17987         this.language = this.language || 'en';
17988         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17989         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17990         
17991         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17992         this.format = this.format || 'm/d/y';
17993         this.isInline = false;
17994         this.isInput = true;
17995         this.component = this.el.select('.add-on', true).first() || false;
17996         this.component = (this.component && this.component.length === 0) ? false : this.component;
17997         this.hasInput = this.component && this.inputEl().length;
17998         
17999         if (typeof(this.minViewMode === 'string')) {
18000             switch (this.minViewMode) {
18001                 case 'months':
18002                     this.minViewMode = 1;
18003                     break;
18004                 case 'years':
18005                     this.minViewMode = 2;
18006                     break;
18007                 default:
18008                     this.minViewMode = 0;
18009                     break;
18010             }
18011         }
18012         
18013         if (typeof(this.viewMode === 'string')) {
18014             switch (this.viewMode) {
18015                 case 'months':
18016                     this.viewMode = 1;
18017                     break;
18018                 case 'years':
18019                     this.viewMode = 2;
18020                     break;
18021                 default:
18022                     this.viewMode = 0;
18023                     break;
18024             }
18025         }
18026                 
18027         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18028         
18029 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18030         
18031         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18032         
18033         this.picker().on('mousedown', this.onMousedown, this);
18034         this.picker().on('click', this.onClick, this);
18035         
18036         this.picker().addClass('datepicker-dropdown');
18037         
18038         this.startViewMode = this.viewMode;
18039         
18040         if(this.singleMode){
18041             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18042                 v.setVisibilityMode(Roo.Element.DISPLAY);
18043                 v.hide();
18044             });
18045             
18046             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18047                 v.setStyle('width', '189px');
18048             });
18049         }
18050         
18051         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18052             if(!this.calendarWeeks){
18053                 v.remove();
18054                 return;
18055             }
18056             
18057             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18058             v.attr('colspan', function(i, val){
18059                 return parseInt(val) + 1;
18060             });
18061         });
18062                         
18063         
18064         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18065         
18066         this.setStartDate(this.startDate);
18067         this.setEndDate(this.endDate);
18068         
18069         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18070         
18071         this.fillDow();
18072         this.fillMonths();
18073         this.update();
18074         this.showMode();
18075         
18076         if(this.isInline) {
18077             this.show();
18078         }
18079     },
18080     
18081     picker : function()
18082     {
18083         return this.pickerEl;
18084 //        return this.el.select('.datepicker', true).first();
18085     },
18086     
18087     fillDow: function()
18088     {
18089         var dowCnt = this.weekStart;
18090         
18091         var dow = {
18092             tag: 'tr',
18093             cn: [
18094                 
18095             ]
18096         };
18097         
18098         if(this.calendarWeeks){
18099             dow.cn.push({
18100                 tag: 'th',
18101                 cls: 'cw',
18102                 html: '&nbsp;'
18103             })
18104         }
18105         
18106         while (dowCnt < this.weekStart + 7) {
18107             dow.cn.push({
18108                 tag: 'th',
18109                 cls: 'dow',
18110                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18111             });
18112         }
18113         
18114         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18115     },
18116     
18117     fillMonths: function()
18118     {    
18119         var i = 0;
18120         var months = this.picker().select('>.datepicker-months td', true).first();
18121         
18122         months.dom.innerHTML = '';
18123         
18124         while (i < 12) {
18125             var month = {
18126                 tag: 'span',
18127                 cls: 'month',
18128                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18129             };
18130             
18131             months.createChild(month);
18132         }
18133         
18134     },
18135     
18136     update: function()
18137     {
18138         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;
18139         
18140         if (this.date < this.startDate) {
18141             this.viewDate = new Date(this.startDate);
18142         } else if (this.date > this.endDate) {
18143             this.viewDate = new Date(this.endDate);
18144         } else {
18145             this.viewDate = new Date(this.date);
18146         }
18147         
18148         this.fill();
18149     },
18150     
18151     fill: function() 
18152     {
18153         var d = new Date(this.viewDate),
18154                 year = d.getUTCFullYear(),
18155                 month = d.getUTCMonth(),
18156                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18157                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18158                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18159                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18160                 currentDate = this.date && this.date.valueOf(),
18161                 today = this.UTCToday();
18162         
18163         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18164         
18165 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18166         
18167 //        this.picker.select('>tfoot th.today').
18168 //                                              .text(dates[this.language].today)
18169 //                                              .toggle(this.todayBtn !== false);
18170     
18171         this.updateNavArrows();
18172         this.fillMonths();
18173                                                 
18174         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18175         
18176         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18177          
18178         prevMonth.setUTCDate(day);
18179         
18180         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18181         
18182         var nextMonth = new Date(prevMonth);
18183         
18184         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18185         
18186         nextMonth = nextMonth.valueOf();
18187         
18188         var fillMonths = false;
18189         
18190         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18191         
18192         while(prevMonth.valueOf() < nextMonth) {
18193             var clsName = '';
18194             
18195             if (prevMonth.getUTCDay() === this.weekStart) {
18196                 if(fillMonths){
18197                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18198                 }
18199                     
18200                 fillMonths = {
18201                     tag: 'tr',
18202                     cn: []
18203                 };
18204                 
18205                 if(this.calendarWeeks){
18206                     // ISO 8601: First week contains first thursday.
18207                     // ISO also states week starts on Monday, but we can be more abstract here.
18208                     var
18209                     // Start of current week: based on weekstart/current date
18210                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18211                     // Thursday of this week
18212                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18213                     // First Thursday of year, year from thursday
18214                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18215                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18216                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18217                     
18218                     fillMonths.cn.push({
18219                         tag: 'td',
18220                         cls: 'cw',
18221                         html: calWeek
18222                     });
18223                 }
18224             }
18225             
18226             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18227                 clsName += ' old';
18228             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18229                 clsName += ' new';
18230             }
18231             if (this.todayHighlight &&
18232                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18233                 prevMonth.getUTCMonth() == today.getMonth() &&
18234                 prevMonth.getUTCDate() == today.getDate()) {
18235                 clsName += ' today';
18236             }
18237             
18238             if (currentDate && prevMonth.valueOf() === currentDate) {
18239                 clsName += ' active';
18240             }
18241             
18242             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18243                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18244                     clsName += ' disabled';
18245             }
18246             
18247             fillMonths.cn.push({
18248                 tag: 'td',
18249                 cls: 'day ' + clsName,
18250                 html: prevMonth.getDate()
18251             });
18252             
18253             prevMonth.setDate(prevMonth.getDate()+1);
18254         }
18255           
18256         var currentYear = this.date && this.date.getUTCFullYear();
18257         var currentMonth = this.date && this.date.getUTCMonth();
18258         
18259         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18260         
18261         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18262             v.removeClass('active');
18263             
18264             if(currentYear === year && k === currentMonth){
18265                 v.addClass('active');
18266             }
18267             
18268             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18269                 v.addClass('disabled');
18270             }
18271             
18272         });
18273         
18274         
18275         year = parseInt(year/10, 10) * 10;
18276         
18277         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18278         
18279         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18280         
18281         year -= 1;
18282         for (var i = -1; i < 11; i++) {
18283             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18284                 tag: 'span',
18285                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18286                 html: year
18287             });
18288             
18289             year += 1;
18290         }
18291     },
18292     
18293     showMode: function(dir) 
18294     {
18295         if (dir) {
18296             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18297         }
18298         
18299         Roo.each(this.picker().select('>div',true).elements, function(v){
18300             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18301             v.hide();
18302         });
18303         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18304     },
18305     
18306     place: function()
18307     {
18308         if(this.isInline) {
18309             return;
18310         }
18311         
18312         this.picker().removeClass(['bottom', 'top']);
18313         
18314         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18315             /*
18316              * place to the top of element!
18317              *
18318              */
18319             
18320             this.picker().addClass('top');
18321             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18322             
18323             return;
18324         }
18325         
18326         this.picker().addClass('bottom');
18327         
18328         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18329     },
18330     
18331     parseDate : function(value)
18332     {
18333         if(!value || value instanceof Date){
18334             return value;
18335         }
18336         var v = Date.parseDate(value, this.format);
18337         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18338             v = Date.parseDate(value, 'Y-m-d');
18339         }
18340         if(!v && this.altFormats){
18341             if(!this.altFormatsArray){
18342                 this.altFormatsArray = this.altFormats.split("|");
18343             }
18344             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18345                 v = Date.parseDate(value, this.altFormatsArray[i]);
18346             }
18347         }
18348         return v;
18349     },
18350     
18351     formatDate : function(date, fmt)
18352     {   
18353         return (!date || !(date instanceof Date)) ?
18354         date : date.dateFormat(fmt || this.format);
18355     },
18356     
18357     onFocus : function()
18358     {
18359         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18360         this.show();
18361     },
18362     
18363     onBlur : function()
18364     {
18365         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18366         
18367         var d = this.inputEl().getValue();
18368         
18369         this.setValue(d);
18370                 
18371         this.hide();
18372     },
18373     
18374     show : function()
18375     {
18376         this.picker().show();
18377         this.update();
18378         this.place();
18379         
18380         this.fireEvent('show', this, this.date);
18381     },
18382     
18383     hide : function()
18384     {
18385         if(this.isInline) {
18386             return;
18387         }
18388         this.picker().hide();
18389         this.viewMode = this.startViewMode;
18390         this.showMode();
18391         
18392         this.fireEvent('hide', this, this.date);
18393         
18394     },
18395     
18396     onMousedown: function(e)
18397     {
18398         e.stopPropagation();
18399         e.preventDefault();
18400     },
18401     
18402     keyup: function(e)
18403     {
18404         Roo.bootstrap.DateField.superclass.keyup.call(this);
18405         this.update();
18406     },
18407
18408     setValue: function(v)
18409     {
18410         if(this.fireEvent('beforeselect', this, v) !== false){
18411             var d = new Date(this.parseDate(v) ).clearTime();
18412         
18413             if(isNaN(d.getTime())){
18414                 this.date = this.viewDate = '';
18415                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18416                 return;
18417             }
18418
18419             v = this.formatDate(d);
18420
18421             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18422
18423             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18424
18425             this.update();
18426
18427             this.fireEvent('select', this, this.date);
18428         }
18429     },
18430     
18431     getValue: function()
18432     {
18433         return this.formatDate(this.date);
18434     },
18435     
18436     fireKey: function(e)
18437     {
18438         if (!this.picker().isVisible()){
18439             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18440                 this.show();
18441             }
18442             return;
18443         }
18444         
18445         var dateChanged = false,
18446         dir, day, month,
18447         newDate, newViewDate;
18448         
18449         switch(e.keyCode){
18450             case 27: // escape
18451                 this.hide();
18452                 e.preventDefault();
18453                 break;
18454             case 37: // left
18455             case 39: // right
18456                 if (!this.keyboardNavigation) {
18457                     break;
18458                 }
18459                 dir = e.keyCode == 37 ? -1 : 1;
18460                 
18461                 if (e.ctrlKey){
18462                     newDate = this.moveYear(this.date, dir);
18463                     newViewDate = this.moveYear(this.viewDate, dir);
18464                 } else if (e.shiftKey){
18465                     newDate = this.moveMonth(this.date, dir);
18466                     newViewDate = this.moveMonth(this.viewDate, dir);
18467                 } else {
18468                     newDate = new Date(this.date);
18469                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18470                     newViewDate = new Date(this.viewDate);
18471                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18472                 }
18473                 if (this.dateWithinRange(newDate)){
18474                     this.date = newDate;
18475                     this.viewDate = newViewDate;
18476                     this.setValue(this.formatDate(this.date));
18477 //                    this.update();
18478                     e.preventDefault();
18479                     dateChanged = true;
18480                 }
18481                 break;
18482             case 38: // up
18483             case 40: // down
18484                 if (!this.keyboardNavigation) {
18485                     break;
18486                 }
18487                 dir = e.keyCode == 38 ? -1 : 1;
18488                 if (e.ctrlKey){
18489                     newDate = this.moveYear(this.date, dir);
18490                     newViewDate = this.moveYear(this.viewDate, dir);
18491                 } else if (e.shiftKey){
18492                     newDate = this.moveMonth(this.date, dir);
18493                     newViewDate = this.moveMonth(this.viewDate, dir);
18494                 } else {
18495                     newDate = new Date(this.date);
18496                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18497                     newViewDate = new Date(this.viewDate);
18498                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18499                 }
18500                 if (this.dateWithinRange(newDate)){
18501                     this.date = newDate;
18502                     this.viewDate = newViewDate;
18503                     this.setValue(this.formatDate(this.date));
18504 //                    this.update();
18505                     e.preventDefault();
18506                     dateChanged = true;
18507                 }
18508                 break;
18509             case 13: // enter
18510                 this.setValue(this.formatDate(this.date));
18511                 this.hide();
18512                 e.preventDefault();
18513                 break;
18514             case 9: // tab
18515                 this.setValue(this.formatDate(this.date));
18516                 this.hide();
18517                 break;
18518             case 16: // shift
18519             case 17: // ctrl
18520             case 18: // alt
18521                 break;
18522             default :
18523                 this.hide();
18524                 
18525         }
18526     },
18527     
18528     
18529     onClick: function(e) 
18530     {
18531         e.stopPropagation();
18532         e.preventDefault();
18533         
18534         var target = e.getTarget();
18535         
18536         if(target.nodeName.toLowerCase() === 'i'){
18537             target = Roo.get(target).dom.parentNode;
18538         }
18539         
18540         var nodeName = target.nodeName;
18541         var className = target.className;
18542         var html = target.innerHTML;
18543         //Roo.log(nodeName);
18544         
18545         switch(nodeName.toLowerCase()) {
18546             case 'th':
18547                 switch(className) {
18548                     case 'switch':
18549                         this.showMode(1);
18550                         break;
18551                     case 'prev':
18552                     case 'next':
18553                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18554                         switch(this.viewMode){
18555                                 case 0:
18556                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18557                                         break;
18558                                 case 1:
18559                                 case 2:
18560                                         this.viewDate = this.moveYear(this.viewDate, dir);
18561                                         break;
18562                         }
18563                         this.fill();
18564                         break;
18565                     case 'today':
18566                         var date = new Date();
18567                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18568 //                        this.fill()
18569                         this.setValue(this.formatDate(this.date));
18570                         
18571                         this.hide();
18572                         break;
18573                 }
18574                 break;
18575             case 'span':
18576                 if (className.indexOf('disabled') < 0) {
18577                     this.viewDate.setUTCDate(1);
18578                     if (className.indexOf('month') > -1) {
18579                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18580                     } else {
18581                         var year = parseInt(html, 10) || 0;
18582                         this.viewDate.setUTCFullYear(year);
18583                         
18584                     }
18585                     
18586                     if(this.singleMode){
18587                         this.setValue(this.formatDate(this.viewDate));
18588                         this.hide();
18589                         return;
18590                     }
18591                     
18592                     this.showMode(-1);
18593                     this.fill();
18594                 }
18595                 break;
18596                 
18597             case 'td':
18598                 //Roo.log(className);
18599                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18600                     var day = parseInt(html, 10) || 1;
18601                     var year = this.viewDate.getUTCFullYear(),
18602                         month = this.viewDate.getUTCMonth();
18603
18604                     if (className.indexOf('old') > -1) {
18605                         if(month === 0 ){
18606                             month = 11;
18607                             year -= 1;
18608                         }else{
18609                             month -= 1;
18610                         }
18611                     } else if (className.indexOf('new') > -1) {
18612                         if (month == 11) {
18613                             month = 0;
18614                             year += 1;
18615                         } else {
18616                             month += 1;
18617                         }
18618                     }
18619                     //Roo.log([year,month,day]);
18620                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18621                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18622 //                    this.fill();
18623                     //Roo.log(this.formatDate(this.date));
18624                     this.setValue(this.formatDate(this.date));
18625                     this.hide();
18626                 }
18627                 break;
18628         }
18629     },
18630     
18631     setStartDate: function(startDate)
18632     {
18633         this.startDate = startDate || -Infinity;
18634         if (this.startDate !== -Infinity) {
18635             this.startDate = this.parseDate(this.startDate);
18636         }
18637         this.update();
18638         this.updateNavArrows();
18639     },
18640
18641     setEndDate: function(endDate)
18642     {
18643         this.endDate = endDate || Infinity;
18644         if (this.endDate !== Infinity) {
18645             this.endDate = this.parseDate(this.endDate);
18646         }
18647         this.update();
18648         this.updateNavArrows();
18649     },
18650     
18651     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18652     {
18653         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18654         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18655             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18656         }
18657         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18658             return parseInt(d, 10);
18659         });
18660         this.update();
18661         this.updateNavArrows();
18662     },
18663     
18664     updateNavArrows: function() 
18665     {
18666         if(this.singleMode){
18667             return;
18668         }
18669         
18670         var d = new Date(this.viewDate),
18671         year = d.getUTCFullYear(),
18672         month = d.getUTCMonth();
18673         
18674         Roo.each(this.picker().select('.prev', true).elements, function(v){
18675             v.show();
18676             switch (this.viewMode) {
18677                 case 0:
18678
18679                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18680                         v.hide();
18681                     }
18682                     break;
18683                 case 1:
18684                 case 2:
18685                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18686                         v.hide();
18687                     }
18688                     break;
18689             }
18690         });
18691         
18692         Roo.each(this.picker().select('.next', true).elements, function(v){
18693             v.show();
18694             switch (this.viewMode) {
18695                 case 0:
18696
18697                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18698                         v.hide();
18699                     }
18700                     break;
18701                 case 1:
18702                 case 2:
18703                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18704                         v.hide();
18705                     }
18706                     break;
18707             }
18708         })
18709     },
18710     
18711     moveMonth: function(date, dir)
18712     {
18713         if (!dir) {
18714             return date;
18715         }
18716         var new_date = new Date(date.valueOf()),
18717         day = new_date.getUTCDate(),
18718         month = new_date.getUTCMonth(),
18719         mag = Math.abs(dir),
18720         new_month, test;
18721         dir = dir > 0 ? 1 : -1;
18722         if (mag == 1){
18723             test = dir == -1
18724             // If going back one month, make sure month is not current month
18725             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18726             ? function(){
18727                 return new_date.getUTCMonth() == month;
18728             }
18729             // If going forward one month, make sure month is as expected
18730             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18731             : function(){
18732                 return new_date.getUTCMonth() != new_month;
18733             };
18734             new_month = month + dir;
18735             new_date.setUTCMonth(new_month);
18736             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18737             if (new_month < 0 || new_month > 11) {
18738                 new_month = (new_month + 12) % 12;
18739             }
18740         } else {
18741             // For magnitudes >1, move one month at a time...
18742             for (var i=0; i<mag; i++) {
18743                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18744                 new_date = this.moveMonth(new_date, dir);
18745             }
18746             // ...then reset the day, keeping it in the new month
18747             new_month = new_date.getUTCMonth();
18748             new_date.setUTCDate(day);
18749             test = function(){
18750                 return new_month != new_date.getUTCMonth();
18751             };
18752         }
18753         // Common date-resetting loop -- if date is beyond end of month, make it
18754         // end of month
18755         while (test()){
18756             new_date.setUTCDate(--day);
18757             new_date.setUTCMonth(new_month);
18758         }
18759         return new_date;
18760     },
18761
18762     moveYear: function(date, dir)
18763     {
18764         return this.moveMonth(date, dir*12);
18765     },
18766
18767     dateWithinRange: function(date)
18768     {
18769         return date >= this.startDate && date <= this.endDate;
18770     },
18771
18772     
18773     remove: function() 
18774     {
18775         this.picker().remove();
18776     },
18777     
18778     validateValue : function(value)
18779     {
18780         if(value.length < 1)  {
18781             if(this.allowBlank){
18782                 return true;
18783             }
18784             return false;
18785         }
18786         
18787         if(value.length < this.minLength){
18788             return false;
18789         }
18790         if(value.length > this.maxLength){
18791             return false;
18792         }
18793         if(this.vtype){
18794             var vt = Roo.form.VTypes;
18795             if(!vt[this.vtype](value, this)){
18796                 return false;
18797             }
18798         }
18799         if(typeof this.validator == "function"){
18800             var msg = this.validator(value);
18801             if(msg !== true){
18802                 return false;
18803             }
18804         }
18805         
18806         if(this.regex && !this.regex.test(value)){
18807             return false;
18808         }
18809         
18810         if(typeof(this.parseDate(value)) == 'undefined'){
18811             return false;
18812         }
18813         
18814         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18815             return false;
18816         }      
18817         
18818         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18819             return false;
18820         } 
18821         
18822         
18823         return true;
18824     }
18825    
18826 });
18827
18828 Roo.apply(Roo.bootstrap.DateField,  {
18829     
18830     head : {
18831         tag: 'thead',
18832         cn: [
18833         {
18834             tag: 'tr',
18835             cn: [
18836             {
18837                 tag: 'th',
18838                 cls: 'prev',
18839                 html: '<i class="fa fa-arrow-left"/>'
18840             },
18841             {
18842                 tag: 'th',
18843                 cls: 'switch',
18844                 colspan: '5'
18845             },
18846             {
18847                 tag: 'th',
18848                 cls: 'next',
18849                 html: '<i class="fa fa-arrow-right"/>'
18850             }
18851
18852             ]
18853         }
18854         ]
18855     },
18856     
18857     content : {
18858         tag: 'tbody',
18859         cn: [
18860         {
18861             tag: 'tr',
18862             cn: [
18863             {
18864                 tag: 'td',
18865                 colspan: '7'
18866             }
18867             ]
18868         }
18869         ]
18870     },
18871     
18872     footer : {
18873         tag: 'tfoot',
18874         cn: [
18875         {
18876             tag: 'tr',
18877             cn: [
18878             {
18879                 tag: 'th',
18880                 colspan: '7',
18881                 cls: 'today'
18882             }
18883                     
18884             ]
18885         }
18886         ]
18887     },
18888     
18889     dates:{
18890         en: {
18891             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18892             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18893             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18894             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18895             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18896             today: "Today"
18897         }
18898     },
18899     
18900     modes: [
18901     {
18902         clsName: 'days',
18903         navFnc: 'Month',
18904         navStep: 1
18905     },
18906     {
18907         clsName: 'months',
18908         navFnc: 'FullYear',
18909         navStep: 1
18910     },
18911     {
18912         clsName: 'years',
18913         navFnc: 'FullYear',
18914         navStep: 10
18915     }]
18916 });
18917
18918 Roo.apply(Roo.bootstrap.DateField,  {
18919   
18920     template : {
18921         tag: 'div',
18922         cls: 'datepicker dropdown-menu roo-dynamic',
18923         cn: [
18924         {
18925             tag: 'div',
18926             cls: 'datepicker-days',
18927             cn: [
18928             {
18929                 tag: 'table',
18930                 cls: 'table-condensed',
18931                 cn:[
18932                 Roo.bootstrap.DateField.head,
18933                 {
18934                     tag: 'tbody'
18935                 },
18936                 Roo.bootstrap.DateField.footer
18937                 ]
18938             }
18939             ]
18940         },
18941         {
18942             tag: 'div',
18943             cls: 'datepicker-months',
18944             cn: [
18945             {
18946                 tag: 'table',
18947                 cls: 'table-condensed',
18948                 cn:[
18949                 Roo.bootstrap.DateField.head,
18950                 Roo.bootstrap.DateField.content,
18951                 Roo.bootstrap.DateField.footer
18952                 ]
18953             }
18954             ]
18955         },
18956         {
18957             tag: 'div',
18958             cls: 'datepicker-years',
18959             cn: [
18960             {
18961                 tag: 'table',
18962                 cls: 'table-condensed',
18963                 cn:[
18964                 Roo.bootstrap.DateField.head,
18965                 Roo.bootstrap.DateField.content,
18966                 Roo.bootstrap.DateField.footer
18967                 ]
18968             }
18969             ]
18970         }
18971         ]
18972     }
18973 });
18974
18975  
18976
18977  /*
18978  * - LGPL
18979  *
18980  * TimeField
18981  * 
18982  */
18983
18984 /**
18985  * @class Roo.bootstrap.TimeField
18986  * @extends Roo.bootstrap.Input
18987  * Bootstrap DateField class
18988  * 
18989  * 
18990  * @constructor
18991  * Create a new TimeField
18992  * @param {Object} config The config object
18993  */
18994
18995 Roo.bootstrap.TimeField = function(config){
18996     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18997     this.addEvents({
18998             /**
18999              * @event show
19000              * Fires when this field show.
19001              * @param {Roo.bootstrap.DateField} thisthis
19002              * @param {Mixed} date The date value
19003              */
19004             show : true,
19005             /**
19006              * @event show
19007              * Fires when this field hide.
19008              * @param {Roo.bootstrap.DateField} this
19009              * @param {Mixed} date The date value
19010              */
19011             hide : true,
19012             /**
19013              * @event select
19014              * Fires when select a date.
19015              * @param {Roo.bootstrap.DateField} this
19016              * @param {Mixed} date The date value
19017              */
19018             select : true
19019         });
19020 };
19021
19022 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19023     
19024     /**
19025      * @cfg {String} format
19026      * The default time format string which can be overriden for localization support.  The format must be
19027      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19028      */
19029     format : "H:i",
19030        
19031     onRender: function(ct, position)
19032     {
19033         
19034         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19035                 
19036         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19037         
19038         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19039         
19040         this.pop = this.picker().select('>.datepicker-time',true).first();
19041         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19042         
19043         this.picker().on('mousedown', this.onMousedown, this);
19044         this.picker().on('click', this.onClick, this);
19045         
19046         this.picker().addClass('datepicker-dropdown');
19047     
19048         this.fillTime();
19049         this.update();
19050             
19051         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19052         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19053         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19054         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19055         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19056         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19057
19058     },
19059     
19060     fireKey: function(e){
19061         if (!this.picker().isVisible()){
19062             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19063                 this.show();
19064             }
19065             return;
19066         }
19067
19068         e.preventDefault();
19069         
19070         switch(e.keyCode){
19071             case 27: // escape
19072                 this.hide();
19073                 break;
19074             case 37: // left
19075             case 39: // right
19076                 this.onTogglePeriod();
19077                 break;
19078             case 38: // up
19079                 this.onIncrementMinutes();
19080                 break;
19081             case 40: // down
19082                 this.onDecrementMinutes();
19083                 break;
19084             case 13: // enter
19085             case 9: // tab
19086                 this.setTime();
19087                 break;
19088         }
19089     },
19090     
19091     onClick: function(e) {
19092         e.stopPropagation();
19093         e.preventDefault();
19094     },
19095     
19096     picker : function()
19097     {
19098         return this.el.select('.datepicker', true).first();
19099     },
19100     
19101     fillTime: function()
19102     {    
19103         var time = this.pop.select('tbody', true).first();
19104         
19105         time.dom.innerHTML = '';
19106         
19107         time.createChild({
19108             tag: 'tr',
19109             cn: [
19110                 {
19111                     tag: 'td',
19112                     cn: [
19113                         {
19114                             tag: 'a',
19115                             href: '#',
19116                             cls: 'btn',
19117                             cn: [
19118                                 {
19119                                     tag: 'span',
19120                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19121                                 }
19122                             ]
19123                         } 
19124                     ]
19125                 },
19126                 {
19127                     tag: 'td',
19128                     cls: 'separator'
19129                 },
19130                 {
19131                     tag: 'td',
19132                     cn: [
19133                         {
19134                             tag: 'a',
19135                             href: '#',
19136                             cls: 'btn',
19137                             cn: [
19138                                 {
19139                                     tag: 'span',
19140                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19141                                 }
19142                             ]
19143                         }
19144                     ]
19145                 },
19146                 {
19147                     tag: 'td',
19148                     cls: 'separator'
19149                 }
19150             ]
19151         });
19152         
19153         time.createChild({
19154             tag: 'tr',
19155             cn: [
19156                 {
19157                     tag: 'td',
19158                     cn: [
19159                         {
19160                             tag: 'span',
19161                             cls: 'timepicker-hour',
19162                             html: '00'
19163                         }  
19164                     ]
19165                 },
19166                 {
19167                     tag: 'td',
19168                     cls: 'separator',
19169                     html: ':'
19170                 },
19171                 {
19172                     tag: 'td',
19173                     cn: [
19174                         {
19175                             tag: 'span',
19176                             cls: 'timepicker-minute',
19177                             html: '00'
19178                         }  
19179                     ]
19180                 },
19181                 {
19182                     tag: 'td',
19183                     cls: 'separator'
19184                 },
19185                 {
19186                     tag: 'td',
19187                     cn: [
19188                         {
19189                             tag: 'button',
19190                             type: 'button',
19191                             cls: 'btn btn-primary period',
19192                             html: 'AM'
19193                             
19194                         }
19195                     ]
19196                 }
19197             ]
19198         });
19199         
19200         time.createChild({
19201             tag: 'tr',
19202             cn: [
19203                 {
19204                     tag: 'td',
19205                     cn: [
19206                         {
19207                             tag: 'a',
19208                             href: '#',
19209                             cls: 'btn',
19210                             cn: [
19211                                 {
19212                                     tag: 'span',
19213                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19214                                 }
19215                             ]
19216                         }
19217                     ]
19218                 },
19219                 {
19220                     tag: 'td',
19221                     cls: 'separator'
19222                 },
19223                 {
19224                     tag: 'td',
19225                     cn: [
19226                         {
19227                             tag: 'a',
19228                             href: '#',
19229                             cls: 'btn',
19230                             cn: [
19231                                 {
19232                                     tag: 'span',
19233                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19234                                 }
19235                             ]
19236                         }
19237                     ]
19238                 },
19239                 {
19240                     tag: 'td',
19241                     cls: 'separator'
19242                 }
19243             ]
19244         });
19245         
19246     },
19247     
19248     update: function()
19249     {
19250         
19251         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19252         
19253         this.fill();
19254     },
19255     
19256     fill: function() 
19257     {
19258         var hours = this.time.getHours();
19259         var minutes = this.time.getMinutes();
19260         var period = 'AM';
19261         
19262         if(hours > 11){
19263             period = 'PM';
19264         }
19265         
19266         if(hours == 0){
19267             hours = 12;
19268         }
19269         
19270         
19271         if(hours > 12){
19272             hours = hours - 12;
19273         }
19274         
19275         if(hours < 10){
19276             hours = '0' + hours;
19277         }
19278         
19279         if(minutes < 10){
19280             minutes = '0' + minutes;
19281         }
19282         
19283         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19284         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19285         this.pop.select('button', true).first().dom.innerHTML = period;
19286         
19287     },
19288     
19289     place: function()
19290     {   
19291         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19292         
19293         var cls = ['bottom'];
19294         
19295         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19296             cls.pop();
19297             cls.push('top');
19298         }
19299         
19300         cls.push('right');
19301         
19302         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19303             cls.pop();
19304             cls.push('left');
19305         }
19306         
19307         this.picker().addClass(cls.join('-'));
19308         
19309         var _this = this;
19310         
19311         Roo.each(cls, function(c){
19312             if(c == 'bottom'){
19313                 _this.picker().setTop(_this.inputEl().getHeight());
19314                 return;
19315             }
19316             if(c == 'top'){
19317                 _this.picker().setTop(0 - _this.picker().getHeight());
19318                 return;
19319             }
19320             
19321             if(c == 'left'){
19322                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19323                 return;
19324             }
19325             if(c == 'right'){
19326                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19327                 return;
19328             }
19329         });
19330         
19331     },
19332   
19333     onFocus : function()
19334     {
19335         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19336         this.show();
19337     },
19338     
19339     onBlur : function()
19340     {
19341         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19342         this.hide();
19343     },
19344     
19345     show : function()
19346     {
19347         this.picker().show();
19348         this.pop.show();
19349         this.update();
19350         this.place();
19351         
19352         this.fireEvent('show', this, this.date);
19353     },
19354     
19355     hide : function()
19356     {
19357         this.picker().hide();
19358         this.pop.hide();
19359         
19360         this.fireEvent('hide', this, this.date);
19361     },
19362     
19363     setTime : function()
19364     {
19365         this.hide();
19366         this.setValue(this.time.format(this.format));
19367         
19368         this.fireEvent('select', this, this.date);
19369         
19370         
19371     },
19372     
19373     onMousedown: function(e){
19374         e.stopPropagation();
19375         e.preventDefault();
19376     },
19377     
19378     onIncrementHours: function()
19379     {
19380         Roo.log('onIncrementHours');
19381         this.time = this.time.add(Date.HOUR, 1);
19382         this.update();
19383         
19384     },
19385     
19386     onDecrementHours: function()
19387     {
19388         Roo.log('onDecrementHours');
19389         this.time = this.time.add(Date.HOUR, -1);
19390         this.update();
19391     },
19392     
19393     onIncrementMinutes: function()
19394     {
19395         Roo.log('onIncrementMinutes');
19396         this.time = this.time.add(Date.MINUTE, 1);
19397         this.update();
19398     },
19399     
19400     onDecrementMinutes: function()
19401     {
19402         Roo.log('onDecrementMinutes');
19403         this.time = this.time.add(Date.MINUTE, -1);
19404         this.update();
19405     },
19406     
19407     onTogglePeriod: function()
19408     {
19409         Roo.log('onTogglePeriod');
19410         this.time = this.time.add(Date.HOUR, 12);
19411         this.update();
19412     }
19413     
19414    
19415 });
19416
19417 Roo.apply(Roo.bootstrap.TimeField,  {
19418     
19419     content : {
19420         tag: 'tbody',
19421         cn: [
19422             {
19423                 tag: 'tr',
19424                 cn: [
19425                 {
19426                     tag: 'td',
19427                     colspan: '7'
19428                 }
19429                 ]
19430             }
19431         ]
19432     },
19433     
19434     footer : {
19435         tag: 'tfoot',
19436         cn: [
19437             {
19438                 tag: 'tr',
19439                 cn: [
19440                 {
19441                     tag: 'th',
19442                     colspan: '7',
19443                     cls: '',
19444                     cn: [
19445                         {
19446                             tag: 'button',
19447                             cls: 'btn btn-info ok',
19448                             html: 'OK'
19449                         }
19450                     ]
19451                 }
19452
19453                 ]
19454             }
19455         ]
19456     }
19457 });
19458
19459 Roo.apply(Roo.bootstrap.TimeField,  {
19460   
19461     template : {
19462         tag: 'div',
19463         cls: 'datepicker dropdown-menu',
19464         cn: [
19465             {
19466                 tag: 'div',
19467                 cls: 'datepicker-time',
19468                 cn: [
19469                 {
19470                     tag: 'table',
19471                     cls: 'table-condensed',
19472                     cn:[
19473                     Roo.bootstrap.TimeField.content,
19474                     Roo.bootstrap.TimeField.footer
19475                     ]
19476                 }
19477                 ]
19478             }
19479         ]
19480     }
19481 });
19482
19483  
19484
19485  /*
19486  * - LGPL
19487  *
19488  * MonthField
19489  * 
19490  */
19491
19492 /**
19493  * @class Roo.bootstrap.MonthField
19494  * @extends Roo.bootstrap.Input
19495  * Bootstrap MonthField class
19496  * 
19497  * @cfg {String} language default en
19498  * 
19499  * @constructor
19500  * Create a new MonthField
19501  * @param {Object} config The config object
19502  */
19503
19504 Roo.bootstrap.MonthField = function(config){
19505     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19506     
19507     this.addEvents({
19508         /**
19509          * @event show
19510          * Fires when this field show.
19511          * @param {Roo.bootstrap.MonthField} this
19512          * @param {Mixed} date The date value
19513          */
19514         show : true,
19515         /**
19516          * @event show
19517          * Fires when this field hide.
19518          * @param {Roo.bootstrap.MonthField} this
19519          * @param {Mixed} date The date value
19520          */
19521         hide : true,
19522         /**
19523          * @event select
19524          * Fires when select a date.
19525          * @param {Roo.bootstrap.MonthField} this
19526          * @param {String} oldvalue The old value
19527          * @param {String} newvalue The new value
19528          */
19529         select : true
19530     });
19531 };
19532
19533 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19534     
19535     onRender: function(ct, position)
19536     {
19537         
19538         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19539         
19540         this.language = this.language || 'en';
19541         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19542         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19543         
19544         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19545         this.isInline = false;
19546         this.isInput = true;
19547         this.component = this.el.select('.add-on', true).first() || false;
19548         this.component = (this.component && this.component.length === 0) ? false : this.component;
19549         this.hasInput = this.component && this.inputEL().length;
19550         
19551         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19552         
19553         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19554         
19555         this.picker().on('mousedown', this.onMousedown, this);
19556         this.picker().on('click', this.onClick, this);
19557         
19558         this.picker().addClass('datepicker-dropdown');
19559         
19560         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19561             v.setStyle('width', '189px');
19562         });
19563         
19564         this.fillMonths();
19565         
19566         this.update();
19567         
19568         if(this.isInline) {
19569             this.show();
19570         }
19571         
19572     },
19573     
19574     setValue: function(v, suppressEvent)
19575     {   
19576         var o = this.getValue();
19577         
19578         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19579         
19580         this.update();
19581
19582         if(suppressEvent !== true){
19583             this.fireEvent('select', this, o, v);
19584         }
19585         
19586     },
19587     
19588     getValue: function()
19589     {
19590         return this.value;
19591     },
19592     
19593     onClick: function(e) 
19594     {
19595         e.stopPropagation();
19596         e.preventDefault();
19597         
19598         var target = e.getTarget();
19599         
19600         if(target.nodeName.toLowerCase() === 'i'){
19601             target = Roo.get(target).dom.parentNode;
19602         }
19603         
19604         var nodeName = target.nodeName;
19605         var className = target.className;
19606         var html = target.innerHTML;
19607         
19608         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19609             return;
19610         }
19611         
19612         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19613         
19614         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19615         
19616         this.hide();
19617                         
19618     },
19619     
19620     picker : function()
19621     {
19622         return this.pickerEl;
19623     },
19624     
19625     fillMonths: function()
19626     {    
19627         var i = 0;
19628         var months = this.picker().select('>.datepicker-months td', true).first();
19629         
19630         months.dom.innerHTML = '';
19631         
19632         while (i < 12) {
19633             var month = {
19634                 tag: 'span',
19635                 cls: 'month',
19636                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19637             };
19638             
19639             months.createChild(month);
19640         }
19641         
19642     },
19643     
19644     update: function()
19645     {
19646         var _this = this;
19647         
19648         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19649             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19650         }
19651         
19652         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19653             e.removeClass('active');
19654             
19655             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19656                 e.addClass('active');
19657             }
19658         })
19659     },
19660     
19661     place: function()
19662     {
19663         if(this.isInline) {
19664             return;
19665         }
19666         
19667         this.picker().removeClass(['bottom', 'top']);
19668         
19669         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19670             /*
19671              * place to the top of element!
19672              *
19673              */
19674             
19675             this.picker().addClass('top');
19676             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19677             
19678             return;
19679         }
19680         
19681         this.picker().addClass('bottom');
19682         
19683         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19684     },
19685     
19686     onFocus : function()
19687     {
19688         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19689         this.show();
19690     },
19691     
19692     onBlur : function()
19693     {
19694         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19695         
19696         var d = this.inputEl().getValue();
19697         
19698         this.setValue(d);
19699                 
19700         this.hide();
19701     },
19702     
19703     show : function()
19704     {
19705         this.picker().show();
19706         this.picker().select('>.datepicker-months', true).first().show();
19707         this.update();
19708         this.place();
19709         
19710         this.fireEvent('show', this, this.date);
19711     },
19712     
19713     hide : function()
19714     {
19715         if(this.isInline) {
19716             return;
19717         }
19718         this.picker().hide();
19719         this.fireEvent('hide', this, this.date);
19720         
19721     },
19722     
19723     onMousedown: function(e)
19724     {
19725         e.stopPropagation();
19726         e.preventDefault();
19727     },
19728     
19729     keyup: function(e)
19730     {
19731         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19732         this.update();
19733     },
19734
19735     fireKey: function(e)
19736     {
19737         if (!this.picker().isVisible()){
19738             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19739                 this.show();
19740             }
19741             return;
19742         }
19743         
19744         var dir;
19745         
19746         switch(e.keyCode){
19747             case 27: // escape
19748                 this.hide();
19749                 e.preventDefault();
19750                 break;
19751             case 37: // left
19752             case 39: // right
19753                 dir = e.keyCode == 37 ? -1 : 1;
19754                 
19755                 this.vIndex = this.vIndex + dir;
19756                 
19757                 if(this.vIndex < 0){
19758                     this.vIndex = 0;
19759                 }
19760                 
19761                 if(this.vIndex > 11){
19762                     this.vIndex = 11;
19763                 }
19764                 
19765                 if(isNaN(this.vIndex)){
19766                     this.vIndex = 0;
19767                 }
19768                 
19769                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19770                 
19771                 break;
19772             case 38: // up
19773             case 40: // down
19774                 
19775                 dir = e.keyCode == 38 ? -1 : 1;
19776                 
19777                 this.vIndex = this.vIndex + dir * 4;
19778                 
19779                 if(this.vIndex < 0){
19780                     this.vIndex = 0;
19781                 }
19782                 
19783                 if(this.vIndex > 11){
19784                     this.vIndex = 11;
19785                 }
19786                 
19787                 if(isNaN(this.vIndex)){
19788                     this.vIndex = 0;
19789                 }
19790                 
19791                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19792                 break;
19793                 
19794             case 13: // enter
19795                 
19796                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19797                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19798                 }
19799                 
19800                 this.hide();
19801                 e.preventDefault();
19802                 break;
19803             case 9: // tab
19804                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19805                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19806                 }
19807                 this.hide();
19808                 break;
19809             case 16: // shift
19810             case 17: // ctrl
19811             case 18: // alt
19812                 break;
19813             default :
19814                 this.hide();
19815                 
19816         }
19817     },
19818     
19819     remove: function() 
19820     {
19821         this.picker().remove();
19822     }
19823    
19824 });
19825
19826 Roo.apply(Roo.bootstrap.MonthField,  {
19827     
19828     content : {
19829         tag: 'tbody',
19830         cn: [
19831         {
19832             tag: 'tr',
19833             cn: [
19834             {
19835                 tag: 'td',
19836                 colspan: '7'
19837             }
19838             ]
19839         }
19840         ]
19841     },
19842     
19843     dates:{
19844         en: {
19845             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19846             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19847         }
19848     }
19849 });
19850
19851 Roo.apply(Roo.bootstrap.MonthField,  {
19852   
19853     template : {
19854         tag: 'div',
19855         cls: 'datepicker dropdown-menu roo-dynamic',
19856         cn: [
19857             {
19858                 tag: 'div',
19859                 cls: 'datepicker-months',
19860                 cn: [
19861                 {
19862                     tag: 'table',
19863                     cls: 'table-condensed',
19864                     cn:[
19865                         Roo.bootstrap.DateField.content
19866                     ]
19867                 }
19868                 ]
19869             }
19870         ]
19871     }
19872 });
19873
19874  
19875
19876  
19877  /*
19878  * - LGPL
19879  *
19880  * CheckBox
19881  * 
19882  */
19883
19884 /**
19885  * @class Roo.bootstrap.CheckBox
19886  * @extends Roo.bootstrap.Input
19887  * Bootstrap CheckBox class
19888  * 
19889  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19890  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19891  * @cfg {String} boxLabel The text that appears beside the checkbox
19892  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19893  * @cfg {Boolean} checked initnal the element
19894  * @cfg {Boolean} inline inline the element (default false)
19895  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19896  * 
19897  * @constructor
19898  * Create a new CheckBox
19899  * @param {Object} config The config object
19900  */
19901
19902 Roo.bootstrap.CheckBox = function(config){
19903     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19904    
19905     this.addEvents({
19906         /**
19907         * @event check
19908         * Fires when the element is checked or unchecked.
19909         * @param {Roo.bootstrap.CheckBox} this This input
19910         * @param {Boolean} checked The new checked value
19911         */
19912        check : true
19913     });
19914     
19915 };
19916
19917 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19918   
19919     inputType: 'checkbox',
19920     inputValue: 1,
19921     valueOff: 0,
19922     boxLabel: false,
19923     checked: false,
19924     weight : false,
19925     inline: false,
19926     
19927     getAutoCreate : function()
19928     {
19929         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19930         
19931         var id = Roo.id();
19932         
19933         var cfg = {};
19934         
19935         cfg.cls = 'form-group ' + this.inputType; //input-group
19936         
19937         if(this.inline){
19938             cfg.cls += ' ' + this.inputType + '-inline';
19939         }
19940         
19941         var input =  {
19942             tag: 'input',
19943             id : id,
19944             type : this.inputType,
19945             value : this.inputValue,
19946             cls : 'roo-' + this.inputType, //'form-box',
19947             placeholder : this.placeholder || ''
19948             
19949         };
19950         
19951         if(this.inputType != 'radio'){
19952             var hidden =  {
19953                 tag: 'input',
19954                 type : 'hidden',
19955                 cls : 'roo-hidden-value',
19956                 value : this.checked ? this.valueOff : this.inputValue
19957             };
19958         }
19959         
19960             
19961         if (this.weight) { // Validity check?
19962             cfg.cls += " " + this.inputType + "-" + this.weight;
19963         }
19964         
19965         if (this.disabled) {
19966             input.disabled=true;
19967         }
19968         
19969         if(this.checked){
19970             input.checked = this.checked;
19971             
19972         }
19973         
19974         
19975         if (this.name) {
19976             
19977             input.name = this.name;
19978             
19979             if(this.inputType != 'radio'){
19980                 hidden.name = this.name;
19981                 input.name = '_hidden_' + this.name;
19982             }
19983         }
19984         
19985         if (this.size) {
19986             input.cls += ' input-' + this.size;
19987         }
19988         
19989         var settings=this;
19990         
19991         ['xs','sm','md','lg'].map(function(size){
19992             if (settings[size]) {
19993                 cfg.cls += ' col-' + size + '-' + settings[size];
19994             }
19995         });
19996         
19997         var inputblock = input;
19998          
19999         if (this.before || this.after) {
20000             
20001             inputblock = {
20002                 cls : 'input-group',
20003                 cn :  [] 
20004             };
20005             
20006             if (this.before) {
20007                 inputblock.cn.push({
20008                     tag :'span',
20009                     cls : 'input-group-addon',
20010                     html : this.before
20011                 });
20012             }
20013             
20014             inputblock.cn.push(input);
20015             
20016             if(this.inputType != 'radio'){
20017                 inputblock.cn.push(hidden);
20018             }
20019             
20020             if (this.after) {
20021                 inputblock.cn.push({
20022                     tag :'span',
20023                     cls : 'input-group-addon',
20024                     html : this.after
20025                 });
20026             }
20027             
20028         }
20029         
20030         if (align ==='left' && this.fieldLabel.length) {
20031 //                Roo.log("left and has label");
20032             cfg.cn = [
20033                 {
20034                     tag: 'label',
20035                     'for' :  id,
20036                     cls : 'control-label',
20037                     html : this.fieldLabel
20038
20039                 },
20040                 {
20041                     cls : "", 
20042                     cn: [
20043                         inputblock
20044                     ]
20045                 }
20046             ];
20047             
20048             if(this.labelWidth > 12){
20049                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20050             }
20051             
20052             if(this.labelWidth < 13 && this.labelmd == 0){
20053                 this.labelmd = this.labelWidth;
20054             }
20055             
20056             if(this.labellg > 0){
20057                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20058                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20059             }
20060             
20061             if(this.labelmd > 0){
20062                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20063                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20064             }
20065             
20066             if(this.labelsm > 0){
20067                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20068                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20069             }
20070             
20071             if(this.labelxs > 0){
20072                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20073                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20074             }
20075             
20076         } else if ( this.fieldLabel.length) {
20077 //                Roo.log(" label");
20078                 cfg.cn = [
20079                    
20080                     {
20081                         tag: this.boxLabel ? 'span' : 'label',
20082                         'for': id,
20083                         cls: 'control-label box-input-label',
20084                         //cls : 'input-group-addon',
20085                         html : this.fieldLabel
20086                         
20087                     },
20088                     
20089                     inputblock
20090                     
20091                 ];
20092
20093         } else {
20094             
20095 //                Roo.log(" no label && no align");
20096                 cfg.cn = [  inputblock ] ;
20097                 
20098                 
20099         }
20100         
20101         if(this.boxLabel){
20102              var boxLabelCfg = {
20103                 tag: 'label',
20104                 //'for': id, // box label is handled by onclick - so no for...
20105                 cls: 'box-label',
20106                 html: this.boxLabel
20107             };
20108             
20109             if(this.tooltip){
20110                 boxLabelCfg.tooltip = this.tooltip;
20111             }
20112              
20113             cfg.cn.push(boxLabelCfg);
20114         }
20115         
20116         if(this.inputType != 'radio'){
20117             cfg.cn.push(hidden);
20118         }
20119         
20120         return cfg;
20121         
20122     },
20123     
20124     /**
20125      * return the real input element.
20126      */
20127     inputEl: function ()
20128     {
20129         return this.el.select('input.roo-' + this.inputType,true).first();
20130     },
20131     hiddenEl: function ()
20132     {
20133         return this.el.select('input.roo-hidden-value',true).first();
20134     },
20135     
20136     labelEl: function()
20137     {
20138         return this.el.select('label.control-label',true).first();
20139     },
20140     /* depricated... */
20141     
20142     label: function()
20143     {
20144         return this.labelEl();
20145     },
20146     
20147     boxLabelEl: function()
20148     {
20149         return this.el.select('label.box-label',true).first();
20150     },
20151     
20152     initEvents : function()
20153     {
20154 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20155         
20156         this.inputEl().on('click', this.onClick,  this);
20157         
20158         if (this.boxLabel) { 
20159             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20160         }
20161         
20162         this.startValue = this.getValue();
20163         
20164         if(this.groupId){
20165             Roo.bootstrap.CheckBox.register(this);
20166         }
20167     },
20168     
20169     onClick : function()
20170     {   
20171         this.setChecked(!this.checked);
20172     },
20173     
20174     setChecked : function(state,suppressEvent)
20175     {
20176         this.startValue = this.getValue();
20177
20178         if(this.inputType == 'radio'){
20179             
20180             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20181                 e.dom.checked = false;
20182             });
20183             
20184             this.inputEl().dom.checked = true;
20185             
20186             this.inputEl().dom.value = this.inputValue;
20187             
20188             if(suppressEvent !== true){
20189                 this.fireEvent('check', this, true);
20190             }
20191             
20192             this.validate();
20193             
20194             return;
20195         }
20196         
20197         this.checked = state;
20198         
20199         this.inputEl().dom.checked = state;
20200         
20201         
20202         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20203         
20204         if(suppressEvent !== true){
20205             this.fireEvent('check', this, state);
20206         }
20207         
20208         this.validate();
20209     },
20210     
20211     getValue : function()
20212     {
20213         if(this.inputType == 'radio'){
20214             return this.getGroupValue();
20215         }
20216         
20217         return this.hiddenEl().dom.value;
20218         
20219     },
20220     
20221     getGroupValue : function()
20222     {
20223         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20224             return '';
20225         }
20226         
20227         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20228     },
20229     
20230     setValue : function(v,suppressEvent)
20231     {
20232         if(this.inputType == 'radio'){
20233             this.setGroupValue(v, suppressEvent);
20234             return;
20235         }
20236         
20237         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20238         
20239         this.validate();
20240     },
20241     
20242     setGroupValue : function(v, suppressEvent)
20243     {
20244         this.startValue = this.getValue();
20245         
20246         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20247             e.dom.checked = false;
20248             
20249             if(e.dom.value == v){
20250                 e.dom.checked = true;
20251             }
20252         });
20253         
20254         if(suppressEvent !== true){
20255             this.fireEvent('check', this, true);
20256         }
20257
20258         this.validate();
20259         
20260         return;
20261     },
20262     
20263     validate : function()
20264     {
20265         if(
20266                 this.disabled || 
20267                 (this.inputType == 'radio' && this.validateRadio()) ||
20268                 (this.inputType == 'checkbox' && this.validateCheckbox())
20269         ){
20270             this.markValid();
20271             return true;
20272         }
20273         
20274         this.markInvalid();
20275         return false;
20276     },
20277     
20278     validateRadio : function()
20279     {
20280         if(this.allowBlank){
20281             return true;
20282         }
20283         
20284         var valid = false;
20285         
20286         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20287             if(!e.dom.checked){
20288                 return;
20289             }
20290             
20291             valid = true;
20292             
20293             return false;
20294         });
20295         
20296         return valid;
20297     },
20298     
20299     validateCheckbox : function()
20300     {
20301         if(!this.groupId){
20302             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20303         }
20304         
20305         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20306         
20307         if(!group){
20308             return false;
20309         }
20310         
20311         var r = false;
20312         
20313         for(var i in group){
20314             if(r){
20315                 break;
20316             }
20317             
20318             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20319         }
20320         
20321         return r;
20322     },
20323     
20324     /**
20325      * Mark this field as valid
20326      */
20327     markValid : function()
20328     {
20329         var _this = this;
20330         
20331         this.fireEvent('valid', this);
20332         
20333         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20334         
20335         if(this.groupId){
20336             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20337         }
20338         
20339         if(label){
20340             label.markValid();
20341         }
20342
20343         if(this.inputType == 'radio'){
20344             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20345                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20346                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20347             });
20348             
20349             return;
20350         }
20351         
20352         if(!this.groupId){
20353             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20354             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20355             return;
20356         }
20357         
20358         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20359             
20360         if(!group){
20361             return;
20362         }
20363         
20364         for(var i in group){
20365             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20366             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20367         }
20368     },
20369     
20370      /**
20371      * Mark this field as invalid
20372      * @param {String} msg The validation message
20373      */
20374     markInvalid : function(msg)
20375     {
20376         if(this.allowBlank){
20377             return;
20378         }
20379         
20380         var _this = this;
20381         
20382         this.fireEvent('invalid', this, msg);
20383         
20384         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20385         
20386         if(this.groupId){
20387             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20388         }
20389         
20390         if(label){
20391             label.markInvalid();
20392         }
20393             
20394         if(this.inputType == 'radio'){
20395             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20396                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20397                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20398             });
20399             
20400             return;
20401         }
20402         
20403         if(!this.groupId){
20404             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20405             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20406             return;
20407         }
20408         
20409         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20410         
20411         if(!group){
20412             return;
20413         }
20414         
20415         for(var i in group){
20416             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20417             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20418         }
20419         
20420     },
20421     
20422     clearInvalid : function()
20423     {
20424         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20425         
20426         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20427         
20428         if (label) {
20429             label.iconEl.removeClass(label.validClass);
20430             label.iconEl.removeClass(label.invalidClass);
20431         }
20432     },
20433     
20434     disable : function()
20435     {
20436         if(this.inputType != 'radio'){
20437             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20438             return;
20439         }
20440         
20441         var _this = this;
20442         
20443         if(this.rendered){
20444             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20445                 _this.getActionEl().addClass(this.disabledClass);
20446                 e.dom.disabled = true;
20447             });
20448         }
20449         
20450         this.disabled = true;
20451         this.fireEvent("disable", this);
20452         return this;
20453     },
20454
20455     enable : function()
20456     {
20457         if(this.inputType != 'radio'){
20458             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20459             return;
20460         }
20461         
20462         var _this = this;
20463         
20464         if(this.rendered){
20465             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20466                 _this.getActionEl().removeClass(this.disabledClass);
20467                 e.dom.disabled = false;
20468             });
20469         }
20470         
20471         this.disabled = false;
20472         this.fireEvent("enable", this);
20473         return this;
20474     }
20475
20476 });
20477
20478 Roo.apply(Roo.bootstrap.CheckBox, {
20479     
20480     groups: {},
20481     
20482      /**
20483     * register a CheckBox Group
20484     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20485     */
20486     register : function(checkbox)
20487     {
20488         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20489             this.groups[checkbox.groupId] = {};
20490         }
20491         
20492         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20493             return;
20494         }
20495         
20496         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20497         
20498     },
20499     /**
20500     * fetch a CheckBox Group based on the group ID
20501     * @param {string} the group ID
20502     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20503     */
20504     get: function(groupId) {
20505         if (typeof(this.groups[groupId]) == 'undefined') {
20506             return false;
20507         }
20508         
20509         return this.groups[groupId] ;
20510     }
20511     
20512     
20513 });
20514 /*
20515  * - LGPL
20516  *
20517  * RadioItem
20518  * 
20519  */
20520
20521 /**
20522  * @class Roo.bootstrap.Radio
20523  * @extends Roo.bootstrap.Component
20524  * Bootstrap Radio class
20525  * @cfg {String} boxLabel - the label associated
20526  * @cfg {String} value - the value of radio
20527  * 
20528  * @constructor
20529  * Create a new Radio
20530  * @param {Object} config The config object
20531  */
20532 Roo.bootstrap.Radio = function(config){
20533     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20534     
20535 };
20536
20537 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20538     
20539     boxLabel : '',
20540     
20541     value : '',
20542     
20543     getAutoCreate : function()
20544     {
20545         var cfg = {
20546             tag : 'div',
20547             cls : 'form-group radio',
20548             cn : [
20549                 {
20550                     tag : 'label',
20551                     cls : 'box-label',
20552                     html : this.boxLabel
20553                 }
20554             ]
20555         };
20556         
20557         return cfg;
20558     },
20559     
20560     initEvents : function() 
20561     {
20562         this.parent().register(this);
20563         
20564         this.el.on('click', this.onClick, this);
20565         
20566     },
20567     
20568     onClick : function()
20569     {
20570         this.setChecked(true);
20571     },
20572     
20573     setChecked : function(state, suppressEvent)
20574     {
20575         this.parent().setValue(this.value, suppressEvent);
20576         
20577     }
20578     
20579 });
20580  
20581
20582  //<script type="text/javascript">
20583
20584 /*
20585  * Based  Ext JS Library 1.1.1
20586  * Copyright(c) 2006-2007, Ext JS, LLC.
20587  * LGPL
20588  *
20589  */
20590  
20591 /**
20592  * @class Roo.HtmlEditorCore
20593  * @extends Roo.Component
20594  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20595  *
20596  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20597  */
20598
20599 Roo.HtmlEditorCore = function(config){
20600     
20601     
20602     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20603     
20604     
20605     this.addEvents({
20606         /**
20607          * @event initialize
20608          * Fires when the editor is fully initialized (including the iframe)
20609          * @param {Roo.HtmlEditorCore} this
20610          */
20611         initialize: true,
20612         /**
20613          * @event activate
20614          * Fires when the editor is first receives the focus. Any insertion must wait
20615          * until after this event.
20616          * @param {Roo.HtmlEditorCore} this
20617          */
20618         activate: true,
20619          /**
20620          * @event beforesync
20621          * Fires before the textarea is updated with content from the editor iframe. Return false
20622          * to cancel the sync.
20623          * @param {Roo.HtmlEditorCore} this
20624          * @param {String} html
20625          */
20626         beforesync: true,
20627          /**
20628          * @event beforepush
20629          * Fires before the iframe editor is updated with content from the textarea. Return false
20630          * to cancel the push.
20631          * @param {Roo.HtmlEditorCore} this
20632          * @param {String} html
20633          */
20634         beforepush: true,
20635          /**
20636          * @event sync
20637          * Fires when the textarea is updated with content from the editor iframe.
20638          * @param {Roo.HtmlEditorCore} this
20639          * @param {String} html
20640          */
20641         sync: true,
20642          /**
20643          * @event push
20644          * Fires when the iframe editor is updated with content from the textarea.
20645          * @param {Roo.HtmlEditorCore} this
20646          * @param {String} html
20647          */
20648         push: true,
20649         
20650         /**
20651          * @event editorevent
20652          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20653          * @param {Roo.HtmlEditorCore} this
20654          */
20655         editorevent: true
20656         
20657     });
20658     
20659     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20660     
20661     // defaults : white / black...
20662     this.applyBlacklists();
20663     
20664     
20665     
20666 };
20667
20668
20669 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20670
20671
20672      /**
20673      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20674      */
20675     
20676     owner : false,
20677     
20678      /**
20679      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20680      *                        Roo.resizable.
20681      */
20682     resizable : false,
20683      /**
20684      * @cfg {Number} height (in pixels)
20685      */   
20686     height: 300,
20687    /**
20688      * @cfg {Number} width (in pixels)
20689      */   
20690     width: 500,
20691     
20692     /**
20693      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20694      * 
20695      */
20696     stylesheets: false,
20697     
20698     // id of frame..
20699     frameId: false,
20700     
20701     // private properties
20702     validationEvent : false,
20703     deferHeight: true,
20704     initialized : false,
20705     activated : false,
20706     sourceEditMode : false,
20707     onFocus : Roo.emptyFn,
20708     iframePad:3,
20709     hideMode:'offsets',
20710     
20711     clearUp: true,
20712     
20713     // blacklist + whitelisted elements..
20714     black: false,
20715     white: false,
20716      
20717     
20718
20719     /**
20720      * Protected method that will not generally be called directly. It
20721      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20722      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20723      */
20724     getDocMarkup : function(){
20725         // body styles..
20726         var st = '';
20727         
20728         // inherit styels from page...?? 
20729         if (this.stylesheets === false) {
20730             
20731             Roo.get(document.head).select('style').each(function(node) {
20732                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20733             });
20734             
20735             Roo.get(document.head).select('link').each(function(node) { 
20736                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20737             });
20738             
20739         } else if (!this.stylesheets.length) {
20740                 // simple..
20741                 st = '<style type="text/css">' +
20742                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20743                    '</style>';
20744         } else { 
20745             
20746         }
20747         
20748         st +=  '<style type="text/css">' +
20749             'IMG { cursor: pointer } ' +
20750         '</style>';
20751
20752         
20753         return '<html><head>' + st  +
20754             //<style type="text/css">' +
20755             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20756             //'</style>' +
20757             ' </head><body class="roo-htmleditor-body"></body></html>';
20758     },
20759
20760     // private
20761     onRender : function(ct, position)
20762     {
20763         var _t = this;
20764         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20765         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20766         
20767         
20768         this.el.dom.style.border = '0 none';
20769         this.el.dom.setAttribute('tabIndex', -1);
20770         this.el.addClass('x-hidden hide');
20771         
20772         
20773         
20774         if(Roo.isIE){ // fix IE 1px bogus margin
20775             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20776         }
20777        
20778         
20779         this.frameId = Roo.id();
20780         
20781          
20782         
20783         var iframe = this.owner.wrap.createChild({
20784             tag: 'iframe',
20785             cls: 'form-control', // bootstrap..
20786             id: this.frameId,
20787             name: this.frameId,
20788             frameBorder : 'no',
20789             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20790         }, this.el
20791         );
20792         
20793         
20794         this.iframe = iframe.dom;
20795
20796          this.assignDocWin();
20797         
20798         this.doc.designMode = 'on';
20799        
20800         this.doc.open();
20801         this.doc.write(this.getDocMarkup());
20802         this.doc.close();
20803
20804         
20805         var task = { // must defer to wait for browser to be ready
20806             run : function(){
20807                 //console.log("run task?" + this.doc.readyState);
20808                 this.assignDocWin();
20809                 if(this.doc.body || this.doc.readyState == 'complete'){
20810                     try {
20811                         this.doc.designMode="on";
20812                     } catch (e) {
20813                         return;
20814                     }
20815                     Roo.TaskMgr.stop(task);
20816                     this.initEditor.defer(10, this);
20817                 }
20818             },
20819             interval : 10,
20820             duration: 10000,
20821             scope: this
20822         };
20823         Roo.TaskMgr.start(task);
20824
20825     },
20826
20827     // private
20828     onResize : function(w, h)
20829     {
20830          Roo.log('resize: ' +w + ',' + h );
20831         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20832         if(!this.iframe){
20833             return;
20834         }
20835         if(typeof w == 'number'){
20836             
20837             this.iframe.style.width = w + 'px';
20838         }
20839         if(typeof h == 'number'){
20840             
20841             this.iframe.style.height = h + 'px';
20842             if(this.doc){
20843                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20844             }
20845         }
20846         
20847     },
20848
20849     /**
20850      * Toggles the editor between standard and source edit mode.
20851      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20852      */
20853     toggleSourceEdit : function(sourceEditMode){
20854         
20855         this.sourceEditMode = sourceEditMode === true;
20856         
20857         if(this.sourceEditMode){
20858  
20859             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20860             
20861         }else{
20862             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20863             //this.iframe.className = '';
20864             this.deferFocus();
20865         }
20866         //this.setSize(this.owner.wrap.getSize());
20867         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20868     },
20869
20870     
20871   
20872
20873     /**
20874      * Protected method that will not generally be called directly. If you need/want
20875      * custom HTML cleanup, this is the method you should override.
20876      * @param {String} html The HTML to be cleaned
20877      * return {String} The cleaned HTML
20878      */
20879     cleanHtml : function(html){
20880         html = String(html);
20881         if(html.length > 5){
20882             if(Roo.isSafari){ // strip safari nonsense
20883                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20884             }
20885         }
20886         if(html == '&nbsp;'){
20887             html = '';
20888         }
20889         return html;
20890     },
20891
20892     /**
20893      * HTML Editor -> Textarea
20894      * Protected method that will not generally be called directly. Syncs the contents
20895      * of the editor iframe with the textarea.
20896      */
20897     syncValue : function(){
20898         if(this.initialized){
20899             var bd = (this.doc.body || this.doc.documentElement);
20900             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20901             var html = bd.innerHTML;
20902             if(Roo.isSafari){
20903                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20904                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20905                 if(m && m[1]){
20906                     html = '<div style="'+m[0]+'">' + html + '</div>';
20907                 }
20908             }
20909             html = this.cleanHtml(html);
20910             // fix up the special chars.. normaly like back quotes in word...
20911             // however we do not want to do this with chinese..
20912             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20913                 var cc = b.charCodeAt();
20914                 if (
20915                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20916                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20917                     (cc >= 0xf900 && cc < 0xfb00 )
20918                 ) {
20919                         return b;
20920                 }
20921                 return "&#"+cc+";" 
20922             });
20923             if(this.owner.fireEvent('beforesync', this, html) !== false){
20924                 this.el.dom.value = html;
20925                 this.owner.fireEvent('sync', this, html);
20926             }
20927         }
20928     },
20929
20930     /**
20931      * Protected method that will not generally be called directly. Pushes the value of the textarea
20932      * into the iframe editor.
20933      */
20934     pushValue : function(){
20935         if(this.initialized){
20936             var v = this.el.dom.value.trim();
20937             
20938 //            if(v.length < 1){
20939 //                v = '&#160;';
20940 //            }
20941             
20942             if(this.owner.fireEvent('beforepush', this, v) !== false){
20943                 var d = (this.doc.body || this.doc.documentElement);
20944                 d.innerHTML = v;
20945                 this.cleanUpPaste();
20946                 this.el.dom.value = d.innerHTML;
20947                 this.owner.fireEvent('push', this, v);
20948             }
20949         }
20950     },
20951
20952     // private
20953     deferFocus : function(){
20954         this.focus.defer(10, this);
20955     },
20956
20957     // doc'ed in Field
20958     focus : function(){
20959         if(this.win && !this.sourceEditMode){
20960             this.win.focus();
20961         }else{
20962             this.el.focus();
20963         }
20964     },
20965     
20966     assignDocWin: function()
20967     {
20968         var iframe = this.iframe;
20969         
20970          if(Roo.isIE){
20971             this.doc = iframe.contentWindow.document;
20972             this.win = iframe.contentWindow;
20973         } else {
20974 //            if (!Roo.get(this.frameId)) {
20975 //                return;
20976 //            }
20977 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20978 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20979             
20980             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20981                 return;
20982             }
20983             
20984             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20985             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20986         }
20987     },
20988     
20989     // private
20990     initEditor : function(){
20991         //console.log("INIT EDITOR");
20992         this.assignDocWin();
20993         
20994         
20995         
20996         this.doc.designMode="on";
20997         this.doc.open();
20998         this.doc.write(this.getDocMarkup());
20999         this.doc.close();
21000         
21001         var dbody = (this.doc.body || this.doc.documentElement);
21002         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21003         // this copies styles from the containing element into thsi one..
21004         // not sure why we need all of this..
21005         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21006         
21007         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21008         //ss['background-attachment'] = 'fixed'; // w3c
21009         dbody.bgProperties = 'fixed'; // ie
21010         //Roo.DomHelper.applyStyles(dbody, ss);
21011         Roo.EventManager.on(this.doc, {
21012             //'mousedown': this.onEditorEvent,
21013             'mouseup': this.onEditorEvent,
21014             'dblclick': this.onEditorEvent,
21015             'click': this.onEditorEvent,
21016             'keyup': this.onEditorEvent,
21017             buffer:100,
21018             scope: this
21019         });
21020         if(Roo.isGecko){
21021             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21022         }
21023         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21024             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21025         }
21026         this.initialized = true;
21027
21028         this.owner.fireEvent('initialize', this);
21029         this.pushValue();
21030     },
21031
21032     // private
21033     onDestroy : function(){
21034         
21035         
21036         
21037         if(this.rendered){
21038             
21039             //for (var i =0; i < this.toolbars.length;i++) {
21040             //    // fixme - ask toolbars for heights?
21041             //    this.toolbars[i].onDestroy();
21042            // }
21043             
21044             //this.wrap.dom.innerHTML = '';
21045             //this.wrap.remove();
21046         }
21047     },
21048
21049     // private
21050     onFirstFocus : function(){
21051         
21052         this.assignDocWin();
21053         
21054         
21055         this.activated = true;
21056          
21057     
21058         if(Roo.isGecko){ // prevent silly gecko errors
21059             this.win.focus();
21060             var s = this.win.getSelection();
21061             if(!s.focusNode || s.focusNode.nodeType != 3){
21062                 var r = s.getRangeAt(0);
21063                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21064                 r.collapse(true);
21065                 this.deferFocus();
21066             }
21067             try{
21068                 this.execCmd('useCSS', true);
21069                 this.execCmd('styleWithCSS', false);
21070             }catch(e){}
21071         }
21072         this.owner.fireEvent('activate', this);
21073     },
21074
21075     // private
21076     adjustFont: function(btn){
21077         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21078         //if(Roo.isSafari){ // safari
21079         //    adjust *= 2;
21080        // }
21081         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21082         if(Roo.isSafari){ // safari
21083             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21084             v =  (v < 10) ? 10 : v;
21085             v =  (v > 48) ? 48 : v;
21086             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21087             
21088         }
21089         
21090         
21091         v = Math.max(1, v+adjust);
21092         
21093         this.execCmd('FontSize', v  );
21094     },
21095
21096     onEditorEvent : function(e)
21097     {
21098         this.owner.fireEvent('editorevent', this, e);
21099       //  this.updateToolbar();
21100         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21101     },
21102
21103     insertTag : function(tg)
21104     {
21105         // could be a bit smarter... -> wrap the current selected tRoo..
21106         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21107             
21108             range = this.createRange(this.getSelection());
21109             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21110             wrappingNode.appendChild(range.extractContents());
21111             range.insertNode(wrappingNode);
21112
21113             return;
21114             
21115             
21116             
21117         }
21118         this.execCmd("formatblock",   tg);
21119         
21120     },
21121     
21122     insertText : function(txt)
21123     {
21124         
21125         
21126         var range = this.createRange();
21127         range.deleteContents();
21128                //alert(Sender.getAttribute('label'));
21129                
21130         range.insertNode(this.doc.createTextNode(txt));
21131     } ,
21132     
21133      
21134
21135     /**
21136      * Executes a Midas editor command on the editor document and performs necessary focus and
21137      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21138      * @param {String} cmd The Midas command
21139      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21140      */
21141     relayCmd : function(cmd, value){
21142         this.win.focus();
21143         this.execCmd(cmd, value);
21144         this.owner.fireEvent('editorevent', this);
21145         //this.updateToolbar();
21146         this.owner.deferFocus();
21147     },
21148
21149     /**
21150      * Executes a Midas editor command directly on the editor document.
21151      * For visual commands, you should use {@link #relayCmd} instead.
21152      * <b>This should only be called after the editor is initialized.</b>
21153      * @param {String} cmd The Midas command
21154      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21155      */
21156     execCmd : function(cmd, value){
21157         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21158         this.syncValue();
21159     },
21160  
21161  
21162    
21163     /**
21164      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21165      * to insert tRoo.
21166      * @param {String} text | dom node.. 
21167      */
21168     insertAtCursor : function(text)
21169     {
21170         
21171         
21172         
21173         if(!this.activated){
21174             return;
21175         }
21176         /*
21177         if(Roo.isIE){
21178             this.win.focus();
21179             var r = this.doc.selection.createRange();
21180             if(r){
21181                 r.collapse(true);
21182                 r.pasteHTML(text);
21183                 this.syncValue();
21184                 this.deferFocus();
21185             
21186             }
21187             return;
21188         }
21189         */
21190         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21191             this.win.focus();
21192             
21193             
21194             // from jquery ui (MIT licenced)
21195             var range, node;
21196             var win = this.win;
21197             
21198             if (win.getSelection && win.getSelection().getRangeAt) {
21199                 range = win.getSelection().getRangeAt(0);
21200                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21201                 range.insertNode(node);
21202             } else if (win.document.selection && win.document.selection.createRange) {
21203                 // no firefox support
21204                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21205                 win.document.selection.createRange().pasteHTML(txt);
21206             } else {
21207                 // no firefox support
21208                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21209                 this.execCmd('InsertHTML', txt);
21210             } 
21211             
21212             this.syncValue();
21213             
21214             this.deferFocus();
21215         }
21216     },
21217  // private
21218     mozKeyPress : function(e){
21219         if(e.ctrlKey){
21220             var c = e.getCharCode(), cmd;
21221           
21222             if(c > 0){
21223                 c = String.fromCharCode(c).toLowerCase();
21224                 switch(c){
21225                     case 'b':
21226                         cmd = 'bold';
21227                         break;
21228                     case 'i':
21229                         cmd = 'italic';
21230                         break;
21231                     
21232                     case 'u':
21233                         cmd = 'underline';
21234                         break;
21235                     
21236                     case 'v':
21237                         this.cleanUpPaste.defer(100, this);
21238                         return;
21239                         
21240                 }
21241                 if(cmd){
21242                     this.win.focus();
21243                     this.execCmd(cmd);
21244                     this.deferFocus();
21245                     e.preventDefault();
21246                 }
21247                 
21248             }
21249         }
21250     },
21251
21252     // private
21253     fixKeys : function(){ // load time branching for fastest keydown performance
21254         if(Roo.isIE){
21255             return function(e){
21256                 var k = e.getKey(), r;
21257                 if(k == e.TAB){
21258                     e.stopEvent();
21259                     r = this.doc.selection.createRange();
21260                     if(r){
21261                         r.collapse(true);
21262                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21263                         this.deferFocus();
21264                     }
21265                     return;
21266                 }
21267                 
21268                 if(k == e.ENTER){
21269                     r = this.doc.selection.createRange();
21270                     if(r){
21271                         var target = r.parentElement();
21272                         if(!target || target.tagName.toLowerCase() != 'li'){
21273                             e.stopEvent();
21274                             r.pasteHTML('<br />');
21275                             r.collapse(false);
21276                             r.select();
21277                         }
21278                     }
21279                 }
21280                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21281                     this.cleanUpPaste.defer(100, this);
21282                     return;
21283                 }
21284                 
21285                 
21286             };
21287         }else if(Roo.isOpera){
21288             return function(e){
21289                 var k = e.getKey();
21290                 if(k == e.TAB){
21291                     e.stopEvent();
21292                     this.win.focus();
21293                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21294                     this.deferFocus();
21295                 }
21296                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21297                     this.cleanUpPaste.defer(100, this);
21298                     return;
21299                 }
21300                 
21301             };
21302         }else if(Roo.isSafari){
21303             return function(e){
21304                 var k = e.getKey();
21305                 
21306                 if(k == e.TAB){
21307                     e.stopEvent();
21308                     this.execCmd('InsertText','\t');
21309                     this.deferFocus();
21310                     return;
21311                 }
21312                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21313                     this.cleanUpPaste.defer(100, this);
21314                     return;
21315                 }
21316                 
21317              };
21318         }
21319     }(),
21320     
21321     getAllAncestors: function()
21322     {
21323         var p = this.getSelectedNode();
21324         var a = [];
21325         if (!p) {
21326             a.push(p); // push blank onto stack..
21327             p = this.getParentElement();
21328         }
21329         
21330         
21331         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21332             a.push(p);
21333             p = p.parentNode;
21334         }
21335         a.push(this.doc.body);
21336         return a;
21337     },
21338     lastSel : false,
21339     lastSelNode : false,
21340     
21341     
21342     getSelection : function() 
21343     {
21344         this.assignDocWin();
21345         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21346     },
21347     
21348     getSelectedNode: function() 
21349     {
21350         // this may only work on Gecko!!!
21351         
21352         // should we cache this!!!!
21353         
21354         
21355         
21356          
21357         var range = this.createRange(this.getSelection()).cloneRange();
21358         
21359         if (Roo.isIE) {
21360             var parent = range.parentElement();
21361             while (true) {
21362                 var testRange = range.duplicate();
21363                 testRange.moveToElementText(parent);
21364                 if (testRange.inRange(range)) {
21365                     break;
21366                 }
21367                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21368                     break;
21369                 }
21370                 parent = parent.parentElement;
21371             }
21372             return parent;
21373         }
21374         
21375         // is ancestor a text element.
21376         var ac =  range.commonAncestorContainer;
21377         if (ac.nodeType == 3) {
21378             ac = ac.parentNode;
21379         }
21380         
21381         var ar = ac.childNodes;
21382          
21383         var nodes = [];
21384         var other_nodes = [];
21385         var has_other_nodes = false;
21386         for (var i=0;i<ar.length;i++) {
21387             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21388                 continue;
21389             }
21390             // fullly contained node.
21391             
21392             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21393                 nodes.push(ar[i]);
21394                 continue;
21395             }
21396             
21397             // probably selected..
21398             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21399                 other_nodes.push(ar[i]);
21400                 continue;
21401             }
21402             // outer..
21403             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21404                 continue;
21405             }
21406             
21407             
21408             has_other_nodes = true;
21409         }
21410         if (!nodes.length && other_nodes.length) {
21411             nodes= other_nodes;
21412         }
21413         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21414             return false;
21415         }
21416         
21417         return nodes[0];
21418     },
21419     createRange: function(sel)
21420     {
21421         // this has strange effects when using with 
21422         // top toolbar - not sure if it's a great idea.
21423         //this.editor.contentWindow.focus();
21424         if (typeof sel != "undefined") {
21425             try {
21426                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21427             } catch(e) {
21428                 return this.doc.createRange();
21429             }
21430         } else {
21431             return this.doc.createRange();
21432         }
21433     },
21434     getParentElement: function()
21435     {
21436         
21437         this.assignDocWin();
21438         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21439         
21440         var range = this.createRange(sel);
21441          
21442         try {
21443             var p = range.commonAncestorContainer;
21444             while (p.nodeType == 3) { // text node
21445                 p = p.parentNode;
21446             }
21447             return p;
21448         } catch (e) {
21449             return null;
21450         }
21451     
21452     },
21453     /***
21454      *
21455      * Range intersection.. the hard stuff...
21456      *  '-1' = before
21457      *  '0' = hits..
21458      *  '1' = after.
21459      *         [ -- selected range --- ]
21460      *   [fail]                        [fail]
21461      *
21462      *    basically..
21463      *      if end is before start or  hits it. fail.
21464      *      if start is after end or hits it fail.
21465      *
21466      *   if either hits (but other is outside. - then it's not 
21467      *   
21468      *    
21469      **/
21470     
21471     
21472     // @see http://www.thismuchiknow.co.uk/?p=64.
21473     rangeIntersectsNode : function(range, node)
21474     {
21475         var nodeRange = node.ownerDocument.createRange();
21476         try {
21477             nodeRange.selectNode(node);
21478         } catch (e) {
21479             nodeRange.selectNodeContents(node);
21480         }
21481     
21482         var rangeStartRange = range.cloneRange();
21483         rangeStartRange.collapse(true);
21484     
21485         var rangeEndRange = range.cloneRange();
21486         rangeEndRange.collapse(false);
21487     
21488         var nodeStartRange = nodeRange.cloneRange();
21489         nodeStartRange.collapse(true);
21490     
21491         var nodeEndRange = nodeRange.cloneRange();
21492         nodeEndRange.collapse(false);
21493     
21494         return rangeStartRange.compareBoundaryPoints(
21495                  Range.START_TO_START, nodeEndRange) == -1 &&
21496                rangeEndRange.compareBoundaryPoints(
21497                  Range.START_TO_START, nodeStartRange) == 1;
21498         
21499          
21500     },
21501     rangeCompareNode : function(range, node)
21502     {
21503         var nodeRange = node.ownerDocument.createRange();
21504         try {
21505             nodeRange.selectNode(node);
21506         } catch (e) {
21507             nodeRange.selectNodeContents(node);
21508         }
21509         
21510         
21511         range.collapse(true);
21512     
21513         nodeRange.collapse(true);
21514      
21515         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21516         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21517          
21518         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21519         
21520         var nodeIsBefore   =  ss == 1;
21521         var nodeIsAfter    = ee == -1;
21522         
21523         if (nodeIsBefore && nodeIsAfter) {
21524             return 0; // outer
21525         }
21526         if (!nodeIsBefore && nodeIsAfter) {
21527             return 1; //right trailed.
21528         }
21529         
21530         if (nodeIsBefore && !nodeIsAfter) {
21531             return 2;  // left trailed.
21532         }
21533         // fully contined.
21534         return 3;
21535     },
21536
21537     // private? - in a new class?
21538     cleanUpPaste :  function()
21539     {
21540         // cleans up the whole document..
21541         Roo.log('cleanuppaste');
21542         
21543         this.cleanUpChildren(this.doc.body);
21544         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21545         if (clean != this.doc.body.innerHTML) {
21546             this.doc.body.innerHTML = clean;
21547         }
21548         
21549     },
21550     
21551     cleanWordChars : function(input) {// change the chars to hex code
21552         var he = Roo.HtmlEditorCore;
21553         
21554         var output = input;
21555         Roo.each(he.swapCodes, function(sw) { 
21556             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21557             
21558             output = output.replace(swapper, sw[1]);
21559         });
21560         
21561         return output;
21562     },
21563     
21564     
21565     cleanUpChildren : function (n)
21566     {
21567         if (!n.childNodes.length) {
21568             return;
21569         }
21570         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21571            this.cleanUpChild(n.childNodes[i]);
21572         }
21573     },
21574     
21575     
21576         
21577     
21578     cleanUpChild : function (node)
21579     {
21580         var ed = this;
21581         //console.log(node);
21582         if (node.nodeName == "#text") {
21583             // clean up silly Windows -- stuff?
21584             return; 
21585         }
21586         if (node.nodeName == "#comment") {
21587             node.parentNode.removeChild(node);
21588             // clean up silly Windows -- stuff?
21589             return; 
21590         }
21591         var lcname = node.tagName.toLowerCase();
21592         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21593         // whitelist of tags..
21594         
21595         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21596             // remove node.
21597             node.parentNode.removeChild(node);
21598             return;
21599             
21600         }
21601         
21602         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21603         
21604         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21605         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21606         
21607         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21608         //    remove_keep_children = true;
21609         //}
21610         
21611         if (remove_keep_children) {
21612             this.cleanUpChildren(node);
21613             // inserts everything just before this node...
21614             while (node.childNodes.length) {
21615                 var cn = node.childNodes[0];
21616                 node.removeChild(cn);
21617                 node.parentNode.insertBefore(cn, node);
21618             }
21619             node.parentNode.removeChild(node);
21620             return;
21621         }
21622         
21623         if (!node.attributes || !node.attributes.length) {
21624             this.cleanUpChildren(node);
21625             return;
21626         }
21627         
21628         function cleanAttr(n,v)
21629         {
21630             
21631             if (v.match(/^\./) || v.match(/^\//)) {
21632                 return;
21633             }
21634             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21635                 return;
21636             }
21637             if (v.match(/^#/)) {
21638                 return;
21639             }
21640 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21641             node.removeAttribute(n);
21642             
21643         }
21644         
21645         var cwhite = this.cwhite;
21646         var cblack = this.cblack;
21647             
21648         function cleanStyle(n,v)
21649         {
21650             if (v.match(/expression/)) { //XSS?? should we even bother..
21651                 node.removeAttribute(n);
21652                 return;
21653             }
21654             
21655             var parts = v.split(/;/);
21656             var clean = [];
21657             
21658             Roo.each(parts, function(p) {
21659                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21660                 if (!p.length) {
21661                     return true;
21662                 }
21663                 var l = p.split(':').shift().replace(/\s+/g,'');
21664                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21665                 
21666                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21667 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21668                     //node.removeAttribute(n);
21669                     return true;
21670                 }
21671                 //Roo.log()
21672                 // only allow 'c whitelisted system attributes'
21673                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21674 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21675                     //node.removeAttribute(n);
21676                     return true;
21677                 }
21678                 
21679                 
21680                  
21681                 
21682                 clean.push(p);
21683                 return true;
21684             });
21685             if (clean.length) { 
21686                 node.setAttribute(n, clean.join(';'));
21687             } else {
21688                 node.removeAttribute(n);
21689             }
21690             
21691         }
21692         
21693         
21694         for (var i = node.attributes.length-1; i > -1 ; i--) {
21695             var a = node.attributes[i];
21696             //console.log(a);
21697             
21698             if (a.name.toLowerCase().substr(0,2)=='on')  {
21699                 node.removeAttribute(a.name);
21700                 continue;
21701             }
21702             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21703                 node.removeAttribute(a.name);
21704                 continue;
21705             }
21706             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21707                 cleanAttr(a.name,a.value); // fixme..
21708                 continue;
21709             }
21710             if (a.name == 'style') {
21711                 cleanStyle(a.name,a.value);
21712                 continue;
21713             }
21714             /// clean up MS crap..
21715             // tecnically this should be a list of valid class'es..
21716             
21717             
21718             if (a.name == 'class') {
21719                 if (a.value.match(/^Mso/)) {
21720                     node.className = '';
21721                 }
21722                 
21723                 if (a.value.match(/body/)) {
21724                     node.className = '';
21725                 }
21726                 continue;
21727             }
21728             
21729             // style cleanup!?
21730             // class cleanup?
21731             
21732         }
21733         
21734         
21735         this.cleanUpChildren(node);
21736         
21737         
21738     },
21739     
21740     /**
21741      * Clean up MS wordisms...
21742      */
21743     cleanWord : function(node)
21744     {
21745         
21746         
21747         if (!node) {
21748             this.cleanWord(this.doc.body);
21749             return;
21750         }
21751         if (node.nodeName == "#text") {
21752             // clean up silly Windows -- stuff?
21753             return; 
21754         }
21755         if (node.nodeName == "#comment") {
21756             node.parentNode.removeChild(node);
21757             // clean up silly Windows -- stuff?
21758             return; 
21759         }
21760         
21761         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21762             node.parentNode.removeChild(node);
21763             return;
21764         }
21765         
21766         // remove - but keep children..
21767         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21768             while (node.childNodes.length) {
21769                 var cn = node.childNodes[0];
21770                 node.removeChild(cn);
21771                 node.parentNode.insertBefore(cn, node);
21772             }
21773             node.parentNode.removeChild(node);
21774             this.iterateChildren(node, this.cleanWord);
21775             return;
21776         }
21777         // clean styles
21778         if (node.className.length) {
21779             
21780             var cn = node.className.split(/\W+/);
21781             var cna = [];
21782             Roo.each(cn, function(cls) {
21783                 if (cls.match(/Mso[a-zA-Z]+/)) {
21784                     return;
21785                 }
21786                 cna.push(cls);
21787             });
21788             node.className = cna.length ? cna.join(' ') : '';
21789             if (!cna.length) {
21790                 node.removeAttribute("class");
21791             }
21792         }
21793         
21794         if (node.hasAttribute("lang")) {
21795             node.removeAttribute("lang");
21796         }
21797         
21798         if (node.hasAttribute("style")) {
21799             
21800             var styles = node.getAttribute("style").split(";");
21801             var nstyle = [];
21802             Roo.each(styles, function(s) {
21803                 if (!s.match(/:/)) {
21804                     return;
21805                 }
21806                 var kv = s.split(":");
21807                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21808                     return;
21809                 }
21810                 // what ever is left... we allow.
21811                 nstyle.push(s);
21812             });
21813             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21814             if (!nstyle.length) {
21815                 node.removeAttribute('style');
21816             }
21817         }
21818         this.iterateChildren(node, this.cleanWord);
21819         
21820         
21821         
21822     },
21823     /**
21824      * iterateChildren of a Node, calling fn each time, using this as the scole..
21825      * @param {DomNode} node node to iterate children of.
21826      * @param {Function} fn method of this class to call on each item.
21827      */
21828     iterateChildren : function(node, fn)
21829     {
21830         if (!node.childNodes.length) {
21831                 return;
21832         }
21833         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21834            fn.call(this, node.childNodes[i])
21835         }
21836     },
21837     
21838     
21839     /**
21840      * cleanTableWidths.
21841      *
21842      * Quite often pasting from word etc.. results in tables with column and widths.
21843      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21844      *
21845      */
21846     cleanTableWidths : function(node)
21847     {
21848          
21849          
21850         if (!node) {
21851             this.cleanTableWidths(this.doc.body);
21852             return;
21853         }
21854         
21855         // ignore list...
21856         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21857             return; 
21858         }
21859         Roo.log(node.tagName);
21860         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21861             this.iterateChildren(node, this.cleanTableWidths);
21862             return;
21863         }
21864         if (node.hasAttribute('width')) {
21865             node.removeAttribute('width');
21866         }
21867         
21868          
21869         if (node.hasAttribute("style")) {
21870             // pretty basic...
21871             
21872             var styles = node.getAttribute("style").split(";");
21873             var nstyle = [];
21874             Roo.each(styles, function(s) {
21875                 if (!s.match(/:/)) {
21876                     return;
21877                 }
21878                 var kv = s.split(":");
21879                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21880                     return;
21881                 }
21882                 // what ever is left... we allow.
21883                 nstyle.push(s);
21884             });
21885             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21886             if (!nstyle.length) {
21887                 node.removeAttribute('style');
21888             }
21889         }
21890         
21891         this.iterateChildren(node, this.cleanTableWidths);
21892         
21893         
21894     },
21895     
21896     
21897     
21898     
21899     domToHTML : function(currentElement, depth, nopadtext) {
21900         
21901         depth = depth || 0;
21902         nopadtext = nopadtext || false;
21903     
21904         if (!currentElement) {
21905             return this.domToHTML(this.doc.body);
21906         }
21907         
21908         //Roo.log(currentElement);
21909         var j;
21910         var allText = false;
21911         var nodeName = currentElement.nodeName;
21912         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21913         
21914         if  (nodeName == '#text') {
21915             
21916             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21917         }
21918         
21919         
21920         var ret = '';
21921         if (nodeName != 'BODY') {
21922              
21923             var i = 0;
21924             // Prints the node tagName, such as <A>, <IMG>, etc
21925             if (tagName) {
21926                 var attr = [];
21927                 for(i = 0; i < currentElement.attributes.length;i++) {
21928                     // quoting?
21929                     var aname = currentElement.attributes.item(i).name;
21930                     if (!currentElement.attributes.item(i).value.length) {
21931                         continue;
21932                     }
21933                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21934                 }
21935                 
21936                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21937             } 
21938             else {
21939                 
21940                 // eack
21941             }
21942         } else {
21943             tagName = false;
21944         }
21945         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21946             return ret;
21947         }
21948         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21949             nopadtext = true;
21950         }
21951         
21952         
21953         // Traverse the tree
21954         i = 0;
21955         var currentElementChild = currentElement.childNodes.item(i);
21956         var allText = true;
21957         var innerHTML  = '';
21958         lastnode = '';
21959         while (currentElementChild) {
21960             // Formatting code (indent the tree so it looks nice on the screen)
21961             var nopad = nopadtext;
21962             if (lastnode == 'SPAN') {
21963                 nopad  = true;
21964             }
21965             // text
21966             if  (currentElementChild.nodeName == '#text') {
21967                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21968                 toadd = nopadtext ? toadd : toadd.trim();
21969                 if (!nopad && toadd.length > 80) {
21970                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21971                 }
21972                 innerHTML  += toadd;
21973                 
21974                 i++;
21975                 currentElementChild = currentElement.childNodes.item(i);
21976                 lastNode = '';
21977                 continue;
21978             }
21979             allText = false;
21980             
21981             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21982                 
21983             // Recursively traverse the tree structure of the child node
21984             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21985             lastnode = currentElementChild.nodeName;
21986             i++;
21987             currentElementChild=currentElement.childNodes.item(i);
21988         }
21989         
21990         ret += innerHTML;
21991         
21992         if (!allText) {
21993                 // The remaining code is mostly for formatting the tree
21994             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21995         }
21996         
21997         
21998         if (tagName) {
21999             ret+= "</"+tagName+">";
22000         }
22001         return ret;
22002         
22003     },
22004         
22005     applyBlacklists : function()
22006     {
22007         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22008         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22009         
22010         this.white = [];
22011         this.black = [];
22012         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22013             if (b.indexOf(tag) > -1) {
22014                 return;
22015             }
22016             this.white.push(tag);
22017             
22018         }, this);
22019         
22020         Roo.each(w, function(tag) {
22021             if (b.indexOf(tag) > -1) {
22022                 return;
22023             }
22024             if (this.white.indexOf(tag) > -1) {
22025                 return;
22026             }
22027             this.white.push(tag);
22028             
22029         }, this);
22030         
22031         
22032         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22033             if (w.indexOf(tag) > -1) {
22034                 return;
22035             }
22036             this.black.push(tag);
22037             
22038         }, this);
22039         
22040         Roo.each(b, function(tag) {
22041             if (w.indexOf(tag) > -1) {
22042                 return;
22043             }
22044             if (this.black.indexOf(tag) > -1) {
22045                 return;
22046             }
22047             this.black.push(tag);
22048             
22049         }, this);
22050         
22051         
22052         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22053         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22054         
22055         this.cwhite = [];
22056         this.cblack = [];
22057         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22058             if (b.indexOf(tag) > -1) {
22059                 return;
22060             }
22061             this.cwhite.push(tag);
22062             
22063         }, this);
22064         
22065         Roo.each(w, function(tag) {
22066             if (b.indexOf(tag) > -1) {
22067                 return;
22068             }
22069             if (this.cwhite.indexOf(tag) > -1) {
22070                 return;
22071             }
22072             this.cwhite.push(tag);
22073             
22074         }, this);
22075         
22076         
22077         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22078             if (w.indexOf(tag) > -1) {
22079                 return;
22080             }
22081             this.cblack.push(tag);
22082             
22083         }, this);
22084         
22085         Roo.each(b, function(tag) {
22086             if (w.indexOf(tag) > -1) {
22087                 return;
22088             }
22089             if (this.cblack.indexOf(tag) > -1) {
22090                 return;
22091             }
22092             this.cblack.push(tag);
22093             
22094         }, this);
22095     },
22096     
22097     setStylesheets : function(stylesheets)
22098     {
22099         if(typeof(stylesheets) == 'string'){
22100             Roo.get(this.iframe.contentDocument.head).createChild({
22101                 tag : 'link',
22102                 rel : 'stylesheet',
22103                 type : 'text/css',
22104                 href : stylesheets
22105             });
22106             
22107             return;
22108         }
22109         var _this = this;
22110      
22111         Roo.each(stylesheets, function(s) {
22112             if(!s.length){
22113                 return;
22114             }
22115             
22116             Roo.get(_this.iframe.contentDocument.head).createChild({
22117                 tag : 'link',
22118                 rel : 'stylesheet',
22119                 type : 'text/css',
22120                 href : s
22121             });
22122         });
22123
22124         
22125     },
22126     
22127     removeStylesheets : function()
22128     {
22129         var _this = this;
22130         
22131         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22132             s.remove();
22133         });
22134     }
22135     
22136     // hide stuff that is not compatible
22137     /**
22138      * @event blur
22139      * @hide
22140      */
22141     /**
22142      * @event change
22143      * @hide
22144      */
22145     /**
22146      * @event focus
22147      * @hide
22148      */
22149     /**
22150      * @event specialkey
22151      * @hide
22152      */
22153     /**
22154      * @cfg {String} fieldClass @hide
22155      */
22156     /**
22157      * @cfg {String} focusClass @hide
22158      */
22159     /**
22160      * @cfg {String} autoCreate @hide
22161      */
22162     /**
22163      * @cfg {String} inputType @hide
22164      */
22165     /**
22166      * @cfg {String} invalidClass @hide
22167      */
22168     /**
22169      * @cfg {String} invalidText @hide
22170      */
22171     /**
22172      * @cfg {String} msgFx @hide
22173      */
22174     /**
22175      * @cfg {String} validateOnBlur @hide
22176      */
22177 });
22178
22179 Roo.HtmlEditorCore.white = [
22180         'area', 'br', 'img', 'input', 'hr', 'wbr',
22181         
22182        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22183        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22184        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22185        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22186        'table',   'ul',         'xmp', 
22187        
22188        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22189       'thead',   'tr', 
22190      
22191       'dir', 'menu', 'ol', 'ul', 'dl',
22192        
22193       'embed',  'object'
22194 ];
22195
22196
22197 Roo.HtmlEditorCore.black = [
22198     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22199         'applet', // 
22200         'base',   'basefont', 'bgsound', 'blink',  'body', 
22201         'frame',  'frameset', 'head',    'html',   'ilayer', 
22202         'iframe', 'layer',  'link',     'meta',    'object',   
22203         'script', 'style' ,'title',  'xml' // clean later..
22204 ];
22205 Roo.HtmlEditorCore.clean = [
22206     'script', 'style', 'title', 'xml'
22207 ];
22208 Roo.HtmlEditorCore.remove = [
22209     'font'
22210 ];
22211 // attributes..
22212
22213 Roo.HtmlEditorCore.ablack = [
22214     'on'
22215 ];
22216     
22217 Roo.HtmlEditorCore.aclean = [ 
22218     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22219 ];
22220
22221 // protocols..
22222 Roo.HtmlEditorCore.pwhite= [
22223         'http',  'https',  'mailto'
22224 ];
22225
22226 // white listed style attributes.
22227 Roo.HtmlEditorCore.cwhite= [
22228       //  'text-align', /// default is to allow most things..
22229       
22230          
22231 //        'font-size'//??
22232 ];
22233
22234 // black listed style attributes.
22235 Roo.HtmlEditorCore.cblack= [
22236       //  'font-size' -- this can be set by the project 
22237 ];
22238
22239
22240 Roo.HtmlEditorCore.swapCodes   =[ 
22241     [    8211, "--" ], 
22242     [    8212, "--" ], 
22243     [    8216,  "'" ],  
22244     [    8217, "'" ],  
22245     [    8220, '"' ],  
22246     [    8221, '"' ],  
22247     [    8226, "*" ],  
22248     [    8230, "..." ]
22249 ]; 
22250
22251     /*
22252  * - LGPL
22253  *
22254  * HtmlEditor
22255  * 
22256  */
22257
22258 /**
22259  * @class Roo.bootstrap.HtmlEditor
22260  * @extends Roo.bootstrap.TextArea
22261  * Bootstrap HtmlEditor class
22262
22263  * @constructor
22264  * Create a new HtmlEditor
22265  * @param {Object} config The config object
22266  */
22267
22268 Roo.bootstrap.HtmlEditor = function(config){
22269     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22270     if (!this.toolbars) {
22271         this.toolbars = [];
22272     }
22273     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22274     this.addEvents({
22275             /**
22276              * @event initialize
22277              * Fires when the editor is fully initialized (including the iframe)
22278              * @param {HtmlEditor} this
22279              */
22280             initialize: true,
22281             /**
22282              * @event activate
22283              * Fires when the editor is first receives the focus. Any insertion must wait
22284              * until after this event.
22285              * @param {HtmlEditor} this
22286              */
22287             activate: true,
22288              /**
22289              * @event beforesync
22290              * Fires before the textarea is updated with content from the editor iframe. Return false
22291              * to cancel the sync.
22292              * @param {HtmlEditor} this
22293              * @param {String} html
22294              */
22295             beforesync: true,
22296              /**
22297              * @event beforepush
22298              * Fires before the iframe editor is updated with content from the textarea. Return false
22299              * to cancel the push.
22300              * @param {HtmlEditor} this
22301              * @param {String} html
22302              */
22303             beforepush: true,
22304              /**
22305              * @event sync
22306              * Fires when the textarea is updated with content from the editor iframe.
22307              * @param {HtmlEditor} this
22308              * @param {String} html
22309              */
22310             sync: true,
22311              /**
22312              * @event push
22313              * Fires when the iframe editor is updated with content from the textarea.
22314              * @param {HtmlEditor} this
22315              * @param {String} html
22316              */
22317             push: true,
22318              /**
22319              * @event editmodechange
22320              * Fires when the editor switches edit modes
22321              * @param {HtmlEditor} this
22322              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22323              */
22324             editmodechange: true,
22325             /**
22326              * @event editorevent
22327              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22328              * @param {HtmlEditor} this
22329              */
22330             editorevent: true,
22331             /**
22332              * @event firstfocus
22333              * Fires when on first focus - needed by toolbars..
22334              * @param {HtmlEditor} this
22335              */
22336             firstfocus: true,
22337             /**
22338              * @event autosave
22339              * Auto save the htmlEditor value as a file into Events
22340              * @param {HtmlEditor} this
22341              */
22342             autosave: true,
22343             /**
22344              * @event savedpreview
22345              * preview the saved version of htmlEditor
22346              * @param {HtmlEditor} this
22347              */
22348             savedpreview: true
22349         });
22350 };
22351
22352
22353 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22354     
22355     
22356       /**
22357      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22358      */
22359     toolbars : false,
22360    
22361      /**
22362      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22363      *                        Roo.resizable.
22364      */
22365     resizable : false,
22366      /**
22367      * @cfg {Number} height (in pixels)
22368      */   
22369     height: 300,
22370    /**
22371      * @cfg {Number} width (in pixels)
22372      */   
22373     width: false,
22374     
22375     /**
22376      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22377      * 
22378      */
22379     stylesheets: false,
22380     
22381     // id of frame..
22382     frameId: false,
22383     
22384     // private properties
22385     validationEvent : false,
22386     deferHeight: true,
22387     initialized : false,
22388     activated : false,
22389     
22390     onFocus : Roo.emptyFn,
22391     iframePad:3,
22392     hideMode:'offsets',
22393     
22394     
22395     tbContainer : false,
22396     
22397     toolbarContainer :function() {
22398         return this.wrap.select('.x-html-editor-tb',true).first();
22399     },
22400
22401     /**
22402      * Protected method that will not generally be called directly. It
22403      * is called when the editor creates its toolbar. Override this method if you need to
22404      * add custom toolbar buttons.
22405      * @param {HtmlEditor} editor
22406      */
22407     createToolbar : function(){
22408         
22409         Roo.log("create toolbars");
22410         
22411         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22412         this.toolbars[0].render(this.toolbarContainer());
22413         
22414         return;
22415         
22416 //        if (!editor.toolbars || !editor.toolbars.length) {
22417 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22418 //        }
22419 //        
22420 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22421 //            editor.toolbars[i] = Roo.factory(
22422 //                    typeof(editor.toolbars[i]) == 'string' ?
22423 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22424 //                Roo.bootstrap.HtmlEditor);
22425 //            editor.toolbars[i].init(editor);
22426 //        }
22427     },
22428
22429      
22430     // private
22431     onRender : function(ct, position)
22432     {
22433        // Roo.log("Call onRender: " + this.xtype);
22434         var _t = this;
22435         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22436       
22437         this.wrap = this.inputEl().wrap({
22438             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22439         });
22440         
22441         this.editorcore.onRender(ct, position);
22442          
22443         if (this.resizable) {
22444             this.resizeEl = new Roo.Resizable(this.wrap, {
22445                 pinned : true,
22446                 wrap: true,
22447                 dynamic : true,
22448                 minHeight : this.height,
22449                 height: this.height,
22450                 handles : this.resizable,
22451                 width: this.width,
22452                 listeners : {
22453                     resize : function(r, w, h) {
22454                         _t.onResize(w,h); // -something
22455                     }
22456                 }
22457             });
22458             
22459         }
22460         this.createToolbar(this);
22461        
22462         
22463         if(!this.width && this.resizable){
22464             this.setSize(this.wrap.getSize());
22465         }
22466         if (this.resizeEl) {
22467             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22468             // should trigger onReize..
22469         }
22470         
22471     },
22472
22473     // private
22474     onResize : function(w, h)
22475     {
22476         Roo.log('resize: ' +w + ',' + h );
22477         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22478         var ew = false;
22479         var eh = false;
22480         
22481         if(this.inputEl() ){
22482             if(typeof w == 'number'){
22483                 var aw = w - this.wrap.getFrameWidth('lr');
22484                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22485                 ew = aw;
22486             }
22487             if(typeof h == 'number'){
22488                  var tbh = -11;  // fixme it needs to tool bar size!
22489                 for (var i =0; i < this.toolbars.length;i++) {
22490                     // fixme - ask toolbars for heights?
22491                     tbh += this.toolbars[i].el.getHeight();
22492                     //if (this.toolbars[i].footer) {
22493                     //    tbh += this.toolbars[i].footer.el.getHeight();
22494                     //}
22495                 }
22496               
22497                 
22498                 
22499                 
22500                 
22501                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22502                 ah -= 5; // knock a few pixes off for look..
22503                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22504                 var eh = ah;
22505             }
22506         }
22507         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22508         this.editorcore.onResize(ew,eh);
22509         
22510     },
22511
22512     /**
22513      * Toggles the editor between standard and source edit mode.
22514      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22515      */
22516     toggleSourceEdit : function(sourceEditMode)
22517     {
22518         this.editorcore.toggleSourceEdit(sourceEditMode);
22519         
22520         if(this.editorcore.sourceEditMode){
22521             Roo.log('editor - showing textarea');
22522             
22523 //            Roo.log('in');
22524 //            Roo.log(this.syncValue());
22525             this.syncValue();
22526             this.inputEl().removeClass(['hide', 'x-hidden']);
22527             this.inputEl().dom.removeAttribute('tabIndex');
22528             this.inputEl().focus();
22529         }else{
22530             Roo.log('editor - hiding textarea');
22531 //            Roo.log('out')
22532 //            Roo.log(this.pushValue()); 
22533             this.pushValue();
22534             
22535             this.inputEl().addClass(['hide', 'x-hidden']);
22536             this.inputEl().dom.setAttribute('tabIndex', -1);
22537             //this.deferFocus();
22538         }
22539          
22540         if(this.resizable){
22541             this.setSize(this.wrap.getSize());
22542         }
22543         
22544         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22545     },
22546  
22547     // private (for BoxComponent)
22548     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22549
22550     // private (for BoxComponent)
22551     getResizeEl : function(){
22552         return this.wrap;
22553     },
22554
22555     // private (for BoxComponent)
22556     getPositionEl : function(){
22557         return this.wrap;
22558     },
22559
22560     // private
22561     initEvents : function(){
22562         this.originalValue = this.getValue();
22563     },
22564
22565 //    /**
22566 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22567 //     * @method
22568 //     */
22569 //    markInvalid : Roo.emptyFn,
22570 //    /**
22571 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22572 //     * @method
22573 //     */
22574 //    clearInvalid : Roo.emptyFn,
22575
22576     setValue : function(v){
22577         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22578         this.editorcore.pushValue();
22579     },
22580
22581      
22582     // private
22583     deferFocus : function(){
22584         this.focus.defer(10, this);
22585     },
22586
22587     // doc'ed in Field
22588     focus : function(){
22589         this.editorcore.focus();
22590         
22591     },
22592       
22593
22594     // private
22595     onDestroy : function(){
22596         
22597         
22598         
22599         if(this.rendered){
22600             
22601             for (var i =0; i < this.toolbars.length;i++) {
22602                 // fixme - ask toolbars for heights?
22603                 this.toolbars[i].onDestroy();
22604             }
22605             
22606             this.wrap.dom.innerHTML = '';
22607             this.wrap.remove();
22608         }
22609     },
22610
22611     // private
22612     onFirstFocus : function(){
22613         //Roo.log("onFirstFocus");
22614         this.editorcore.onFirstFocus();
22615          for (var i =0; i < this.toolbars.length;i++) {
22616             this.toolbars[i].onFirstFocus();
22617         }
22618         
22619     },
22620     
22621     // private
22622     syncValue : function()
22623     {   
22624         this.editorcore.syncValue();
22625     },
22626     
22627     pushValue : function()
22628     {   
22629         this.editorcore.pushValue();
22630     }
22631      
22632     
22633     // hide stuff that is not compatible
22634     /**
22635      * @event blur
22636      * @hide
22637      */
22638     /**
22639      * @event change
22640      * @hide
22641      */
22642     /**
22643      * @event focus
22644      * @hide
22645      */
22646     /**
22647      * @event specialkey
22648      * @hide
22649      */
22650     /**
22651      * @cfg {String} fieldClass @hide
22652      */
22653     /**
22654      * @cfg {String} focusClass @hide
22655      */
22656     /**
22657      * @cfg {String} autoCreate @hide
22658      */
22659     /**
22660      * @cfg {String} inputType @hide
22661      */
22662     /**
22663      * @cfg {String} invalidClass @hide
22664      */
22665     /**
22666      * @cfg {String} invalidText @hide
22667      */
22668     /**
22669      * @cfg {String} msgFx @hide
22670      */
22671     /**
22672      * @cfg {String} validateOnBlur @hide
22673      */
22674 });
22675  
22676     
22677    
22678    
22679    
22680       
22681 Roo.namespace('Roo.bootstrap.htmleditor');
22682 /**
22683  * @class Roo.bootstrap.HtmlEditorToolbar1
22684  * Basic Toolbar
22685  * 
22686  * Usage:
22687  *
22688  new Roo.bootstrap.HtmlEditor({
22689     ....
22690     toolbars : [
22691         new Roo.bootstrap.HtmlEditorToolbar1({
22692             disable : { fonts: 1 , format: 1, ..., ... , ...],
22693             btns : [ .... ]
22694         })
22695     }
22696      
22697  * 
22698  * @cfg {Object} disable List of elements to disable..
22699  * @cfg {Array} btns List of additional buttons.
22700  * 
22701  * 
22702  * NEEDS Extra CSS? 
22703  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22704  */
22705  
22706 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22707 {
22708     
22709     Roo.apply(this, config);
22710     
22711     // default disabled, based on 'good practice'..
22712     this.disable = this.disable || {};
22713     Roo.applyIf(this.disable, {
22714         fontSize : true,
22715         colors : true,
22716         specialElements : true
22717     });
22718     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22719     
22720     this.editor = config.editor;
22721     this.editorcore = config.editor.editorcore;
22722     
22723     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22724     
22725     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22726     // dont call parent... till later.
22727 }
22728 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22729      
22730     bar : true,
22731     
22732     editor : false,
22733     editorcore : false,
22734     
22735     
22736     formats : [
22737         "p" ,  
22738         "h1","h2","h3","h4","h5","h6", 
22739         "pre", "code", 
22740         "abbr", "acronym", "address", "cite", "samp", "var",
22741         'div','span'
22742     ],
22743     
22744     onRender : function(ct, position)
22745     {
22746        // Roo.log("Call onRender: " + this.xtype);
22747         
22748        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22749        Roo.log(this.el);
22750        this.el.dom.style.marginBottom = '0';
22751        var _this = this;
22752        var editorcore = this.editorcore;
22753        var editor= this.editor;
22754        
22755        var children = [];
22756        var btn = function(id,cmd , toggle, handler){
22757        
22758             var  event = toggle ? 'toggle' : 'click';
22759        
22760             var a = {
22761                 size : 'sm',
22762                 xtype: 'Button',
22763                 xns: Roo.bootstrap,
22764                 glyphicon : id,
22765                 cmd : id || cmd,
22766                 enableToggle:toggle !== false,
22767                 //html : 'submit'
22768                 pressed : toggle ? false : null,
22769                 listeners : {}
22770             };
22771             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22772                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22773             };
22774             children.push(a);
22775             return a;
22776        }
22777         
22778         var style = {
22779                 xtype: 'Button',
22780                 size : 'sm',
22781                 xns: Roo.bootstrap,
22782                 glyphicon : 'font',
22783                 //html : 'submit'
22784                 menu : {
22785                     xtype: 'Menu',
22786                     xns: Roo.bootstrap,
22787                     items:  []
22788                 }
22789         };
22790         Roo.each(this.formats, function(f) {
22791             style.menu.items.push({
22792                 xtype :'MenuItem',
22793                 xns: Roo.bootstrap,
22794                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22795                 tagname : f,
22796                 listeners : {
22797                     click : function()
22798                     {
22799                         editorcore.insertTag(this.tagname);
22800                         editor.focus();
22801                     }
22802                 }
22803                 
22804             });
22805         });
22806          children.push(style);   
22807             
22808             
22809         btn('bold',false,true);
22810         btn('italic',false,true);
22811         btn('align-left', 'justifyleft',true);
22812         btn('align-center', 'justifycenter',true);
22813         btn('align-right' , 'justifyright',true);
22814         btn('link', false, false, function(btn) {
22815             //Roo.log("create link?");
22816             var url = prompt(this.createLinkText, this.defaultLinkValue);
22817             if(url && url != 'http:/'+'/'){
22818                 this.editorcore.relayCmd('createlink', url);
22819             }
22820         }),
22821         btn('list','insertunorderedlist',true);
22822         btn('pencil', false,true, function(btn){
22823                 Roo.log(this);
22824                 
22825                 this.toggleSourceEdit(btn.pressed);
22826         });
22827         /*
22828         var cog = {
22829                 xtype: 'Button',
22830                 size : 'sm',
22831                 xns: Roo.bootstrap,
22832                 glyphicon : 'cog',
22833                 //html : 'submit'
22834                 menu : {
22835                     xtype: 'Menu',
22836                     xns: Roo.bootstrap,
22837                     items:  []
22838                 }
22839         };
22840         
22841         cog.menu.items.push({
22842             xtype :'MenuItem',
22843             xns: Roo.bootstrap,
22844             html : Clean styles,
22845             tagname : f,
22846             listeners : {
22847                 click : function()
22848                 {
22849                     editorcore.insertTag(this.tagname);
22850                     editor.focus();
22851                 }
22852             }
22853             
22854         });
22855        */
22856         
22857          
22858        this.xtype = 'NavSimplebar';
22859         
22860         for(var i=0;i< children.length;i++) {
22861             
22862             this.buttons.add(this.addxtypeChild(children[i]));
22863             
22864         }
22865         
22866         editor.on('editorevent', this.updateToolbar, this);
22867     },
22868     onBtnClick : function(id)
22869     {
22870        this.editorcore.relayCmd(id);
22871        this.editorcore.focus();
22872     },
22873     
22874     /**
22875      * Protected method that will not generally be called directly. It triggers
22876      * a toolbar update by reading the markup state of the current selection in the editor.
22877      */
22878     updateToolbar: function(){
22879
22880         if(!this.editorcore.activated){
22881             this.editor.onFirstFocus(); // is this neeed?
22882             return;
22883         }
22884
22885         var btns = this.buttons; 
22886         var doc = this.editorcore.doc;
22887         btns.get('bold').setActive(doc.queryCommandState('bold'));
22888         btns.get('italic').setActive(doc.queryCommandState('italic'));
22889         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22890         
22891         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22892         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22893         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22894         
22895         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22896         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22897          /*
22898         
22899         var ans = this.editorcore.getAllAncestors();
22900         if (this.formatCombo) {
22901             
22902             
22903             var store = this.formatCombo.store;
22904             this.formatCombo.setValue("");
22905             for (var i =0; i < ans.length;i++) {
22906                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22907                     // select it..
22908                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22909                     break;
22910                 }
22911             }
22912         }
22913         
22914         
22915         
22916         // hides menus... - so this cant be on a menu...
22917         Roo.bootstrap.MenuMgr.hideAll();
22918         */
22919         Roo.bootstrap.MenuMgr.hideAll();
22920         //this.editorsyncValue();
22921     },
22922     onFirstFocus: function() {
22923         this.buttons.each(function(item){
22924            item.enable();
22925         });
22926     },
22927     toggleSourceEdit : function(sourceEditMode){
22928         
22929           
22930         if(sourceEditMode){
22931             Roo.log("disabling buttons");
22932            this.buttons.each( function(item){
22933                 if(item.cmd != 'pencil'){
22934                     item.disable();
22935                 }
22936             });
22937           
22938         }else{
22939             Roo.log("enabling buttons");
22940             if(this.editorcore.initialized){
22941                 this.buttons.each( function(item){
22942                     item.enable();
22943                 });
22944             }
22945             
22946         }
22947         Roo.log("calling toggole on editor");
22948         // tell the editor that it's been pressed..
22949         this.editor.toggleSourceEdit(sourceEditMode);
22950        
22951     }
22952 });
22953
22954
22955
22956
22957
22958 /**
22959  * @class Roo.bootstrap.Table.AbstractSelectionModel
22960  * @extends Roo.util.Observable
22961  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22962  * implemented by descendant classes.  This class should not be directly instantiated.
22963  * @constructor
22964  */
22965 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22966     this.locked = false;
22967     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22968 };
22969
22970
22971 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22972     /** @ignore Called by the grid automatically. Do not call directly. */
22973     init : function(grid){
22974         this.grid = grid;
22975         this.initEvents();
22976     },
22977
22978     /**
22979      * Locks the selections.
22980      */
22981     lock : function(){
22982         this.locked = true;
22983     },
22984
22985     /**
22986      * Unlocks the selections.
22987      */
22988     unlock : function(){
22989         this.locked = false;
22990     },
22991
22992     /**
22993      * Returns true if the selections are locked.
22994      * @return {Boolean}
22995      */
22996     isLocked : function(){
22997         return this.locked;
22998     }
22999 });
23000 /**
23001  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23002  * @class Roo.bootstrap.Table.RowSelectionModel
23003  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23004  * It supports multiple selections and keyboard selection/navigation. 
23005  * @constructor
23006  * @param {Object} config
23007  */
23008
23009 Roo.bootstrap.Table.RowSelectionModel = function(config){
23010     Roo.apply(this, config);
23011     this.selections = new Roo.util.MixedCollection(false, function(o){
23012         return o.id;
23013     });
23014
23015     this.last = false;
23016     this.lastActive = false;
23017
23018     this.addEvents({
23019         /**
23020              * @event selectionchange
23021              * Fires when the selection changes
23022              * @param {SelectionModel} this
23023              */
23024             "selectionchange" : true,
23025         /**
23026              * @event afterselectionchange
23027              * Fires after the selection changes (eg. by key press or clicking)
23028              * @param {SelectionModel} this
23029              */
23030             "afterselectionchange" : true,
23031         /**
23032              * @event beforerowselect
23033              * Fires when a row is selected being selected, return false to cancel.
23034              * @param {SelectionModel} this
23035              * @param {Number} rowIndex The selected index
23036              * @param {Boolean} keepExisting False if other selections will be cleared
23037              */
23038             "beforerowselect" : true,
23039         /**
23040              * @event rowselect
23041              * Fires when a row is selected.
23042              * @param {SelectionModel} this
23043              * @param {Number} rowIndex The selected index
23044              * @param {Roo.data.Record} r The record
23045              */
23046             "rowselect" : true,
23047         /**
23048              * @event rowdeselect
23049              * Fires when a row is deselected.
23050              * @param {SelectionModel} this
23051              * @param {Number} rowIndex The selected index
23052              */
23053         "rowdeselect" : true
23054     });
23055     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23056     this.locked = false;
23057  };
23058
23059 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23060     /**
23061      * @cfg {Boolean} singleSelect
23062      * True to allow selection of only one row at a time (defaults to false)
23063      */
23064     singleSelect : false,
23065
23066     // private
23067     initEvents : function()
23068     {
23069
23070         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23071         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23072         //}else{ // allow click to work like normal
23073          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23074         //}
23075         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23076         this.grid.on("rowclick", this.handleMouseDown, this);
23077         
23078         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23079             "up" : function(e){
23080                 if(!e.shiftKey){
23081                     this.selectPrevious(e.shiftKey);
23082                 }else if(this.last !== false && this.lastActive !== false){
23083                     var last = this.last;
23084                     this.selectRange(this.last,  this.lastActive-1);
23085                     this.grid.getView().focusRow(this.lastActive);
23086                     if(last !== false){
23087                         this.last = last;
23088                     }
23089                 }else{
23090                     this.selectFirstRow();
23091                 }
23092                 this.fireEvent("afterselectionchange", this);
23093             },
23094             "down" : function(e){
23095                 if(!e.shiftKey){
23096                     this.selectNext(e.shiftKey);
23097                 }else if(this.last !== false && this.lastActive !== false){
23098                     var last = this.last;
23099                     this.selectRange(this.last,  this.lastActive+1);
23100                     this.grid.getView().focusRow(this.lastActive);
23101                     if(last !== false){
23102                         this.last = last;
23103                     }
23104                 }else{
23105                     this.selectFirstRow();
23106                 }
23107                 this.fireEvent("afterselectionchange", this);
23108             },
23109             scope: this
23110         });
23111         this.grid.store.on('load', function(){
23112             this.selections.clear();
23113         },this);
23114         /*
23115         var view = this.grid.view;
23116         view.on("refresh", this.onRefresh, this);
23117         view.on("rowupdated", this.onRowUpdated, this);
23118         view.on("rowremoved", this.onRemove, this);
23119         */
23120     },
23121
23122     // private
23123     onRefresh : function()
23124     {
23125         var ds = this.grid.store, i, v = this.grid.view;
23126         var s = this.selections;
23127         s.each(function(r){
23128             if((i = ds.indexOfId(r.id)) != -1){
23129                 v.onRowSelect(i);
23130             }else{
23131                 s.remove(r);
23132             }
23133         });
23134     },
23135
23136     // private
23137     onRemove : function(v, index, r){
23138         this.selections.remove(r);
23139     },
23140
23141     // private
23142     onRowUpdated : function(v, index, r){
23143         if(this.isSelected(r)){
23144             v.onRowSelect(index);
23145         }
23146     },
23147
23148     /**
23149      * Select records.
23150      * @param {Array} records The records to select
23151      * @param {Boolean} keepExisting (optional) True to keep existing selections
23152      */
23153     selectRecords : function(records, keepExisting)
23154     {
23155         if(!keepExisting){
23156             this.clearSelections();
23157         }
23158             var ds = this.grid.store;
23159         for(var i = 0, len = records.length; i < len; i++){
23160             this.selectRow(ds.indexOf(records[i]), true);
23161         }
23162     },
23163
23164     /**
23165      * Gets the number of selected rows.
23166      * @return {Number}
23167      */
23168     getCount : function(){
23169         return this.selections.length;
23170     },
23171
23172     /**
23173      * Selects the first row in the grid.
23174      */
23175     selectFirstRow : function(){
23176         this.selectRow(0);
23177     },
23178
23179     /**
23180      * Select the last row.
23181      * @param {Boolean} keepExisting (optional) True to keep existing selections
23182      */
23183     selectLastRow : function(keepExisting){
23184         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23185         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23186     },
23187
23188     /**
23189      * Selects the row immediately following the last selected row.
23190      * @param {Boolean} keepExisting (optional) True to keep existing selections
23191      */
23192     selectNext : function(keepExisting)
23193     {
23194             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23195             this.selectRow(this.last+1, keepExisting);
23196             this.grid.getView().focusRow(this.last);
23197         }
23198     },
23199
23200     /**
23201      * Selects the row that precedes the last selected row.
23202      * @param {Boolean} keepExisting (optional) True to keep existing selections
23203      */
23204     selectPrevious : function(keepExisting){
23205         if(this.last){
23206             this.selectRow(this.last-1, keepExisting);
23207             this.grid.getView().focusRow(this.last);
23208         }
23209     },
23210
23211     /**
23212      * Returns the selected records
23213      * @return {Array} Array of selected records
23214      */
23215     getSelections : function(){
23216         return [].concat(this.selections.items);
23217     },
23218
23219     /**
23220      * Returns the first selected record.
23221      * @return {Record}
23222      */
23223     getSelected : function(){
23224         return this.selections.itemAt(0);
23225     },
23226
23227
23228     /**
23229      * Clears all selections.
23230      */
23231     clearSelections : function(fast)
23232     {
23233         if(this.locked) {
23234             return;
23235         }
23236         if(fast !== true){
23237                 var ds = this.grid.store;
23238             var s = this.selections;
23239             s.each(function(r){
23240                 this.deselectRow(ds.indexOfId(r.id));
23241             }, this);
23242             s.clear();
23243         }else{
23244             this.selections.clear();
23245         }
23246         this.last = false;
23247     },
23248
23249
23250     /**
23251      * Selects all rows.
23252      */
23253     selectAll : function(){
23254         if(this.locked) {
23255             return;
23256         }
23257         this.selections.clear();
23258         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23259             this.selectRow(i, true);
23260         }
23261     },
23262
23263     /**
23264      * Returns True if there is a selection.
23265      * @return {Boolean}
23266      */
23267     hasSelection : function(){
23268         return this.selections.length > 0;
23269     },
23270
23271     /**
23272      * Returns True if the specified row is selected.
23273      * @param {Number/Record} record The record or index of the record to check
23274      * @return {Boolean}
23275      */
23276     isSelected : function(index){
23277             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23278         return (r && this.selections.key(r.id) ? true : false);
23279     },
23280
23281     /**
23282      * Returns True if the specified record id is selected.
23283      * @param {String} id The id of record to check
23284      * @return {Boolean}
23285      */
23286     isIdSelected : function(id){
23287         return (this.selections.key(id) ? true : false);
23288     },
23289
23290
23291     // private
23292     handleMouseDBClick : function(e, t){
23293         
23294     },
23295     // private
23296     handleMouseDown : function(e, t)
23297     {
23298             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23299         if(this.isLocked() || rowIndex < 0 ){
23300             return;
23301         };
23302         if(e.shiftKey && this.last !== false){
23303             var last = this.last;
23304             this.selectRange(last, rowIndex, e.ctrlKey);
23305             this.last = last; // reset the last
23306             t.focus();
23307     
23308         }else{
23309             var isSelected = this.isSelected(rowIndex);
23310             //Roo.log("select row:" + rowIndex);
23311             if(isSelected){
23312                 this.deselectRow(rowIndex);
23313             } else {
23314                         this.selectRow(rowIndex, true);
23315             }
23316     
23317             /*
23318                 if(e.button !== 0 && isSelected){
23319                 alert('rowIndex 2: ' + rowIndex);
23320                     view.focusRow(rowIndex);
23321                 }else if(e.ctrlKey && isSelected){
23322                     this.deselectRow(rowIndex);
23323                 }else if(!isSelected){
23324                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23325                     view.focusRow(rowIndex);
23326                 }
23327             */
23328         }
23329         this.fireEvent("afterselectionchange", this);
23330     },
23331     // private
23332     handleDragableRowClick :  function(grid, rowIndex, e) 
23333     {
23334         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23335             this.selectRow(rowIndex, false);
23336             grid.view.focusRow(rowIndex);
23337              this.fireEvent("afterselectionchange", this);
23338         }
23339     },
23340     
23341     /**
23342      * Selects multiple rows.
23343      * @param {Array} rows Array of the indexes of the row to select
23344      * @param {Boolean} keepExisting (optional) True to keep existing selections
23345      */
23346     selectRows : function(rows, keepExisting){
23347         if(!keepExisting){
23348             this.clearSelections();
23349         }
23350         for(var i = 0, len = rows.length; i < len; i++){
23351             this.selectRow(rows[i], true);
23352         }
23353     },
23354
23355     /**
23356      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23357      * @param {Number} startRow The index of the first row in the range
23358      * @param {Number} endRow The index of the last row in the range
23359      * @param {Boolean} keepExisting (optional) True to retain existing selections
23360      */
23361     selectRange : function(startRow, endRow, keepExisting){
23362         if(this.locked) {
23363             return;
23364         }
23365         if(!keepExisting){
23366             this.clearSelections();
23367         }
23368         if(startRow <= endRow){
23369             for(var i = startRow; i <= endRow; i++){
23370                 this.selectRow(i, true);
23371             }
23372         }else{
23373             for(var i = startRow; i >= endRow; i--){
23374                 this.selectRow(i, true);
23375             }
23376         }
23377     },
23378
23379     /**
23380      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23381      * @param {Number} startRow The index of the first row in the range
23382      * @param {Number} endRow The index of the last row in the range
23383      */
23384     deselectRange : function(startRow, endRow, preventViewNotify){
23385         if(this.locked) {
23386             return;
23387         }
23388         for(var i = startRow; i <= endRow; i++){
23389             this.deselectRow(i, preventViewNotify);
23390         }
23391     },
23392
23393     /**
23394      * Selects a row.
23395      * @param {Number} row The index of the row to select
23396      * @param {Boolean} keepExisting (optional) True to keep existing selections
23397      */
23398     selectRow : function(index, keepExisting, preventViewNotify)
23399     {
23400             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23401             return;
23402         }
23403         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23404             if(!keepExisting || this.singleSelect){
23405                 this.clearSelections();
23406             }
23407             
23408             var r = this.grid.store.getAt(index);
23409             //console.log('selectRow - record id :' + r.id);
23410             
23411             this.selections.add(r);
23412             this.last = this.lastActive = index;
23413             if(!preventViewNotify){
23414                 var proxy = new Roo.Element(
23415                                 this.grid.getRowDom(index)
23416                 );
23417                 proxy.addClass('bg-info info');
23418             }
23419             this.fireEvent("rowselect", this, index, r);
23420             this.fireEvent("selectionchange", this);
23421         }
23422     },
23423
23424     /**
23425      * Deselects a row.
23426      * @param {Number} row The index of the row to deselect
23427      */
23428     deselectRow : function(index, preventViewNotify)
23429     {
23430         if(this.locked) {
23431             return;
23432         }
23433         if(this.last == index){
23434             this.last = false;
23435         }
23436         if(this.lastActive == index){
23437             this.lastActive = false;
23438         }
23439         
23440         var r = this.grid.store.getAt(index);
23441         if (!r) {
23442             return;
23443         }
23444         
23445         this.selections.remove(r);
23446         //.console.log('deselectRow - record id :' + r.id);
23447         if(!preventViewNotify){
23448         
23449             var proxy = new Roo.Element(
23450                 this.grid.getRowDom(index)
23451             );
23452             proxy.removeClass('bg-info info');
23453         }
23454         this.fireEvent("rowdeselect", this, index);
23455         this.fireEvent("selectionchange", this);
23456     },
23457
23458     // private
23459     restoreLast : function(){
23460         if(this._last){
23461             this.last = this._last;
23462         }
23463     },
23464
23465     // private
23466     acceptsNav : function(row, col, cm){
23467         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23468     },
23469
23470     // private
23471     onEditorKey : function(field, e){
23472         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23473         if(k == e.TAB){
23474             e.stopEvent();
23475             ed.completeEdit();
23476             if(e.shiftKey){
23477                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23478             }else{
23479                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23480             }
23481         }else if(k == e.ENTER && !e.ctrlKey){
23482             e.stopEvent();
23483             ed.completeEdit();
23484             if(e.shiftKey){
23485                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23486             }else{
23487                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23488             }
23489         }else if(k == e.ESC){
23490             ed.cancelEdit();
23491         }
23492         if(newCell){
23493             g.startEditing(newCell[0], newCell[1]);
23494         }
23495     }
23496 });
23497 /*
23498  * Based on:
23499  * Ext JS Library 1.1.1
23500  * Copyright(c) 2006-2007, Ext JS, LLC.
23501  *
23502  * Originally Released Under LGPL - original licence link has changed is not relivant.
23503  *
23504  * Fork - LGPL
23505  * <script type="text/javascript">
23506  */
23507  
23508 /**
23509  * @class Roo.bootstrap.PagingToolbar
23510  * @extends Roo.bootstrap.NavSimplebar
23511  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23512  * @constructor
23513  * Create a new PagingToolbar
23514  * @param {Object} config The config object
23515  * @param {Roo.data.Store} store
23516  */
23517 Roo.bootstrap.PagingToolbar = function(config)
23518 {
23519     // old args format still supported... - xtype is prefered..
23520         // created from xtype...
23521     
23522     this.ds = config.dataSource;
23523     
23524     if (config.store && !this.ds) {
23525         this.store= Roo.factory(config.store, Roo.data);
23526         this.ds = this.store;
23527         this.ds.xmodule = this.xmodule || false;
23528     }
23529     
23530     this.toolbarItems = [];
23531     if (config.items) {
23532         this.toolbarItems = config.items;
23533     }
23534     
23535     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23536     
23537     this.cursor = 0;
23538     
23539     if (this.ds) { 
23540         this.bind(this.ds);
23541     }
23542     
23543     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23544     
23545 };
23546
23547 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23548     /**
23549      * @cfg {Roo.data.Store} dataSource
23550      * The underlying data store providing the paged data
23551      */
23552     /**
23553      * @cfg {String/HTMLElement/Element} container
23554      * container The id or element that will contain the toolbar
23555      */
23556     /**
23557      * @cfg {Boolean} displayInfo
23558      * True to display the displayMsg (defaults to false)
23559      */
23560     /**
23561      * @cfg {Number} pageSize
23562      * The number of records to display per page (defaults to 20)
23563      */
23564     pageSize: 20,
23565     /**
23566      * @cfg {String} displayMsg
23567      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23568      */
23569     displayMsg : 'Displaying {0} - {1} of {2}',
23570     /**
23571      * @cfg {String} emptyMsg
23572      * The message to display when no records are found (defaults to "No data to display")
23573      */
23574     emptyMsg : 'No data to display',
23575     /**
23576      * Customizable piece of the default paging text (defaults to "Page")
23577      * @type String
23578      */
23579     beforePageText : "Page",
23580     /**
23581      * Customizable piece of the default paging text (defaults to "of %0")
23582      * @type String
23583      */
23584     afterPageText : "of {0}",
23585     /**
23586      * Customizable piece of the default paging text (defaults to "First Page")
23587      * @type String
23588      */
23589     firstText : "First Page",
23590     /**
23591      * Customizable piece of the default paging text (defaults to "Previous Page")
23592      * @type String
23593      */
23594     prevText : "Previous Page",
23595     /**
23596      * Customizable piece of the default paging text (defaults to "Next Page")
23597      * @type String
23598      */
23599     nextText : "Next Page",
23600     /**
23601      * Customizable piece of the default paging text (defaults to "Last Page")
23602      * @type String
23603      */
23604     lastText : "Last Page",
23605     /**
23606      * Customizable piece of the default paging text (defaults to "Refresh")
23607      * @type String
23608      */
23609     refreshText : "Refresh",
23610
23611     buttons : false,
23612     // private
23613     onRender : function(ct, position) 
23614     {
23615         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23616         this.navgroup.parentId = this.id;
23617         this.navgroup.onRender(this.el, null);
23618         // add the buttons to the navgroup
23619         
23620         if(this.displayInfo){
23621             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23622             this.displayEl = this.el.select('.x-paging-info', true).first();
23623 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23624 //            this.displayEl = navel.el.select('span',true).first();
23625         }
23626         
23627         var _this = this;
23628         
23629         if(this.buttons){
23630             Roo.each(_this.buttons, function(e){ // this might need to use render????
23631                Roo.factory(e).onRender(_this.el, null);
23632             });
23633         }
23634             
23635         Roo.each(_this.toolbarItems, function(e) {
23636             _this.navgroup.addItem(e);
23637         });
23638         
23639         
23640         this.first = this.navgroup.addItem({
23641             tooltip: this.firstText,
23642             cls: "prev",
23643             icon : 'fa fa-backward',
23644             disabled: true,
23645             preventDefault: true,
23646             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23647         });
23648         
23649         this.prev =  this.navgroup.addItem({
23650             tooltip: this.prevText,
23651             cls: "prev",
23652             icon : 'fa fa-step-backward',
23653             disabled: true,
23654             preventDefault: true,
23655             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23656         });
23657     //this.addSeparator();
23658         
23659         
23660         var field = this.navgroup.addItem( {
23661             tagtype : 'span',
23662             cls : 'x-paging-position',
23663             
23664             html : this.beforePageText  +
23665                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23666                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23667          } ); //?? escaped?
23668         
23669         this.field = field.el.select('input', true).first();
23670         this.field.on("keydown", this.onPagingKeydown, this);
23671         this.field.on("focus", function(){this.dom.select();});
23672     
23673     
23674         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23675         //this.field.setHeight(18);
23676         //this.addSeparator();
23677         this.next = this.navgroup.addItem({
23678             tooltip: this.nextText,
23679             cls: "next",
23680             html : ' <i class="fa fa-step-forward">',
23681             disabled: true,
23682             preventDefault: true,
23683             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23684         });
23685         this.last = this.navgroup.addItem({
23686             tooltip: this.lastText,
23687             icon : 'fa fa-forward',
23688             cls: "next",
23689             disabled: true,
23690             preventDefault: true,
23691             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23692         });
23693     //this.addSeparator();
23694         this.loading = this.navgroup.addItem({
23695             tooltip: this.refreshText,
23696             icon: 'fa fa-refresh',
23697             preventDefault: true,
23698             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23699         });
23700         
23701     },
23702
23703     // private
23704     updateInfo : function(){
23705         if(this.displayEl){
23706             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23707             var msg = count == 0 ?
23708                 this.emptyMsg :
23709                 String.format(
23710                     this.displayMsg,
23711                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23712                 );
23713             this.displayEl.update(msg);
23714         }
23715     },
23716
23717     // private
23718     onLoad : function(ds, r, o){
23719        this.cursor = o.params ? o.params.start : 0;
23720        var d = this.getPageData(),
23721             ap = d.activePage,
23722             ps = d.pages;
23723         
23724        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23725        this.field.dom.value = ap;
23726        this.first.setDisabled(ap == 1);
23727        this.prev.setDisabled(ap == 1);
23728        this.next.setDisabled(ap == ps);
23729        this.last.setDisabled(ap == ps);
23730        this.loading.enable();
23731        this.updateInfo();
23732     },
23733
23734     // private
23735     getPageData : function(){
23736         var total = this.ds.getTotalCount();
23737         return {
23738             total : total,
23739             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23740             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23741         };
23742     },
23743
23744     // private
23745     onLoadError : function(){
23746         this.loading.enable();
23747     },
23748
23749     // private
23750     onPagingKeydown : function(e){
23751         var k = e.getKey();
23752         var d = this.getPageData();
23753         if(k == e.RETURN){
23754             var v = this.field.dom.value, pageNum;
23755             if(!v || isNaN(pageNum = parseInt(v, 10))){
23756                 this.field.dom.value = d.activePage;
23757                 return;
23758             }
23759             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23760             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23761             e.stopEvent();
23762         }
23763         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))
23764         {
23765           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23766           this.field.dom.value = pageNum;
23767           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23768           e.stopEvent();
23769         }
23770         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23771         {
23772           var v = this.field.dom.value, pageNum; 
23773           var increment = (e.shiftKey) ? 10 : 1;
23774           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23775                 increment *= -1;
23776           }
23777           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23778             this.field.dom.value = d.activePage;
23779             return;
23780           }
23781           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23782           {
23783             this.field.dom.value = parseInt(v, 10) + increment;
23784             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23785             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23786           }
23787           e.stopEvent();
23788         }
23789     },
23790
23791     // private
23792     beforeLoad : function(){
23793         if(this.loading){
23794             this.loading.disable();
23795         }
23796     },
23797
23798     // private
23799     onClick : function(which){
23800         
23801         var ds = this.ds;
23802         if (!ds) {
23803             return;
23804         }
23805         
23806         switch(which){
23807             case "first":
23808                 ds.load({params:{start: 0, limit: this.pageSize}});
23809             break;
23810             case "prev":
23811                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23812             break;
23813             case "next":
23814                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23815             break;
23816             case "last":
23817                 var total = ds.getTotalCount();
23818                 var extra = total % this.pageSize;
23819                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23820                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23821             break;
23822             case "refresh":
23823                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23824             break;
23825         }
23826     },
23827
23828     /**
23829      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23830      * @param {Roo.data.Store} store The data store to unbind
23831      */
23832     unbind : function(ds){
23833         ds.un("beforeload", this.beforeLoad, this);
23834         ds.un("load", this.onLoad, this);
23835         ds.un("loadexception", this.onLoadError, this);
23836         ds.un("remove", this.updateInfo, this);
23837         ds.un("add", this.updateInfo, this);
23838         this.ds = undefined;
23839     },
23840
23841     /**
23842      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23843      * @param {Roo.data.Store} store The data store to bind
23844      */
23845     bind : function(ds){
23846         ds.on("beforeload", this.beforeLoad, this);
23847         ds.on("load", this.onLoad, this);
23848         ds.on("loadexception", this.onLoadError, this);
23849         ds.on("remove", this.updateInfo, this);
23850         ds.on("add", this.updateInfo, this);
23851         this.ds = ds;
23852     }
23853 });/*
23854  * - LGPL
23855  *
23856  * element
23857  * 
23858  */
23859
23860 /**
23861  * @class Roo.bootstrap.MessageBar
23862  * @extends Roo.bootstrap.Component
23863  * Bootstrap MessageBar class
23864  * @cfg {String} html contents of the MessageBar
23865  * @cfg {String} weight (info | success | warning | danger) default info
23866  * @cfg {String} beforeClass insert the bar before the given class
23867  * @cfg {Boolean} closable (true | false) default false
23868  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23869  * 
23870  * @constructor
23871  * Create a new Element
23872  * @param {Object} config The config object
23873  */
23874
23875 Roo.bootstrap.MessageBar = function(config){
23876     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23877 };
23878
23879 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23880     
23881     html: '',
23882     weight: 'info',
23883     closable: false,
23884     fixed: false,
23885     beforeClass: 'bootstrap-sticky-wrap',
23886     
23887     getAutoCreate : function(){
23888         
23889         var cfg = {
23890             tag: 'div',
23891             cls: 'alert alert-dismissable alert-' + this.weight,
23892             cn: [
23893                 {
23894                     tag: 'span',
23895                     cls: 'message',
23896                     html: this.html || ''
23897                 }
23898             ]
23899         };
23900         
23901         if(this.fixed){
23902             cfg.cls += ' alert-messages-fixed';
23903         }
23904         
23905         if(this.closable){
23906             cfg.cn.push({
23907                 tag: 'button',
23908                 cls: 'close',
23909                 html: 'x'
23910             });
23911         }
23912         
23913         return cfg;
23914     },
23915     
23916     onRender : function(ct, position)
23917     {
23918         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23919         
23920         if(!this.el){
23921             var cfg = Roo.apply({},  this.getAutoCreate());
23922             cfg.id = Roo.id();
23923             
23924             if (this.cls) {
23925                 cfg.cls += ' ' + this.cls;
23926             }
23927             if (this.style) {
23928                 cfg.style = this.style;
23929             }
23930             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23931             
23932             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23933         }
23934         
23935         this.el.select('>button.close').on('click', this.hide, this);
23936         
23937     },
23938     
23939     show : function()
23940     {
23941         if (!this.rendered) {
23942             this.render();
23943         }
23944         
23945         this.el.show();
23946         
23947         this.fireEvent('show', this);
23948         
23949     },
23950     
23951     hide : function()
23952     {
23953         if (!this.rendered) {
23954             this.render();
23955         }
23956         
23957         this.el.hide();
23958         
23959         this.fireEvent('hide', this);
23960     },
23961     
23962     update : function()
23963     {
23964 //        var e = this.el.dom.firstChild;
23965 //        
23966 //        if(this.closable){
23967 //            e = e.nextSibling;
23968 //        }
23969 //        
23970 //        e.data = this.html || '';
23971
23972         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23973     }
23974    
23975 });
23976
23977  
23978
23979      /*
23980  * - LGPL
23981  *
23982  * Graph
23983  * 
23984  */
23985
23986
23987 /**
23988  * @class Roo.bootstrap.Graph
23989  * @extends Roo.bootstrap.Component
23990  * Bootstrap Graph class
23991 > Prameters
23992  -sm {number} sm 4
23993  -md {number} md 5
23994  @cfg {String} graphtype  bar | vbar | pie
23995  @cfg {number} g_x coodinator | centre x (pie)
23996  @cfg {number} g_y coodinator | centre y (pie)
23997  @cfg {number} g_r radius (pie)
23998  @cfg {number} g_height height of the chart (respected by all elements in the set)
23999  @cfg {number} g_width width of the chart (respected by all elements in the set)
24000  @cfg {Object} title The title of the chart
24001     
24002  -{Array}  values
24003  -opts (object) options for the chart 
24004      o {
24005      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24006      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24007      o vgutter (number)
24008      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.
24009      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24010      o to
24011      o stretch (boolean)
24012      o }
24013  -opts (object) options for the pie
24014      o{
24015      o cut
24016      o startAngle (number)
24017      o endAngle (number)
24018      } 
24019  *
24020  * @constructor
24021  * Create a new Input
24022  * @param {Object} config The config object
24023  */
24024
24025 Roo.bootstrap.Graph = function(config){
24026     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24027     
24028     this.addEvents({
24029         // img events
24030         /**
24031          * @event click
24032          * The img click event for the img.
24033          * @param {Roo.EventObject} e
24034          */
24035         "click" : true
24036     });
24037 };
24038
24039 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24040     
24041     sm: 4,
24042     md: 5,
24043     graphtype: 'bar',
24044     g_height: 250,
24045     g_width: 400,
24046     g_x: 50,
24047     g_y: 50,
24048     g_r: 30,
24049     opts:{
24050         //g_colors: this.colors,
24051         g_type: 'soft',
24052         g_gutter: '20%'
24053
24054     },
24055     title : false,
24056
24057     getAutoCreate : function(){
24058         
24059         var cfg = {
24060             tag: 'div',
24061             html : null
24062         };
24063         
24064         
24065         return  cfg;
24066     },
24067
24068     onRender : function(ct,position){
24069         
24070         
24071         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24072         
24073         if (typeof(Raphael) == 'undefined') {
24074             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24075             return;
24076         }
24077         
24078         this.raphael = Raphael(this.el.dom);
24079         
24080                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24081                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24082                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24083                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24084                 /*
24085                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24086                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24087                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24088                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24089                 
24090                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24091                 r.barchart(330, 10, 300, 220, data1);
24092                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24093                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24094                 */
24095                 
24096                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24097                 // r.barchart(30, 30, 560, 250,  xdata, {
24098                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24099                 //     axis : "0 0 1 1",
24100                 //     axisxlabels :  xdata
24101                 //     //yvalues : cols,
24102                    
24103                 // });
24104 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24105 //        
24106 //        this.load(null,xdata,{
24107 //                axis : "0 0 1 1",
24108 //                axisxlabels :  xdata
24109 //                });
24110
24111     },
24112
24113     load : function(graphtype,xdata,opts)
24114     {
24115         this.raphael.clear();
24116         if(!graphtype) {
24117             graphtype = this.graphtype;
24118         }
24119         if(!opts){
24120             opts = this.opts;
24121         }
24122         var r = this.raphael,
24123             fin = function () {
24124                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24125             },
24126             fout = function () {
24127                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24128             },
24129             pfin = function() {
24130                 this.sector.stop();
24131                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24132
24133                 if (this.label) {
24134                     this.label[0].stop();
24135                     this.label[0].attr({ r: 7.5 });
24136                     this.label[1].attr({ "font-weight": 800 });
24137                 }
24138             },
24139             pfout = function() {
24140                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24141
24142                 if (this.label) {
24143                     this.label[0].animate({ r: 5 }, 500, "bounce");
24144                     this.label[1].attr({ "font-weight": 400 });
24145                 }
24146             };
24147
24148         switch(graphtype){
24149             case 'bar':
24150                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24151                 break;
24152             case 'hbar':
24153                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24154                 break;
24155             case 'pie':
24156 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24157 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24158 //            
24159                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24160                 
24161                 break;
24162
24163         }
24164         
24165         if(this.title){
24166             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24167         }
24168         
24169     },
24170     
24171     setTitle: function(o)
24172     {
24173         this.title = o;
24174     },
24175     
24176     initEvents: function() {
24177         
24178         if(!this.href){
24179             this.el.on('click', this.onClick, this);
24180         }
24181     },
24182     
24183     onClick : function(e)
24184     {
24185         Roo.log('img onclick');
24186         this.fireEvent('click', this, e);
24187     }
24188    
24189 });
24190
24191  
24192 /*
24193  * - LGPL
24194  *
24195  * numberBox
24196  * 
24197  */
24198 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24199
24200 /**
24201  * @class Roo.bootstrap.dash.NumberBox
24202  * @extends Roo.bootstrap.Component
24203  * Bootstrap NumberBox class
24204  * @cfg {String} headline Box headline
24205  * @cfg {String} content Box content
24206  * @cfg {String} icon Box icon
24207  * @cfg {String} footer Footer text
24208  * @cfg {String} fhref Footer href
24209  * 
24210  * @constructor
24211  * Create a new NumberBox
24212  * @param {Object} config The config object
24213  */
24214
24215
24216 Roo.bootstrap.dash.NumberBox = function(config){
24217     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24218     
24219 };
24220
24221 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24222     
24223     headline : '',
24224     content : '',
24225     icon : '',
24226     footer : '',
24227     fhref : '',
24228     ficon : '',
24229     
24230     getAutoCreate : function(){
24231         
24232         var cfg = {
24233             tag : 'div',
24234             cls : 'small-box ',
24235             cn : [
24236                 {
24237                     tag : 'div',
24238                     cls : 'inner',
24239                     cn :[
24240                         {
24241                             tag : 'h3',
24242                             cls : 'roo-headline',
24243                             html : this.headline
24244                         },
24245                         {
24246                             tag : 'p',
24247                             cls : 'roo-content',
24248                             html : this.content
24249                         }
24250                     ]
24251                 }
24252             ]
24253         };
24254         
24255         if(this.icon){
24256             cfg.cn.push({
24257                 tag : 'div',
24258                 cls : 'icon',
24259                 cn :[
24260                     {
24261                         tag : 'i',
24262                         cls : 'ion ' + this.icon
24263                     }
24264                 ]
24265             });
24266         }
24267         
24268         if(this.footer){
24269             var footer = {
24270                 tag : 'a',
24271                 cls : 'small-box-footer',
24272                 href : this.fhref || '#',
24273                 html : this.footer
24274             };
24275             
24276             cfg.cn.push(footer);
24277             
24278         }
24279         
24280         return  cfg;
24281     },
24282
24283     onRender : function(ct,position){
24284         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24285
24286
24287        
24288                 
24289     },
24290
24291     setHeadline: function (value)
24292     {
24293         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24294     },
24295     
24296     setFooter: function (value, href)
24297     {
24298         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24299         
24300         if(href){
24301             this.el.select('a.small-box-footer',true).first().attr('href', href);
24302         }
24303         
24304     },
24305
24306     setContent: function (value)
24307     {
24308         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24309     },
24310
24311     initEvents: function() 
24312     {   
24313         
24314     }
24315     
24316 });
24317
24318  
24319 /*
24320  * - LGPL
24321  *
24322  * TabBox
24323  * 
24324  */
24325 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24326
24327 /**
24328  * @class Roo.bootstrap.dash.TabBox
24329  * @extends Roo.bootstrap.Component
24330  * Bootstrap TabBox class
24331  * @cfg {String} title Title of the TabBox
24332  * @cfg {String} icon Icon of the TabBox
24333  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24334  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24335  * 
24336  * @constructor
24337  * Create a new TabBox
24338  * @param {Object} config The config object
24339  */
24340
24341
24342 Roo.bootstrap.dash.TabBox = function(config){
24343     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24344     this.addEvents({
24345         // raw events
24346         /**
24347          * @event addpane
24348          * When a pane is added
24349          * @param {Roo.bootstrap.dash.TabPane} pane
24350          */
24351         "addpane" : true,
24352         /**
24353          * @event activatepane
24354          * When a pane is activated
24355          * @param {Roo.bootstrap.dash.TabPane} pane
24356          */
24357         "activatepane" : true
24358         
24359          
24360     });
24361     
24362     this.panes = [];
24363 };
24364
24365 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24366
24367     title : '',
24368     icon : false,
24369     showtabs : true,
24370     tabScrollable : false,
24371     
24372     getChildContainer : function()
24373     {
24374         return this.el.select('.tab-content', true).first();
24375     },
24376     
24377     getAutoCreate : function(){
24378         
24379         var header = {
24380             tag: 'li',
24381             cls: 'pull-left header',
24382             html: this.title,
24383             cn : []
24384         };
24385         
24386         if(this.icon){
24387             header.cn.push({
24388                 tag: 'i',
24389                 cls: 'fa ' + this.icon
24390             });
24391         }
24392         
24393         var h = {
24394             tag: 'ul',
24395             cls: 'nav nav-tabs pull-right',
24396             cn: [
24397                 header
24398             ]
24399         };
24400         
24401         if(this.tabScrollable){
24402             h = {
24403                 tag: 'div',
24404                 cls: 'tab-header',
24405                 cn: [
24406                     {
24407                         tag: 'ul',
24408                         cls: 'nav nav-tabs pull-right',
24409                         cn: [
24410                             header
24411                         ]
24412                     }
24413                 ]
24414             };
24415         }
24416         
24417         var cfg = {
24418             tag: 'div',
24419             cls: 'nav-tabs-custom',
24420             cn: [
24421                 h,
24422                 {
24423                     tag: 'div',
24424                     cls: 'tab-content no-padding',
24425                     cn: []
24426                 }
24427             ]
24428         };
24429
24430         return  cfg;
24431     },
24432     initEvents : function()
24433     {
24434         //Roo.log('add add pane handler');
24435         this.on('addpane', this.onAddPane, this);
24436     },
24437      /**
24438      * Updates the box title
24439      * @param {String} html to set the title to.
24440      */
24441     setTitle : function(value)
24442     {
24443         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24444     },
24445     onAddPane : function(pane)
24446     {
24447         this.panes.push(pane);
24448         //Roo.log('addpane');
24449         //Roo.log(pane);
24450         // tabs are rendere left to right..
24451         if(!this.showtabs){
24452             return;
24453         }
24454         
24455         var ctr = this.el.select('.nav-tabs', true).first();
24456          
24457          
24458         var existing = ctr.select('.nav-tab',true);
24459         var qty = existing.getCount();;
24460         
24461         
24462         var tab = ctr.createChild({
24463             tag : 'li',
24464             cls : 'nav-tab' + (qty ? '' : ' active'),
24465             cn : [
24466                 {
24467                     tag : 'a',
24468                     href:'#',
24469                     html : pane.title
24470                 }
24471             ]
24472         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24473         pane.tab = tab;
24474         
24475         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24476         if (!qty) {
24477             pane.el.addClass('active');
24478         }
24479         
24480                 
24481     },
24482     onTabClick : function(ev,un,ob,pane)
24483     {
24484         //Roo.log('tab - prev default');
24485         ev.preventDefault();
24486         
24487         
24488         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24489         pane.tab.addClass('active');
24490         //Roo.log(pane.title);
24491         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24492         // technically we should have a deactivate event.. but maybe add later.
24493         // and it should not de-activate the selected tab...
24494         this.fireEvent('activatepane', pane);
24495         pane.el.addClass('active');
24496         pane.fireEvent('activate');
24497         
24498         
24499     },
24500     
24501     getActivePane : function()
24502     {
24503         var r = false;
24504         Roo.each(this.panes, function(p) {
24505             if(p.el.hasClass('active')){
24506                 r = p;
24507                 return false;
24508             }
24509             
24510             return;
24511         });
24512         
24513         return r;
24514     }
24515     
24516     
24517 });
24518
24519  
24520 /*
24521  * - LGPL
24522  *
24523  * Tab pane
24524  * 
24525  */
24526 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24527 /**
24528  * @class Roo.bootstrap.TabPane
24529  * @extends Roo.bootstrap.Component
24530  * Bootstrap TabPane class
24531  * @cfg {Boolean} active (false | true) Default false
24532  * @cfg {String} title title of panel
24533
24534  * 
24535  * @constructor
24536  * Create a new TabPane
24537  * @param {Object} config The config object
24538  */
24539
24540 Roo.bootstrap.dash.TabPane = function(config){
24541     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24542     
24543     this.addEvents({
24544         // raw events
24545         /**
24546          * @event activate
24547          * When a pane is activated
24548          * @param {Roo.bootstrap.dash.TabPane} pane
24549          */
24550         "activate" : true
24551          
24552     });
24553 };
24554
24555 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24556     
24557     active : false,
24558     title : '',
24559     
24560     // the tabBox that this is attached to.
24561     tab : false,
24562      
24563     getAutoCreate : function() 
24564     {
24565         var cfg = {
24566             tag: 'div',
24567             cls: 'tab-pane'
24568         };
24569         
24570         if(this.active){
24571             cfg.cls += ' active';
24572         }
24573         
24574         return cfg;
24575     },
24576     initEvents  : function()
24577     {
24578         //Roo.log('trigger add pane handler');
24579         this.parent().fireEvent('addpane', this)
24580     },
24581     
24582      /**
24583      * Updates the tab title 
24584      * @param {String} html to set the title to.
24585      */
24586     setTitle: function(str)
24587     {
24588         if (!this.tab) {
24589             return;
24590         }
24591         this.title = str;
24592         this.tab.select('a', true).first().dom.innerHTML = str;
24593         
24594     }
24595     
24596     
24597     
24598 });
24599
24600  
24601
24602
24603  /*
24604  * - LGPL
24605  *
24606  * menu
24607  * 
24608  */
24609 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24610
24611 /**
24612  * @class Roo.bootstrap.menu.Menu
24613  * @extends Roo.bootstrap.Component
24614  * Bootstrap Menu class - container for Menu
24615  * @cfg {String} html Text of the menu
24616  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24617  * @cfg {String} icon Font awesome icon
24618  * @cfg {String} pos Menu align to (top | bottom) default bottom
24619  * 
24620  * 
24621  * @constructor
24622  * Create a new Menu
24623  * @param {Object} config The config object
24624  */
24625
24626
24627 Roo.bootstrap.menu.Menu = function(config){
24628     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24629     
24630     this.addEvents({
24631         /**
24632          * @event beforeshow
24633          * Fires before this menu is displayed
24634          * @param {Roo.bootstrap.menu.Menu} this
24635          */
24636         beforeshow : true,
24637         /**
24638          * @event beforehide
24639          * Fires before this menu is hidden
24640          * @param {Roo.bootstrap.menu.Menu} this
24641          */
24642         beforehide : true,
24643         /**
24644          * @event show
24645          * Fires after this menu is displayed
24646          * @param {Roo.bootstrap.menu.Menu} this
24647          */
24648         show : true,
24649         /**
24650          * @event hide
24651          * Fires after this menu is hidden
24652          * @param {Roo.bootstrap.menu.Menu} this
24653          */
24654         hide : true,
24655         /**
24656          * @event click
24657          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24658          * @param {Roo.bootstrap.menu.Menu} this
24659          * @param {Roo.EventObject} e
24660          */
24661         click : true
24662     });
24663     
24664 };
24665
24666 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24667     
24668     submenu : false,
24669     html : '',
24670     weight : 'default',
24671     icon : false,
24672     pos : 'bottom',
24673     
24674     
24675     getChildContainer : function() {
24676         if(this.isSubMenu){
24677             return this.el;
24678         }
24679         
24680         return this.el.select('ul.dropdown-menu', true).first();  
24681     },
24682     
24683     getAutoCreate : function()
24684     {
24685         var text = [
24686             {
24687                 tag : 'span',
24688                 cls : 'roo-menu-text',
24689                 html : this.html
24690             }
24691         ];
24692         
24693         if(this.icon){
24694             text.unshift({
24695                 tag : 'i',
24696                 cls : 'fa ' + this.icon
24697             })
24698         }
24699         
24700         
24701         var cfg = {
24702             tag : 'div',
24703             cls : 'btn-group',
24704             cn : [
24705                 {
24706                     tag : 'button',
24707                     cls : 'dropdown-button btn btn-' + this.weight,
24708                     cn : text
24709                 },
24710                 {
24711                     tag : 'button',
24712                     cls : 'dropdown-toggle btn btn-' + this.weight,
24713                     cn : [
24714                         {
24715                             tag : 'span',
24716                             cls : 'caret'
24717                         }
24718                     ]
24719                 },
24720                 {
24721                     tag : 'ul',
24722                     cls : 'dropdown-menu'
24723                 }
24724             ]
24725             
24726         };
24727         
24728         if(this.pos == 'top'){
24729             cfg.cls += ' dropup';
24730         }
24731         
24732         if(this.isSubMenu){
24733             cfg = {
24734                 tag : 'ul',
24735                 cls : 'dropdown-menu'
24736             }
24737         }
24738         
24739         return cfg;
24740     },
24741     
24742     onRender : function(ct, position)
24743     {
24744         this.isSubMenu = ct.hasClass('dropdown-submenu');
24745         
24746         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24747     },
24748     
24749     initEvents : function() 
24750     {
24751         if(this.isSubMenu){
24752             return;
24753         }
24754         
24755         this.hidden = true;
24756         
24757         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24758         this.triggerEl.on('click', this.onTriggerPress, this);
24759         
24760         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24761         this.buttonEl.on('click', this.onClick, this);
24762         
24763     },
24764     
24765     list : function()
24766     {
24767         if(this.isSubMenu){
24768             return this.el;
24769         }
24770         
24771         return this.el.select('ul.dropdown-menu', true).first();
24772     },
24773     
24774     onClick : function(e)
24775     {
24776         this.fireEvent("click", this, e);
24777     },
24778     
24779     onTriggerPress  : function(e)
24780     {   
24781         if (this.isVisible()) {
24782             this.hide();
24783         } else {
24784             this.show();
24785         }
24786     },
24787     
24788     isVisible : function(){
24789         return !this.hidden;
24790     },
24791     
24792     show : function()
24793     {
24794         this.fireEvent("beforeshow", this);
24795         
24796         this.hidden = false;
24797         this.el.addClass('open');
24798         
24799         Roo.get(document).on("mouseup", this.onMouseUp, this);
24800         
24801         this.fireEvent("show", this);
24802         
24803         
24804     },
24805     
24806     hide : function()
24807     {
24808         this.fireEvent("beforehide", this);
24809         
24810         this.hidden = true;
24811         this.el.removeClass('open');
24812         
24813         Roo.get(document).un("mouseup", this.onMouseUp);
24814         
24815         this.fireEvent("hide", this);
24816     },
24817     
24818     onMouseUp : function()
24819     {
24820         this.hide();
24821     }
24822     
24823 });
24824
24825  
24826  /*
24827  * - LGPL
24828  *
24829  * menu item
24830  * 
24831  */
24832 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24833
24834 /**
24835  * @class Roo.bootstrap.menu.Item
24836  * @extends Roo.bootstrap.Component
24837  * Bootstrap MenuItem class
24838  * @cfg {Boolean} submenu (true | false) default false
24839  * @cfg {String} html text of the item
24840  * @cfg {String} href the link
24841  * @cfg {Boolean} disable (true | false) default false
24842  * @cfg {Boolean} preventDefault (true | false) default true
24843  * @cfg {String} icon Font awesome icon
24844  * @cfg {String} pos Submenu align to (left | right) default right 
24845  * 
24846  * 
24847  * @constructor
24848  * Create a new Item
24849  * @param {Object} config The config object
24850  */
24851
24852
24853 Roo.bootstrap.menu.Item = function(config){
24854     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24855     this.addEvents({
24856         /**
24857          * @event mouseover
24858          * Fires when the mouse is hovering over this menu
24859          * @param {Roo.bootstrap.menu.Item} this
24860          * @param {Roo.EventObject} e
24861          */
24862         mouseover : true,
24863         /**
24864          * @event mouseout
24865          * Fires when the mouse exits this menu
24866          * @param {Roo.bootstrap.menu.Item} this
24867          * @param {Roo.EventObject} e
24868          */
24869         mouseout : true,
24870         // raw events
24871         /**
24872          * @event click
24873          * The raw click event for the entire grid.
24874          * @param {Roo.EventObject} e
24875          */
24876         click : true
24877     });
24878 };
24879
24880 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24881     
24882     submenu : false,
24883     href : '',
24884     html : '',
24885     preventDefault: true,
24886     disable : false,
24887     icon : false,
24888     pos : 'right',
24889     
24890     getAutoCreate : function()
24891     {
24892         var text = [
24893             {
24894                 tag : 'span',
24895                 cls : 'roo-menu-item-text',
24896                 html : this.html
24897             }
24898         ];
24899         
24900         if(this.icon){
24901             text.unshift({
24902                 tag : 'i',
24903                 cls : 'fa ' + this.icon
24904             })
24905         }
24906         
24907         var cfg = {
24908             tag : 'li',
24909             cn : [
24910                 {
24911                     tag : 'a',
24912                     href : this.href || '#',
24913                     cn : text
24914                 }
24915             ]
24916         };
24917         
24918         if(this.disable){
24919             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24920         }
24921         
24922         if(this.submenu){
24923             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24924             
24925             if(this.pos == 'left'){
24926                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24927             }
24928         }
24929         
24930         return cfg;
24931     },
24932     
24933     initEvents : function() 
24934     {
24935         this.el.on('mouseover', this.onMouseOver, this);
24936         this.el.on('mouseout', this.onMouseOut, this);
24937         
24938         this.el.select('a', true).first().on('click', this.onClick, this);
24939         
24940     },
24941     
24942     onClick : function(e)
24943     {
24944         if(this.preventDefault){
24945             e.preventDefault();
24946         }
24947         
24948         this.fireEvent("click", this, e);
24949     },
24950     
24951     onMouseOver : function(e)
24952     {
24953         if(this.submenu && this.pos == 'left'){
24954             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24955         }
24956         
24957         this.fireEvent("mouseover", this, e);
24958     },
24959     
24960     onMouseOut : function(e)
24961     {
24962         this.fireEvent("mouseout", this, e);
24963     }
24964 });
24965
24966  
24967
24968  /*
24969  * - LGPL
24970  *
24971  * menu separator
24972  * 
24973  */
24974 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24975
24976 /**
24977  * @class Roo.bootstrap.menu.Separator
24978  * @extends Roo.bootstrap.Component
24979  * Bootstrap Separator class
24980  * 
24981  * @constructor
24982  * Create a new Separator
24983  * @param {Object} config The config object
24984  */
24985
24986
24987 Roo.bootstrap.menu.Separator = function(config){
24988     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24989 };
24990
24991 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24992     
24993     getAutoCreate : function(){
24994         var cfg = {
24995             tag : 'li',
24996             cls: 'divider'
24997         };
24998         
24999         return cfg;
25000     }
25001    
25002 });
25003
25004  
25005
25006  /*
25007  * - LGPL
25008  *
25009  * Tooltip
25010  * 
25011  */
25012
25013 /**
25014  * @class Roo.bootstrap.Tooltip
25015  * Bootstrap Tooltip class
25016  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25017  * to determine which dom element triggers the tooltip.
25018  * 
25019  * It needs to add support for additional attributes like tooltip-position
25020  * 
25021  * @constructor
25022  * Create a new Toolti
25023  * @param {Object} config The config object
25024  */
25025
25026 Roo.bootstrap.Tooltip = function(config){
25027     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25028     
25029     this.alignment = Roo.bootstrap.Tooltip.alignment;
25030     
25031     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25032         this.alignment = config.alignment;
25033     }
25034     
25035 };
25036
25037 Roo.apply(Roo.bootstrap.Tooltip, {
25038     /**
25039      * @function init initialize tooltip monitoring.
25040      * @static
25041      */
25042     currentEl : false,
25043     currentTip : false,
25044     currentRegion : false,
25045     
25046     //  init : delay?
25047     
25048     init : function()
25049     {
25050         Roo.get(document).on('mouseover', this.enter ,this);
25051         Roo.get(document).on('mouseout', this.leave, this);
25052          
25053         
25054         this.currentTip = new Roo.bootstrap.Tooltip();
25055     },
25056     
25057     enter : function(ev)
25058     {
25059         var dom = ev.getTarget();
25060         
25061         //Roo.log(['enter',dom]);
25062         var el = Roo.fly(dom);
25063         if (this.currentEl) {
25064             //Roo.log(dom);
25065             //Roo.log(this.currentEl);
25066             //Roo.log(this.currentEl.contains(dom));
25067             if (this.currentEl == el) {
25068                 return;
25069             }
25070             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25071                 return;
25072             }
25073
25074         }
25075         
25076         if (this.currentTip.el) {
25077             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25078         }    
25079         //Roo.log(ev);
25080         
25081         if(!el || el.dom == document){
25082             return;
25083         }
25084         
25085         var bindEl = el;
25086         
25087         // you can not look for children, as if el is the body.. then everythign is the child..
25088         if (!el.attr('tooltip')) { //
25089             if (!el.select("[tooltip]").elements.length) {
25090                 return;
25091             }
25092             // is the mouse over this child...?
25093             bindEl = el.select("[tooltip]").first();
25094             var xy = ev.getXY();
25095             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25096                 //Roo.log("not in region.");
25097                 return;
25098             }
25099             //Roo.log("child element over..");
25100             
25101         }
25102         this.currentEl = bindEl;
25103         this.currentTip.bind(bindEl);
25104         this.currentRegion = Roo.lib.Region.getRegion(dom);
25105         this.currentTip.enter();
25106         
25107     },
25108     leave : function(ev)
25109     {
25110         var dom = ev.getTarget();
25111         //Roo.log(['leave',dom]);
25112         if (!this.currentEl) {
25113             return;
25114         }
25115         
25116         
25117         if (dom != this.currentEl.dom) {
25118             return;
25119         }
25120         var xy = ev.getXY();
25121         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25122             return;
25123         }
25124         // only activate leave if mouse cursor is outside... bounding box..
25125         
25126         
25127         
25128         
25129         if (this.currentTip) {
25130             this.currentTip.leave();
25131         }
25132         //Roo.log('clear currentEl');
25133         this.currentEl = false;
25134         
25135         
25136     },
25137     alignment : {
25138         'left' : ['r-l', [-2,0], 'right'],
25139         'right' : ['l-r', [2,0], 'left'],
25140         'bottom' : ['t-b', [0,2], 'top'],
25141         'top' : [ 'b-t', [0,-2], 'bottom']
25142     }
25143     
25144 });
25145
25146
25147 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25148     
25149     
25150     bindEl : false,
25151     
25152     delay : null, // can be { show : 300 , hide: 500}
25153     
25154     timeout : null,
25155     
25156     hoverState : null, //???
25157     
25158     placement : 'bottom', 
25159     
25160     alignment : false,
25161     
25162     getAutoCreate : function(){
25163     
25164         var cfg = {
25165            cls : 'tooltip',
25166            role : 'tooltip',
25167            cn : [
25168                 {
25169                     cls : 'tooltip-arrow'
25170                 },
25171                 {
25172                     cls : 'tooltip-inner'
25173                 }
25174            ]
25175         };
25176         
25177         return cfg;
25178     },
25179     bind : function(el)
25180     {
25181         this.bindEl = el;
25182     },
25183       
25184     
25185     enter : function () {
25186        
25187         if (this.timeout != null) {
25188             clearTimeout(this.timeout);
25189         }
25190         
25191         this.hoverState = 'in';
25192          //Roo.log("enter - show");
25193         if (!this.delay || !this.delay.show) {
25194             this.show();
25195             return;
25196         }
25197         var _t = this;
25198         this.timeout = setTimeout(function () {
25199             if (_t.hoverState == 'in') {
25200                 _t.show();
25201             }
25202         }, this.delay.show);
25203     },
25204     leave : function()
25205     {
25206         clearTimeout(this.timeout);
25207     
25208         this.hoverState = 'out';
25209          if (!this.delay || !this.delay.hide) {
25210             this.hide();
25211             return;
25212         }
25213        
25214         var _t = this;
25215         this.timeout = setTimeout(function () {
25216             //Roo.log("leave - timeout");
25217             
25218             if (_t.hoverState == 'out') {
25219                 _t.hide();
25220                 Roo.bootstrap.Tooltip.currentEl = false;
25221             }
25222         }, delay);
25223     },
25224     
25225     show : function (msg)
25226     {
25227         if (!this.el) {
25228             this.render(document.body);
25229         }
25230         // set content.
25231         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25232         
25233         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25234         
25235         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25236         
25237         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25238         
25239         var placement = typeof this.placement == 'function' ?
25240             this.placement.call(this, this.el, on_el) :
25241             this.placement;
25242             
25243         var autoToken = /\s?auto?\s?/i;
25244         var autoPlace = autoToken.test(placement);
25245         if (autoPlace) {
25246             placement = placement.replace(autoToken, '') || 'top';
25247         }
25248         
25249         //this.el.detach()
25250         //this.el.setXY([0,0]);
25251         this.el.show();
25252         //this.el.dom.style.display='block';
25253         
25254         //this.el.appendTo(on_el);
25255         
25256         var p = this.getPosition();
25257         var box = this.el.getBox();
25258         
25259         if (autoPlace) {
25260             // fixme..
25261         }
25262         
25263         var align = this.alignment[placement];
25264         
25265         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25266         
25267         if(placement == 'top' || placement == 'bottom'){
25268             if(xy[0] < 0){
25269                 placement = 'right';
25270             }
25271             
25272             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25273                 placement = 'left';
25274             }
25275             
25276             var scroll = Roo.select('body', true).first().getScroll();
25277             
25278             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25279                 placement = 'top';
25280             }
25281             
25282         }
25283         
25284         this.el.alignTo(this.bindEl, align[0],align[1]);
25285         //var arrow = this.el.select('.arrow',true).first();
25286         //arrow.set(align[2], 
25287         
25288         this.el.addClass(placement);
25289         
25290         this.el.addClass('in fade');
25291         
25292         this.hoverState = null;
25293         
25294         if (this.el.hasClass('fade')) {
25295             // fade it?
25296         }
25297         
25298     },
25299     hide : function()
25300     {
25301          
25302         if (!this.el) {
25303             return;
25304         }
25305         //this.el.setXY([0,0]);
25306         this.el.removeClass('in');
25307         //this.el.hide();
25308         
25309     }
25310     
25311 });
25312  
25313
25314  /*
25315  * - LGPL
25316  *
25317  * Location Picker
25318  * 
25319  */
25320
25321 /**
25322  * @class Roo.bootstrap.LocationPicker
25323  * @extends Roo.bootstrap.Component
25324  * Bootstrap LocationPicker class
25325  * @cfg {Number} latitude Position when init default 0
25326  * @cfg {Number} longitude Position when init default 0
25327  * @cfg {Number} zoom default 15
25328  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25329  * @cfg {Boolean} mapTypeControl default false
25330  * @cfg {Boolean} disableDoubleClickZoom default false
25331  * @cfg {Boolean} scrollwheel default true
25332  * @cfg {Boolean} streetViewControl default false
25333  * @cfg {Number} radius default 0
25334  * @cfg {String} locationName
25335  * @cfg {Boolean} draggable default true
25336  * @cfg {Boolean} enableAutocomplete default false
25337  * @cfg {Boolean} enableReverseGeocode default true
25338  * @cfg {String} markerTitle
25339  * 
25340  * @constructor
25341  * Create a new LocationPicker
25342  * @param {Object} config The config object
25343  */
25344
25345
25346 Roo.bootstrap.LocationPicker = function(config){
25347     
25348     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25349     
25350     this.addEvents({
25351         /**
25352          * @event initial
25353          * Fires when the picker initialized.
25354          * @param {Roo.bootstrap.LocationPicker} this
25355          * @param {Google Location} location
25356          */
25357         initial : true,
25358         /**
25359          * @event positionchanged
25360          * Fires when the picker position changed.
25361          * @param {Roo.bootstrap.LocationPicker} this
25362          * @param {Google Location} location
25363          */
25364         positionchanged : true,
25365         /**
25366          * @event resize
25367          * Fires when the map resize.
25368          * @param {Roo.bootstrap.LocationPicker} this
25369          */
25370         resize : true,
25371         /**
25372          * @event show
25373          * Fires when the map show.
25374          * @param {Roo.bootstrap.LocationPicker} this
25375          */
25376         show : true,
25377         /**
25378          * @event hide
25379          * Fires when the map hide.
25380          * @param {Roo.bootstrap.LocationPicker} this
25381          */
25382         hide : true,
25383         /**
25384          * @event mapClick
25385          * Fires when click the map.
25386          * @param {Roo.bootstrap.LocationPicker} this
25387          * @param {Map event} e
25388          */
25389         mapClick : true,
25390         /**
25391          * @event mapRightClick
25392          * Fires when right click the map.
25393          * @param {Roo.bootstrap.LocationPicker} this
25394          * @param {Map event} e
25395          */
25396         mapRightClick : true,
25397         /**
25398          * @event markerClick
25399          * Fires when click the marker.
25400          * @param {Roo.bootstrap.LocationPicker} this
25401          * @param {Map event} e
25402          */
25403         markerClick : true,
25404         /**
25405          * @event markerRightClick
25406          * Fires when right click the marker.
25407          * @param {Roo.bootstrap.LocationPicker} this
25408          * @param {Map event} e
25409          */
25410         markerRightClick : true,
25411         /**
25412          * @event OverlayViewDraw
25413          * Fires when OverlayView Draw
25414          * @param {Roo.bootstrap.LocationPicker} this
25415          */
25416         OverlayViewDraw : true,
25417         /**
25418          * @event OverlayViewOnAdd
25419          * Fires when OverlayView Draw
25420          * @param {Roo.bootstrap.LocationPicker} this
25421          */
25422         OverlayViewOnAdd : true,
25423         /**
25424          * @event OverlayViewOnRemove
25425          * Fires when OverlayView Draw
25426          * @param {Roo.bootstrap.LocationPicker} this
25427          */
25428         OverlayViewOnRemove : true,
25429         /**
25430          * @event OverlayViewShow
25431          * Fires when OverlayView Draw
25432          * @param {Roo.bootstrap.LocationPicker} this
25433          * @param {Pixel} cpx
25434          */
25435         OverlayViewShow : true,
25436         /**
25437          * @event OverlayViewHide
25438          * Fires when OverlayView Draw
25439          * @param {Roo.bootstrap.LocationPicker} this
25440          */
25441         OverlayViewHide : true,
25442         /**
25443          * @event loadexception
25444          * Fires when load google lib failed.
25445          * @param {Roo.bootstrap.LocationPicker} this
25446          */
25447         loadexception : true
25448     });
25449         
25450 };
25451
25452 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25453     
25454     gMapContext: false,
25455     
25456     latitude: 0,
25457     longitude: 0,
25458     zoom: 15,
25459     mapTypeId: false,
25460     mapTypeControl: false,
25461     disableDoubleClickZoom: false,
25462     scrollwheel: true,
25463     streetViewControl: false,
25464     radius: 0,
25465     locationName: '',
25466     draggable: true,
25467     enableAutocomplete: false,
25468     enableReverseGeocode: true,
25469     markerTitle: '',
25470     
25471     getAutoCreate: function()
25472     {
25473
25474         var cfg = {
25475             tag: 'div',
25476             cls: 'roo-location-picker'
25477         };
25478         
25479         return cfg
25480     },
25481     
25482     initEvents: function(ct, position)
25483     {       
25484         if(!this.el.getWidth() || this.isApplied()){
25485             return;
25486         }
25487         
25488         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25489         
25490         this.initial();
25491     },
25492     
25493     initial: function()
25494     {
25495         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25496             this.fireEvent('loadexception', this);
25497             return;
25498         }
25499         
25500         if(!this.mapTypeId){
25501             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25502         }
25503         
25504         this.gMapContext = this.GMapContext();
25505         
25506         this.initOverlayView();
25507         
25508         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25509         
25510         var _this = this;
25511                 
25512         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25513             _this.setPosition(_this.gMapContext.marker.position);
25514         });
25515         
25516         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25517             _this.fireEvent('mapClick', this, event);
25518             
25519         });
25520
25521         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25522             _this.fireEvent('mapRightClick', this, event);
25523             
25524         });
25525         
25526         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25527             _this.fireEvent('markerClick', this, event);
25528             
25529         });
25530
25531         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25532             _this.fireEvent('markerRightClick', this, event);
25533             
25534         });
25535         
25536         this.setPosition(this.gMapContext.location);
25537         
25538         this.fireEvent('initial', this, this.gMapContext.location);
25539     },
25540     
25541     initOverlayView: function()
25542     {
25543         var _this = this;
25544         
25545         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25546             
25547             draw: function()
25548             {
25549                 _this.fireEvent('OverlayViewDraw', _this);
25550             },
25551             
25552             onAdd: function()
25553             {
25554                 _this.fireEvent('OverlayViewOnAdd', _this);
25555             },
25556             
25557             onRemove: function()
25558             {
25559                 _this.fireEvent('OverlayViewOnRemove', _this);
25560             },
25561             
25562             show: function(cpx)
25563             {
25564                 _this.fireEvent('OverlayViewShow', _this, cpx);
25565             },
25566             
25567             hide: function()
25568             {
25569                 _this.fireEvent('OverlayViewHide', _this);
25570             }
25571             
25572         });
25573     },
25574     
25575     fromLatLngToContainerPixel: function(event)
25576     {
25577         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25578     },
25579     
25580     isApplied: function() 
25581     {
25582         return this.getGmapContext() == false ? false : true;
25583     },
25584     
25585     getGmapContext: function() 
25586     {
25587         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25588     },
25589     
25590     GMapContext: function() 
25591     {
25592         var position = new google.maps.LatLng(this.latitude, this.longitude);
25593         
25594         var _map = new google.maps.Map(this.el.dom, {
25595             center: position,
25596             zoom: this.zoom,
25597             mapTypeId: this.mapTypeId,
25598             mapTypeControl: this.mapTypeControl,
25599             disableDoubleClickZoom: this.disableDoubleClickZoom,
25600             scrollwheel: this.scrollwheel,
25601             streetViewControl: this.streetViewControl,
25602             locationName: this.locationName,
25603             draggable: this.draggable,
25604             enableAutocomplete: this.enableAutocomplete,
25605             enableReverseGeocode: this.enableReverseGeocode
25606         });
25607         
25608         var _marker = new google.maps.Marker({
25609             position: position,
25610             map: _map,
25611             title: this.markerTitle,
25612             draggable: this.draggable
25613         });
25614         
25615         return {
25616             map: _map,
25617             marker: _marker,
25618             circle: null,
25619             location: position,
25620             radius: this.radius,
25621             locationName: this.locationName,
25622             addressComponents: {
25623                 formatted_address: null,
25624                 addressLine1: null,
25625                 addressLine2: null,
25626                 streetName: null,
25627                 streetNumber: null,
25628                 city: null,
25629                 district: null,
25630                 state: null,
25631                 stateOrProvince: null
25632             },
25633             settings: this,
25634             domContainer: this.el.dom,
25635             geodecoder: new google.maps.Geocoder()
25636         };
25637     },
25638     
25639     drawCircle: function(center, radius, options) 
25640     {
25641         if (this.gMapContext.circle != null) {
25642             this.gMapContext.circle.setMap(null);
25643         }
25644         if (radius > 0) {
25645             radius *= 1;
25646             options = Roo.apply({}, options, {
25647                 strokeColor: "#0000FF",
25648                 strokeOpacity: .35,
25649                 strokeWeight: 2,
25650                 fillColor: "#0000FF",
25651                 fillOpacity: .2
25652             });
25653             
25654             options.map = this.gMapContext.map;
25655             options.radius = radius;
25656             options.center = center;
25657             this.gMapContext.circle = new google.maps.Circle(options);
25658             return this.gMapContext.circle;
25659         }
25660         
25661         return null;
25662     },
25663     
25664     setPosition: function(location) 
25665     {
25666         this.gMapContext.location = location;
25667         this.gMapContext.marker.setPosition(location);
25668         this.gMapContext.map.panTo(location);
25669         this.drawCircle(location, this.gMapContext.radius, {});
25670         
25671         var _this = this;
25672         
25673         if (this.gMapContext.settings.enableReverseGeocode) {
25674             this.gMapContext.geodecoder.geocode({
25675                 latLng: this.gMapContext.location
25676             }, function(results, status) {
25677                 
25678                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25679                     _this.gMapContext.locationName = results[0].formatted_address;
25680                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25681                     
25682                     _this.fireEvent('positionchanged', this, location);
25683                 }
25684             });
25685             
25686             return;
25687         }
25688         
25689         this.fireEvent('positionchanged', this, location);
25690     },
25691     
25692     resize: function()
25693     {
25694         google.maps.event.trigger(this.gMapContext.map, "resize");
25695         
25696         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25697         
25698         this.fireEvent('resize', this);
25699     },
25700     
25701     setPositionByLatLng: function(latitude, longitude)
25702     {
25703         this.setPosition(new google.maps.LatLng(latitude, longitude));
25704     },
25705     
25706     getCurrentPosition: function() 
25707     {
25708         return {
25709             latitude: this.gMapContext.location.lat(),
25710             longitude: this.gMapContext.location.lng()
25711         };
25712     },
25713     
25714     getAddressName: function() 
25715     {
25716         return this.gMapContext.locationName;
25717     },
25718     
25719     getAddressComponents: function() 
25720     {
25721         return this.gMapContext.addressComponents;
25722     },
25723     
25724     address_component_from_google_geocode: function(address_components) 
25725     {
25726         var result = {};
25727         
25728         for (var i = 0; i < address_components.length; i++) {
25729             var component = address_components[i];
25730             if (component.types.indexOf("postal_code") >= 0) {
25731                 result.postalCode = component.short_name;
25732             } else if (component.types.indexOf("street_number") >= 0) {
25733                 result.streetNumber = component.short_name;
25734             } else if (component.types.indexOf("route") >= 0) {
25735                 result.streetName = component.short_name;
25736             } else if (component.types.indexOf("neighborhood") >= 0) {
25737                 result.city = component.short_name;
25738             } else if (component.types.indexOf("locality") >= 0) {
25739                 result.city = component.short_name;
25740             } else if (component.types.indexOf("sublocality") >= 0) {
25741                 result.district = component.short_name;
25742             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25743                 result.stateOrProvince = component.short_name;
25744             } else if (component.types.indexOf("country") >= 0) {
25745                 result.country = component.short_name;
25746             }
25747         }
25748         
25749         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25750         result.addressLine2 = "";
25751         return result;
25752     },
25753     
25754     setZoomLevel: function(zoom)
25755     {
25756         this.gMapContext.map.setZoom(zoom);
25757     },
25758     
25759     show: function()
25760     {
25761         if(!this.el){
25762             return;
25763         }
25764         
25765         this.el.show();
25766         
25767         this.resize();
25768         
25769         this.fireEvent('show', this);
25770     },
25771     
25772     hide: function()
25773     {
25774         if(!this.el){
25775             return;
25776         }
25777         
25778         this.el.hide();
25779         
25780         this.fireEvent('hide', this);
25781     }
25782     
25783 });
25784
25785 Roo.apply(Roo.bootstrap.LocationPicker, {
25786     
25787     OverlayView : function(map, options)
25788     {
25789         options = options || {};
25790         
25791         this.setMap(map);
25792     }
25793     
25794     
25795 });/*
25796  * - LGPL
25797  *
25798  * Alert
25799  * 
25800  */
25801
25802 /**
25803  * @class Roo.bootstrap.Alert
25804  * @extends Roo.bootstrap.Component
25805  * Bootstrap Alert class
25806  * @cfg {String} title The title of alert
25807  * @cfg {String} html The content of alert
25808  * @cfg {String} weight (  success | info | warning | danger )
25809  * @cfg {String} faicon font-awesomeicon
25810  * 
25811  * @constructor
25812  * Create a new alert
25813  * @param {Object} config The config object
25814  */
25815
25816
25817 Roo.bootstrap.Alert = function(config){
25818     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25819     
25820 };
25821
25822 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25823     
25824     title: '',
25825     html: '',
25826     weight: false,
25827     faicon: false,
25828     
25829     getAutoCreate : function()
25830     {
25831         
25832         var cfg = {
25833             tag : 'div',
25834             cls : 'alert',
25835             cn : [
25836                 {
25837                     tag : 'i',
25838                     cls : 'roo-alert-icon'
25839                     
25840                 },
25841                 {
25842                     tag : 'b',
25843                     cls : 'roo-alert-title',
25844                     html : this.title
25845                 },
25846                 {
25847                     tag : 'span',
25848                     cls : 'roo-alert-text',
25849                     html : this.html
25850                 }
25851             ]
25852         };
25853         
25854         if(this.faicon){
25855             cfg.cn[0].cls += ' fa ' + this.faicon;
25856         }
25857         
25858         if(this.weight){
25859             cfg.cls += ' alert-' + this.weight;
25860         }
25861         
25862         return cfg;
25863     },
25864     
25865     initEvents: function() 
25866     {
25867         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25868     },
25869     
25870     setTitle : function(str)
25871     {
25872         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25873     },
25874     
25875     setText : function(str)
25876     {
25877         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25878     },
25879     
25880     setWeight : function(weight)
25881     {
25882         if(this.weight){
25883             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25884         }
25885         
25886         this.weight = weight;
25887         
25888         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25889     },
25890     
25891     setIcon : function(icon)
25892     {
25893         if(this.faicon){
25894             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25895         }
25896         
25897         this.faicon = icon;
25898         
25899         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25900     },
25901     
25902     hide: function() 
25903     {
25904         this.el.hide();   
25905     },
25906     
25907     show: function() 
25908     {  
25909         this.el.show();   
25910     }
25911     
25912 });
25913
25914  
25915 /*
25916 * Licence: LGPL
25917 */
25918
25919 /**
25920  * @class Roo.bootstrap.UploadCropbox
25921  * @extends Roo.bootstrap.Component
25922  * Bootstrap UploadCropbox class
25923  * @cfg {String} emptyText show when image has been loaded
25924  * @cfg {String} rotateNotify show when image too small to rotate
25925  * @cfg {Number} errorTimeout default 3000
25926  * @cfg {Number} minWidth default 300
25927  * @cfg {Number} minHeight default 300
25928  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25929  * @cfg {Boolean} isDocument (true|false) default false
25930  * @cfg {String} url action url
25931  * @cfg {String} paramName default 'imageUpload'
25932  * @cfg {String} method default POST
25933  * @cfg {Boolean} loadMask (true|false) default true
25934  * @cfg {Boolean} loadingText default 'Loading...'
25935  * 
25936  * @constructor
25937  * Create a new UploadCropbox
25938  * @param {Object} config The config object
25939  */
25940
25941 Roo.bootstrap.UploadCropbox = function(config){
25942     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25943     
25944     this.addEvents({
25945         /**
25946          * @event beforeselectfile
25947          * Fire before select file
25948          * @param {Roo.bootstrap.UploadCropbox} this
25949          */
25950         "beforeselectfile" : true,
25951         /**
25952          * @event initial
25953          * Fire after initEvent
25954          * @param {Roo.bootstrap.UploadCropbox} this
25955          */
25956         "initial" : true,
25957         /**
25958          * @event crop
25959          * Fire after initEvent
25960          * @param {Roo.bootstrap.UploadCropbox} this
25961          * @param {String} data
25962          */
25963         "crop" : true,
25964         /**
25965          * @event prepare
25966          * Fire when preparing the file data
25967          * @param {Roo.bootstrap.UploadCropbox} this
25968          * @param {Object} file
25969          */
25970         "prepare" : true,
25971         /**
25972          * @event exception
25973          * Fire when get exception
25974          * @param {Roo.bootstrap.UploadCropbox} this
25975          * @param {XMLHttpRequest} xhr
25976          */
25977         "exception" : true,
25978         /**
25979          * @event beforeloadcanvas
25980          * Fire before load the canvas
25981          * @param {Roo.bootstrap.UploadCropbox} this
25982          * @param {String} src
25983          */
25984         "beforeloadcanvas" : true,
25985         /**
25986          * @event trash
25987          * Fire when trash image
25988          * @param {Roo.bootstrap.UploadCropbox} this
25989          */
25990         "trash" : true,
25991         /**
25992          * @event download
25993          * Fire when download the image
25994          * @param {Roo.bootstrap.UploadCropbox} this
25995          */
25996         "download" : true,
25997         /**
25998          * @event footerbuttonclick
25999          * Fire when footerbuttonclick
26000          * @param {Roo.bootstrap.UploadCropbox} this
26001          * @param {String} type
26002          */
26003         "footerbuttonclick" : true,
26004         /**
26005          * @event resize
26006          * Fire when resize
26007          * @param {Roo.bootstrap.UploadCropbox} this
26008          */
26009         "resize" : true,
26010         /**
26011          * @event rotate
26012          * Fire when rotate the image
26013          * @param {Roo.bootstrap.UploadCropbox} this
26014          * @param {String} pos
26015          */
26016         "rotate" : true,
26017         /**
26018          * @event inspect
26019          * Fire when inspect the file
26020          * @param {Roo.bootstrap.UploadCropbox} this
26021          * @param {Object} file
26022          */
26023         "inspect" : true,
26024         /**
26025          * @event upload
26026          * Fire when xhr upload the file
26027          * @param {Roo.bootstrap.UploadCropbox} this
26028          * @param {Object} data
26029          */
26030         "upload" : true,
26031         /**
26032          * @event arrange
26033          * Fire when arrange the file data
26034          * @param {Roo.bootstrap.UploadCropbox} this
26035          * @param {Object} formData
26036          */
26037         "arrange" : true
26038     });
26039     
26040     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26041 };
26042
26043 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26044     
26045     emptyText : 'Click to upload image',
26046     rotateNotify : 'Image is too small to rotate',
26047     errorTimeout : 3000,
26048     scale : 0,
26049     baseScale : 1,
26050     rotate : 0,
26051     dragable : false,
26052     pinching : false,
26053     mouseX : 0,
26054     mouseY : 0,
26055     cropData : false,
26056     minWidth : 300,
26057     minHeight : 300,
26058     file : false,
26059     exif : {},
26060     baseRotate : 1,
26061     cropType : 'image/jpeg',
26062     buttons : false,
26063     canvasLoaded : false,
26064     isDocument : false,
26065     method : 'POST',
26066     paramName : 'imageUpload',
26067     loadMask : true,
26068     loadingText : 'Loading...',
26069     maskEl : false,
26070     
26071     getAutoCreate : function()
26072     {
26073         var cfg = {
26074             tag : 'div',
26075             cls : 'roo-upload-cropbox',
26076             cn : [
26077                 {
26078                     tag : 'input',
26079                     cls : 'roo-upload-cropbox-selector',
26080                     type : 'file'
26081                 },
26082                 {
26083                     tag : 'div',
26084                     cls : 'roo-upload-cropbox-body',
26085                     style : 'cursor:pointer',
26086                     cn : [
26087                         {
26088                             tag : 'div',
26089                             cls : 'roo-upload-cropbox-preview'
26090                         },
26091                         {
26092                             tag : 'div',
26093                             cls : 'roo-upload-cropbox-thumb'
26094                         },
26095                         {
26096                             tag : 'div',
26097                             cls : 'roo-upload-cropbox-empty-notify',
26098                             html : this.emptyText
26099                         },
26100                         {
26101                             tag : 'div',
26102                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26103                             html : this.rotateNotify
26104                         }
26105                     ]
26106                 },
26107                 {
26108                     tag : 'div',
26109                     cls : 'roo-upload-cropbox-footer',
26110                     cn : {
26111                         tag : 'div',
26112                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26113                         cn : []
26114                     }
26115                 }
26116             ]
26117         };
26118         
26119         return cfg;
26120     },
26121     
26122     onRender : function(ct, position)
26123     {
26124         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26125         
26126         if (this.buttons.length) {
26127             
26128             Roo.each(this.buttons, function(bb) {
26129                 
26130                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26131                 
26132                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26133                 
26134             }, this);
26135         }
26136         
26137         if(this.loadMask){
26138             this.maskEl = this.el;
26139         }
26140     },
26141     
26142     initEvents : function()
26143     {
26144         this.urlAPI = (window.createObjectURL && window) || 
26145                                 (window.URL && URL.revokeObjectURL && URL) || 
26146                                 (window.webkitURL && webkitURL);
26147                         
26148         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26149         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26150         
26151         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26152         this.selectorEl.hide();
26153         
26154         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26155         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26156         
26157         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26158         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26159         this.thumbEl.hide();
26160         
26161         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26162         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26163         
26164         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26165         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26166         this.errorEl.hide();
26167         
26168         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26169         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26170         this.footerEl.hide();
26171         
26172         this.setThumbBoxSize();
26173         
26174         this.bind();
26175         
26176         this.resize();
26177         
26178         this.fireEvent('initial', this);
26179     },
26180
26181     bind : function()
26182     {
26183         var _this = this;
26184         
26185         window.addEventListener("resize", function() { _this.resize(); } );
26186         
26187         this.bodyEl.on('click', this.beforeSelectFile, this);
26188         
26189         if(Roo.isTouch){
26190             this.bodyEl.on('touchstart', this.onTouchStart, this);
26191             this.bodyEl.on('touchmove', this.onTouchMove, this);
26192             this.bodyEl.on('touchend', this.onTouchEnd, this);
26193         }
26194         
26195         if(!Roo.isTouch){
26196             this.bodyEl.on('mousedown', this.onMouseDown, this);
26197             this.bodyEl.on('mousemove', this.onMouseMove, this);
26198             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26199             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26200             Roo.get(document).on('mouseup', this.onMouseUp, this);
26201         }
26202         
26203         this.selectorEl.on('change', this.onFileSelected, this);
26204     },
26205     
26206     reset : function()
26207     {    
26208         this.scale = 0;
26209         this.baseScale = 1;
26210         this.rotate = 0;
26211         this.baseRotate = 1;
26212         this.dragable = false;
26213         this.pinching = false;
26214         this.mouseX = 0;
26215         this.mouseY = 0;
26216         this.cropData = false;
26217         this.notifyEl.dom.innerHTML = this.emptyText;
26218         
26219         this.selectorEl.dom.value = '';
26220         
26221     },
26222     
26223     resize : function()
26224     {
26225         if(this.fireEvent('resize', this) != false){
26226             this.setThumbBoxPosition();
26227             this.setCanvasPosition();
26228         }
26229     },
26230     
26231     onFooterButtonClick : function(e, el, o, type)
26232     {
26233         switch (type) {
26234             case 'rotate-left' :
26235                 this.onRotateLeft(e);
26236                 break;
26237             case 'rotate-right' :
26238                 this.onRotateRight(e);
26239                 break;
26240             case 'picture' :
26241                 this.beforeSelectFile(e);
26242                 break;
26243             case 'trash' :
26244                 this.trash(e);
26245                 break;
26246             case 'crop' :
26247                 this.crop(e);
26248                 break;
26249             case 'download' :
26250                 this.download(e);
26251                 break;
26252             default :
26253                 break;
26254         }
26255         
26256         this.fireEvent('footerbuttonclick', this, type);
26257     },
26258     
26259     beforeSelectFile : function(e)
26260     {
26261         e.preventDefault();
26262         
26263         if(this.fireEvent('beforeselectfile', this) != false){
26264             this.selectorEl.dom.click();
26265         }
26266     },
26267     
26268     onFileSelected : function(e)
26269     {
26270         e.preventDefault();
26271         
26272         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26273             return;
26274         }
26275         
26276         var file = this.selectorEl.dom.files[0];
26277         
26278         if(this.fireEvent('inspect', this, file) != false){
26279             this.prepare(file);
26280         }
26281         
26282     },
26283     
26284     trash : function(e)
26285     {
26286         this.fireEvent('trash', this);
26287     },
26288     
26289     download : function(e)
26290     {
26291         this.fireEvent('download', this);
26292     },
26293     
26294     loadCanvas : function(src)
26295     {   
26296         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26297             
26298             this.reset();
26299             
26300             this.imageEl = document.createElement('img');
26301             
26302             var _this = this;
26303             
26304             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26305             
26306             this.imageEl.src = src;
26307         }
26308     },
26309     
26310     onLoadCanvas : function()
26311     {   
26312         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26313         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26314         
26315         this.bodyEl.un('click', this.beforeSelectFile, this);
26316         
26317         this.notifyEl.hide();
26318         this.thumbEl.show();
26319         this.footerEl.show();
26320         
26321         this.baseRotateLevel();
26322         
26323         if(this.isDocument){
26324             this.setThumbBoxSize();
26325         }
26326         
26327         this.setThumbBoxPosition();
26328         
26329         this.baseScaleLevel();
26330         
26331         this.draw();
26332         
26333         this.resize();
26334         
26335         this.canvasLoaded = true;
26336         
26337         if(this.loadMask){
26338             this.maskEl.unmask();
26339         }
26340         
26341     },
26342     
26343     setCanvasPosition : function()
26344     {   
26345         if(!this.canvasEl){
26346             return;
26347         }
26348         
26349         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26350         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26351         
26352         this.previewEl.setLeft(pw);
26353         this.previewEl.setTop(ph);
26354         
26355     },
26356     
26357     onMouseDown : function(e)
26358     {   
26359         e.stopEvent();
26360         
26361         this.dragable = true;
26362         this.pinching = false;
26363         
26364         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26365             this.dragable = false;
26366             return;
26367         }
26368         
26369         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26370         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26371         
26372     },
26373     
26374     onMouseMove : function(e)
26375     {   
26376         e.stopEvent();
26377         
26378         if(!this.canvasLoaded){
26379             return;
26380         }
26381         
26382         if (!this.dragable){
26383             return;
26384         }
26385         
26386         var minX = Math.ceil(this.thumbEl.getLeft(true));
26387         var minY = Math.ceil(this.thumbEl.getTop(true));
26388         
26389         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26390         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26391         
26392         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26393         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26394         
26395         x = x - this.mouseX;
26396         y = y - this.mouseY;
26397         
26398         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26399         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26400         
26401         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26402         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26403         
26404         this.previewEl.setLeft(bgX);
26405         this.previewEl.setTop(bgY);
26406         
26407         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26408         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26409     },
26410     
26411     onMouseUp : function(e)
26412     {   
26413         e.stopEvent();
26414         
26415         this.dragable = false;
26416     },
26417     
26418     onMouseWheel : function(e)
26419     {   
26420         e.stopEvent();
26421         
26422         this.startScale = this.scale;
26423         
26424         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26425         
26426         if(!this.zoomable()){
26427             this.scale = this.startScale;
26428             return;
26429         }
26430         
26431         this.draw();
26432         
26433         return;
26434     },
26435     
26436     zoomable : function()
26437     {
26438         var minScale = this.thumbEl.getWidth() / this.minWidth;
26439         
26440         if(this.minWidth < this.minHeight){
26441             minScale = this.thumbEl.getHeight() / this.minHeight;
26442         }
26443         
26444         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26445         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26446         
26447         if(
26448                 this.isDocument &&
26449                 (this.rotate == 0 || this.rotate == 180) && 
26450                 (
26451                     width > this.imageEl.OriginWidth || 
26452                     height > this.imageEl.OriginHeight ||
26453                     (width < this.minWidth && height < this.minHeight)
26454                 )
26455         ){
26456             return false;
26457         }
26458         
26459         if(
26460                 this.isDocument &&
26461                 (this.rotate == 90 || this.rotate == 270) && 
26462                 (
26463                     width > this.imageEl.OriginWidth || 
26464                     height > this.imageEl.OriginHeight ||
26465                     (width < this.minHeight && height < this.minWidth)
26466                 )
26467         ){
26468             return false;
26469         }
26470         
26471         if(
26472                 !this.isDocument &&
26473                 (this.rotate == 0 || this.rotate == 180) && 
26474                 (
26475                     width < this.minWidth || 
26476                     width > this.imageEl.OriginWidth || 
26477                     height < this.minHeight || 
26478                     height > this.imageEl.OriginHeight
26479                 )
26480         ){
26481             return false;
26482         }
26483         
26484         if(
26485                 !this.isDocument &&
26486                 (this.rotate == 90 || this.rotate == 270) && 
26487                 (
26488                     width < this.minHeight || 
26489                     width > this.imageEl.OriginWidth || 
26490                     height < this.minWidth || 
26491                     height > this.imageEl.OriginHeight
26492                 )
26493         ){
26494             return false;
26495         }
26496         
26497         return true;
26498         
26499     },
26500     
26501     onRotateLeft : function(e)
26502     {   
26503         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26504             
26505             var minScale = this.thumbEl.getWidth() / this.minWidth;
26506             
26507             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26508             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26509             
26510             this.startScale = this.scale;
26511             
26512             while (this.getScaleLevel() < minScale){
26513             
26514                 this.scale = this.scale + 1;
26515                 
26516                 if(!this.zoomable()){
26517                     break;
26518                 }
26519                 
26520                 if(
26521                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26522                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26523                 ){
26524                     continue;
26525                 }
26526                 
26527                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26528
26529                 this.draw();
26530                 
26531                 return;
26532             }
26533             
26534             this.scale = this.startScale;
26535             
26536             this.onRotateFail();
26537             
26538             return false;
26539         }
26540         
26541         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26542
26543         if(this.isDocument){
26544             this.setThumbBoxSize();
26545             this.setThumbBoxPosition();
26546             this.setCanvasPosition();
26547         }
26548         
26549         this.draw();
26550         
26551         this.fireEvent('rotate', this, 'left');
26552         
26553     },
26554     
26555     onRotateRight : function(e)
26556     {
26557         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26558             
26559             var minScale = this.thumbEl.getWidth() / this.minWidth;
26560         
26561             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26562             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26563             
26564             this.startScale = this.scale;
26565             
26566             while (this.getScaleLevel() < minScale){
26567             
26568                 this.scale = this.scale + 1;
26569                 
26570                 if(!this.zoomable()){
26571                     break;
26572                 }
26573                 
26574                 if(
26575                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26576                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26577                 ){
26578                     continue;
26579                 }
26580                 
26581                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26582
26583                 this.draw();
26584                 
26585                 return;
26586             }
26587             
26588             this.scale = this.startScale;
26589             
26590             this.onRotateFail();
26591             
26592             return false;
26593         }
26594         
26595         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26596
26597         if(this.isDocument){
26598             this.setThumbBoxSize();
26599             this.setThumbBoxPosition();
26600             this.setCanvasPosition();
26601         }
26602         
26603         this.draw();
26604         
26605         this.fireEvent('rotate', this, 'right');
26606     },
26607     
26608     onRotateFail : function()
26609     {
26610         this.errorEl.show(true);
26611         
26612         var _this = this;
26613         
26614         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26615     },
26616     
26617     draw : function()
26618     {
26619         this.previewEl.dom.innerHTML = '';
26620         
26621         var canvasEl = document.createElement("canvas");
26622         
26623         var contextEl = canvasEl.getContext("2d");
26624         
26625         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26626         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26627         var center = this.imageEl.OriginWidth / 2;
26628         
26629         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26630             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26631             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26632             center = this.imageEl.OriginHeight / 2;
26633         }
26634         
26635         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26636         
26637         contextEl.translate(center, center);
26638         contextEl.rotate(this.rotate * Math.PI / 180);
26639
26640         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26641         
26642         this.canvasEl = document.createElement("canvas");
26643         
26644         this.contextEl = this.canvasEl.getContext("2d");
26645         
26646         switch (this.rotate) {
26647             case 0 :
26648                 
26649                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26650                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26651                 
26652                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26653                 
26654                 break;
26655             case 90 : 
26656                 
26657                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26658                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26659                 
26660                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26661                     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);
26662                     break;
26663                 }
26664                 
26665                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26666                 
26667                 break;
26668             case 180 :
26669                 
26670                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26671                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26672                 
26673                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26674                     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);
26675                     break;
26676                 }
26677                 
26678                 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);
26679                 
26680                 break;
26681             case 270 :
26682                 
26683                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26684                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26685         
26686                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26687                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26688                     break;
26689                 }
26690                 
26691                 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);
26692                 
26693                 break;
26694             default : 
26695                 break;
26696         }
26697         
26698         this.previewEl.appendChild(this.canvasEl);
26699         
26700         this.setCanvasPosition();
26701     },
26702     
26703     crop : function()
26704     {
26705         if(!this.canvasLoaded){
26706             return;
26707         }
26708         
26709         var imageCanvas = document.createElement("canvas");
26710         
26711         var imageContext = imageCanvas.getContext("2d");
26712         
26713         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26714         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26715         
26716         var center = imageCanvas.width / 2;
26717         
26718         imageContext.translate(center, center);
26719         
26720         imageContext.rotate(this.rotate * Math.PI / 180);
26721         
26722         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26723         
26724         var canvas = document.createElement("canvas");
26725         
26726         var context = canvas.getContext("2d");
26727                 
26728         canvas.width = this.minWidth;
26729         canvas.height = this.minHeight;
26730
26731         switch (this.rotate) {
26732             case 0 :
26733                 
26734                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26735                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26736                 
26737                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26738                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26739                 
26740                 var targetWidth = this.minWidth - 2 * x;
26741                 var targetHeight = this.minHeight - 2 * y;
26742                 
26743                 var scale = 1;
26744                 
26745                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26746                     scale = targetWidth / width;
26747                 }
26748                 
26749                 if(x > 0 && y == 0){
26750                     scale = targetHeight / height;
26751                 }
26752                 
26753                 if(x > 0 && y > 0){
26754                     scale = targetWidth / width;
26755                     
26756                     if(width < height){
26757                         scale = targetHeight / height;
26758                     }
26759                 }
26760                 
26761                 context.scale(scale, scale);
26762                 
26763                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26764                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26765
26766                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26767                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26768
26769                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26770                 
26771                 break;
26772             case 90 : 
26773                 
26774                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26775                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26776                 
26777                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26778                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26779                 
26780                 var targetWidth = this.minWidth - 2 * x;
26781                 var targetHeight = this.minHeight - 2 * y;
26782                 
26783                 var scale = 1;
26784                 
26785                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26786                     scale = targetWidth / width;
26787                 }
26788                 
26789                 if(x > 0 && y == 0){
26790                     scale = targetHeight / height;
26791                 }
26792                 
26793                 if(x > 0 && y > 0){
26794                     scale = targetWidth / width;
26795                     
26796                     if(width < height){
26797                         scale = targetHeight / height;
26798                     }
26799                 }
26800                 
26801                 context.scale(scale, scale);
26802                 
26803                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26804                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26805
26806                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26807                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26808                 
26809                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26810                 
26811                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26812                 
26813                 break;
26814             case 180 :
26815                 
26816                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26817                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26818                 
26819                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26820                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26821                 
26822                 var targetWidth = this.minWidth - 2 * x;
26823                 var targetHeight = this.minHeight - 2 * y;
26824                 
26825                 var scale = 1;
26826                 
26827                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26828                     scale = targetWidth / width;
26829                 }
26830                 
26831                 if(x > 0 && y == 0){
26832                     scale = targetHeight / height;
26833                 }
26834                 
26835                 if(x > 0 && y > 0){
26836                     scale = targetWidth / width;
26837                     
26838                     if(width < height){
26839                         scale = targetHeight / height;
26840                     }
26841                 }
26842                 
26843                 context.scale(scale, scale);
26844                 
26845                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26846                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26847
26848                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26849                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26850
26851                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26852                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26853                 
26854                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26855                 
26856                 break;
26857             case 270 :
26858                 
26859                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26860                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26861                 
26862                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26863                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26864                 
26865                 var targetWidth = this.minWidth - 2 * x;
26866                 var targetHeight = this.minHeight - 2 * y;
26867                 
26868                 var scale = 1;
26869                 
26870                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26871                     scale = targetWidth / width;
26872                 }
26873                 
26874                 if(x > 0 && y == 0){
26875                     scale = targetHeight / height;
26876                 }
26877                 
26878                 if(x > 0 && y > 0){
26879                     scale = targetWidth / width;
26880                     
26881                     if(width < height){
26882                         scale = targetHeight / height;
26883                     }
26884                 }
26885                 
26886                 context.scale(scale, scale);
26887                 
26888                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26889                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26890
26891                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26892                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26893                 
26894                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26895                 
26896                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26897                 
26898                 break;
26899             default : 
26900                 break;
26901         }
26902         
26903         this.cropData = canvas.toDataURL(this.cropType);
26904         
26905         if(this.fireEvent('crop', this, this.cropData) !== false){
26906             this.process(this.file, this.cropData);
26907         }
26908         
26909         return;
26910         
26911     },
26912     
26913     setThumbBoxSize : function()
26914     {
26915         var width, height;
26916         
26917         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26918             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26919             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26920             
26921             this.minWidth = width;
26922             this.minHeight = height;
26923             
26924             if(this.rotate == 90 || this.rotate == 270){
26925                 this.minWidth = height;
26926                 this.minHeight = width;
26927             }
26928         }
26929         
26930         height = 300;
26931         width = Math.ceil(this.minWidth * height / this.minHeight);
26932         
26933         if(this.minWidth > this.minHeight){
26934             width = 300;
26935             height = Math.ceil(this.minHeight * width / this.minWidth);
26936         }
26937         
26938         this.thumbEl.setStyle({
26939             width : width + 'px',
26940             height : height + 'px'
26941         });
26942
26943         return;
26944             
26945     },
26946     
26947     setThumbBoxPosition : function()
26948     {
26949         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26950         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26951         
26952         this.thumbEl.setLeft(x);
26953         this.thumbEl.setTop(y);
26954         
26955     },
26956     
26957     baseRotateLevel : function()
26958     {
26959         this.baseRotate = 1;
26960         
26961         if(
26962                 typeof(this.exif) != 'undefined' &&
26963                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26964                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26965         ){
26966             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26967         }
26968         
26969         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26970         
26971     },
26972     
26973     baseScaleLevel : function()
26974     {
26975         var width, height;
26976         
26977         if(this.isDocument){
26978             
26979             if(this.baseRotate == 6 || this.baseRotate == 8){
26980             
26981                 height = this.thumbEl.getHeight();
26982                 this.baseScale = height / this.imageEl.OriginWidth;
26983
26984                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26985                     width = this.thumbEl.getWidth();
26986                     this.baseScale = width / this.imageEl.OriginHeight;
26987                 }
26988
26989                 return;
26990             }
26991
26992             height = this.thumbEl.getHeight();
26993             this.baseScale = height / this.imageEl.OriginHeight;
26994
26995             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26996                 width = this.thumbEl.getWidth();
26997                 this.baseScale = width / this.imageEl.OriginWidth;
26998             }
26999
27000             return;
27001         }
27002         
27003         if(this.baseRotate == 6 || this.baseRotate == 8){
27004             
27005             width = this.thumbEl.getHeight();
27006             this.baseScale = width / this.imageEl.OriginHeight;
27007             
27008             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27009                 height = this.thumbEl.getWidth();
27010                 this.baseScale = height / this.imageEl.OriginHeight;
27011             }
27012             
27013             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27014                 height = this.thumbEl.getWidth();
27015                 this.baseScale = height / this.imageEl.OriginHeight;
27016                 
27017                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27018                     width = this.thumbEl.getHeight();
27019                     this.baseScale = width / this.imageEl.OriginWidth;
27020                 }
27021             }
27022             
27023             return;
27024         }
27025         
27026         width = this.thumbEl.getWidth();
27027         this.baseScale = width / this.imageEl.OriginWidth;
27028         
27029         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27030             height = this.thumbEl.getHeight();
27031             this.baseScale = height / this.imageEl.OriginHeight;
27032         }
27033         
27034         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27035             
27036             height = this.thumbEl.getHeight();
27037             this.baseScale = height / this.imageEl.OriginHeight;
27038             
27039             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27040                 width = this.thumbEl.getWidth();
27041                 this.baseScale = width / this.imageEl.OriginWidth;
27042             }
27043             
27044         }
27045         
27046         return;
27047     },
27048     
27049     getScaleLevel : function()
27050     {
27051         return this.baseScale * Math.pow(1.1, this.scale);
27052     },
27053     
27054     onTouchStart : function(e)
27055     {
27056         if(!this.canvasLoaded){
27057             this.beforeSelectFile(e);
27058             return;
27059         }
27060         
27061         var touches = e.browserEvent.touches;
27062         
27063         if(!touches){
27064             return;
27065         }
27066         
27067         if(touches.length == 1){
27068             this.onMouseDown(e);
27069             return;
27070         }
27071         
27072         if(touches.length != 2){
27073             return;
27074         }
27075         
27076         var coords = [];
27077         
27078         for(var i = 0, finger; finger = touches[i]; i++){
27079             coords.push(finger.pageX, finger.pageY);
27080         }
27081         
27082         var x = Math.pow(coords[0] - coords[2], 2);
27083         var y = Math.pow(coords[1] - coords[3], 2);
27084         
27085         this.startDistance = Math.sqrt(x + y);
27086         
27087         this.startScale = this.scale;
27088         
27089         this.pinching = true;
27090         this.dragable = false;
27091         
27092     },
27093     
27094     onTouchMove : function(e)
27095     {
27096         if(!this.pinching && !this.dragable){
27097             return;
27098         }
27099         
27100         var touches = e.browserEvent.touches;
27101         
27102         if(!touches){
27103             return;
27104         }
27105         
27106         if(this.dragable){
27107             this.onMouseMove(e);
27108             return;
27109         }
27110         
27111         var coords = [];
27112         
27113         for(var i = 0, finger; finger = touches[i]; i++){
27114             coords.push(finger.pageX, finger.pageY);
27115         }
27116         
27117         var x = Math.pow(coords[0] - coords[2], 2);
27118         var y = Math.pow(coords[1] - coords[3], 2);
27119         
27120         this.endDistance = Math.sqrt(x + y);
27121         
27122         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27123         
27124         if(!this.zoomable()){
27125             this.scale = this.startScale;
27126             return;
27127         }
27128         
27129         this.draw();
27130         
27131     },
27132     
27133     onTouchEnd : function(e)
27134     {
27135         this.pinching = false;
27136         this.dragable = false;
27137         
27138     },
27139     
27140     process : function(file, crop)
27141     {
27142         if(this.loadMask){
27143             this.maskEl.mask(this.loadingText);
27144         }
27145         
27146         this.xhr = new XMLHttpRequest();
27147         
27148         file.xhr = this.xhr;
27149
27150         this.xhr.open(this.method, this.url, true);
27151         
27152         var headers = {
27153             "Accept": "application/json",
27154             "Cache-Control": "no-cache",
27155             "X-Requested-With": "XMLHttpRequest"
27156         };
27157         
27158         for (var headerName in headers) {
27159             var headerValue = headers[headerName];
27160             if (headerValue) {
27161                 this.xhr.setRequestHeader(headerName, headerValue);
27162             }
27163         }
27164         
27165         var _this = this;
27166         
27167         this.xhr.onload = function()
27168         {
27169             _this.xhrOnLoad(_this.xhr);
27170         }
27171         
27172         this.xhr.onerror = function()
27173         {
27174             _this.xhrOnError(_this.xhr);
27175         }
27176         
27177         var formData = new FormData();
27178
27179         formData.append('returnHTML', 'NO');
27180         
27181         if(crop){
27182             formData.append('crop', crop);
27183         }
27184         
27185         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27186             formData.append(this.paramName, file, file.name);
27187         }
27188         
27189         if(typeof(file.filename) != 'undefined'){
27190             formData.append('filename', file.filename);
27191         }
27192         
27193         if(typeof(file.mimetype) != 'undefined'){
27194             formData.append('mimetype', file.mimetype);
27195         }
27196         
27197         if(this.fireEvent('arrange', this, formData) != false){
27198             this.xhr.send(formData);
27199         };
27200     },
27201     
27202     xhrOnLoad : function(xhr)
27203     {
27204         if(this.loadMask){
27205             this.maskEl.unmask();
27206         }
27207         
27208         if (xhr.readyState !== 4) {
27209             this.fireEvent('exception', this, xhr);
27210             return;
27211         }
27212
27213         var response = Roo.decode(xhr.responseText);
27214         
27215         if(!response.success){
27216             this.fireEvent('exception', this, xhr);
27217             return;
27218         }
27219         
27220         var response = Roo.decode(xhr.responseText);
27221         
27222         this.fireEvent('upload', this, response);
27223         
27224     },
27225     
27226     xhrOnError : function()
27227     {
27228         if(this.loadMask){
27229             this.maskEl.unmask();
27230         }
27231         
27232         Roo.log('xhr on error');
27233         
27234         var response = Roo.decode(xhr.responseText);
27235           
27236         Roo.log(response);
27237         
27238     },
27239     
27240     prepare : function(file)
27241     {   
27242         if(this.loadMask){
27243             this.maskEl.mask(this.loadingText);
27244         }
27245         
27246         this.file = false;
27247         this.exif = {};
27248         
27249         if(typeof(file) === 'string'){
27250             this.loadCanvas(file);
27251             return;
27252         }
27253         
27254         if(!file || !this.urlAPI){
27255             return;
27256         }
27257         
27258         this.file = file;
27259         this.cropType = file.type;
27260         
27261         var _this = this;
27262         
27263         if(this.fireEvent('prepare', this, this.file) != false){
27264             
27265             var reader = new FileReader();
27266             
27267             reader.onload = function (e) {
27268                 if (e.target.error) {
27269                     Roo.log(e.target.error);
27270                     return;
27271                 }
27272                 
27273                 var buffer = e.target.result,
27274                     dataView = new DataView(buffer),
27275                     offset = 2,
27276                     maxOffset = dataView.byteLength - 4,
27277                     markerBytes,
27278                     markerLength;
27279                 
27280                 if (dataView.getUint16(0) === 0xffd8) {
27281                     while (offset < maxOffset) {
27282                         markerBytes = dataView.getUint16(offset);
27283                         
27284                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27285                             markerLength = dataView.getUint16(offset + 2) + 2;
27286                             if (offset + markerLength > dataView.byteLength) {
27287                                 Roo.log('Invalid meta data: Invalid segment size.');
27288                                 break;
27289                             }
27290                             
27291                             if(markerBytes == 0xffe1){
27292                                 _this.parseExifData(
27293                                     dataView,
27294                                     offset,
27295                                     markerLength
27296                                 );
27297                             }
27298                             
27299                             offset += markerLength;
27300                             
27301                             continue;
27302                         }
27303                         
27304                         break;
27305                     }
27306                     
27307                 }
27308                 
27309                 var url = _this.urlAPI.createObjectURL(_this.file);
27310                 
27311                 _this.loadCanvas(url);
27312                 
27313                 return;
27314             }
27315             
27316             reader.readAsArrayBuffer(this.file);
27317             
27318         }
27319         
27320     },
27321     
27322     parseExifData : function(dataView, offset, length)
27323     {
27324         var tiffOffset = offset + 10,
27325             littleEndian,
27326             dirOffset;
27327     
27328         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27329             // No Exif data, might be XMP data instead
27330             return;
27331         }
27332         
27333         // Check for the ASCII code for "Exif" (0x45786966):
27334         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27335             // No Exif data, might be XMP data instead
27336             return;
27337         }
27338         if (tiffOffset + 8 > dataView.byteLength) {
27339             Roo.log('Invalid Exif data: Invalid segment size.');
27340             return;
27341         }
27342         // Check for the two null bytes:
27343         if (dataView.getUint16(offset + 8) !== 0x0000) {
27344             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27345             return;
27346         }
27347         // Check the byte alignment:
27348         switch (dataView.getUint16(tiffOffset)) {
27349         case 0x4949:
27350             littleEndian = true;
27351             break;
27352         case 0x4D4D:
27353             littleEndian = false;
27354             break;
27355         default:
27356             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27357             return;
27358         }
27359         // Check for the TIFF tag marker (0x002A):
27360         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27361             Roo.log('Invalid Exif data: Missing TIFF marker.');
27362             return;
27363         }
27364         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27365         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27366         
27367         this.parseExifTags(
27368             dataView,
27369             tiffOffset,
27370             tiffOffset + dirOffset,
27371             littleEndian
27372         );
27373     },
27374     
27375     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27376     {
27377         var tagsNumber,
27378             dirEndOffset,
27379             i;
27380         if (dirOffset + 6 > dataView.byteLength) {
27381             Roo.log('Invalid Exif data: Invalid directory offset.');
27382             return;
27383         }
27384         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27385         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27386         if (dirEndOffset + 4 > dataView.byteLength) {
27387             Roo.log('Invalid Exif data: Invalid directory size.');
27388             return;
27389         }
27390         for (i = 0; i < tagsNumber; i += 1) {
27391             this.parseExifTag(
27392                 dataView,
27393                 tiffOffset,
27394                 dirOffset + 2 + 12 * i, // tag offset
27395                 littleEndian
27396             );
27397         }
27398         // Return the offset to the next directory:
27399         return dataView.getUint32(dirEndOffset, littleEndian);
27400     },
27401     
27402     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27403     {
27404         var tag = dataView.getUint16(offset, littleEndian);
27405         
27406         this.exif[tag] = this.getExifValue(
27407             dataView,
27408             tiffOffset,
27409             offset,
27410             dataView.getUint16(offset + 2, littleEndian), // tag type
27411             dataView.getUint32(offset + 4, littleEndian), // tag length
27412             littleEndian
27413         );
27414     },
27415     
27416     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27417     {
27418         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27419             tagSize,
27420             dataOffset,
27421             values,
27422             i,
27423             str,
27424             c;
27425     
27426         if (!tagType) {
27427             Roo.log('Invalid Exif data: Invalid tag type.');
27428             return;
27429         }
27430         
27431         tagSize = tagType.size * length;
27432         // Determine if the value is contained in the dataOffset bytes,
27433         // or if the value at the dataOffset is a pointer to the actual data:
27434         dataOffset = tagSize > 4 ?
27435                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27436         if (dataOffset + tagSize > dataView.byteLength) {
27437             Roo.log('Invalid Exif data: Invalid data offset.');
27438             return;
27439         }
27440         if (length === 1) {
27441             return tagType.getValue(dataView, dataOffset, littleEndian);
27442         }
27443         values = [];
27444         for (i = 0; i < length; i += 1) {
27445             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27446         }
27447         
27448         if (tagType.ascii) {
27449             str = '';
27450             // Concatenate the chars:
27451             for (i = 0; i < values.length; i += 1) {
27452                 c = values[i];
27453                 // Ignore the terminating NULL byte(s):
27454                 if (c === '\u0000') {
27455                     break;
27456                 }
27457                 str += c;
27458             }
27459             return str;
27460         }
27461         return values;
27462     }
27463     
27464 });
27465
27466 Roo.apply(Roo.bootstrap.UploadCropbox, {
27467     tags : {
27468         'Orientation': 0x0112
27469     },
27470     
27471     Orientation: {
27472             1: 0, //'top-left',
27473 //            2: 'top-right',
27474             3: 180, //'bottom-right',
27475 //            4: 'bottom-left',
27476 //            5: 'left-top',
27477             6: 90, //'right-top',
27478 //            7: 'right-bottom',
27479             8: 270 //'left-bottom'
27480     },
27481     
27482     exifTagTypes : {
27483         // byte, 8-bit unsigned int:
27484         1: {
27485             getValue: function (dataView, dataOffset) {
27486                 return dataView.getUint8(dataOffset);
27487             },
27488             size: 1
27489         },
27490         // ascii, 8-bit byte:
27491         2: {
27492             getValue: function (dataView, dataOffset) {
27493                 return String.fromCharCode(dataView.getUint8(dataOffset));
27494             },
27495             size: 1,
27496             ascii: true
27497         },
27498         // short, 16 bit int:
27499         3: {
27500             getValue: function (dataView, dataOffset, littleEndian) {
27501                 return dataView.getUint16(dataOffset, littleEndian);
27502             },
27503             size: 2
27504         },
27505         // long, 32 bit int:
27506         4: {
27507             getValue: function (dataView, dataOffset, littleEndian) {
27508                 return dataView.getUint32(dataOffset, littleEndian);
27509             },
27510             size: 4
27511         },
27512         // rational = two long values, first is numerator, second is denominator:
27513         5: {
27514             getValue: function (dataView, dataOffset, littleEndian) {
27515                 return dataView.getUint32(dataOffset, littleEndian) /
27516                     dataView.getUint32(dataOffset + 4, littleEndian);
27517             },
27518             size: 8
27519         },
27520         // slong, 32 bit signed int:
27521         9: {
27522             getValue: function (dataView, dataOffset, littleEndian) {
27523                 return dataView.getInt32(dataOffset, littleEndian);
27524             },
27525             size: 4
27526         },
27527         // srational, two slongs, first is numerator, second is denominator:
27528         10: {
27529             getValue: function (dataView, dataOffset, littleEndian) {
27530                 return dataView.getInt32(dataOffset, littleEndian) /
27531                     dataView.getInt32(dataOffset + 4, littleEndian);
27532             },
27533             size: 8
27534         }
27535     },
27536     
27537     footer : {
27538         STANDARD : [
27539             {
27540                 tag : 'div',
27541                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27542                 action : 'rotate-left',
27543                 cn : [
27544                     {
27545                         tag : 'button',
27546                         cls : 'btn btn-default',
27547                         html : '<i class="fa fa-undo"></i>'
27548                     }
27549                 ]
27550             },
27551             {
27552                 tag : 'div',
27553                 cls : 'btn-group roo-upload-cropbox-picture',
27554                 action : 'picture',
27555                 cn : [
27556                     {
27557                         tag : 'button',
27558                         cls : 'btn btn-default',
27559                         html : '<i class="fa fa-picture-o"></i>'
27560                     }
27561                 ]
27562             },
27563             {
27564                 tag : 'div',
27565                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27566                 action : 'rotate-right',
27567                 cn : [
27568                     {
27569                         tag : 'button',
27570                         cls : 'btn btn-default',
27571                         html : '<i class="fa fa-repeat"></i>'
27572                     }
27573                 ]
27574             }
27575         ],
27576         DOCUMENT : [
27577             {
27578                 tag : 'div',
27579                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27580                 action : 'rotate-left',
27581                 cn : [
27582                     {
27583                         tag : 'button',
27584                         cls : 'btn btn-default',
27585                         html : '<i class="fa fa-undo"></i>'
27586                     }
27587                 ]
27588             },
27589             {
27590                 tag : 'div',
27591                 cls : 'btn-group roo-upload-cropbox-download',
27592                 action : 'download',
27593                 cn : [
27594                     {
27595                         tag : 'button',
27596                         cls : 'btn btn-default',
27597                         html : '<i class="fa fa-download"></i>'
27598                     }
27599                 ]
27600             },
27601             {
27602                 tag : 'div',
27603                 cls : 'btn-group roo-upload-cropbox-crop',
27604                 action : 'crop',
27605                 cn : [
27606                     {
27607                         tag : 'button',
27608                         cls : 'btn btn-default',
27609                         html : '<i class="fa fa-crop"></i>'
27610                     }
27611                 ]
27612             },
27613             {
27614                 tag : 'div',
27615                 cls : 'btn-group roo-upload-cropbox-trash',
27616                 action : 'trash',
27617                 cn : [
27618                     {
27619                         tag : 'button',
27620                         cls : 'btn btn-default',
27621                         html : '<i class="fa fa-trash"></i>'
27622                     }
27623                 ]
27624             },
27625             {
27626                 tag : 'div',
27627                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27628                 action : 'rotate-right',
27629                 cn : [
27630                     {
27631                         tag : 'button',
27632                         cls : 'btn btn-default',
27633                         html : '<i class="fa fa-repeat"></i>'
27634                     }
27635                 ]
27636             }
27637         ],
27638         ROTATOR : [
27639             {
27640                 tag : 'div',
27641                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27642                 action : 'rotate-left',
27643                 cn : [
27644                     {
27645                         tag : 'button',
27646                         cls : 'btn btn-default',
27647                         html : '<i class="fa fa-undo"></i>'
27648                     }
27649                 ]
27650             },
27651             {
27652                 tag : 'div',
27653                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27654                 action : 'rotate-right',
27655                 cn : [
27656                     {
27657                         tag : 'button',
27658                         cls : 'btn btn-default',
27659                         html : '<i class="fa fa-repeat"></i>'
27660                     }
27661                 ]
27662             }
27663         ]
27664     }
27665 });
27666
27667 /*
27668 * Licence: LGPL
27669 */
27670
27671 /**
27672  * @class Roo.bootstrap.DocumentManager
27673  * @extends Roo.bootstrap.Component
27674  * Bootstrap DocumentManager class
27675  * @cfg {String} paramName default 'imageUpload'
27676  * @cfg {String} toolTipName default 'filename'
27677  * @cfg {String} method default POST
27678  * @cfg {String} url action url
27679  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27680  * @cfg {Boolean} multiple multiple upload default true
27681  * @cfg {Number} thumbSize default 300
27682  * @cfg {String} fieldLabel
27683  * @cfg {Number} labelWidth default 4
27684  * @cfg {String} labelAlign (left|top) default left
27685  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27686 * @cfg {Number} labellg set the width of label (1-12)
27687  * @cfg {Number} labelmd set the width of label (1-12)
27688  * @cfg {Number} labelsm set the width of label (1-12)
27689  * @cfg {Number} labelxs set the width of label (1-12)
27690  * 
27691  * @constructor
27692  * Create a new DocumentManager
27693  * @param {Object} config The config object
27694  */
27695
27696 Roo.bootstrap.DocumentManager = function(config){
27697     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27698     
27699     this.files = [];
27700     this.delegates = [];
27701     
27702     this.addEvents({
27703         /**
27704          * @event initial
27705          * Fire when initial the DocumentManager
27706          * @param {Roo.bootstrap.DocumentManager} this
27707          */
27708         "initial" : true,
27709         /**
27710          * @event inspect
27711          * inspect selected file
27712          * @param {Roo.bootstrap.DocumentManager} this
27713          * @param {File} file
27714          */
27715         "inspect" : true,
27716         /**
27717          * @event exception
27718          * Fire when xhr load exception
27719          * @param {Roo.bootstrap.DocumentManager} this
27720          * @param {XMLHttpRequest} xhr
27721          */
27722         "exception" : true,
27723         /**
27724          * @event afterupload
27725          * Fire when xhr load exception
27726          * @param {Roo.bootstrap.DocumentManager} this
27727          * @param {XMLHttpRequest} xhr
27728          */
27729         "afterupload" : true,
27730         /**
27731          * @event prepare
27732          * prepare the form data
27733          * @param {Roo.bootstrap.DocumentManager} this
27734          * @param {Object} formData
27735          */
27736         "prepare" : true,
27737         /**
27738          * @event remove
27739          * Fire when remove the file
27740          * @param {Roo.bootstrap.DocumentManager} this
27741          * @param {Object} file
27742          */
27743         "remove" : true,
27744         /**
27745          * @event refresh
27746          * Fire after refresh the file
27747          * @param {Roo.bootstrap.DocumentManager} this
27748          */
27749         "refresh" : true,
27750         /**
27751          * @event click
27752          * Fire after click the image
27753          * @param {Roo.bootstrap.DocumentManager} this
27754          * @param {Object} file
27755          */
27756         "click" : true,
27757         /**
27758          * @event edit
27759          * Fire when upload a image and editable set to true
27760          * @param {Roo.bootstrap.DocumentManager} this
27761          * @param {Object} file
27762          */
27763         "edit" : true,
27764         /**
27765          * @event beforeselectfile
27766          * Fire before select file
27767          * @param {Roo.bootstrap.DocumentManager} this
27768          */
27769         "beforeselectfile" : true,
27770         /**
27771          * @event process
27772          * Fire before process file
27773          * @param {Roo.bootstrap.DocumentManager} this
27774          * @param {Object} file
27775          */
27776         "process" : true
27777         
27778     });
27779 };
27780
27781 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27782     
27783     boxes : 0,
27784     inputName : '',
27785     thumbSize : 300,
27786     multiple : true,
27787     files : false,
27788     method : 'POST',
27789     url : '',
27790     paramName : 'imageUpload',
27791     toolTipName : 'filename',
27792     fieldLabel : '',
27793     labelWidth : 4,
27794     labelAlign : 'left',
27795     editable : true,
27796     delegates : false,
27797     xhr : false, 
27798     
27799     labellg : 0,
27800     labelmd : 0,
27801     labelsm : 0,
27802     labelxs : 0,
27803     
27804     getAutoCreate : function()
27805     {   
27806         var managerWidget = {
27807             tag : 'div',
27808             cls : 'roo-document-manager',
27809             cn : [
27810                 {
27811                     tag : 'input',
27812                     cls : 'roo-document-manager-selector',
27813                     type : 'file'
27814                 },
27815                 {
27816                     tag : 'div',
27817                     cls : 'roo-document-manager-uploader',
27818                     cn : [
27819                         {
27820                             tag : 'div',
27821                             cls : 'roo-document-manager-upload-btn',
27822                             html : '<i class="fa fa-plus"></i>'
27823                         }
27824                     ]
27825                     
27826                 }
27827             ]
27828         };
27829         
27830         var content = [
27831             {
27832                 tag : 'div',
27833                 cls : 'column col-md-12',
27834                 cn : managerWidget
27835             }
27836         ];
27837         
27838         if(this.fieldLabel.length){
27839             
27840             content = [
27841                 {
27842                     tag : 'div',
27843                     cls : 'column col-md-12',
27844                     html : this.fieldLabel
27845                 },
27846                 {
27847                     tag : 'div',
27848                     cls : 'column col-md-12',
27849                     cn : managerWidget
27850                 }
27851             ];
27852
27853             if(this.labelAlign == 'left'){
27854                 content = [
27855                     {
27856                         tag : 'div',
27857                         cls : 'column',
27858                         html : this.fieldLabel
27859                     },
27860                     {
27861                         tag : 'div',
27862                         cls : 'column',
27863                         cn : managerWidget
27864                     }
27865                 ];
27866                 
27867                 if(this.labelWidth > 12){
27868                     content[0].style = "width: " + this.labelWidth + 'px';
27869                 }
27870
27871                 if(this.labelWidth < 13 && this.labelmd == 0){
27872                     this.labelmd = this.labelWidth;
27873                 }
27874
27875                 if(this.labellg > 0){
27876                     content[0].cls += ' col-lg-' + this.labellg;
27877                     content[1].cls += ' col-lg-' + (12 - this.labellg);
27878                 }
27879
27880                 if(this.labelmd > 0){
27881                     content[0].cls += ' col-md-' + this.labelmd;
27882                     content[1].cls += ' col-md-' + (12 - this.labelmd);
27883                 }
27884
27885                 if(this.labelsm > 0){
27886                     content[0].cls += ' col-sm-' + this.labelsm;
27887                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
27888                 }
27889
27890                 if(this.labelxs > 0){
27891                     content[0].cls += ' col-xs-' + this.labelxs;
27892                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
27893                 }
27894                 
27895             }
27896         }
27897         
27898         var cfg = {
27899             tag : 'div',
27900             cls : 'row clearfix',
27901             cn : content
27902         };
27903         
27904         return cfg;
27905         
27906     },
27907     
27908     initEvents : function()
27909     {
27910         this.managerEl = this.el.select('.roo-document-manager', true).first();
27911         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27912         
27913         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27914         this.selectorEl.hide();
27915         
27916         if(this.multiple){
27917             this.selectorEl.attr('multiple', 'multiple');
27918         }
27919         
27920         this.selectorEl.on('change', this.onFileSelected, this);
27921         
27922         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27923         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27924         
27925         this.uploader.on('click', this.onUploaderClick, this);
27926         
27927         this.renderProgressDialog();
27928         
27929         var _this = this;
27930         
27931         window.addEventListener("resize", function() { _this.refresh(); } );
27932         
27933         this.fireEvent('initial', this);
27934     },
27935     
27936     renderProgressDialog : function()
27937     {
27938         var _this = this;
27939         
27940         this.progressDialog = new Roo.bootstrap.Modal({
27941             cls : 'roo-document-manager-progress-dialog',
27942             allow_close : false,
27943             title : '',
27944             buttons : [
27945                 {
27946                     name  :'cancel',
27947                     weight : 'danger',
27948                     html : 'Cancel'
27949                 }
27950             ], 
27951             listeners : { 
27952                 btnclick : function() {
27953                     _this.uploadCancel();
27954                     this.hide();
27955                 }
27956             }
27957         });
27958          
27959         this.progressDialog.render(Roo.get(document.body));
27960          
27961         this.progress = new Roo.bootstrap.Progress({
27962             cls : 'roo-document-manager-progress',
27963             active : true,
27964             striped : true
27965         });
27966         
27967         this.progress.render(this.progressDialog.getChildContainer());
27968         
27969         this.progressBar = new Roo.bootstrap.ProgressBar({
27970             cls : 'roo-document-manager-progress-bar',
27971             aria_valuenow : 0,
27972             aria_valuemin : 0,
27973             aria_valuemax : 12,
27974             panel : 'success'
27975         });
27976         
27977         this.progressBar.render(this.progress.getChildContainer());
27978     },
27979     
27980     onUploaderClick : function(e)
27981     {
27982         e.preventDefault();
27983      
27984         if(this.fireEvent('beforeselectfile', this) != false){
27985             this.selectorEl.dom.click();
27986         }
27987         
27988     },
27989     
27990     onFileSelected : function(e)
27991     {
27992         e.preventDefault();
27993         
27994         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27995             return;
27996         }
27997         
27998         Roo.each(this.selectorEl.dom.files, function(file){
27999             if(this.fireEvent('inspect', this, file) != false){
28000                 this.files.push(file);
28001             }
28002         }, this);
28003         
28004         this.queue();
28005         
28006     },
28007     
28008     queue : function()
28009     {
28010         this.selectorEl.dom.value = '';
28011         
28012         if(!this.files.length){
28013             return;
28014         }
28015         
28016         if(this.boxes > 0 && this.files.length > this.boxes){
28017             this.files = this.files.slice(0, this.boxes);
28018         }
28019         
28020         this.uploader.show();
28021         
28022         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28023             this.uploader.hide();
28024         }
28025         
28026         var _this = this;
28027         
28028         var files = [];
28029         
28030         var docs = [];
28031         
28032         Roo.each(this.files, function(file){
28033             
28034             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28035                 var f = this.renderPreview(file);
28036                 files.push(f);
28037                 return;
28038             }
28039             
28040             if(file.type.indexOf('image') != -1){
28041                 this.delegates.push(
28042                     (function(){
28043                         _this.process(file);
28044                     }).createDelegate(this)
28045                 );
28046         
28047                 return;
28048             }
28049             
28050             docs.push(
28051                 (function(){
28052                     _this.process(file);
28053                 }).createDelegate(this)
28054             );
28055             
28056         }, this);
28057         
28058         this.files = files;
28059         
28060         this.delegates = this.delegates.concat(docs);
28061         
28062         if(!this.delegates.length){
28063             this.refresh();
28064             return;
28065         }
28066         
28067         this.progressBar.aria_valuemax = this.delegates.length;
28068         
28069         this.arrange();
28070         
28071         return;
28072     },
28073     
28074     arrange : function()
28075     {
28076         if(!this.delegates.length){
28077             this.progressDialog.hide();
28078             this.refresh();
28079             return;
28080         }
28081         
28082         var delegate = this.delegates.shift();
28083         
28084         this.progressDialog.show();
28085         
28086         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28087         
28088         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28089         
28090         delegate();
28091     },
28092     
28093     refresh : function()
28094     {
28095         this.uploader.show();
28096         
28097         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28098             this.uploader.hide();
28099         }
28100         
28101         Roo.isTouch ? this.closable(false) : this.closable(true);
28102         
28103         this.fireEvent('refresh', this);
28104     },
28105     
28106     onRemove : function(e, el, o)
28107     {
28108         e.preventDefault();
28109         
28110         this.fireEvent('remove', this, o);
28111         
28112     },
28113     
28114     remove : function(o)
28115     {
28116         var files = [];
28117         
28118         Roo.each(this.files, function(file){
28119             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28120                 files.push(file);
28121                 return;
28122             }
28123
28124             o.target.remove();
28125
28126         }, this);
28127         
28128         this.files = files;
28129         
28130         this.refresh();
28131     },
28132     
28133     clear : function()
28134     {
28135         Roo.each(this.files, function(file){
28136             if(!file.target){
28137                 return;
28138             }
28139             
28140             file.target.remove();
28141
28142         }, this);
28143         
28144         this.files = [];
28145         
28146         this.refresh();
28147     },
28148     
28149     onClick : function(e, el, o)
28150     {
28151         e.preventDefault();
28152         
28153         this.fireEvent('click', this, o);
28154         
28155     },
28156     
28157     closable : function(closable)
28158     {
28159         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28160             
28161             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28162             
28163             if(closable){
28164                 el.show();
28165                 return;
28166             }
28167             
28168             el.hide();
28169             
28170         }, this);
28171     },
28172     
28173     xhrOnLoad : function(xhr)
28174     {
28175         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28176             el.remove();
28177         }, this);
28178         
28179         if (xhr.readyState !== 4) {
28180             this.arrange();
28181             this.fireEvent('exception', this, xhr);
28182             return;
28183         }
28184
28185         var response = Roo.decode(xhr.responseText);
28186         
28187         if(!response.success){
28188             this.arrange();
28189             this.fireEvent('exception', this, xhr);
28190             return;
28191         }
28192         
28193         var file = this.renderPreview(response.data);
28194         
28195         this.files.push(file);
28196         
28197         this.arrange();
28198         
28199         this.fireEvent('afterupload', this, xhr);
28200         
28201     },
28202     
28203     xhrOnError : function(xhr)
28204     {
28205         Roo.log('xhr on error');
28206         
28207         var response = Roo.decode(xhr.responseText);
28208           
28209         Roo.log(response);
28210         
28211         this.arrange();
28212     },
28213     
28214     process : function(file)
28215     {
28216         if(this.fireEvent('process', this, file) !== false){
28217             if(this.editable && file.type.indexOf('image') != -1){
28218                 this.fireEvent('edit', this, file);
28219                 return;
28220             }
28221
28222             this.uploadStart(file, false);
28223
28224             return;
28225         }
28226         
28227     },
28228     
28229     uploadStart : function(file, crop)
28230     {
28231         this.xhr = new XMLHttpRequest();
28232         
28233         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28234             this.arrange();
28235             return;
28236         }
28237         
28238         file.xhr = this.xhr;
28239             
28240         this.managerEl.createChild({
28241             tag : 'div',
28242             cls : 'roo-document-manager-loading',
28243             cn : [
28244                 {
28245                     tag : 'div',
28246                     tooltip : file.name,
28247                     cls : 'roo-document-manager-thumb',
28248                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28249                 }
28250             ]
28251
28252         });
28253
28254         this.xhr.open(this.method, this.url, true);
28255         
28256         var headers = {
28257             "Accept": "application/json",
28258             "Cache-Control": "no-cache",
28259             "X-Requested-With": "XMLHttpRequest"
28260         };
28261         
28262         for (var headerName in headers) {
28263             var headerValue = headers[headerName];
28264             if (headerValue) {
28265                 this.xhr.setRequestHeader(headerName, headerValue);
28266             }
28267         }
28268         
28269         var _this = this;
28270         
28271         this.xhr.onload = function()
28272         {
28273             _this.xhrOnLoad(_this.xhr);
28274         }
28275         
28276         this.xhr.onerror = function()
28277         {
28278             _this.xhrOnError(_this.xhr);
28279         }
28280         
28281         var formData = new FormData();
28282
28283         formData.append('returnHTML', 'NO');
28284         
28285         if(crop){
28286             formData.append('crop', crop);
28287         }
28288         
28289         formData.append(this.paramName, file, file.name);
28290         
28291         var options = {
28292             file : file, 
28293             manually : false
28294         };
28295         
28296         if(this.fireEvent('prepare', this, formData, options) != false){
28297             
28298             if(options.manually){
28299                 return;
28300             }
28301             
28302             this.xhr.send(formData);
28303             return;
28304         };
28305         
28306         this.uploadCancel();
28307     },
28308     
28309     uploadCancel : function()
28310     {
28311         if (this.xhr) {
28312             this.xhr.abort();
28313         }
28314         
28315         this.delegates = [];
28316         
28317         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28318             el.remove();
28319         }, this);
28320         
28321         this.arrange();
28322     },
28323     
28324     renderPreview : function(file)
28325     {
28326         if(typeof(file.target) != 'undefined' && file.target){
28327             return file;
28328         }
28329         
28330         var previewEl = this.managerEl.createChild({
28331             tag : 'div',
28332             cls : 'roo-document-manager-preview',
28333             cn : [
28334                 {
28335                     tag : 'div',
28336                     tooltip : file[this.toolTipName],
28337                     cls : 'roo-document-manager-thumb',
28338                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28339                 },
28340                 {
28341                     tag : 'button',
28342                     cls : 'close',
28343                     html : '<i class="fa fa-times-circle"></i>'
28344                 }
28345             ]
28346         });
28347
28348         var close = previewEl.select('button.close', true).first();
28349
28350         close.on('click', this.onRemove, this, file);
28351
28352         file.target = previewEl;
28353
28354         var image = previewEl.select('img', true).first();
28355         
28356         var _this = this;
28357         
28358         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28359         
28360         image.on('click', this.onClick, this, file);
28361         
28362         return file;
28363         
28364     },
28365     
28366     onPreviewLoad : function(file, image)
28367     {
28368         if(typeof(file.target) == 'undefined' || !file.target){
28369             return;
28370         }
28371         
28372         var width = image.dom.naturalWidth || image.dom.width;
28373         var height = image.dom.naturalHeight || image.dom.height;
28374         
28375         if(width > height){
28376             file.target.addClass('wide');
28377             return;
28378         }
28379         
28380         file.target.addClass('tall');
28381         return;
28382         
28383     },
28384     
28385     uploadFromSource : function(file, crop)
28386     {
28387         this.xhr = new XMLHttpRequest();
28388         
28389         this.managerEl.createChild({
28390             tag : 'div',
28391             cls : 'roo-document-manager-loading',
28392             cn : [
28393                 {
28394                     tag : 'div',
28395                     tooltip : file.name,
28396                     cls : 'roo-document-manager-thumb',
28397                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28398                 }
28399             ]
28400
28401         });
28402
28403         this.xhr.open(this.method, this.url, true);
28404         
28405         var headers = {
28406             "Accept": "application/json",
28407             "Cache-Control": "no-cache",
28408             "X-Requested-With": "XMLHttpRequest"
28409         };
28410         
28411         for (var headerName in headers) {
28412             var headerValue = headers[headerName];
28413             if (headerValue) {
28414                 this.xhr.setRequestHeader(headerName, headerValue);
28415             }
28416         }
28417         
28418         var _this = this;
28419         
28420         this.xhr.onload = function()
28421         {
28422             _this.xhrOnLoad(_this.xhr);
28423         }
28424         
28425         this.xhr.onerror = function()
28426         {
28427             _this.xhrOnError(_this.xhr);
28428         }
28429         
28430         var formData = new FormData();
28431
28432         formData.append('returnHTML', 'NO');
28433         
28434         formData.append('crop', crop);
28435         
28436         if(typeof(file.filename) != 'undefined'){
28437             formData.append('filename', file.filename);
28438         }
28439         
28440         if(typeof(file.mimetype) != 'undefined'){
28441             formData.append('mimetype', file.mimetype);
28442         }
28443         
28444         if(this.fireEvent('prepare', this, formData) != false){
28445             this.xhr.send(formData);
28446         };
28447     }
28448 });
28449
28450 /*
28451 * Licence: LGPL
28452 */
28453
28454 /**
28455  * @class Roo.bootstrap.DocumentViewer
28456  * @extends Roo.bootstrap.Component
28457  * Bootstrap DocumentViewer class
28458  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28459  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28460  * 
28461  * @constructor
28462  * Create a new DocumentViewer
28463  * @param {Object} config The config object
28464  */
28465
28466 Roo.bootstrap.DocumentViewer = function(config){
28467     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28468     
28469     this.addEvents({
28470         /**
28471          * @event initial
28472          * Fire after initEvent
28473          * @param {Roo.bootstrap.DocumentViewer} this
28474          */
28475         "initial" : true,
28476         /**
28477          * @event click
28478          * Fire after click
28479          * @param {Roo.bootstrap.DocumentViewer} this
28480          */
28481         "click" : true,
28482         /**
28483          * @event download
28484          * Fire after download button
28485          * @param {Roo.bootstrap.DocumentViewer} this
28486          */
28487         "download" : true,
28488         /**
28489          * @event trash
28490          * Fire after trash button
28491          * @param {Roo.bootstrap.DocumentViewer} this
28492          */
28493         "trash" : true
28494         
28495     });
28496 };
28497
28498 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28499     
28500     showDownload : true,
28501     
28502     showTrash : true,
28503     
28504     getAutoCreate : function()
28505     {
28506         var cfg = {
28507             tag : 'div',
28508             cls : 'roo-document-viewer',
28509             cn : [
28510                 {
28511                     tag : 'div',
28512                     cls : 'roo-document-viewer-body',
28513                     cn : [
28514                         {
28515                             tag : 'div',
28516                             cls : 'roo-document-viewer-thumb',
28517                             cn : [
28518                                 {
28519                                     tag : 'img',
28520                                     cls : 'roo-document-viewer-image'
28521                                 }
28522                             ]
28523                         }
28524                     ]
28525                 },
28526                 {
28527                     tag : 'div',
28528                     cls : 'roo-document-viewer-footer',
28529                     cn : {
28530                         tag : 'div',
28531                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28532                         cn : [
28533                             {
28534                                 tag : 'div',
28535                                 cls : 'btn-group roo-document-viewer-download',
28536                                 cn : [
28537                                     {
28538                                         tag : 'button',
28539                                         cls : 'btn btn-default',
28540                                         html : '<i class="fa fa-download"></i>'
28541                                     }
28542                                 ]
28543                             },
28544                             {
28545                                 tag : 'div',
28546                                 cls : 'btn-group roo-document-viewer-trash',
28547                                 cn : [
28548                                     {
28549                                         tag : 'button',
28550                                         cls : 'btn btn-default',
28551                                         html : '<i class="fa fa-trash"></i>'
28552                                     }
28553                                 ]
28554                             }
28555                         ]
28556                     }
28557                 }
28558             ]
28559         };
28560         
28561         return cfg;
28562     },
28563     
28564     initEvents : function()
28565     {
28566         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28567         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28568         
28569         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28570         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28571         
28572         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28573         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28574         
28575         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28576         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28577         
28578         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28579         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28580         
28581         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28582         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28583         
28584         this.bodyEl.on('click', this.onClick, this);
28585         this.downloadBtn.on('click', this.onDownload, this);
28586         this.trashBtn.on('click', this.onTrash, this);
28587         
28588         this.downloadBtn.hide();
28589         this.trashBtn.hide();
28590         
28591         if(this.showDownload){
28592             this.downloadBtn.show();
28593         }
28594         
28595         if(this.showTrash){
28596             this.trashBtn.show();
28597         }
28598         
28599         if(!this.showDownload && !this.showTrash) {
28600             this.footerEl.hide();
28601         }
28602         
28603     },
28604     
28605     initial : function()
28606     {
28607         this.fireEvent('initial', this);
28608         
28609     },
28610     
28611     onClick : function(e)
28612     {
28613         e.preventDefault();
28614         
28615         this.fireEvent('click', this);
28616     },
28617     
28618     onDownload : function(e)
28619     {
28620         e.preventDefault();
28621         
28622         this.fireEvent('download', this);
28623     },
28624     
28625     onTrash : function(e)
28626     {
28627         e.preventDefault();
28628         
28629         this.fireEvent('trash', this);
28630     }
28631     
28632 });
28633 /*
28634  * - LGPL
28635  *
28636  * nav progress bar
28637  * 
28638  */
28639
28640 /**
28641  * @class Roo.bootstrap.NavProgressBar
28642  * @extends Roo.bootstrap.Component
28643  * Bootstrap NavProgressBar class
28644  * 
28645  * @constructor
28646  * Create a new nav progress bar
28647  * @param {Object} config The config object
28648  */
28649
28650 Roo.bootstrap.NavProgressBar = function(config){
28651     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28652
28653     this.bullets = this.bullets || [];
28654    
28655 //    Roo.bootstrap.NavProgressBar.register(this);
28656      this.addEvents({
28657         /**
28658              * @event changed
28659              * Fires when the active item changes
28660              * @param {Roo.bootstrap.NavProgressBar} this
28661              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28662              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28663          */
28664         'changed': true
28665      });
28666     
28667 };
28668
28669 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28670     
28671     bullets : [],
28672     barItems : [],
28673     
28674     getAutoCreate : function()
28675     {
28676         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28677         
28678         cfg = {
28679             tag : 'div',
28680             cls : 'roo-navigation-bar-group',
28681             cn : [
28682                 {
28683                     tag : 'div',
28684                     cls : 'roo-navigation-top-bar'
28685                 },
28686                 {
28687                     tag : 'div',
28688                     cls : 'roo-navigation-bullets-bar',
28689                     cn : [
28690                         {
28691                             tag : 'ul',
28692                             cls : 'roo-navigation-bar'
28693                         }
28694                     ]
28695                 },
28696                 
28697                 {
28698                     tag : 'div',
28699                     cls : 'roo-navigation-bottom-bar'
28700                 }
28701             ]
28702             
28703         };
28704         
28705         return cfg;
28706         
28707     },
28708     
28709     initEvents: function() 
28710     {
28711         
28712     },
28713     
28714     onRender : function(ct, position) 
28715     {
28716         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28717         
28718         if(this.bullets.length){
28719             Roo.each(this.bullets, function(b){
28720                this.addItem(b);
28721             }, this);
28722         }
28723         
28724         this.format();
28725         
28726     },
28727     
28728     addItem : function(cfg)
28729     {
28730         var item = new Roo.bootstrap.NavProgressItem(cfg);
28731         
28732         item.parentId = this.id;
28733         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28734         
28735         if(cfg.html){
28736             var top = new Roo.bootstrap.Element({
28737                 tag : 'div',
28738                 cls : 'roo-navigation-bar-text'
28739             });
28740             
28741             var bottom = new Roo.bootstrap.Element({
28742                 tag : 'div',
28743                 cls : 'roo-navigation-bar-text'
28744             });
28745             
28746             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28747             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28748             
28749             var topText = new Roo.bootstrap.Element({
28750                 tag : 'span',
28751                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28752             });
28753             
28754             var bottomText = new Roo.bootstrap.Element({
28755                 tag : 'span',
28756                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28757             });
28758             
28759             topText.onRender(top.el, null);
28760             bottomText.onRender(bottom.el, null);
28761             
28762             item.topEl = top;
28763             item.bottomEl = bottom;
28764         }
28765         
28766         this.barItems.push(item);
28767         
28768         return item;
28769     },
28770     
28771     getActive : function()
28772     {
28773         var active = false;
28774         
28775         Roo.each(this.barItems, function(v){
28776             
28777             if (!v.isActive()) {
28778                 return;
28779             }
28780             
28781             active = v;
28782             return false;
28783             
28784         });
28785         
28786         return active;
28787     },
28788     
28789     setActiveItem : function(item)
28790     {
28791         var prev = false;
28792         
28793         Roo.each(this.barItems, function(v){
28794             if (v.rid == item.rid) {
28795                 return ;
28796             }
28797             
28798             if (v.isActive()) {
28799                 v.setActive(false);
28800                 prev = v;
28801             }
28802         });
28803
28804         item.setActive(true);
28805         
28806         this.fireEvent('changed', this, item, prev);
28807     },
28808     
28809     getBarItem: function(rid)
28810     {
28811         var ret = false;
28812         
28813         Roo.each(this.barItems, function(e) {
28814             if (e.rid != rid) {
28815                 return;
28816             }
28817             
28818             ret =  e;
28819             return false;
28820         });
28821         
28822         return ret;
28823     },
28824     
28825     indexOfItem : function(item)
28826     {
28827         var index = false;
28828         
28829         Roo.each(this.barItems, function(v, i){
28830             
28831             if (v.rid != item.rid) {
28832                 return;
28833             }
28834             
28835             index = i;
28836             return false
28837         });
28838         
28839         return index;
28840     },
28841     
28842     setActiveNext : function()
28843     {
28844         var i = this.indexOfItem(this.getActive());
28845         
28846         if (i > this.barItems.length) {
28847             return;
28848         }
28849         
28850         this.setActiveItem(this.barItems[i+1]);
28851     },
28852     
28853     setActivePrev : function()
28854     {
28855         var i = this.indexOfItem(this.getActive());
28856         
28857         if (i  < 1) {
28858             return;
28859         }
28860         
28861         this.setActiveItem(this.barItems[i-1]);
28862     },
28863     
28864     format : function()
28865     {
28866         if(!this.barItems.length){
28867             return;
28868         }
28869      
28870         var width = 100 / this.barItems.length;
28871         
28872         Roo.each(this.barItems, function(i){
28873             i.el.setStyle('width', width + '%');
28874             i.topEl.el.setStyle('width', width + '%');
28875             i.bottomEl.el.setStyle('width', width + '%');
28876         }, this);
28877         
28878     }
28879     
28880 });
28881 /*
28882  * - LGPL
28883  *
28884  * Nav Progress Item
28885  * 
28886  */
28887
28888 /**
28889  * @class Roo.bootstrap.NavProgressItem
28890  * @extends Roo.bootstrap.Component
28891  * Bootstrap NavProgressItem class
28892  * @cfg {String} rid the reference id
28893  * @cfg {Boolean} active (true|false) Is item active default false
28894  * @cfg {Boolean} disabled (true|false) Is item active default false
28895  * @cfg {String} html
28896  * @cfg {String} position (top|bottom) text position default bottom
28897  * @cfg {String} icon show icon instead of number
28898  * 
28899  * @constructor
28900  * Create a new NavProgressItem
28901  * @param {Object} config The config object
28902  */
28903 Roo.bootstrap.NavProgressItem = function(config){
28904     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28905     this.addEvents({
28906         // raw events
28907         /**
28908          * @event click
28909          * The raw click event for the entire grid.
28910          * @param {Roo.bootstrap.NavProgressItem} this
28911          * @param {Roo.EventObject} e
28912          */
28913         "click" : true
28914     });
28915    
28916 };
28917
28918 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28919     
28920     rid : '',
28921     active : false,
28922     disabled : false,
28923     html : '',
28924     position : 'bottom',
28925     icon : false,
28926     
28927     getAutoCreate : function()
28928     {
28929         var iconCls = 'roo-navigation-bar-item-icon';
28930         
28931         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28932         
28933         var cfg = {
28934             tag: 'li',
28935             cls: 'roo-navigation-bar-item',
28936             cn : [
28937                 {
28938                     tag : 'i',
28939                     cls : iconCls
28940                 }
28941             ]
28942         };
28943         
28944         if(this.active){
28945             cfg.cls += ' active';
28946         }
28947         if(this.disabled){
28948             cfg.cls += ' disabled';
28949         }
28950         
28951         return cfg;
28952     },
28953     
28954     disable : function()
28955     {
28956         this.setDisabled(true);
28957     },
28958     
28959     enable : function()
28960     {
28961         this.setDisabled(false);
28962     },
28963     
28964     initEvents: function() 
28965     {
28966         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28967         
28968         this.iconEl.on('click', this.onClick, this);
28969     },
28970     
28971     onClick : function(e)
28972     {
28973         e.preventDefault();
28974         
28975         if(this.disabled){
28976             return;
28977         }
28978         
28979         if(this.fireEvent('click', this, e) === false){
28980             return;
28981         };
28982         
28983         this.parent().setActiveItem(this);
28984     },
28985     
28986     isActive: function () 
28987     {
28988         return this.active;
28989     },
28990     
28991     setActive : function(state)
28992     {
28993         if(this.active == state){
28994             return;
28995         }
28996         
28997         this.active = state;
28998         
28999         if (state) {
29000             this.el.addClass('active');
29001             return;
29002         }
29003         
29004         this.el.removeClass('active');
29005         
29006         return;
29007     },
29008     
29009     setDisabled : function(state)
29010     {
29011         if(this.disabled == state){
29012             return;
29013         }
29014         
29015         this.disabled = state;
29016         
29017         if (state) {
29018             this.el.addClass('disabled');
29019             return;
29020         }
29021         
29022         this.el.removeClass('disabled');
29023     },
29024     
29025     tooltipEl : function()
29026     {
29027         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29028     }
29029 });
29030  
29031
29032  /*
29033  * - LGPL
29034  *
29035  * FieldLabel
29036  * 
29037  */
29038
29039 /**
29040  * @class Roo.bootstrap.FieldLabel
29041  * @extends Roo.bootstrap.Component
29042  * Bootstrap FieldLabel class
29043  * @cfg {String} html contents of the element
29044  * @cfg {String} tag tag of the element default label
29045  * @cfg {String} cls class of the element
29046  * @cfg {String} target label target 
29047  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29048  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29049  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29050  * @cfg {String} iconTooltip default "This field is required"
29051  * 
29052  * @constructor
29053  * Create a new FieldLabel
29054  * @param {Object} config The config object
29055  */
29056
29057 Roo.bootstrap.FieldLabel = function(config){
29058     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29059     
29060     this.addEvents({
29061             /**
29062              * @event invalid
29063              * Fires after the field has been marked as invalid.
29064              * @param {Roo.form.FieldLabel} this
29065              * @param {String} msg The validation message
29066              */
29067             invalid : true,
29068             /**
29069              * @event valid
29070              * Fires after the field has been validated with no errors.
29071              * @param {Roo.form.FieldLabel} this
29072              */
29073             valid : true
29074         });
29075 };
29076
29077 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29078     
29079     tag: 'label',
29080     cls: '',
29081     html: '',
29082     target: '',
29083     allowBlank : true,
29084     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29085     validClass : 'text-success fa fa-lg fa-check',
29086     iconTooltip : 'This field is required',
29087     
29088     getAutoCreate : function(){
29089         
29090         var cfg = {
29091             tag : this.tag,
29092             cls : 'roo-bootstrap-field-label ' + this.cls,
29093             for : this.target,
29094             cn : [
29095                 {
29096                     tag : 'i',
29097                     cls : '',
29098                     tooltip : this.iconTooltip
29099                 },
29100                 {
29101                     tag : 'span',
29102                     html : this.html
29103                 }
29104             ] 
29105         };
29106         
29107         return cfg;
29108     },
29109     
29110     initEvents: function() 
29111     {
29112         Roo.bootstrap.Element.superclass.initEvents.call(this);
29113         
29114         this.iconEl = this.el.select('i', true).first();
29115         
29116         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29117         
29118         Roo.bootstrap.FieldLabel.register(this);
29119     },
29120     
29121     /**
29122      * Mark this field as valid
29123      */
29124     markValid : function()
29125     {
29126         this.iconEl.show();
29127         
29128         this.iconEl.removeClass(this.invalidClass);
29129         
29130         this.iconEl.addClass(this.validClass);
29131         
29132         this.fireEvent('valid', this);
29133     },
29134     
29135     /**
29136      * Mark this field as invalid
29137      * @param {String} msg The validation message
29138      */
29139     markInvalid : function(msg)
29140     {
29141         this.iconEl.show();
29142         
29143         this.iconEl.removeClass(this.validClass);
29144         
29145         this.iconEl.addClass(this.invalidClass);
29146         
29147         this.fireEvent('invalid', this, msg);
29148     }
29149     
29150    
29151 });
29152
29153 Roo.apply(Roo.bootstrap.FieldLabel, {
29154     
29155     groups: {},
29156     
29157      /**
29158     * register a FieldLabel Group
29159     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29160     */
29161     register : function(label)
29162     {
29163         if(this.groups.hasOwnProperty(label.target)){
29164             return;
29165         }
29166      
29167         this.groups[label.target] = label;
29168         
29169     },
29170     /**
29171     * fetch a FieldLabel Group based on the target
29172     * @param {string} target
29173     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29174     */
29175     get: function(target) {
29176         if (typeof(this.groups[target]) == 'undefined') {
29177             return false;
29178         }
29179         
29180         return this.groups[target] ;
29181     }
29182 });
29183
29184  
29185
29186  /*
29187  * - LGPL
29188  *
29189  * page DateSplitField.
29190  * 
29191  */
29192
29193
29194 /**
29195  * @class Roo.bootstrap.DateSplitField
29196  * @extends Roo.bootstrap.Component
29197  * Bootstrap DateSplitField class
29198  * @cfg {string} fieldLabel - the label associated
29199  * @cfg {Number} labelWidth set the width of label (0-12)
29200  * @cfg {String} labelAlign (top|left)
29201  * @cfg {Boolean} dayAllowBlank (true|false) default false
29202  * @cfg {Boolean} monthAllowBlank (true|false) default false
29203  * @cfg {Boolean} yearAllowBlank (true|false) default false
29204  * @cfg {string} dayPlaceholder 
29205  * @cfg {string} monthPlaceholder
29206  * @cfg {string} yearPlaceholder
29207  * @cfg {string} dayFormat default 'd'
29208  * @cfg {string} monthFormat default 'm'
29209  * @cfg {string} yearFormat default 'Y'
29210  * @cfg {Number} labellg set the width of label (1-12)
29211  * @cfg {Number} labelmd set the width of label (1-12)
29212  * @cfg {Number} labelsm set the width of label (1-12)
29213  * @cfg {Number} labelxs set the width of label (1-12)
29214
29215  *     
29216  * @constructor
29217  * Create a new DateSplitField
29218  * @param {Object} config The config object
29219  */
29220
29221 Roo.bootstrap.DateSplitField = function(config){
29222     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29223     
29224     this.addEvents({
29225         // raw events
29226          /**
29227          * @event years
29228          * getting the data of years
29229          * @param {Roo.bootstrap.DateSplitField} this
29230          * @param {Object} years
29231          */
29232         "years" : true,
29233         /**
29234          * @event days
29235          * getting the data of days
29236          * @param {Roo.bootstrap.DateSplitField} this
29237          * @param {Object} days
29238          */
29239         "days" : true,
29240         /**
29241          * @event invalid
29242          * Fires after the field has been marked as invalid.
29243          * @param {Roo.form.Field} this
29244          * @param {String} msg The validation message
29245          */
29246         invalid : true,
29247        /**
29248          * @event valid
29249          * Fires after the field has been validated with no errors.
29250          * @param {Roo.form.Field} this
29251          */
29252         valid : true
29253     });
29254 };
29255
29256 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29257     
29258     fieldLabel : '',
29259     labelAlign : 'top',
29260     labelWidth : 3,
29261     dayAllowBlank : false,
29262     monthAllowBlank : false,
29263     yearAllowBlank : false,
29264     dayPlaceholder : '',
29265     monthPlaceholder : '',
29266     yearPlaceholder : '',
29267     dayFormat : 'd',
29268     monthFormat : 'm',
29269     yearFormat : 'Y',
29270     isFormField : true,
29271     labellg : 0,
29272     labelmd : 0,
29273     labelsm : 0,
29274     labelxs : 0,
29275     
29276     getAutoCreate : function()
29277     {
29278         var cfg = {
29279             tag : 'div',
29280             cls : 'row roo-date-split-field-group',
29281             cn : [
29282                 {
29283                     tag : 'input',
29284                     type : 'hidden',
29285                     cls : 'form-hidden-field roo-date-split-field-group-value',
29286                     name : this.name
29287                 }
29288             ]
29289         };
29290         
29291         var labelCls = 'col-md-12';
29292         var contentCls = 'col-md-4';
29293         
29294         if(this.fieldLabel){
29295             
29296             var label = {
29297                 tag : 'div',
29298                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29299                 cn : [
29300                     {
29301                         tag : 'label',
29302                         html : this.fieldLabel
29303                     }
29304                 ]
29305             };
29306             
29307             if(this.labelAlign == 'left'){
29308             
29309                 if(this.labelWidth > 12){
29310                     label.style = "width: " + this.labelWidth + 'px';
29311                 }
29312
29313                 if(this.labelWidth < 13 && this.labelmd == 0){
29314                     this.labelmd = this.labelWidth;
29315                 }
29316
29317                 if(this.labellg > 0){
29318                     labelCls = ' col-lg-' + this.labellg;
29319                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29320                 }
29321
29322                 if(this.labelmd > 0){
29323                     labelCls = ' col-md-' + this.labelmd;
29324                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29325                 }
29326
29327                 if(this.labelsm > 0){
29328                     labelCls = ' col-sm-' + this.labelsm;
29329                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29330                 }
29331
29332                 if(this.labelxs > 0){
29333                     labelCls = ' col-xs-' + this.labelxs;
29334                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29335                 }
29336             }
29337             
29338             label.cls += ' ' + labelCls;
29339             
29340             cfg.cn.push(label);
29341         }
29342         
29343         Roo.each(['day', 'month', 'year'], function(t){
29344             cfg.cn.push({
29345                 tag : 'div',
29346                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29347             });
29348         }, this);
29349         
29350         return cfg;
29351     },
29352     
29353     inputEl: function ()
29354     {
29355         return this.el.select('.roo-date-split-field-group-value', true).first();
29356     },
29357     
29358     onRender : function(ct, position) 
29359     {
29360         var _this = this;
29361         
29362         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29363         
29364         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29365         
29366         this.dayField = new Roo.bootstrap.ComboBox({
29367             allowBlank : this.dayAllowBlank,
29368             alwaysQuery : true,
29369             displayField : 'value',
29370             editable : false,
29371             fieldLabel : '',
29372             forceSelection : true,
29373             mode : 'local',
29374             placeholder : this.dayPlaceholder,
29375             selectOnFocus : true,
29376             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29377             triggerAction : 'all',
29378             typeAhead : true,
29379             valueField : 'value',
29380             store : new Roo.data.SimpleStore({
29381                 data : (function() {    
29382                     var days = [];
29383                     _this.fireEvent('days', _this, days);
29384                     return days;
29385                 })(),
29386                 fields : [ 'value' ]
29387             }),
29388             listeners : {
29389                 select : function (_self, record, index)
29390                 {
29391                     _this.setValue(_this.getValue());
29392                 }
29393             }
29394         });
29395
29396         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29397         
29398         this.monthField = new Roo.bootstrap.MonthField({
29399             after : '<i class=\"fa fa-calendar\"></i>',
29400             allowBlank : this.monthAllowBlank,
29401             placeholder : this.monthPlaceholder,
29402             readOnly : true,
29403             listeners : {
29404                 render : function (_self)
29405                 {
29406                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29407                         e.preventDefault();
29408                         _self.focus();
29409                     });
29410                 },
29411                 select : function (_self, oldvalue, newvalue)
29412                 {
29413                     _this.setValue(_this.getValue());
29414                 }
29415             }
29416         });
29417         
29418         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29419         
29420         this.yearField = new Roo.bootstrap.ComboBox({
29421             allowBlank : this.yearAllowBlank,
29422             alwaysQuery : true,
29423             displayField : 'value',
29424             editable : false,
29425             fieldLabel : '',
29426             forceSelection : true,
29427             mode : 'local',
29428             placeholder : this.yearPlaceholder,
29429             selectOnFocus : true,
29430             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29431             triggerAction : 'all',
29432             typeAhead : true,
29433             valueField : 'value',
29434             store : new Roo.data.SimpleStore({
29435                 data : (function() {
29436                     var years = [];
29437                     _this.fireEvent('years', _this, years);
29438                     return years;
29439                 })(),
29440                 fields : [ 'value' ]
29441             }),
29442             listeners : {
29443                 select : function (_self, record, index)
29444                 {
29445                     _this.setValue(_this.getValue());
29446                 }
29447             }
29448         });
29449
29450         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29451     },
29452     
29453     setValue : function(v, format)
29454     {
29455         this.inputEl.dom.value = v;
29456         
29457         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29458         
29459         var d = Date.parseDate(v, f);
29460         
29461         if(!d){
29462             this.validate();
29463             return;
29464         }
29465         
29466         this.setDay(d.format(this.dayFormat));
29467         this.setMonth(d.format(this.monthFormat));
29468         this.setYear(d.format(this.yearFormat));
29469         
29470         this.validate();
29471         
29472         return;
29473     },
29474     
29475     setDay : function(v)
29476     {
29477         this.dayField.setValue(v);
29478         this.inputEl.dom.value = this.getValue();
29479         this.validate();
29480         return;
29481     },
29482     
29483     setMonth : function(v)
29484     {
29485         this.monthField.setValue(v, true);
29486         this.inputEl.dom.value = this.getValue();
29487         this.validate();
29488         return;
29489     },
29490     
29491     setYear : function(v)
29492     {
29493         this.yearField.setValue(v);
29494         this.inputEl.dom.value = this.getValue();
29495         this.validate();
29496         return;
29497     },
29498     
29499     getDay : function()
29500     {
29501         return this.dayField.getValue();
29502     },
29503     
29504     getMonth : function()
29505     {
29506         return this.monthField.getValue();
29507     },
29508     
29509     getYear : function()
29510     {
29511         return this.yearField.getValue();
29512     },
29513     
29514     getValue : function()
29515     {
29516         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29517         
29518         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29519         
29520         return date;
29521     },
29522     
29523     reset : function()
29524     {
29525         this.setDay('');
29526         this.setMonth('');
29527         this.setYear('');
29528         this.inputEl.dom.value = '';
29529         this.validate();
29530         return;
29531     },
29532     
29533     validate : function()
29534     {
29535         var d = this.dayField.validate();
29536         var m = this.monthField.validate();
29537         var y = this.yearField.validate();
29538         
29539         var valid = true;
29540         
29541         if(
29542                 (!this.dayAllowBlank && !d) ||
29543                 (!this.monthAllowBlank && !m) ||
29544                 (!this.yearAllowBlank && !y)
29545         ){
29546             valid = false;
29547         }
29548         
29549         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29550             return valid;
29551         }
29552         
29553         if(valid){
29554             this.markValid();
29555             return valid;
29556         }
29557         
29558         this.markInvalid();
29559         
29560         return valid;
29561     },
29562     
29563     markValid : function()
29564     {
29565         
29566         var label = this.el.select('label', true).first();
29567         var icon = this.el.select('i.fa-star', true).first();
29568
29569         if(label && icon){
29570             icon.remove();
29571         }
29572         
29573         this.fireEvent('valid', this);
29574     },
29575     
29576      /**
29577      * Mark this field as invalid
29578      * @param {String} msg The validation message
29579      */
29580     markInvalid : function(msg)
29581     {
29582         
29583         var label = this.el.select('label', true).first();
29584         var icon = this.el.select('i.fa-star', true).first();
29585
29586         if(label && !icon){
29587             this.el.select('.roo-date-split-field-label', true).createChild({
29588                 tag : 'i',
29589                 cls : 'text-danger fa fa-lg fa-star',
29590                 tooltip : 'This field is required',
29591                 style : 'margin-right:5px;'
29592             }, label, true);
29593         }
29594         
29595         this.fireEvent('invalid', this, msg);
29596     },
29597     
29598     clearInvalid : function()
29599     {
29600         var label = this.el.select('label', true).first();
29601         var icon = this.el.select('i.fa-star', true).first();
29602
29603         if(label && icon){
29604             icon.remove();
29605         }
29606         
29607         this.fireEvent('valid', this);
29608     },
29609     
29610     getName: function()
29611     {
29612         return this.name;
29613     }
29614     
29615 });
29616
29617  /**
29618  *
29619  * This is based on 
29620  * http://masonry.desandro.com
29621  *
29622  * The idea is to render all the bricks based on vertical width...
29623  *
29624  * The original code extends 'outlayer' - we might need to use that....
29625  * 
29626  */
29627
29628
29629 /**
29630  * @class Roo.bootstrap.LayoutMasonry
29631  * @extends Roo.bootstrap.Component
29632  * Bootstrap Layout Masonry class
29633  * 
29634  * @constructor
29635  * Create a new Element
29636  * @param {Object} config The config object
29637  */
29638
29639 Roo.bootstrap.LayoutMasonry = function(config){
29640     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29641     
29642     this.bricks = [];
29643     
29644 };
29645
29646 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29647     
29648     /**
29649      * @cfg {Boolean} isLayoutInstant = no animation?
29650      */   
29651     isLayoutInstant : false, // needed?
29652    
29653     /**
29654      * @cfg {Number} boxWidth  width of the columns
29655      */   
29656     boxWidth : 450,
29657     
29658       /**
29659      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29660      */   
29661     boxHeight : 0,
29662     
29663     /**
29664      * @cfg {Number} padWidth padding below box..
29665      */   
29666     padWidth : 10, 
29667     
29668     /**
29669      * @cfg {Number} gutter gutter width..
29670      */   
29671     gutter : 10,
29672     
29673      /**
29674      * @cfg {Number} maxCols maximum number of columns
29675      */   
29676     
29677     maxCols: 0,
29678     
29679     /**
29680      * @cfg {Boolean} isAutoInitial defalut true
29681      */   
29682     isAutoInitial : true, 
29683     
29684     containerWidth: 0,
29685     
29686     /**
29687      * @cfg {Boolean} isHorizontal defalut false
29688      */   
29689     isHorizontal : false, 
29690
29691     currentSize : null,
29692     
29693     tag: 'div',
29694     
29695     cls: '',
29696     
29697     bricks: null, //CompositeElement
29698     
29699     cols : 1,
29700     
29701     _isLayoutInited : false,
29702     
29703 //    isAlternative : false, // only use for vertical layout...
29704     
29705     /**
29706      * @cfg {Number} alternativePadWidth padding below box..
29707      */   
29708     alternativePadWidth : 50, 
29709     
29710     getAutoCreate : function(){
29711         
29712         var cfg = {
29713             tag: this.tag,
29714             cls: 'blog-masonary-wrapper ' + this.cls,
29715             cn : {
29716                 cls : 'mas-boxes masonary'
29717             }
29718         };
29719         
29720         return cfg;
29721     },
29722     
29723     getChildContainer: function( )
29724     {
29725         if (this.boxesEl) {
29726             return this.boxesEl;
29727         }
29728         
29729         this.boxesEl = this.el.select('.mas-boxes').first();
29730         
29731         return this.boxesEl;
29732     },
29733     
29734     
29735     initEvents : function()
29736     {
29737         var _this = this;
29738         
29739         if(this.isAutoInitial){
29740             Roo.log('hook children rendered');
29741             this.on('childrenrendered', function() {
29742                 Roo.log('children rendered');
29743                 _this.initial();
29744             } ,this);
29745         }
29746     },
29747     
29748     initial : function()
29749     {
29750         this.currentSize = this.el.getBox(true);
29751         
29752         Roo.EventManager.onWindowResize(this.resize, this); 
29753
29754         if(!this.isAutoInitial){
29755             this.layout();
29756             return;
29757         }
29758         
29759         this.layout();
29760         
29761         return;
29762         //this.layout.defer(500,this);
29763         
29764     },
29765     
29766     resize : function()
29767     {
29768         Roo.log('resize');
29769         
29770         var cs = this.el.getBox(true);
29771         
29772         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29773             Roo.log("no change in with or X");
29774             return;
29775         }
29776         
29777         this.currentSize = cs;
29778         
29779         this.layout();
29780         
29781     },
29782     
29783     layout : function()
29784     {   
29785         this._resetLayout();
29786         
29787         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29788         
29789         this.layoutItems( isInstant );
29790       
29791         this._isLayoutInited = true;
29792         
29793     },
29794     
29795     _resetLayout : function()
29796     {
29797         if(this.isHorizontal){
29798             this.horizontalMeasureColumns();
29799             return;
29800         }
29801         
29802         this.verticalMeasureColumns();
29803         
29804     },
29805     
29806     verticalMeasureColumns : function()
29807     {
29808         this.getContainerWidth();
29809         
29810 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29811 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29812 //            return;
29813 //        }
29814         
29815         var boxWidth = this.boxWidth + this.padWidth;
29816         
29817         if(this.containerWidth < this.boxWidth){
29818             boxWidth = this.containerWidth
29819         }
29820         
29821         var containerWidth = this.containerWidth;
29822         
29823         var cols = Math.floor(containerWidth / boxWidth);
29824         
29825         this.cols = Math.max( cols, 1 );
29826         
29827         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29828         
29829         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29830         
29831         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29832         
29833         this.colWidth = boxWidth + avail - this.padWidth;
29834         
29835         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29836         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29837     },
29838     
29839     horizontalMeasureColumns : function()
29840     {
29841         this.getContainerWidth();
29842         
29843         var boxWidth = this.boxWidth;
29844         
29845         if(this.containerWidth < boxWidth){
29846             boxWidth = this.containerWidth;
29847         }
29848         
29849         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29850         
29851         this.el.setHeight(boxWidth);
29852         
29853     },
29854     
29855     getContainerWidth : function()
29856     {
29857         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29858     },
29859     
29860     layoutItems : function( isInstant )
29861     {
29862         var items = Roo.apply([], this.bricks);
29863         
29864         if(this.isHorizontal){
29865             this._horizontalLayoutItems( items , isInstant );
29866             return;
29867         }
29868         
29869 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29870 //            this._verticalAlternativeLayoutItems( items , isInstant );
29871 //            return;
29872 //        }
29873         
29874         this._verticalLayoutItems( items , isInstant );
29875         
29876     },
29877     
29878     _verticalLayoutItems : function ( items , isInstant)
29879     {
29880         if ( !items || !items.length ) {
29881             return;
29882         }
29883         
29884         var standard = [
29885             ['xs', 'xs', 'xs', 'tall'],
29886             ['xs', 'xs', 'tall'],
29887             ['xs', 'xs', 'sm'],
29888             ['xs', 'xs', 'xs'],
29889             ['xs', 'tall'],
29890             ['xs', 'sm'],
29891             ['xs', 'xs'],
29892             ['xs'],
29893             
29894             ['sm', 'xs', 'xs'],
29895             ['sm', 'xs'],
29896             ['sm'],
29897             
29898             ['tall', 'xs', 'xs', 'xs'],
29899             ['tall', 'xs', 'xs'],
29900             ['tall', 'xs'],
29901             ['tall']
29902             
29903         ];
29904         
29905         var queue = [];
29906         
29907         var boxes = [];
29908         
29909         var box = [];
29910         
29911         Roo.each(items, function(item, k){
29912             
29913             switch (item.size) {
29914                 // these layouts take up a full box,
29915                 case 'md' :
29916                 case 'md-left' :
29917                 case 'md-right' :
29918                 case 'wide' :
29919                     
29920                     if(box.length){
29921                         boxes.push(box);
29922                         box = [];
29923                     }
29924                     
29925                     boxes.push([item]);
29926                     
29927                     break;
29928                     
29929                 case 'xs' :
29930                 case 'sm' :
29931                 case 'tall' :
29932                     
29933                     box.push(item);
29934                     
29935                     break;
29936                 default :
29937                     break;
29938                     
29939             }
29940             
29941         }, this);
29942         
29943         if(box.length){
29944             boxes.push(box);
29945             box = [];
29946         }
29947         
29948         var filterPattern = function(box, length)
29949         {
29950             if(!box.length){
29951                 return;
29952             }
29953             
29954             var match = false;
29955             
29956             var pattern = box.slice(0, length);
29957             
29958             var format = [];
29959             
29960             Roo.each(pattern, function(i){
29961                 format.push(i.size);
29962             }, this);
29963             
29964             Roo.each(standard, function(s){
29965                 
29966                 if(String(s) != String(format)){
29967                     return;
29968                 }
29969                 
29970                 match = true;
29971                 return false;
29972                 
29973             }, this);
29974             
29975             if(!match && length == 1){
29976                 return;
29977             }
29978             
29979             if(!match){
29980                 filterPattern(box, length - 1);
29981                 return;
29982             }
29983                 
29984             queue.push(pattern);
29985
29986             box = box.slice(length, box.length);
29987
29988             filterPattern(box, 4);
29989
29990             return;
29991             
29992         }
29993         
29994         Roo.each(boxes, function(box, k){
29995             
29996             if(!box.length){
29997                 return;
29998             }
29999             
30000             if(box.length == 1){
30001                 queue.push(box);
30002                 return;
30003             }
30004             
30005             filterPattern(box, 4);
30006             
30007         }, this);
30008         
30009         this._processVerticalLayoutQueue( queue, isInstant );
30010         
30011     },
30012     
30013 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30014 //    {
30015 //        if ( !items || !items.length ) {
30016 //            return;
30017 //        }
30018 //
30019 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30020 //        
30021 //    },
30022     
30023     _horizontalLayoutItems : function ( items , isInstant)
30024     {
30025         if ( !items || !items.length || items.length < 3) {
30026             return;
30027         }
30028         
30029         items.reverse();
30030         
30031         var eItems = items.slice(0, 3);
30032         
30033         items = items.slice(3, items.length);
30034         
30035         var standard = [
30036             ['xs', 'xs', 'xs', 'wide'],
30037             ['xs', 'xs', 'wide'],
30038             ['xs', 'xs', 'sm'],
30039             ['xs', 'xs', 'xs'],
30040             ['xs', 'wide'],
30041             ['xs', 'sm'],
30042             ['xs', 'xs'],
30043             ['xs'],
30044             
30045             ['sm', 'xs', 'xs'],
30046             ['sm', 'xs'],
30047             ['sm'],
30048             
30049             ['wide', 'xs', 'xs', 'xs'],
30050             ['wide', 'xs', 'xs'],
30051             ['wide', 'xs'],
30052             ['wide'],
30053             
30054             ['wide-thin']
30055         ];
30056         
30057         var queue = [];
30058         
30059         var boxes = [];
30060         
30061         var box = [];
30062         
30063         Roo.each(items, function(item, k){
30064             
30065             switch (item.size) {
30066                 case 'md' :
30067                 case 'md-left' :
30068                 case 'md-right' :
30069                 case 'tall' :
30070                     
30071                     if(box.length){
30072                         boxes.push(box);
30073                         box = [];
30074                     }
30075                     
30076                     boxes.push([item]);
30077                     
30078                     break;
30079                     
30080                 case 'xs' :
30081                 case 'sm' :
30082                 case 'wide' :
30083                 case 'wide-thin' :
30084                     
30085                     box.push(item);
30086                     
30087                     break;
30088                 default :
30089                     break;
30090                     
30091             }
30092             
30093         }, this);
30094         
30095         if(box.length){
30096             boxes.push(box);
30097             box = [];
30098         }
30099         
30100         var filterPattern = function(box, length)
30101         {
30102             if(!box.length){
30103                 return;
30104             }
30105             
30106             var match = false;
30107             
30108             var pattern = box.slice(0, length);
30109             
30110             var format = [];
30111             
30112             Roo.each(pattern, function(i){
30113                 format.push(i.size);
30114             }, this);
30115             
30116             Roo.each(standard, function(s){
30117                 
30118                 if(String(s) != String(format)){
30119                     return;
30120                 }
30121                 
30122                 match = true;
30123                 return false;
30124                 
30125             }, this);
30126             
30127             if(!match && length == 1){
30128                 return;
30129             }
30130             
30131             if(!match){
30132                 filterPattern(box, length - 1);
30133                 return;
30134             }
30135                 
30136             queue.push(pattern);
30137
30138             box = box.slice(length, box.length);
30139
30140             filterPattern(box, 4);
30141
30142             return;
30143             
30144         }
30145         
30146         Roo.each(boxes, function(box, k){
30147             
30148             if(!box.length){
30149                 return;
30150             }
30151             
30152             if(box.length == 1){
30153                 queue.push(box);
30154                 return;
30155             }
30156             
30157             filterPattern(box, 4);
30158             
30159         }, this);
30160         
30161         
30162         var prune = [];
30163         
30164         var pos = this.el.getBox(true);
30165         
30166         var minX = pos.x;
30167         
30168         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30169         
30170         var hit_end = false;
30171         
30172         Roo.each(queue, function(box){
30173             
30174             if(hit_end){
30175                 
30176                 Roo.each(box, function(b){
30177                 
30178                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30179                     b.el.hide();
30180
30181                 }, this);
30182
30183                 return;
30184             }
30185             
30186             var mx = 0;
30187             
30188             Roo.each(box, function(b){
30189                 
30190                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30191                 b.el.show();
30192
30193                 mx = Math.max(mx, b.x);
30194                 
30195             }, this);
30196             
30197             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30198             
30199             if(maxX < minX){
30200                 
30201                 Roo.each(box, function(b){
30202                 
30203                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30204                     b.el.hide();
30205                     
30206                 }, this);
30207                 
30208                 hit_end = true;
30209                 
30210                 return;
30211             }
30212             
30213             prune.push(box);
30214             
30215         }, this);
30216         
30217         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30218     },
30219     
30220     /** Sets position of item in DOM
30221     * @param {Element} item
30222     * @param {Number} x - horizontal position
30223     * @param {Number} y - vertical position
30224     * @param {Boolean} isInstant - disables transitions
30225     */
30226     _processVerticalLayoutQueue : function( queue, isInstant )
30227     {
30228         var pos = this.el.getBox(true);
30229         var x = pos.x;
30230         var y = pos.y;
30231         var maxY = [];
30232         
30233         for (var i = 0; i < this.cols; i++){
30234             maxY[i] = pos.y;
30235         }
30236         
30237         Roo.each(queue, function(box, k){
30238             
30239             var col = k % this.cols;
30240             
30241             Roo.each(box, function(b,kk){
30242                 
30243                 b.el.position('absolute');
30244                 
30245                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30246                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30247                 
30248                 if(b.size == 'md-left' || b.size == 'md-right'){
30249                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30250                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30251                 }
30252                 
30253                 b.el.setWidth(width);
30254                 b.el.setHeight(height);
30255                 // iframe?
30256                 b.el.select('iframe',true).setSize(width,height);
30257                 
30258             }, this);
30259             
30260             for (var i = 0; i < this.cols; i++){
30261                 
30262                 if(maxY[i] < maxY[col]){
30263                     col = i;
30264                     continue;
30265                 }
30266                 
30267                 col = Math.min(col, i);
30268                 
30269             }
30270             
30271             x = pos.x + col * (this.colWidth + this.padWidth);
30272             
30273             y = maxY[col];
30274             
30275             var positions = [];
30276             
30277             switch (box.length){
30278                 case 1 :
30279                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30280                     break;
30281                 case 2 :
30282                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30283                     break;
30284                 case 3 :
30285                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30286                     break;
30287                 case 4 :
30288                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30289                     break;
30290                 default :
30291                     break;
30292             }
30293             
30294             Roo.each(box, function(b,kk){
30295                 
30296                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30297                 
30298                 var sz = b.el.getSize();
30299                 
30300                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30301                 
30302             }, this);
30303             
30304         }, this);
30305         
30306         var mY = 0;
30307         
30308         for (var i = 0; i < this.cols; i++){
30309             mY = Math.max(mY, maxY[i]);
30310         }
30311         
30312         this.el.setHeight(mY - pos.y);
30313         
30314     },
30315     
30316 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30317 //    {
30318 //        var pos = this.el.getBox(true);
30319 //        var x = pos.x;
30320 //        var y = pos.y;
30321 //        var maxX = pos.right;
30322 //        
30323 //        var maxHeight = 0;
30324 //        
30325 //        Roo.each(items, function(item, k){
30326 //            
30327 //            var c = k % 2;
30328 //            
30329 //            item.el.position('absolute');
30330 //                
30331 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30332 //
30333 //            item.el.setWidth(width);
30334 //
30335 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30336 //
30337 //            item.el.setHeight(height);
30338 //            
30339 //            if(c == 0){
30340 //                item.el.setXY([x, y], isInstant ? false : true);
30341 //            } else {
30342 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30343 //            }
30344 //            
30345 //            y = y + height + this.alternativePadWidth;
30346 //            
30347 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30348 //            
30349 //        }, this);
30350 //        
30351 //        this.el.setHeight(maxHeight);
30352 //        
30353 //    },
30354     
30355     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30356     {
30357         var pos = this.el.getBox(true);
30358         
30359         var minX = pos.x;
30360         var minY = pos.y;
30361         
30362         var maxX = pos.right;
30363         
30364         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30365         
30366         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30367         
30368         Roo.each(queue, function(box, k){
30369             
30370             Roo.each(box, function(b, kk){
30371                 
30372                 b.el.position('absolute');
30373                 
30374                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30375                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30376                 
30377                 if(b.size == 'md-left' || b.size == 'md-right'){
30378                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30379                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30380                 }
30381                 
30382                 b.el.setWidth(width);
30383                 b.el.setHeight(height);
30384                 
30385             }, this);
30386             
30387             if(!box.length){
30388                 return;
30389             }
30390             
30391             var positions = [];
30392             
30393             switch (box.length){
30394                 case 1 :
30395                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30396                     break;
30397                 case 2 :
30398                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30399                     break;
30400                 case 3 :
30401                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30402                     break;
30403                 case 4 :
30404                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30405                     break;
30406                 default :
30407                     break;
30408             }
30409             
30410             Roo.each(box, function(b,kk){
30411                 
30412                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30413                 
30414                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30415                 
30416             }, this);
30417             
30418         }, this);
30419         
30420     },
30421     
30422     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30423     {
30424         Roo.each(eItems, function(b,k){
30425             
30426             b.size = (k == 0) ? 'sm' : 'xs';
30427             b.x = (k == 0) ? 2 : 1;
30428             b.y = (k == 0) ? 2 : 1;
30429             
30430             b.el.position('absolute');
30431             
30432             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30433                 
30434             b.el.setWidth(width);
30435             
30436             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30437             
30438             b.el.setHeight(height);
30439             
30440         }, this);
30441
30442         var positions = [];
30443         
30444         positions.push({
30445             x : maxX - this.unitWidth * 2 - this.gutter,
30446             y : minY
30447         });
30448         
30449         positions.push({
30450             x : maxX - this.unitWidth,
30451             y : minY + (this.unitWidth + this.gutter) * 2
30452         });
30453         
30454         positions.push({
30455             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30456             y : minY
30457         });
30458         
30459         Roo.each(eItems, function(b,k){
30460             
30461             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30462
30463         }, this);
30464         
30465     },
30466     
30467     getVerticalOneBoxColPositions : function(x, y, box)
30468     {
30469         var pos = [];
30470         
30471         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30472         
30473         if(box[0].size == 'md-left'){
30474             rand = 0;
30475         }
30476         
30477         if(box[0].size == 'md-right'){
30478             rand = 1;
30479         }
30480         
30481         pos.push({
30482             x : x + (this.unitWidth + this.gutter) * rand,
30483             y : y
30484         });
30485         
30486         return pos;
30487     },
30488     
30489     getVerticalTwoBoxColPositions : function(x, y, box)
30490     {
30491         var pos = [];
30492         
30493         if(box[0].size == 'xs'){
30494             
30495             pos.push({
30496                 x : x,
30497                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30498             });
30499
30500             pos.push({
30501                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30502                 y : y
30503             });
30504             
30505             return pos;
30506             
30507         }
30508         
30509         pos.push({
30510             x : x,
30511             y : y
30512         });
30513
30514         pos.push({
30515             x : x + (this.unitWidth + this.gutter) * 2,
30516             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30517         });
30518         
30519         return pos;
30520         
30521     },
30522     
30523     getVerticalThreeBoxColPositions : function(x, y, box)
30524     {
30525         var pos = [];
30526         
30527         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30528             
30529             pos.push({
30530                 x : x,
30531                 y : y
30532             });
30533
30534             pos.push({
30535                 x : x + (this.unitWidth + this.gutter) * 1,
30536                 y : y
30537             });
30538             
30539             pos.push({
30540                 x : x + (this.unitWidth + this.gutter) * 2,
30541                 y : y
30542             });
30543             
30544             return pos;
30545             
30546         }
30547         
30548         if(box[0].size == 'xs' && box[1].size == 'xs'){
30549             
30550             pos.push({
30551                 x : x,
30552                 y : y
30553             });
30554
30555             pos.push({
30556                 x : x,
30557                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30558             });
30559             
30560             pos.push({
30561                 x : x + (this.unitWidth + this.gutter) * 1,
30562                 y : y
30563             });
30564             
30565             return pos;
30566             
30567         }
30568         
30569         pos.push({
30570             x : x,
30571             y : y
30572         });
30573
30574         pos.push({
30575             x : x + (this.unitWidth + this.gutter) * 2,
30576             y : y
30577         });
30578
30579         pos.push({
30580             x : x + (this.unitWidth + this.gutter) * 2,
30581             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30582         });
30583             
30584         return pos;
30585         
30586     },
30587     
30588     getVerticalFourBoxColPositions : function(x, y, box)
30589     {
30590         var pos = [];
30591         
30592         if(box[0].size == 'xs'){
30593             
30594             pos.push({
30595                 x : x,
30596                 y : y
30597             });
30598
30599             pos.push({
30600                 x : x,
30601                 y : y + (this.unitHeight + this.gutter) * 1
30602             });
30603             
30604             pos.push({
30605                 x : x,
30606                 y : y + (this.unitHeight + this.gutter) * 2
30607             });
30608             
30609             pos.push({
30610                 x : x + (this.unitWidth + this.gutter) * 1,
30611                 y : y
30612             });
30613             
30614             return pos;
30615             
30616         }
30617         
30618         pos.push({
30619             x : x,
30620             y : y
30621         });
30622
30623         pos.push({
30624             x : x + (this.unitWidth + this.gutter) * 2,
30625             y : y
30626         });
30627
30628         pos.push({
30629             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30630             y : y + (this.unitHeight + this.gutter) * 1
30631         });
30632
30633         pos.push({
30634             x : x + (this.unitWidth + this.gutter) * 2,
30635             y : y + (this.unitWidth + this.gutter) * 2
30636         });
30637
30638         return pos;
30639         
30640     },
30641     
30642     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30643     {
30644         var pos = [];
30645         
30646         if(box[0].size == 'md-left'){
30647             pos.push({
30648                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30649                 y : minY
30650             });
30651             
30652             return pos;
30653         }
30654         
30655         if(box[0].size == 'md-right'){
30656             pos.push({
30657                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30658                 y : minY + (this.unitWidth + this.gutter) * 1
30659             });
30660             
30661             return pos;
30662         }
30663         
30664         var rand = Math.floor(Math.random() * (4 - box[0].y));
30665         
30666         pos.push({
30667             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30668             y : minY + (this.unitWidth + this.gutter) * rand
30669         });
30670         
30671         return pos;
30672         
30673     },
30674     
30675     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30676     {
30677         var pos = [];
30678         
30679         if(box[0].size == 'xs'){
30680             
30681             pos.push({
30682                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30683                 y : minY
30684             });
30685
30686             pos.push({
30687                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30688                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30689             });
30690             
30691             return pos;
30692             
30693         }
30694         
30695         pos.push({
30696             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30697             y : minY
30698         });
30699
30700         pos.push({
30701             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30702             y : minY + (this.unitWidth + this.gutter) * 2
30703         });
30704         
30705         return pos;
30706         
30707     },
30708     
30709     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30710     {
30711         var pos = [];
30712         
30713         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30714             
30715             pos.push({
30716                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30717                 y : minY
30718             });
30719
30720             pos.push({
30721                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30722                 y : minY + (this.unitWidth + this.gutter) * 1
30723             });
30724             
30725             pos.push({
30726                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30727                 y : minY + (this.unitWidth + this.gutter) * 2
30728             });
30729             
30730             return pos;
30731             
30732         }
30733         
30734         if(box[0].size == 'xs' && box[1].size == 'xs'){
30735             
30736             pos.push({
30737                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30738                 y : minY
30739             });
30740
30741             pos.push({
30742                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30743                 y : minY
30744             });
30745             
30746             pos.push({
30747                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30748                 y : minY + (this.unitWidth + this.gutter) * 1
30749             });
30750             
30751             return pos;
30752             
30753         }
30754         
30755         pos.push({
30756             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30757             y : minY
30758         });
30759
30760         pos.push({
30761             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30762             y : minY + (this.unitWidth + this.gutter) * 2
30763         });
30764
30765         pos.push({
30766             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30767             y : minY + (this.unitWidth + this.gutter) * 2
30768         });
30769             
30770         return pos;
30771         
30772     },
30773     
30774     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30775     {
30776         var pos = [];
30777         
30778         if(box[0].size == 'xs'){
30779             
30780             pos.push({
30781                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30782                 y : minY
30783             });
30784
30785             pos.push({
30786                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30787                 y : minY
30788             });
30789             
30790             pos.push({
30791                 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),
30792                 y : minY
30793             });
30794             
30795             pos.push({
30796                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30797                 y : minY + (this.unitWidth + this.gutter) * 1
30798             });
30799             
30800             return pos;
30801             
30802         }
30803         
30804         pos.push({
30805             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30806             y : minY
30807         });
30808         
30809         pos.push({
30810             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30811             y : minY + (this.unitWidth + this.gutter) * 2
30812         });
30813         
30814         pos.push({
30815             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30816             y : minY + (this.unitWidth + this.gutter) * 2
30817         });
30818         
30819         pos.push({
30820             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),
30821             y : minY + (this.unitWidth + this.gutter) * 2
30822         });
30823
30824         return pos;
30825         
30826     }
30827     
30828 });
30829
30830  
30831
30832  /**
30833  *
30834  * This is based on 
30835  * http://masonry.desandro.com
30836  *
30837  * The idea is to render all the bricks based on vertical width...
30838  *
30839  * The original code extends 'outlayer' - we might need to use that....
30840  * 
30841  */
30842
30843
30844 /**
30845  * @class Roo.bootstrap.LayoutMasonryAuto
30846  * @extends Roo.bootstrap.Component
30847  * Bootstrap Layout Masonry class
30848  * 
30849  * @constructor
30850  * Create a new Element
30851  * @param {Object} config The config object
30852  */
30853
30854 Roo.bootstrap.LayoutMasonryAuto = function(config){
30855     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30856 };
30857
30858 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30859     
30860       /**
30861      * @cfg {Boolean} isFitWidth  - resize the width..
30862      */   
30863     isFitWidth : false,  // options..
30864     /**
30865      * @cfg {Boolean} isOriginLeft = left align?
30866      */   
30867     isOriginLeft : true,
30868     /**
30869      * @cfg {Boolean} isOriginTop = top align?
30870      */   
30871     isOriginTop : false,
30872     /**
30873      * @cfg {Boolean} isLayoutInstant = no animation?
30874      */   
30875     isLayoutInstant : false, // needed?
30876     /**
30877      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30878      */   
30879     isResizingContainer : true,
30880     /**
30881      * @cfg {Number} columnWidth  width of the columns 
30882      */   
30883     
30884     columnWidth : 0,
30885     
30886     /**
30887      * @cfg {Number} maxCols maximum number of columns
30888      */   
30889     
30890     maxCols: 0,
30891     /**
30892      * @cfg {Number} padHeight padding below box..
30893      */   
30894     
30895     padHeight : 10, 
30896     
30897     /**
30898      * @cfg {Boolean} isAutoInitial defalut true
30899      */   
30900     
30901     isAutoInitial : true, 
30902     
30903     // private?
30904     gutter : 0,
30905     
30906     containerWidth: 0,
30907     initialColumnWidth : 0,
30908     currentSize : null,
30909     
30910     colYs : null, // array.
30911     maxY : 0,
30912     padWidth: 10,
30913     
30914     
30915     tag: 'div',
30916     cls: '',
30917     bricks: null, //CompositeElement
30918     cols : 0, // array?
30919     // element : null, // wrapped now this.el
30920     _isLayoutInited : null, 
30921     
30922     
30923     getAutoCreate : function(){
30924         
30925         var cfg = {
30926             tag: this.tag,
30927             cls: 'blog-masonary-wrapper ' + this.cls,
30928             cn : {
30929                 cls : 'mas-boxes masonary'
30930             }
30931         };
30932         
30933         return cfg;
30934     },
30935     
30936     getChildContainer: function( )
30937     {
30938         if (this.boxesEl) {
30939             return this.boxesEl;
30940         }
30941         
30942         this.boxesEl = this.el.select('.mas-boxes').first();
30943         
30944         return this.boxesEl;
30945     },
30946     
30947     
30948     initEvents : function()
30949     {
30950         var _this = this;
30951         
30952         if(this.isAutoInitial){
30953             Roo.log('hook children rendered');
30954             this.on('childrenrendered', function() {
30955                 Roo.log('children rendered');
30956                 _this.initial();
30957             } ,this);
30958         }
30959         
30960     },
30961     
30962     initial : function()
30963     {
30964         this.reloadItems();
30965
30966         this.currentSize = this.el.getBox(true);
30967
30968         /// was window resize... - let's see if this works..
30969         Roo.EventManager.onWindowResize(this.resize, this); 
30970
30971         if(!this.isAutoInitial){
30972             this.layout();
30973             return;
30974         }
30975         
30976         this.layout.defer(500,this);
30977     },
30978     
30979     reloadItems: function()
30980     {
30981         this.bricks = this.el.select('.masonry-brick', true);
30982         
30983         this.bricks.each(function(b) {
30984             //Roo.log(b.getSize());
30985             if (!b.attr('originalwidth')) {
30986                 b.attr('originalwidth',  b.getSize().width);
30987             }
30988             
30989         });
30990         
30991         Roo.log(this.bricks.elements.length);
30992     },
30993     
30994     resize : function()
30995     {
30996         Roo.log('resize');
30997         var cs = this.el.getBox(true);
30998         
30999         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31000             Roo.log("no change in with or X");
31001             return;
31002         }
31003         this.currentSize = cs;
31004         this.layout();
31005     },
31006     
31007     layout : function()
31008     {
31009          Roo.log('layout');
31010         this._resetLayout();
31011         //this._manageStamps();
31012       
31013         // don't animate first layout
31014         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31015         this.layoutItems( isInstant );
31016       
31017         // flag for initalized
31018         this._isLayoutInited = true;
31019     },
31020     
31021     layoutItems : function( isInstant )
31022     {
31023         //var items = this._getItemsForLayout( this.items );
31024         // original code supports filtering layout items.. we just ignore it..
31025         
31026         this._layoutItems( this.bricks , isInstant );
31027       
31028         this._postLayout();
31029     },
31030     _layoutItems : function ( items , isInstant)
31031     {
31032        //this.fireEvent( 'layout', this, items );
31033     
31034
31035         if ( !items || !items.elements.length ) {
31036           // no items, emit event with empty array
31037             return;
31038         }
31039
31040         var queue = [];
31041         items.each(function(item) {
31042             Roo.log("layout item");
31043             Roo.log(item);
31044             // get x/y object from method
31045             var position = this._getItemLayoutPosition( item );
31046             // enqueue
31047             position.item = item;
31048             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31049             queue.push( position );
31050         }, this);
31051       
31052         this._processLayoutQueue( queue );
31053     },
31054     /** Sets position of item in DOM
31055     * @param {Element} item
31056     * @param {Number} x - horizontal position
31057     * @param {Number} y - vertical position
31058     * @param {Boolean} isInstant - disables transitions
31059     */
31060     _processLayoutQueue : function( queue )
31061     {
31062         for ( var i=0, len = queue.length; i < len; i++ ) {
31063             var obj = queue[i];
31064             obj.item.position('absolute');
31065             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31066         }
31067     },
31068       
31069     
31070     /**
31071     * Any logic you want to do after each layout,
31072     * i.e. size the container
31073     */
31074     _postLayout : function()
31075     {
31076         this.resizeContainer();
31077     },
31078     
31079     resizeContainer : function()
31080     {
31081         if ( !this.isResizingContainer ) {
31082             return;
31083         }
31084         var size = this._getContainerSize();
31085         if ( size ) {
31086             this.el.setSize(size.width,size.height);
31087             this.boxesEl.setSize(size.width,size.height);
31088         }
31089     },
31090     
31091     
31092     
31093     _resetLayout : function()
31094     {
31095         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31096         this.colWidth = this.el.getWidth();
31097         //this.gutter = this.el.getWidth(); 
31098         
31099         this.measureColumns();
31100
31101         // reset column Y
31102         var i = this.cols;
31103         this.colYs = [];
31104         while (i--) {
31105             this.colYs.push( 0 );
31106         }
31107     
31108         this.maxY = 0;
31109     },
31110
31111     measureColumns : function()
31112     {
31113         this.getContainerWidth();
31114       // if columnWidth is 0, default to outerWidth of first item
31115         if ( !this.columnWidth ) {
31116             var firstItem = this.bricks.first();
31117             Roo.log(firstItem);
31118             this.columnWidth  = this.containerWidth;
31119             if (firstItem && firstItem.attr('originalwidth') ) {
31120                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31121             }
31122             // columnWidth fall back to item of first element
31123             Roo.log("set column width?");
31124                         this.initialColumnWidth = this.columnWidth  ;
31125
31126             // if first elem has no width, default to size of container
31127             
31128         }
31129         
31130         
31131         if (this.initialColumnWidth) {
31132             this.columnWidth = this.initialColumnWidth;
31133         }
31134         
31135         
31136             
31137         // column width is fixed at the top - however if container width get's smaller we should
31138         // reduce it...
31139         
31140         // this bit calcs how man columns..
31141             
31142         var columnWidth = this.columnWidth += this.gutter;
31143       
31144         // calculate columns
31145         var containerWidth = this.containerWidth + this.gutter;
31146         
31147         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31148         // fix rounding errors, typically with gutters
31149         var excess = columnWidth - containerWidth % columnWidth;
31150         
31151         
31152         // if overshoot is less than a pixel, round up, otherwise floor it
31153         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31154         cols = Math[ mathMethod ]( cols );
31155         this.cols = Math.max( cols, 1 );
31156         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31157         
31158          // padding positioning..
31159         var totalColWidth = this.cols * this.columnWidth;
31160         var padavail = this.containerWidth - totalColWidth;
31161         // so for 2 columns - we need 3 'pads'
31162         
31163         var padNeeded = (1+this.cols) * this.padWidth;
31164         
31165         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31166         
31167         this.columnWidth += padExtra
31168         //this.padWidth = Math.floor(padavail /  ( this.cols));
31169         
31170         // adjust colum width so that padding is fixed??
31171         
31172         // we have 3 columns ... total = width * 3
31173         // we have X left over... that should be used by 
31174         
31175         //if (this.expandC) {
31176             
31177         //}
31178         
31179         
31180         
31181     },
31182     
31183     getContainerWidth : function()
31184     {
31185        /* // container is parent if fit width
31186         var container = this.isFitWidth ? this.element.parentNode : this.element;
31187         // check that this.size and size are there
31188         // IE8 triggers resize on body size change, so they might not be
31189         
31190         var size = getSize( container );  //FIXME
31191         this.containerWidth = size && size.innerWidth; //FIXME
31192         */
31193          
31194         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31195         
31196     },
31197     
31198     _getItemLayoutPosition : function( item )  // what is item?
31199     {
31200         // we resize the item to our columnWidth..
31201       
31202         item.setWidth(this.columnWidth);
31203         item.autoBoxAdjust  = false;
31204         
31205         var sz = item.getSize();
31206  
31207         // how many columns does this brick span
31208         var remainder = this.containerWidth % this.columnWidth;
31209         
31210         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31211         // round if off by 1 pixel, otherwise use ceil
31212         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31213         colSpan = Math.min( colSpan, this.cols );
31214         
31215         // normally this should be '1' as we dont' currently allow multi width columns..
31216         
31217         var colGroup = this._getColGroup( colSpan );
31218         // get the minimum Y value from the columns
31219         var minimumY = Math.min.apply( Math, colGroup );
31220         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31221         
31222         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31223          
31224         // position the brick
31225         var position = {
31226             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31227             y: this.currentSize.y + minimumY + this.padHeight
31228         };
31229         
31230         Roo.log(position);
31231         // apply setHeight to necessary columns
31232         var setHeight = minimumY + sz.height + this.padHeight;
31233         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31234         
31235         var setSpan = this.cols + 1 - colGroup.length;
31236         for ( var i = 0; i < setSpan; i++ ) {
31237           this.colYs[ shortColIndex + i ] = setHeight ;
31238         }
31239       
31240         return position;
31241     },
31242     
31243     /**
31244      * @param {Number} colSpan - number of columns the element spans
31245      * @returns {Array} colGroup
31246      */
31247     _getColGroup : function( colSpan )
31248     {
31249         if ( colSpan < 2 ) {
31250           // if brick spans only one column, use all the column Ys
31251           return this.colYs;
31252         }
31253       
31254         var colGroup = [];
31255         // how many different places could this brick fit horizontally
31256         var groupCount = this.cols + 1 - colSpan;
31257         // for each group potential horizontal position
31258         for ( var i = 0; i < groupCount; i++ ) {
31259           // make an array of colY values for that one group
31260           var groupColYs = this.colYs.slice( i, i + colSpan );
31261           // and get the max value of the array
31262           colGroup[i] = Math.max.apply( Math, groupColYs );
31263         }
31264         return colGroup;
31265     },
31266     /*
31267     _manageStamp : function( stamp )
31268     {
31269         var stampSize =  stamp.getSize();
31270         var offset = stamp.getBox();
31271         // get the columns that this stamp affects
31272         var firstX = this.isOriginLeft ? offset.x : offset.right;
31273         var lastX = firstX + stampSize.width;
31274         var firstCol = Math.floor( firstX / this.columnWidth );
31275         firstCol = Math.max( 0, firstCol );
31276         
31277         var lastCol = Math.floor( lastX / this.columnWidth );
31278         // lastCol should not go over if multiple of columnWidth #425
31279         lastCol -= lastX % this.columnWidth ? 0 : 1;
31280         lastCol = Math.min( this.cols - 1, lastCol );
31281         
31282         // set colYs to bottom of the stamp
31283         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31284             stampSize.height;
31285             
31286         for ( var i = firstCol; i <= lastCol; i++ ) {
31287           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31288         }
31289     },
31290     */
31291     
31292     _getContainerSize : function()
31293     {
31294         this.maxY = Math.max.apply( Math, this.colYs );
31295         var size = {
31296             height: this.maxY
31297         };
31298       
31299         if ( this.isFitWidth ) {
31300             size.width = this._getContainerFitWidth();
31301         }
31302       
31303         return size;
31304     },
31305     
31306     _getContainerFitWidth : function()
31307     {
31308         var unusedCols = 0;
31309         // count unused columns
31310         var i = this.cols;
31311         while ( --i ) {
31312           if ( this.colYs[i] !== 0 ) {
31313             break;
31314           }
31315           unusedCols++;
31316         }
31317         // fit container to columns that have been used
31318         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31319     },
31320     
31321     needsResizeLayout : function()
31322     {
31323         var previousWidth = this.containerWidth;
31324         this.getContainerWidth();
31325         return previousWidth !== this.containerWidth;
31326     }
31327  
31328 });
31329
31330  
31331
31332  /*
31333  * - LGPL
31334  *
31335  * element
31336  * 
31337  */
31338
31339 /**
31340  * @class Roo.bootstrap.MasonryBrick
31341  * @extends Roo.bootstrap.Component
31342  * Bootstrap MasonryBrick class
31343  * 
31344  * @constructor
31345  * Create a new MasonryBrick
31346  * @param {Object} config The config object
31347  */
31348
31349 Roo.bootstrap.MasonryBrick = function(config){
31350     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31351     
31352     this.addEvents({
31353         // raw events
31354         /**
31355          * @event click
31356          * When a MasonryBrick is clcik
31357          * @param {Roo.bootstrap.MasonryBrick} this
31358          * @param {Roo.EventObject} e
31359          */
31360         "click" : true
31361     });
31362 };
31363
31364 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31365     
31366     /**
31367      * @cfg {String} title
31368      */   
31369     title : '',
31370     /**
31371      * @cfg {String} html
31372      */   
31373     html : '',
31374     /**
31375      * @cfg {String} bgimage
31376      */   
31377     bgimage : '',
31378     /**
31379      * @cfg {String} videourl
31380      */   
31381     videourl : '',
31382     /**
31383      * @cfg {String} cls
31384      */   
31385     cls : '',
31386     /**
31387      * @cfg {String} href
31388      */   
31389     href : '',
31390     /**
31391      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31392      */   
31393     size : 'xs',
31394     
31395     /**
31396      * @cfg {String} (center|bottom) placetitle
31397      */   
31398     placetitle : '',
31399     
31400     /**
31401      * @cfg {Boolean} isFitContainer defalut true
31402      */   
31403     isFitContainer : true, 
31404     
31405     /**
31406      * @cfg {Boolean} preventDefault defalut false
31407      */   
31408     preventDefault : false, 
31409     
31410     getAutoCreate : function()
31411     {
31412         if(!this.isFitContainer){
31413             return this.getSplitAutoCreate();
31414         }
31415         
31416         var cls = 'masonry-brick masonry-brick-full';
31417         
31418         if(this.href.length){
31419             cls += ' masonry-brick-link';
31420         }
31421         
31422         if(this.bgimage.length){
31423             cls += ' masonry-brick-image';
31424         }
31425         
31426         if(!this.html.length){
31427             cls += ' enable-mask';
31428         }
31429         
31430         if(this.size){
31431             cls += ' masonry-' + this.size + '-brick';
31432         }
31433         
31434         if(this.placetitle.length){
31435             
31436             switch (this.placetitle) {
31437                 case 'center' :
31438                     cls += ' masonry-center-title';
31439                     break;
31440                 case 'bottom' :
31441                     cls += ' masonry-bottom-title';
31442                     break;
31443                 default:
31444                     break;
31445             }
31446             
31447         } else {
31448             if(!this.html.length && !this.bgimage.length){
31449                 cls += ' masonry-center-title';
31450             }
31451
31452             if(!this.html.length && this.bgimage.length){
31453                 cls += ' masonry-bottom-title';
31454             }
31455         }
31456         
31457         if(this.cls){
31458             cls += ' ' + this.cls;
31459         }
31460         
31461         var cfg = {
31462             tag: (this.href.length) ? 'a' : 'div',
31463             cls: cls,
31464             cn: [
31465                 {
31466                     tag: 'div',
31467                     cls: 'masonry-brick-paragraph',
31468                     cn: []
31469                 }
31470             ]
31471         };
31472         
31473         if(this.href.length){
31474             cfg.href = this.href;
31475         }
31476         
31477         var cn = cfg.cn[0].cn;
31478         
31479         if(this.title.length){
31480             cn.push({
31481                 tag: 'h4',
31482                 cls: 'masonry-brick-title',
31483                 html: this.title
31484             });
31485         }
31486         
31487         if(this.html.length){
31488             cn.push({
31489                 tag: 'p',
31490                 cls: 'masonry-brick-text',
31491                 html: this.html
31492             });
31493         }  
31494         if (!this.title.length && !this.html.length) {
31495             cfg.cn[0].cls += ' hide';
31496         }
31497         
31498         if(this.bgimage.length){
31499             cfg.cn.push({
31500                 tag: 'img',
31501                 cls: 'masonry-brick-image-view',
31502                 src: this.bgimage
31503             });
31504         }
31505         
31506         if(this.videourl.length){
31507             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31508             // youtube support only?
31509             cfg.cn.push({
31510                 tag: 'iframe',
31511                 cls: 'masonry-brick-image-view',
31512                 src: vurl,
31513                 frameborder : 0,
31514                 allowfullscreen : true
31515             });
31516             
31517             
31518         }
31519         
31520         cfg.cn.push({
31521             tag: 'div',
31522             cls: 'masonry-brick-mask'
31523         });
31524         
31525         return cfg;
31526         
31527     },
31528     
31529     getSplitAutoCreate : function()
31530     {
31531         var cls = 'masonry-brick masonry-brick-split';
31532         
31533         if(this.href.length){
31534             cls += ' masonry-brick-link';
31535         }
31536         
31537         if(this.bgimage.length){
31538             cls += ' masonry-brick-image';
31539         }
31540         
31541         if(this.size){
31542             cls += ' masonry-' + this.size + '-brick';
31543         }
31544         
31545         switch (this.placetitle) {
31546             case 'center' :
31547                 cls += ' masonry-center-title';
31548                 break;
31549             case 'bottom' :
31550                 cls += ' masonry-bottom-title';
31551                 break;
31552             default:
31553                 if(!this.bgimage.length){
31554                     cls += ' masonry-center-title';
31555                 }
31556
31557                 if(this.bgimage.length){
31558                     cls += ' masonry-bottom-title';
31559                 }
31560                 break;
31561         }
31562         
31563         if(this.cls){
31564             cls += ' ' + this.cls;
31565         }
31566         
31567         var cfg = {
31568             tag: (this.href.length) ? 'a' : 'div',
31569             cls: cls,
31570             cn: [
31571                 {
31572                     tag: 'div',
31573                     cls: 'masonry-brick-split-head',
31574                     cn: [
31575                         {
31576                             tag: 'div',
31577                             cls: 'masonry-brick-paragraph',
31578                             cn: []
31579                         }
31580                     ]
31581                 },
31582                 {
31583                     tag: 'div',
31584                     cls: 'masonry-brick-split-body',
31585                     cn: []
31586                 }
31587             ]
31588         };
31589         
31590         if(this.href.length){
31591             cfg.href = this.href;
31592         }
31593         
31594         if(this.title.length){
31595             cfg.cn[0].cn[0].cn.push({
31596                 tag: 'h4',
31597                 cls: 'masonry-brick-title',
31598                 html: this.title
31599             });
31600         }
31601         
31602         if(this.html.length){
31603             cfg.cn[1].cn.push({
31604                 tag: 'p',
31605                 cls: 'masonry-brick-text',
31606                 html: this.html
31607             });
31608         }
31609
31610         if(this.bgimage.length){
31611             cfg.cn[0].cn.push({
31612                 tag: 'img',
31613                 cls: 'masonry-brick-image-view',
31614                 src: this.bgimage
31615             });
31616         }
31617         
31618         if(this.videourl.length){
31619             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31620             // youtube support only?
31621             cfg.cn[0].cn.cn.push({
31622                 tag: 'iframe',
31623                 cls: 'masonry-brick-image-view',
31624                 src: vurl,
31625                 frameborder : 0,
31626                 allowfullscreen : true
31627             });
31628         }
31629         
31630         return cfg;
31631     },
31632     
31633     initEvents: function() 
31634     {
31635         switch (this.size) {
31636             case 'xs' :
31637                 this.x = 1;
31638                 this.y = 1;
31639                 break;
31640             case 'sm' :
31641                 this.x = 2;
31642                 this.y = 2;
31643                 break;
31644             case 'md' :
31645             case 'md-left' :
31646             case 'md-right' :
31647                 this.x = 3;
31648                 this.y = 3;
31649                 break;
31650             case 'tall' :
31651                 this.x = 2;
31652                 this.y = 3;
31653                 break;
31654             case 'wide' :
31655                 this.x = 3;
31656                 this.y = 2;
31657                 break;
31658             case 'wide-thin' :
31659                 this.x = 3;
31660                 this.y = 1;
31661                 break;
31662                         
31663             default :
31664                 break;
31665         }
31666         
31667         if(Roo.isTouch){
31668             this.el.on('touchstart', this.onTouchStart, this);
31669             this.el.on('touchmove', this.onTouchMove, this);
31670             this.el.on('touchend', this.onTouchEnd, this);
31671             this.el.on('contextmenu', this.onContextMenu, this);
31672         } else {
31673             this.el.on('mouseenter'  ,this.enter, this);
31674             this.el.on('mouseleave', this.leave, this);
31675             this.el.on('click', this.onClick, this);
31676         }
31677         
31678         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31679             this.parent().bricks.push(this);   
31680         }
31681         
31682     },
31683     
31684     onClick: function(e, el)
31685     {
31686         var time = this.endTimer - this.startTimer;
31687         
31688         if(Roo.isTouch){
31689             if(time > 1000){
31690                 e.preventDefault();
31691                 return;
31692             }
31693         }
31694         
31695         if(!this.preventDefault){
31696             return;
31697         }
31698         
31699         e.preventDefault();
31700         this.fireEvent('click', this);
31701     },
31702     
31703     enter: function(e, el)
31704     {
31705         e.preventDefault();
31706         
31707         if(!this.isFitContainer){
31708             return;
31709         }
31710         
31711         if(this.bgimage.length && this.html.length){
31712             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31713         }
31714     },
31715     
31716     leave: function(e, el)
31717     {
31718         e.preventDefault();
31719         
31720         if(!this.isFitContainer){
31721             return;
31722         }
31723         
31724         if(this.bgimage.length && this.html.length){
31725             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31726         }
31727     },
31728     
31729     onTouchStart: function(e, el)
31730     {
31731 //        e.preventDefault();
31732         
31733         this.touchmoved = false;
31734         
31735         if(!this.isFitContainer){
31736             return;
31737         }
31738         
31739         if(!this.bgimage.length || !this.html.length){
31740             return;
31741         }
31742         
31743         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31744         
31745         this.timer = new Date().getTime();
31746         
31747     },
31748     
31749     onTouchMove: function(e, el)
31750     {
31751         this.touchmoved = true;
31752     },
31753     
31754     onContextMenu : function(e,el)
31755     {
31756         e.preventDefault();
31757         e.stopPropagation();
31758         return false;
31759     },
31760     
31761     onTouchEnd: function(e, el)
31762     {
31763 //        e.preventDefault();
31764         
31765         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31766         
31767             this.leave(e,el);
31768             
31769             return;
31770         }
31771         
31772         if(!this.bgimage.length || !this.html.length){
31773             
31774             if(this.href.length){
31775                 window.location.href = this.href;
31776             }
31777             
31778             return;
31779         }
31780         
31781         if(!this.isFitContainer){
31782             return;
31783         }
31784         
31785         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31786         
31787         window.location.href = this.href;
31788     }
31789     
31790 });
31791
31792  
31793
31794  /*
31795  * - LGPL
31796  *
31797  * element
31798  * 
31799  */
31800
31801 /**
31802  * @class Roo.bootstrap.Brick
31803  * @extends Roo.bootstrap.Component
31804  * Bootstrap Brick class
31805  * 
31806  * @constructor
31807  * Create a new Brick
31808  * @param {Object} config The config object
31809  */
31810
31811 Roo.bootstrap.Brick = function(config){
31812     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31813     
31814     this.addEvents({
31815         // raw events
31816         /**
31817          * @event click
31818          * When a Brick is click
31819          * @param {Roo.bootstrap.Brick} this
31820          * @param {Roo.EventObject} e
31821          */
31822         "click" : true
31823     });
31824 };
31825
31826 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31827     
31828     /**
31829      * @cfg {String} title
31830      */   
31831     title : '',
31832     /**
31833      * @cfg {String} html
31834      */   
31835     html : '',
31836     /**
31837      * @cfg {String} bgimage
31838      */   
31839     bgimage : '',
31840     /**
31841      * @cfg {String} cls
31842      */   
31843     cls : '',
31844     /**
31845      * @cfg {String} href
31846      */   
31847     href : '',
31848     /**
31849      * @cfg {String} video
31850      */   
31851     video : '',
31852     /**
31853      * @cfg {Boolean} square
31854      */   
31855     square : true,
31856     
31857     getAutoCreate : function()
31858     {
31859         var cls = 'roo-brick';
31860         
31861         if(this.href.length){
31862             cls += ' roo-brick-link';
31863         }
31864         
31865         if(this.bgimage.length){
31866             cls += ' roo-brick-image';
31867         }
31868         
31869         if(!this.html.length && !this.bgimage.length){
31870             cls += ' roo-brick-center-title';
31871         }
31872         
31873         if(!this.html.length && this.bgimage.length){
31874             cls += ' roo-brick-bottom-title';
31875         }
31876         
31877         if(this.cls){
31878             cls += ' ' + this.cls;
31879         }
31880         
31881         var cfg = {
31882             tag: (this.href.length) ? 'a' : 'div',
31883             cls: cls,
31884             cn: [
31885                 {
31886                     tag: 'div',
31887                     cls: 'roo-brick-paragraph',
31888                     cn: []
31889                 }
31890             ]
31891         };
31892         
31893         if(this.href.length){
31894             cfg.href = this.href;
31895         }
31896         
31897         var cn = cfg.cn[0].cn;
31898         
31899         if(this.title.length){
31900             cn.push({
31901                 tag: 'h4',
31902                 cls: 'roo-brick-title',
31903                 html: this.title
31904             });
31905         }
31906         
31907         if(this.html.length){
31908             cn.push({
31909                 tag: 'p',
31910                 cls: 'roo-brick-text',
31911                 html: this.html
31912             });
31913         } else {
31914             cn.cls += ' hide';
31915         }
31916         
31917         if(this.bgimage.length){
31918             cfg.cn.push({
31919                 tag: 'img',
31920                 cls: 'roo-brick-image-view',
31921                 src: this.bgimage
31922             });
31923         }
31924         
31925         return cfg;
31926     },
31927     
31928     initEvents: function() 
31929     {
31930         if(this.title.length || this.html.length){
31931             this.el.on('mouseenter'  ,this.enter, this);
31932             this.el.on('mouseleave', this.leave, this);
31933         }
31934         
31935         
31936         Roo.EventManager.onWindowResize(this.resize, this); 
31937         
31938         this.resize();
31939     },
31940     
31941     resize : function()
31942     {
31943         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31944         
31945         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31946         
31947         if(this.bgimage.length){
31948             var image = this.el.select('.roo-brick-image-view', true).first();
31949             image.setWidth(paragraph.getWidth());
31950             image.setHeight(paragraph.getWidth());
31951             
31952             this.el.setHeight(paragraph.getWidth());
31953             
31954         }
31955         
31956     },
31957     
31958     enter: function(e, el)
31959     {
31960         e.preventDefault();
31961         
31962         if(this.bgimage.length){
31963             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31964             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31965         }
31966     },
31967     
31968     leave: function(e, el)
31969     {
31970         e.preventDefault();
31971         
31972         if(this.bgimage.length){
31973             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31974             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31975         }
31976     }
31977     
31978 });
31979
31980  
31981
31982  /*
31983  * - LGPL
31984  *
31985  * Input
31986  * 
31987  */
31988
31989 /**
31990  * @class Roo.bootstrap.NumberField
31991  * @extends Roo.bootstrap.Input
31992  * Bootstrap NumberField class
31993  * 
31994  * 
31995  * 
31996  * 
31997  * @constructor
31998  * Create a new NumberField
31999  * @param {Object} config The config object
32000  */
32001
32002 Roo.bootstrap.NumberField = function(config){
32003     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32004 };
32005
32006 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32007     
32008     /**
32009      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32010      */
32011     allowDecimals : true,
32012     /**
32013      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32014      */
32015     decimalSeparator : ".",
32016     /**
32017      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32018      */
32019     decimalPrecision : 2,
32020     /**
32021      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32022      */
32023     allowNegative : true,
32024     /**
32025      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32026      */
32027     minValue : Number.NEGATIVE_INFINITY,
32028     /**
32029      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32030      */
32031     maxValue : Number.MAX_VALUE,
32032     /**
32033      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32034      */
32035     minText : "The minimum value for this field is {0}",
32036     /**
32037      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32038      */
32039     maxText : "The maximum value for this field is {0}",
32040     /**
32041      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32042      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32043      */
32044     nanText : "{0} is not a valid number",
32045     /**
32046      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32047      */
32048     castInt : true,
32049
32050     // private
32051     initEvents : function()
32052     {   
32053         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32054         
32055         var allowed = "0123456789";
32056         
32057         if(this.allowDecimals){
32058             allowed += this.decimalSeparator;
32059         }
32060         
32061         if(this.allowNegative){
32062             allowed += "-";
32063         }
32064         
32065         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32066         
32067         var keyPress = function(e){
32068             
32069             var k = e.getKey();
32070             
32071             var c = e.getCharCode();
32072             
32073             if(
32074                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32075                     allowed.indexOf(String.fromCharCode(c)) === -1
32076             ){
32077                 e.stopEvent();
32078                 return;
32079             }
32080             
32081             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32082                 return;
32083             }
32084             
32085             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32086                 e.stopEvent();
32087             }
32088         };
32089         
32090         this.el.on("keypress", keyPress, this);
32091     },
32092     
32093     validateValue : function(value)
32094     {
32095         
32096         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32097             return false;
32098         }
32099         
32100         var num = this.parseValue(value);
32101         
32102         if(isNaN(num)){
32103             this.markInvalid(String.format(this.nanText, value));
32104             return false;
32105         }
32106         
32107         if(num < this.minValue){
32108             this.markInvalid(String.format(this.minText, this.minValue));
32109             return false;
32110         }
32111         
32112         if(num > this.maxValue){
32113             this.markInvalid(String.format(this.maxText, this.maxValue));
32114             return false;
32115         }
32116         
32117         return true;
32118     },
32119
32120     getValue : function()
32121     {
32122         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32123     },
32124
32125     parseValue : function(value)
32126     {
32127         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32128         return isNaN(value) ? '' : value;
32129     },
32130
32131     fixPrecision : function(value)
32132     {
32133         var nan = isNaN(value);
32134         
32135         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32136             return nan ? '' : value;
32137         }
32138         return parseFloat(value).toFixed(this.decimalPrecision);
32139     },
32140
32141     setValue : function(v)
32142     {
32143         v = this.fixPrecision(v);
32144         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32145     },
32146
32147     decimalPrecisionFcn : function(v)
32148     {
32149         return Math.floor(v);
32150     },
32151
32152     beforeBlur : function()
32153     {
32154         if(!this.castInt){
32155             return;
32156         }
32157         
32158         var v = this.parseValue(this.getRawValue());
32159         if(v){
32160             this.setValue(v);
32161         }
32162     }
32163     
32164 });
32165
32166  
32167
32168 /*
32169 * Licence: LGPL
32170 */
32171
32172 /**
32173  * @class Roo.bootstrap.DocumentSlider
32174  * @extends Roo.bootstrap.Component
32175  * Bootstrap DocumentSlider class
32176  * 
32177  * @constructor
32178  * Create a new DocumentViewer
32179  * @param {Object} config The config object
32180  */
32181
32182 Roo.bootstrap.DocumentSlider = function(config){
32183     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32184     
32185     this.files = [];
32186     
32187     this.addEvents({
32188         /**
32189          * @event initial
32190          * Fire after initEvent
32191          * @param {Roo.bootstrap.DocumentSlider} this
32192          */
32193         "initial" : true,
32194         /**
32195          * @event update
32196          * Fire after update
32197          * @param {Roo.bootstrap.DocumentSlider} this
32198          */
32199         "update" : true,
32200         /**
32201          * @event click
32202          * Fire after click
32203          * @param {Roo.bootstrap.DocumentSlider} this
32204          */
32205         "click" : true
32206     });
32207 };
32208
32209 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32210     
32211     files : false,
32212     
32213     indicator : 0,
32214     
32215     getAutoCreate : function()
32216     {
32217         var cfg = {
32218             tag : 'div',
32219             cls : 'roo-document-slider',
32220             cn : [
32221                 {
32222                     tag : 'div',
32223                     cls : 'roo-document-slider-header',
32224                     cn : [
32225                         {
32226                             tag : 'div',
32227                             cls : 'roo-document-slider-header-title'
32228                         }
32229                     ]
32230                 },
32231                 {
32232                     tag : 'div',
32233                     cls : 'roo-document-slider-body',
32234                     cn : [
32235                         {
32236                             tag : 'div',
32237                             cls : 'roo-document-slider-prev',
32238                             cn : [
32239                                 {
32240                                     tag : 'i',
32241                                     cls : 'fa fa-chevron-left'
32242                                 }
32243                             ]
32244                         },
32245                         {
32246                             tag : 'div',
32247                             cls : 'roo-document-slider-thumb',
32248                             cn : [
32249                                 {
32250                                     tag : 'img',
32251                                     cls : 'roo-document-slider-image'
32252                                 }
32253                             ]
32254                         },
32255                         {
32256                             tag : 'div',
32257                             cls : 'roo-document-slider-next',
32258                             cn : [
32259                                 {
32260                                     tag : 'i',
32261                                     cls : 'fa fa-chevron-right'
32262                                 }
32263                             ]
32264                         }
32265                     ]
32266                 }
32267             ]
32268         };
32269         
32270         return cfg;
32271     },
32272     
32273     initEvents : function()
32274     {
32275         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32276         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32277         
32278         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32279         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32280         
32281         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32282         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32283         
32284         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32285         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32286         
32287         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32288         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32289         
32290         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32291         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32292         
32293         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32294         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32295         
32296         this.thumbEl.on('click', this.onClick, this);
32297         
32298         this.prevIndicator.on('click', this.prev, this);
32299         
32300         this.nextIndicator.on('click', this.next, this);
32301         
32302     },
32303     
32304     initial : function()
32305     {
32306         if(this.files.length){
32307             this.indicator = 1;
32308             this.update()
32309         }
32310         
32311         this.fireEvent('initial', this);
32312     },
32313     
32314     update : function()
32315     {
32316         this.imageEl.attr('src', this.files[this.indicator - 1]);
32317         
32318         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32319         
32320         this.prevIndicator.show();
32321         
32322         if(this.indicator == 1){
32323             this.prevIndicator.hide();
32324         }
32325         
32326         this.nextIndicator.show();
32327         
32328         if(this.indicator == this.files.length){
32329             this.nextIndicator.hide();
32330         }
32331         
32332         this.thumbEl.scrollTo('top');
32333         
32334         this.fireEvent('update', this);
32335     },
32336     
32337     onClick : function(e)
32338     {
32339         e.preventDefault();
32340         
32341         this.fireEvent('click', this);
32342     },
32343     
32344     prev : function(e)
32345     {
32346         e.preventDefault();
32347         
32348         this.indicator = Math.max(1, this.indicator - 1);
32349         
32350         this.update();
32351     },
32352     
32353     next : function(e)
32354     {
32355         e.preventDefault();
32356         
32357         this.indicator = Math.min(this.files.length, this.indicator + 1);
32358         
32359         this.update();
32360     }
32361 });
32362 /*
32363  * - LGPL
32364  *
32365  * RadioSet
32366  *
32367  *
32368  */
32369
32370 /**
32371  * @class Roo.bootstrap.RadioSet
32372  * @extends Roo.bootstrap.Input
32373  * Bootstrap RadioSet class
32374  * @cfg {String} indicatorpos (left|right) default left
32375  * @cfg {Boolean} inline (true|false) inline the element (default true)
32376  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32377  * @constructor
32378  * Create a new RadioSet
32379  * @param {Object} config The config object
32380  */
32381
32382 Roo.bootstrap.RadioSet = function(config){
32383     
32384     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32385     
32386     this.radioes = [];
32387     
32388     Roo.bootstrap.RadioSet.register(this);
32389     
32390     this.addEvents({
32391         /**
32392         * @event check
32393         * Fires when the element is checked or unchecked.
32394         * @param {Roo.bootstrap.RadioSet} this This radio
32395         * @param {Roo.bootstrap.Radio} item The checked item
32396         */
32397        check : true
32398     });
32399     
32400 };
32401
32402 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32403
32404     radioes : false,
32405     
32406     inline : true,
32407     
32408     weight : '',
32409     
32410     indicatorpos : 'left',
32411     
32412     getAutoCreate : function()
32413     {
32414         var label = {
32415             tag : 'label',
32416             cls : 'roo-radio-set-label',
32417             cn : [
32418                 {
32419                     tag : 'span',
32420                     html : this.fieldLabel
32421                 }
32422             ]
32423         };
32424         
32425         if(this.indicatorpos == 'left'){
32426             label.cn.unshift({
32427                 tag : 'i',
32428                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32429                 tooltip : 'This field is required'
32430             });
32431         } else {
32432             label.cn.push({
32433                 tag : 'i',
32434                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32435                 tooltip : 'This field is required'
32436             });
32437         }
32438         
32439         var items = {
32440             tag : 'div',
32441             cls : 'roo-radio-set-items'
32442         };
32443         
32444         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32445         
32446         if (align === 'left' && this.fieldLabel.length) {
32447             
32448             items = {
32449                 cls : "roo-radio-set-right", 
32450                 cn: [
32451                     items
32452                 ]
32453             };
32454             
32455             if(this.labelWidth > 12){
32456                 label.style = "width: " + this.labelWidth + 'px';
32457             }
32458             
32459             if(this.labelWidth < 13 && this.labelmd == 0){
32460                 this.labelmd = this.labelWidth;
32461             }
32462             
32463             if(this.labellg > 0){
32464                 label.cls += ' col-lg-' + this.labellg;
32465                 items.cls += ' col-lg-' + (12 - this.labellg);
32466             }
32467             
32468             if(this.labelmd > 0){
32469                 label.cls += ' col-md-' + this.labelmd;
32470                 items.cls += ' col-md-' + (12 - this.labelmd);
32471             }
32472             
32473             if(this.labelsm > 0){
32474                 label.cls += ' col-sm-' + this.labelsm;
32475                 items.cls += ' col-sm-' + (12 - this.labelsm);
32476             }
32477             
32478             if(this.labelxs > 0){
32479                 label.cls += ' col-xs-' + this.labelxs;
32480                 items.cls += ' col-xs-' + (12 - this.labelxs);
32481             }
32482         }
32483         
32484         var cfg = {
32485             tag : 'div',
32486             cls : 'roo-radio-set',
32487             cn : [
32488                 {
32489                     tag : 'input',
32490                     cls : 'roo-radio-set-input',
32491                     type : 'hidden',
32492                     name : this.name,
32493                     value : this.value ? this.value :  ''
32494                 },
32495                 label,
32496                 items
32497             ]
32498         };
32499         
32500         if(this.weight.length){
32501             cfg.cls += ' roo-radio-' + this.weight;
32502         }
32503         
32504         if(this.inline) {
32505             cfg.cls += ' roo-radio-set-inline';
32506         }
32507         
32508         return cfg;
32509         
32510     },
32511
32512     initEvents : function()
32513     {
32514         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32515         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32516         
32517         if(!this.fieldLabel.length){
32518             this.labelEl.hide();
32519         }
32520         
32521         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32522         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32523         
32524         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32525         this.indicatorEl().hide();
32526         
32527         this.originalValue = this.getValue();
32528         
32529     },
32530     
32531     inputEl: function ()
32532     {
32533         return this.el.select('.roo-radio-set-input', true).first();
32534     },
32535     
32536     getChildContainer : function()
32537     {
32538         return this.itemsEl;
32539     },
32540     
32541     register : function(item)
32542     {
32543         this.radioes.push(item);
32544         
32545     },
32546     
32547     validate : function()
32548     {   
32549         var valid = false;
32550         
32551         Roo.each(this.radioes, function(i){
32552             if(!i.checked){
32553                 return;
32554             }
32555             
32556             valid = true;
32557             return false;
32558         });
32559         
32560         if(this.disabled || this.allowBlank || valid){
32561             this.markValid();
32562             return true;
32563         }
32564         
32565         this.markInvalid();
32566         return false;
32567         
32568     },
32569     
32570     markValid : function()
32571     {
32572         if(this.labelEl.isVisible(true)){
32573             this.indicatorEl().hide();
32574         }
32575         
32576         this.el.removeClass([this.invalidClass, this.validClass]);
32577         this.el.addClass(this.validClass);
32578         
32579         this.fireEvent('valid', this);
32580     },
32581     
32582     markInvalid : function(msg)
32583     {
32584         if(this.allowBlank || this.disabled){
32585             return;
32586         }
32587         
32588         if(this.labelEl.isVisible(true)){
32589             this.indicatorEl().show();
32590         }
32591         
32592         this.el.removeClass([this.invalidClass, this.validClass]);
32593         this.el.addClass(this.invalidClass);
32594         
32595         this.fireEvent('invalid', this, msg);
32596         
32597     },
32598     
32599     setValue : function(v, suppressEvent)
32600     {   
32601         Roo.each(this.radioes, function(i){
32602             
32603             i.checked = false;
32604             i.el.removeClass('checked');
32605             
32606             if(i.value === v || i.value.toString() === v.toString()){
32607                 i.checked = true;
32608                 i.el.addClass('checked');
32609                 
32610                 if(suppressEvent !== true){
32611                     this.fireEvent('check', this, i);
32612                 }
32613             }
32614             
32615         }, this);
32616         
32617         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32618         
32619     },
32620     
32621     clearInvalid : function(){
32622         
32623         if(!this.el || this.preventMark){
32624             return;
32625         }
32626         
32627         if(this.labelEl.isVisible(true)){
32628             this.indicatorEl().hide();
32629         }
32630         
32631         this.el.removeClass([this.invalidClass]);
32632         
32633         this.fireEvent('valid', this);
32634     }
32635     
32636 });
32637
32638 Roo.apply(Roo.bootstrap.RadioSet, {
32639     
32640     groups: {},
32641     
32642     register : function(set)
32643     {
32644         this.groups[set.name] = set;
32645     },
32646     
32647     get: function(name) 
32648     {
32649         if (typeof(this.groups[name]) == 'undefined') {
32650             return false;
32651         }
32652         
32653         return this.groups[name] ;
32654     }
32655     
32656 });
32657 /*
32658  * Based on:
32659  * Ext JS Library 1.1.1
32660  * Copyright(c) 2006-2007, Ext JS, LLC.
32661  *
32662  * Originally Released Under LGPL - original licence link has changed is not relivant.
32663  *
32664  * Fork - LGPL
32665  * <script type="text/javascript">
32666  */
32667
32668
32669 /**
32670  * @class Roo.bootstrap.SplitBar
32671  * @extends Roo.util.Observable
32672  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32673  * <br><br>
32674  * Usage:
32675  * <pre><code>
32676 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32677                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32678 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32679 split.minSize = 100;
32680 split.maxSize = 600;
32681 split.animate = true;
32682 split.on('moved', splitterMoved);
32683 </code></pre>
32684  * @constructor
32685  * Create a new SplitBar
32686  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32687  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32688  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32689  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32690                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32691                         position of the SplitBar).
32692  */
32693 Roo.bootstrap.SplitBar = function(cfg){
32694     
32695     /** @private */
32696     
32697     //{
32698     //  dragElement : elm
32699     //  resizingElement: el,
32700         // optional..
32701     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32702     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32703         // existingProxy ???
32704     //}
32705     
32706     this.el = Roo.get(cfg.dragElement, true);
32707     this.el.dom.unselectable = "on";
32708     /** @private */
32709     this.resizingEl = Roo.get(cfg.resizingElement, true);
32710
32711     /**
32712      * @private
32713      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32714      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32715      * @type Number
32716      */
32717     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32718     
32719     /**
32720      * The minimum size of the resizing element. (Defaults to 0)
32721      * @type Number
32722      */
32723     this.minSize = 0;
32724     
32725     /**
32726      * The maximum size of the resizing element. (Defaults to 2000)
32727      * @type Number
32728      */
32729     this.maxSize = 2000;
32730     
32731     /**
32732      * Whether to animate the transition to the new size
32733      * @type Boolean
32734      */
32735     this.animate = false;
32736     
32737     /**
32738      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32739      * @type Boolean
32740      */
32741     this.useShim = false;
32742     
32743     /** @private */
32744     this.shim = null;
32745     
32746     if(!cfg.existingProxy){
32747         /** @private */
32748         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32749     }else{
32750         this.proxy = Roo.get(cfg.existingProxy).dom;
32751     }
32752     /** @private */
32753     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32754     
32755     /** @private */
32756     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32757     
32758     /** @private */
32759     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32760     
32761     /** @private */
32762     this.dragSpecs = {};
32763     
32764     /**
32765      * @private The adapter to use to positon and resize elements
32766      */
32767     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32768     this.adapter.init(this);
32769     
32770     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32771         /** @private */
32772         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32773         this.el.addClass("roo-splitbar-h");
32774     }else{
32775         /** @private */
32776         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32777         this.el.addClass("roo-splitbar-v");
32778     }
32779     
32780     this.addEvents({
32781         /**
32782          * @event resize
32783          * Fires when the splitter is moved (alias for {@link #event-moved})
32784          * @param {Roo.bootstrap.SplitBar} this
32785          * @param {Number} newSize the new width or height
32786          */
32787         "resize" : true,
32788         /**
32789          * @event moved
32790          * Fires when the splitter is moved
32791          * @param {Roo.bootstrap.SplitBar} this
32792          * @param {Number} newSize the new width or height
32793          */
32794         "moved" : true,
32795         /**
32796          * @event beforeresize
32797          * Fires before the splitter is dragged
32798          * @param {Roo.bootstrap.SplitBar} this
32799          */
32800         "beforeresize" : true,
32801
32802         "beforeapply" : true
32803     });
32804
32805     Roo.util.Observable.call(this);
32806 };
32807
32808 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32809     onStartProxyDrag : function(x, y){
32810         this.fireEvent("beforeresize", this);
32811         if(!this.overlay){
32812             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32813             o.unselectable();
32814             o.enableDisplayMode("block");
32815             // all splitbars share the same overlay
32816             Roo.bootstrap.SplitBar.prototype.overlay = o;
32817         }
32818         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32819         this.overlay.show();
32820         Roo.get(this.proxy).setDisplayed("block");
32821         var size = this.adapter.getElementSize(this);
32822         this.activeMinSize = this.getMinimumSize();;
32823         this.activeMaxSize = this.getMaximumSize();;
32824         var c1 = size - this.activeMinSize;
32825         var c2 = Math.max(this.activeMaxSize - size, 0);
32826         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32827             this.dd.resetConstraints();
32828             this.dd.setXConstraint(
32829                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32830                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32831             );
32832             this.dd.setYConstraint(0, 0);
32833         }else{
32834             this.dd.resetConstraints();
32835             this.dd.setXConstraint(0, 0);
32836             this.dd.setYConstraint(
32837                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32838                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32839             );
32840          }
32841         this.dragSpecs.startSize = size;
32842         this.dragSpecs.startPoint = [x, y];
32843         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32844     },
32845     
32846     /** 
32847      * @private Called after the drag operation by the DDProxy
32848      */
32849     onEndProxyDrag : function(e){
32850         Roo.get(this.proxy).setDisplayed(false);
32851         var endPoint = Roo.lib.Event.getXY(e);
32852         if(this.overlay){
32853             this.overlay.hide();
32854         }
32855         var newSize;
32856         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32857             newSize = this.dragSpecs.startSize + 
32858                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32859                     endPoint[0] - this.dragSpecs.startPoint[0] :
32860                     this.dragSpecs.startPoint[0] - endPoint[0]
32861                 );
32862         }else{
32863             newSize = this.dragSpecs.startSize + 
32864                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32865                     endPoint[1] - this.dragSpecs.startPoint[1] :
32866                     this.dragSpecs.startPoint[1] - endPoint[1]
32867                 );
32868         }
32869         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32870         if(newSize != this.dragSpecs.startSize){
32871             if(this.fireEvent('beforeapply', this, newSize) !== false){
32872                 this.adapter.setElementSize(this, newSize);
32873                 this.fireEvent("moved", this, newSize);
32874                 this.fireEvent("resize", this, newSize);
32875             }
32876         }
32877     },
32878     
32879     /**
32880      * Get the adapter this SplitBar uses
32881      * @return The adapter object
32882      */
32883     getAdapter : function(){
32884         return this.adapter;
32885     },
32886     
32887     /**
32888      * Set the adapter this SplitBar uses
32889      * @param {Object} adapter A SplitBar adapter object
32890      */
32891     setAdapter : function(adapter){
32892         this.adapter = adapter;
32893         this.adapter.init(this);
32894     },
32895     
32896     /**
32897      * Gets the minimum size for the resizing element
32898      * @return {Number} The minimum size
32899      */
32900     getMinimumSize : function(){
32901         return this.minSize;
32902     },
32903     
32904     /**
32905      * Sets the minimum size for the resizing element
32906      * @param {Number} minSize The minimum size
32907      */
32908     setMinimumSize : function(minSize){
32909         this.minSize = minSize;
32910     },
32911     
32912     /**
32913      * Gets the maximum size for the resizing element
32914      * @return {Number} The maximum size
32915      */
32916     getMaximumSize : function(){
32917         return this.maxSize;
32918     },
32919     
32920     /**
32921      * Sets the maximum size for the resizing element
32922      * @param {Number} maxSize The maximum size
32923      */
32924     setMaximumSize : function(maxSize){
32925         this.maxSize = maxSize;
32926     },
32927     
32928     /**
32929      * Sets the initialize size for the resizing element
32930      * @param {Number} size The initial size
32931      */
32932     setCurrentSize : function(size){
32933         var oldAnimate = this.animate;
32934         this.animate = false;
32935         this.adapter.setElementSize(this, size);
32936         this.animate = oldAnimate;
32937     },
32938     
32939     /**
32940      * Destroy this splitbar. 
32941      * @param {Boolean} removeEl True to remove the element
32942      */
32943     destroy : function(removeEl){
32944         if(this.shim){
32945             this.shim.remove();
32946         }
32947         this.dd.unreg();
32948         this.proxy.parentNode.removeChild(this.proxy);
32949         if(removeEl){
32950             this.el.remove();
32951         }
32952     }
32953 });
32954
32955 /**
32956  * @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.
32957  */
32958 Roo.bootstrap.SplitBar.createProxy = function(dir){
32959     var proxy = new Roo.Element(document.createElement("div"));
32960     proxy.unselectable();
32961     var cls = 'roo-splitbar-proxy';
32962     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32963     document.body.appendChild(proxy.dom);
32964     return proxy.dom;
32965 };
32966
32967 /** 
32968  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32969  * Default Adapter. It assumes the splitter and resizing element are not positioned
32970  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32971  */
32972 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32973 };
32974
32975 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32976     // do nothing for now
32977     init : function(s){
32978     
32979     },
32980     /**
32981      * Called before drag operations to get the current size of the resizing element. 
32982      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32983      */
32984      getElementSize : function(s){
32985         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32986             return s.resizingEl.getWidth();
32987         }else{
32988             return s.resizingEl.getHeight();
32989         }
32990     },
32991     
32992     /**
32993      * Called after drag operations to set the size of the resizing element.
32994      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32995      * @param {Number} newSize The new size to set
32996      * @param {Function} onComplete A function to be invoked when resizing is complete
32997      */
32998     setElementSize : function(s, newSize, onComplete){
32999         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33000             if(!s.animate){
33001                 s.resizingEl.setWidth(newSize);
33002                 if(onComplete){
33003                     onComplete(s, newSize);
33004                 }
33005             }else{
33006                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33007             }
33008         }else{
33009             
33010             if(!s.animate){
33011                 s.resizingEl.setHeight(newSize);
33012                 if(onComplete){
33013                     onComplete(s, newSize);
33014                 }
33015             }else{
33016                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33017             }
33018         }
33019     }
33020 };
33021
33022 /** 
33023  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33024  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33025  * Adapter that  moves the splitter element to align with the resized sizing element. 
33026  * Used with an absolute positioned SplitBar.
33027  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33028  * document.body, make sure you assign an id to the body element.
33029  */
33030 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33031     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33032     this.container = Roo.get(container);
33033 };
33034
33035 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33036     init : function(s){
33037         this.basic.init(s);
33038     },
33039     
33040     getElementSize : function(s){
33041         return this.basic.getElementSize(s);
33042     },
33043     
33044     setElementSize : function(s, newSize, onComplete){
33045         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33046     },
33047     
33048     moveSplitter : function(s){
33049         var yes = Roo.bootstrap.SplitBar;
33050         switch(s.placement){
33051             case yes.LEFT:
33052                 s.el.setX(s.resizingEl.getRight());
33053                 break;
33054             case yes.RIGHT:
33055                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33056                 break;
33057             case yes.TOP:
33058                 s.el.setY(s.resizingEl.getBottom());
33059                 break;
33060             case yes.BOTTOM:
33061                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33062                 break;
33063         }
33064     }
33065 };
33066
33067 /**
33068  * Orientation constant - Create a vertical SplitBar
33069  * @static
33070  * @type Number
33071  */
33072 Roo.bootstrap.SplitBar.VERTICAL = 1;
33073
33074 /**
33075  * Orientation constant - Create a horizontal SplitBar
33076  * @static
33077  * @type Number
33078  */
33079 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33080
33081 /**
33082  * Placement constant - The resizing element is to the left of the splitter element
33083  * @static
33084  * @type Number
33085  */
33086 Roo.bootstrap.SplitBar.LEFT = 1;
33087
33088 /**
33089  * Placement constant - The resizing element is to the right of the splitter element
33090  * @static
33091  * @type Number
33092  */
33093 Roo.bootstrap.SplitBar.RIGHT = 2;
33094
33095 /**
33096  * Placement constant - The resizing element is positioned above the splitter element
33097  * @static
33098  * @type Number
33099  */
33100 Roo.bootstrap.SplitBar.TOP = 3;
33101
33102 /**
33103  * Placement constant - The resizing element is positioned under splitter element
33104  * @static
33105  * @type Number
33106  */
33107 Roo.bootstrap.SplitBar.BOTTOM = 4;
33108 Roo.namespace("Roo.bootstrap.layout");/*
33109  * Based on:
33110  * Ext JS Library 1.1.1
33111  * Copyright(c) 2006-2007, Ext JS, LLC.
33112  *
33113  * Originally Released Under LGPL - original licence link has changed is not relivant.
33114  *
33115  * Fork - LGPL
33116  * <script type="text/javascript">
33117  */
33118
33119 /**
33120  * @class Roo.bootstrap.layout.Manager
33121  * @extends Roo.bootstrap.Component
33122  * Base class for layout managers.
33123  */
33124 Roo.bootstrap.layout.Manager = function(config)
33125 {
33126     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33127
33128
33129
33130
33131
33132     /** false to disable window resize monitoring @type Boolean */
33133     this.monitorWindowResize = true;
33134     this.regions = {};
33135     this.addEvents({
33136         /**
33137          * @event layout
33138          * Fires when a layout is performed.
33139          * @param {Roo.LayoutManager} this
33140          */
33141         "layout" : true,
33142         /**
33143          * @event regionresized
33144          * Fires when the user resizes a region.
33145          * @param {Roo.LayoutRegion} region The resized region
33146          * @param {Number} newSize The new size (width for east/west, height for north/south)
33147          */
33148         "regionresized" : true,
33149         /**
33150          * @event regioncollapsed
33151          * Fires when a region is collapsed.
33152          * @param {Roo.LayoutRegion} region The collapsed region
33153          */
33154         "regioncollapsed" : true,
33155         /**
33156          * @event regionexpanded
33157          * Fires when a region is expanded.
33158          * @param {Roo.LayoutRegion} region The expanded region
33159          */
33160         "regionexpanded" : true
33161     });
33162     this.updating = false;
33163
33164     if (config.el) {
33165         this.el = Roo.get(config.el);
33166         this.initEvents();
33167     }
33168
33169 };
33170
33171 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33172
33173
33174     regions : null,
33175
33176     monitorWindowResize : true,
33177
33178
33179     updating : false,
33180
33181
33182     onRender : function(ct, position)
33183     {
33184         if(!this.el){
33185             this.el = Roo.get(ct);
33186             this.initEvents();
33187         }
33188         //this.fireEvent('render',this);
33189     },
33190
33191
33192     initEvents: function()
33193     {
33194
33195
33196         // ie scrollbar fix
33197         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33198             document.body.scroll = "no";
33199         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33200             this.el.position('relative');
33201         }
33202         this.id = this.el.id;
33203         this.el.addClass("roo-layout-container");
33204         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33205         if(this.el.dom != document.body ) {
33206             this.el.on('resize', this.layout,this);
33207             this.el.on('show', this.layout,this);
33208         }
33209
33210     },
33211
33212     /**
33213      * Returns true if this layout is currently being updated
33214      * @return {Boolean}
33215      */
33216     isUpdating : function(){
33217         return this.updating;
33218     },
33219
33220     /**
33221      * Suspend the LayoutManager from doing auto-layouts while
33222      * making multiple add or remove calls
33223      */
33224     beginUpdate : function(){
33225         this.updating = true;
33226     },
33227
33228     /**
33229      * Restore auto-layouts and optionally disable the manager from performing a layout
33230      * @param {Boolean} noLayout true to disable a layout update
33231      */
33232     endUpdate : function(noLayout){
33233         this.updating = false;
33234         if(!noLayout){
33235             this.layout();
33236         }
33237     },
33238
33239     layout: function(){
33240         // abstract...
33241     },
33242
33243     onRegionResized : function(region, newSize){
33244         this.fireEvent("regionresized", region, newSize);
33245         this.layout();
33246     },
33247
33248     onRegionCollapsed : function(region){
33249         this.fireEvent("regioncollapsed", region);
33250     },
33251
33252     onRegionExpanded : function(region){
33253         this.fireEvent("regionexpanded", region);
33254     },
33255
33256     /**
33257      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33258      * performs box-model adjustments.
33259      * @return {Object} The size as an object {width: (the width), height: (the height)}
33260      */
33261     getViewSize : function()
33262     {
33263         var size;
33264         if(this.el.dom != document.body){
33265             size = this.el.getSize();
33266         }else{
33267             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33268         }
33269         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33270         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33271         return size;
33272     },
33273
33274     /**
33275      * Returns the Element this layout is bound to.
33276      * @return {Roo.Element}
33277      */
33278     getEl : function(){
33279         return this.el;
33280     },
33281
33282     /**
33283      * Returns the specified region.
33284      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33285      * @return {Roo.LayoutRegion}
33286      */
33287     getRegion : function(target){
33288         return this.regions[target.toLowerCase()];
33289     },
33290
33291     onWindowResize : function(){
33292         if(this.monitorWindowResize){
33293             this.layout();
33294         }
33295     }
33296 });
33297 /*
33298  * Based on:
33299  * Ext JS Library 1.1.1
33300  * Copyright(c) 2006-2007, Ext JS, LLC.
33301  *
33302  * Originally Released Under LGPL - original licence link has changed is not relivant.
33303  *
33304  * Fork - LGPL
33305  * <script type="text/javascript">
33306  */
33307 /**
33308  * @class Roo.bootstrap.layout.Border
33309  * @extends Roo.bootstrap.layout.Manager
33310  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33311  * please see: examples/bootstrap/nested.html<br><br>
33312  
33313 <b>The container the layout is rendered into can be either the body element or any other element.
33314 If it is not the body element, the container needs to either be an absolute positioned element,
33315 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33316 the container size if it is not the body element.</b>
33317
33318 * @constructor
33319 * Create a new Border
33320 * @param {Object} config Configuration options
33321  */
33322 Roo.bootstrap.layout.Border = function(config){
33323     config = config || {};
33324     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33325     
33326     
33327     
33328     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33329         if(config[region]){
33330             config[region].region = region;
33331             this.addRegion(config[region]);
33332         }
33333     },this);
33334     
33335 };
33336
33337 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33338
33339 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33340     /**
33341      * Creates and adds a new region if it doesn't already exist.
33342      * @param {String} target The target region key (north, south, east, west or center).
33343      * @param {Object} config The regions config object
33344      * @return {BorderLayoutRegion} The new region
33345      */
33346     addRegion : function(config)
33347     {
33348         if(!this.regions[config.region]){
33349             var r = this.factory(config);
33350             this.bindRegion(r);
33351         }
33352         return this.regions[config.region];
33353     },
33354
33355     // private (kinda)
33356     bindRegion : function(r){
33357         this.regions[r.config.region] = r;
33358         
33359         r.on("visibilitychange",    this.layout, this);
33360         r.on("paneladded",          this.layout, this);
33361         r.on("panelremoved",        this.layout, this);
33362         r.on("invalidated",         this.layout, this);
33363         r.on("resized",             this.onRegionResized, this);
33364         r.on("collapsed",           this.onRegionCollapsed, this);
33365         r.on("expanded",            this.onRegionExpanded, this);
33366     },
33367
33368     /**
33369      * Performs a layout update.
33370      */
33371     layout : function()
33372     {
33373         if(this.updating) {
33374             return;
33375         }
33376         
33377         // render all the rebions if they have not been done alreayd?
33378         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33379             if(this.regions[region] && !this.regions[region].bodyEl){
33380                 this.regions[region].onRender(this.el)
33381             }
33382         },this);
33383         
33384         var size = this.getViewSize();
33385         var w = size.width;
33386         var h = size.height;
33387         var centerW = w;
33388         var centerH = h;
33389         var centerY = 0;
33390         var centerX = 0;
33391         //var x = 0, y = 0;
33392
33393         var rs = this.regions;
33394         var north = rs["north"];
33395         var south = rs["south"]; 
33396         var west = rs["west"];
33397         var east = rs["east"];
33398         var center = rs["center"];
33399         //if(this.hideOnLayout){ // not supported anymore
33400             //c.el.setStyle("display", "none");
33401         //}
33402         if(north && north.isVisible()){
33403             var b = north.getBox();
33404             var m = north.getMargins();
33405             b.width = w - (m.left+m.right);
33406             b.x = m.left;
33407             b.y = m.top;
33408             centerY = b.height + b.y + m.bottom;
33409             centerH -= centerY;
33410             north.updateBox(this.safeBox(b));
33411         }
33412         if(south && south.isVisible()){
33413             var b = south.getBox();
33414             var m = south.getMargins();
33415             b.width = w - (m.left+m.right);
33416             b.x = m.left;
33417             var totalHeight = (b.height + m.top + m.bottom);
33418             b.y = h - totalHeight + m.top;
33419             centerH -= totalHeight;
33420             south.updateBox(this.safeBox(b));
33421         }
33422         if(west && west.isVisible()){
33423             var b = west.getBox();
33424             var m = west.getMargins();
33425             b.height = centerH - (m.top+m.bottom);
33426             b.x = m.left;
33427             b.y = centerY + m.top;
33428             var totalWidth = (b.width + m.left + m.right);
33429             centerX += totalWidth;
33430             centerW -= totalWidth;
33431             west.updateBox(this.safeBox(b));
33432         }
33433         if(east && east.isVisible()){
33434             var b = east.getBox();
33435             var m = east.getMargins();
33436             b.height = centerH - (m.top+m.bottom);
33437             var totalWidth = (b.width + m.left + m.right);
33438             b.x = w - totalWidth + m.left;
33439             b.y = centerY + m.top;
33440             centerW -= totalWidth;
33441             east.updateBox(this.safeBox(b));
33442         }
33443         if(center){
33444             var m = center.getMargins();
33445             var centerBox = {
33446                 x: centerX + m.left,
33447                 y: centerY + m.top,
33448                 width: centerW - (m.left+m.right),
33449                 height: centerH - (m.top+m.bottom)
33450             };
33451             //if(this.hideOnLayout){
33452                 //center.el.setStyle("display", "block");
33453             //}
33454             center.updateBox(this.safeBox(centerBox));
33455         }
33456         this.el.repaint();
33457         this.fireEvent("layout", this);
33458     },
33459
33460     // private
33461     safeBox : function(box){
33462         box.width = Math.max(0, box.width);
33463         box.height = Math.max(0, box.height);
33464         return box;
33465     },
33466
33467     /**
33468      * Adds a ContentPanel (or subclass) to this layout.
33469      * @param {String} target The target region key (north, south, east, west or center).
33470      * @param {Roo.ContentPanel} panel The panel to add
33471      * @return {Roo.ContentPanel} The added panel
33472      */
33473     add : function(target, panel){
33474          
33475         target = target.toLowerCase();
33476         return this.regions[target].add(panel);
33477     },
33478
33479     /**
33480      * Remove a ContentPanel (or subclass) to this layout.
33481      * @param {String} target The target region key (north, south, east, west or center).
33482      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33483      * @return {Roo.ContentPanel} The removed panel
33484      */
33485     remove : function(target, panel){
33486         target = target.toLowerCase();
33487         return this.regions[target].remove(panel);
33488     },
33489
33490     /**
33491      * Searches all regions for a panel with the specified id
33492      * @param {String} panelId
33493      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33494      */
33495     findPanel : function(panelId){
33496         var rs = this.regions;
33497         for(var target in rs){
33498             if(typeof rs[target] != "function"){
33499                 var p = rs[target].getPanel(panelId);
33500                 if(p){
33501                     return p;
33502                 }
33503             }
33504         }
33505         return null;
33506     },
33507
33508     /**
33509      * Searches all regions for a panel with the specified id and activates (shows) it.
33510      * @param {String/ContentPanel} panelId The panels id or the panel itself
33511      * @return {Roo.ContentPanel} The shown panel or null
33512      */
33513     showPanel : function(panelId) {
33514       var rs = this.regions;
33515       for(var target in rs){
33516          var r = rs[target];
33517          if(typeof r != "function"){
33518             if(r.hasPanel(panelId)){
33519                return r.showPanel(panelId);
33520             }
33521          }
33522       }
33523       return null;
33524    },
33525
33526    /**
33527      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33528      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33529      */
33530    /*
33531     restoreState : function(provider){
33532         if(!provider){
33533             provider = Roo.state.Manager;
33534         }
33535         var sm = new Roo.LayoutStateManager();
33536         sm.init(this, provider);
33537     },
33538 */
33539  
33540  
33541     /**
33542      * Adds a xtype elements to the layout.
33543      * <pre><code>
33544
33545 layout.addxtype({
33546        xtype : 'ContentPanel',
33547        region: 'west',
33548        items: [ .... ]
33549    }
33550 );
33551
33552 layout.addxtype({
33553         xtype : 'NestedLayoutPanel',
33554         region: 'west',
33555         layout: {
33556            center: { },
33557            west: { }   
33558         },
33559         items : [ ... list of content panels or nested layout panels.. ]
33560    }
33561 );
33562 </code></pre>
33563      * @param {Object} cfg Xtype definition of item to add.
33564      */
33565     addxtype : function(cfg)
33566     {
33567         // basically accepts a pannel...
33568         // can accept a layout region..!?!?
33569         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33570         
33571         
33572         // theory?  children can only be panels??
33573         
33574         //if (!cfg.xtype.match(/Panel$/)) {
33575         //    return false;
33576         //}
33577         var ret = false;
33578         
33579         if (typeof(cfg.region) == 'undefined') {
33580             Roo.log("Failed to add Panel, region was not set");
33581             Roo.log(cfg);
33582             return false;
33583         }
33584         var region = cfg.region;
33585         delete cfg.region;
33586         
33587           
33588         var xitems = [];
33589         if (cfg.items) {
33590             xitems = cfg.items;
33591             delete cfg.items;
33592         }
33593         var nb = false;
33594         
33595         switch(cfg.xtype) 
33596         {
33597             case 'Content':  // ContentPanel (el, cfg)
33598             case 'Scroll':  // ContentPanel (el, cfg)
33599             case 'View': 
33600                 cfg.autoCreate = true;
33601                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33602                 //} else {
33603                 //    var el = this.el.createChild();
33604                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33605                 //}
33606                 
33607                 this.add(region, ret);
33608                 break;
33609             
33610             /*
33611             case 'TreePanel': // our new panel!
33612                 cfg.el = this.el.createChild();
33613                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33614                 this.add(region, ret);
33615                 break;
33616             */
33617             
33618             case 'Nest': 
33619                 // create a new Layout (which is  a Border Layout...
33620                 
33621                 var clayout = cfg.layout;
33622                 clayout.el  = this.el.createChild();
33623                 clayout.items   = clayout.items  || [];
33624                 
33625                 delete cfg.layout;
33626                 
33627                 // replace this exitems with the clayout ones..
33628                 xitems = clayout.items;
33629                  
33630                 // force background off if it's in center...
33631                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33632                     cfg.background = false;
33633                 }
33634                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33635                 
33636                 
33637                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33638                 //console.log('adding nested layout panel '  + cfg.toSource());
33639                 this.add(region, ret);
33640                 nb = {}; /// find first...
33641                 break;
33642             
33643             case 'Grid':
33644                 
33645                 // needs grid and region
33646                 
33647                 //var el = this.getRegion(region).el.createChild();
33648                 /*
33649                  *var el = this.el.createChild();
33650                 // create the grid first...
33651                 cfg.grid.container = el;
33652                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33653                 */
33654                 
33655                 if (region == 'center' && this.active ) {
33656                     cfg.background = false;
33657                 }
33658                 
33659                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33660                 
33661                 this.add(region, ret);
33662                 /*
33663                 if (cfg.background) {
33664                     // render grid on panel activation (if panel background)
33665                     ret.on('activate', function(gp) {
33666                         if (!gp.grid.rendered) {
33667                     //        gp.grid.render(el);
33668                         }
33669                     });
33670                 } else {
33671                   //  cfg.grid.render(el);
33672                 }
33673                 */
33674                 break;
33675            
33676            
33677             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33678                 // it was the old xcomponent building that caused this before.
33679                 // espeically if border is the top element in the tree.
33680                 ret = this;
33681                 break; 
33682                 
33683                     
33684                 
33685                 
33686                 
33687             default:
33688                 /*
33689                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33690                     
33691                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33692                     this.add(region, ret);
33693                 } else {
33694                 */
33695                     Roo.log(cfg);
33696                     throw "Can not add '" + cfg.xtype + "' to Border";
33697                     return null;
33698              
33699                                 
33700              
33701         }
33702         this.beginUpdate();
33703         // add children..
33704         var region = '';
33705         var abn = {};
33706         Roo.each(xitems, function(i)  {
33707             region = nb && i.region ? i.region : false;
33708             
33709             var add = ret.addxtype(i);
33710            
33711             if (region) {
33712                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33713                 if (!i.background) {
33714                     abn[region] = nb[region] ;
33715                 }
33716             }
33717             
33718         });
33719         this.endUpdate();
33720
33721         // make the last non-background panel active..
33722         //if (nb) { Roo.log(abn); }
33723         if (nb) {
33724             
33725             for(var r in abn) {
33726                 region = this.getRegion(r);
33727                 if (region) {
33728                     // tried using nb[r], but it does not work..
33729                      
33730                     region.showPanel(abn[r]);
33731                    
33732                 }
33733             }
33734         }
33735         return ret;
33736         
33737     },
33738     
33739     
33740 // private
33741     factory : function(cfg)
33742     {
33743         
33744         var validRegions = Roo.bootstrap.layout.Border.regions;
33745
33746         var target = cfg.region;
33747         cfg.mgr = this;
33748         
33749         var r = Roo.bootstrap.layout;
33750         Roo.log(target);
33751         switch(target){
33752             case "north":
33753                 return new r.North(cfg);
33754             case "south":
33755                 return new r.South(cfg);
33756             case "east":
33757                 return new r.East(cfg);
33758             case "west":
33759                 return new r.West(cfg);
33760             case "center":
33761                 return new r.Center(cfg);
33762         }
33763         throw 'Layout region "'+target+'" not supported.';
33764     }
33765     
33766     
33767 });
33768  /*
33769  * Based on:
33770  * Ext JS Library 1.1.1
33771  * Copyright(c) 2006-2007, Ext JS, LLC.
33772  *
33773  * Originally Released Under LGPL - original licence link has changed is not relivant.
33774  *
33775  * Fork - LGPL
33776  * <script type="text/javascript">
33777  */
33778  
33779 /**
33780  * @class Roo.bootstrap.layout.Basic
33781  * @extends Roo.util.Observable
33782  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33783  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33784  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33785  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33786  * @cfg {string}   region  the region that it inhabits..
33787  * @cfg {bool}   skipConfig skip config?
33788  * 
33789
33790  */
33791 Roo.bootstrap.layout.Basic = function(config){
33792     
33793     this.mgr = config.mgr;
33794     
33795     this.position = config.region;
33796     
33797     var skipConfig = config.skipConfig;
33798     
33799     this.events = {
33800         /**
33801          * @scope Roo.BasicLayoutRegion
33802          */
33803         
33804         /**
33805          * @event beforeremove
33806          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33807          * @param {Roo.LayoutRegion} this
33808          * @param {Roo.ContentPanel} panel The panel
33809          * @param {Object} e The cancel event object
33810          */
33811         "beforeremove" : true,
33812         /**
33813          * @event invalidated
33814          * Fires when the layout for this region is changed.
33815          * @param {Roo.LayoutRegion} this
33816          */
33817         "invalidated" : true,
33818         /**
33819          * @event visibilitychange
33820          * Fires when this region is shown or hidden 
33821          * @param {Roo.LayoutRegion} this
33822          * @param {Boolean} visibility true or false
33823          */
33824         "visibilitychange" : true,
33825         /**
33826          * @event paneladded
33827          * Fires when a panel is added. 
33828          * @param {Roo.LayoutRegion} this
33829          * @param {Roo.ContentPanel} panel The panel
33830          */
33831         "paneladded" : true,
33832         /**
33833          * @event panelremoved
33834          * Fires when a panel is removed. 
33835          * @param {Roo.LayoutRegion} this
33836          * @param {Roo.ContentPanel} panel The panel
33837          */
33838         "panelremoved" : true,
33839         /**
33840          * @event beforecollapse
33841          * Fires when this region before collapse.
33842          * @param {Roo.LayoutRegion} this
33843          */
33844         "beforecollapse" : true,
33845         /**
33846          * @event collapsed
33847          * Fires when this region is collapsed.
33848          * @param {Roo.LayoutRegion} this
33849          */
33850         "collapsed" : true,
33851         /**
33852          * @event expanded
33853          * Fires when this region is expanded.
33854          * @param {Roo.LayoutRegion} this
33855          */
33856         "expanded" : true,
33857         /**
33858          * @event slideshow
33859          * Fires when this region is slid into view.
33860          * @param {Roo.LayoutRegion} this
33861          */
33862         "slideshow" : true,
33863         /**
33864          * @event slidehide
33865          * Fires when this region slides out of view. 
33866          * @param {Roo.LayoutRegion} this
33867          */
33868         "slidehide" : true,
33869         /**
33870          * @event panelactivated
33871          * Fires when a panel is activated. 
33872          * @param {Roo.LayoutRegion} this
33873          * @param {Roo.ContentPanel} panel The activated panel
33874          */
33875         "panelactivated" : true,
33876         /**
33877          * @event resized
33878          * Fires when the user resizes this region. 
33879          * @param {Roo.LayoutRegion} this
33880          * @param {Number} newSize The new size (width for east/west, height for north/south)
33881          */
33882         "resized" : true
33883     };
33884     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33885     this.panels = new Roo.util.MixedCollection();
33886     this.panels.getKey = this.getPanelId.createDelegate(this);
33887     this.box = null;
33888     this.activePanel = null;
33889     // ensure listeners are added...
33890     
33891     if (config.listeners || config.events) {
33892         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33893             listeners : config.listeners || {},
33894             events : config.events || {}
33895         });
33896     }
33897     
33898     if(skipConfig !== true){
33899         this.applyConfig(config);
33900     }
33901 };
33902
33903 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33904 {
33905     getPanelId : function(p){
33906         return p.getId();
33907     },
33908     
33909     applyConfig : function(config){
33910         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33911         this.config = config;
33912         
33913     },
33914     
33915     /**
33916      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33917      * the width, for horizontal (north, south) the height.
33918      * @param {Number} newSize The new width or height
33919      */
33920     resizeTo : function(newSize){
33921         var el = this.el ? this.el :
33922                  (this.activePanel ? this.activePanel.getEl() : null);
33923         if(el){
33924             switch(this.position){
33925                 case "east":
33926                 case "west":
33927                     el.setWidth(newSize);
33928                     this.fireEvent("resized", this, newSize);
33929                 break;
33930                 case "north":
33931                 case "south":
33932                     el.setHeight(newSize);
33933                     this.fireEvent("resized", this, newSize);
33934                 break;                
33935             }
33936         }
33937     },
33938     
33939     getBox : function(){
33940         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33941     },
33942     
33943     getMargins : function(){
33944         return this.margins;
33945     },
33946     
33947     updateBox : function(box){
33948         this.box = box;
33949         var el = this.activePanel.getEl();
33950         el.dom.style.left = box.x + "px";
33951         el.dom.style.top = box.y + "px";
33952         this.activePanel.setSize(box.width, box.height);
33953     },
33954     
33955     /**
33956      * Returns the container element for this region.
33957      * @return {Roo.Element}
33958      */
33959     getEl : function(){
33960         return this.activePanel;
33961     },
33962     
33963     /**
33964      * Returns true if this region is currently visible.
33965      * @return {Boolean}
33966      */
33967     isVisible : function(){
33968         return this.activePanel ? true : false;
33969     },
33970     
33971     setActivePanel : function(panel){
33972         panel = this.getPanel(panel);
33973         if(this.activePanel && this.activePanel != panel){
33974             this.activePanel.setActiveState(false);
33975             this.activePanel.getEl().setLeftTop(-10000,-10000);
33976         }
33977         this.activePanel = panel;
33978         panel.setActiveState(true);
33979         if(this.box){
33980             panel.setSize(this.box.width, this.box.height);
33981         }
33982         this.fireEvent("panelactivated", this, panel);
33983         this.fireEvent("invalidated");
33984     },
33985     
33986     /**
33987      * Show the specified panel.
33988      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33989      * @return {Roo.ContentPanel} The shown panel or null
33990      */
33991     showPanel : function(panel){
33992         panel = this.getPanel(panel);
33993         if(panel){
33994             this.setActivePanel(panel);
33995         }
33996         return panel;
33997     },
33998     
33999     /**
34000      * Get the active panel for this region.
34001      * @return {Roo.ContentPanel} The active panel or null
34002      */
34003     getActivePanel : function(){
34004         return this.activePanel;
34005     },
34006     
34007     /**
34008      * Add the passed ContentPanel(s)
34009      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34010      * @return {Roo.ContentPanel} The panel added (if only one was added)
34011      */
34012     add : function(panel){
34013         if(arguments.length > 1){
34014             for(var i = 0, len = arguments.length; i < len; i++) {
34015                 this.add(arguments[i]);
34016             }
34017             return null;
34018         }
34019         if(this.hasPanel(panel)){
34020             this.showPanel(panel);
34021             return panel;
34022         }
34023         var el = panel.getEl();
34024         if(el.dom.parentNode != this.mgr.el.dom){
34025             this.mgr.el.dom.appendChild(el.dom);
34026         }
34027         if(panel.setRegion){
34028             panel.setRegion(this);
34029         }
34030         this.panels.add(panel);
34031         el.setStyle("position", "absolute");
34032         if(!panel.background){
34033             this.setActivePanel(panel);
34034             if(this.config.initialSize && this.panels.getCount()==1){
34035                 this.resizeTo(this.config.initialSize);
34036             }
34037         }
34038         this.fireEvent("paneladded", this, panel);
34039         return panel;
34040     },
34041     
34042     /**
34043      * Returns true if the panel is in this region.
34044      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34045      * @return {Boolean}
34046      */
34047     hasPanel : function(panel){
34048         if(typeof panel == "object"){ // must be panel obj
34049             panel = panel.getId();
34050         }
34051         return this.getPanel(panel) ? true : false;
34052     },
34053     
34054     /**
34055      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34056      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34057      * @param {Boolean} preservePanel Overrides the config preservePanel option
34058      * @return {Roo.ContentPanel} The panel that was removed
34059      */
34060     remove : function(panel, preservePanel){
34061         panel = this.getPanel(panel);
34062         if(!panel){
34063             return null;
34064         }
34065         var e = {};
34066         this.fireEvent("beforeremove", this, panel, e);
34067         if(e.cancel === true){
34068             return null;
34069         }
34070         var panelId = panel.getId();
34071         this.panels.removeKey(panelId);
34072         return panel;
34073     },
34074     
34075     /**
34076      * Returns the panel specified or null if it's not in this region.
34077      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34078      * @return {Roo.ContentPanel}
34079      */
34080     getPanel : function(id){
34081         if(typeof id == "object"){ // must be panel obj
34082             return id;
34083         }
34084         return this.panels.get(id);
34085     },
34086     
34087     /**
34088      * Returns this regions position (north/south/east/west/center).
34089      * @return {String} 
34090      */
34091     getPosition: function(){
34092         return this.position;    
34093     }
34094 });/*
34095  * Based on:
34096  * Ext JS Library 1.1.1
34097  * Copyright(c) 2006-2007, Ext JS, LLC.
34098  *
34099  * Originally Released Under LGPL - original licence link has changed is not relivant.
34100  *
34101  * Fork - LGPL
34102  * <script type="text/javascript">
34103  */
34104  
34105 /**
34106  * @class Roo.bootstrap.layout.Region
34107  * @extends Roo.bootstrap.layout.Basic
34108  * This class represents a region in a layout manager.
34109  
34110  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34111  * @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})
34112  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34113  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34114  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34115  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34116  * @cfg {String}    title           The title for the region (overrides panel titles)
34117  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34118  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34119  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34120  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34121  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34122  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34123  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34124  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34125  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34126  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34127
34128  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34129  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34130  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34131  * @cfg {Number}    width           For East/West panels
34132  * @cfg {Number}    height          For North/South panels
34133  * @cfg {Boolean}   split           To show the splitter
34134  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34135  * 
34136  * @cfg {string}   cls             Extra CSS classes to add to region
34137  * 
34138  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34139  * @cfg {string}   region  the region that it inhabits..
34140  *
34141
34142  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34143  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34144
34145  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34146  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34147  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34148  */
34149 Roo.bootstrap.layout.Region = function(config)
34150 {
34151     this.applyConfig(config);
34152
34153     var mgr = config.mgr;
34154     var pos = config.region;
34155     config.skipConfig = true;
34156     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34157     
34158     if (mgr.el) {
34159         this.onRender(mgr.el);   
34160     }
34161      
34162     this.visible = true;
34163     this.collapsed = false;
34164     this.unrendered_panels = [];
34165 };
34166
34167 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34168
34169     position: '', // set by wrapper (eg. north/south etc..)
34170     unrendered_panels : null,  // unrendered panels.
34171     createBody : function(){
34172         /** This region's body element 
34173         * @type Roo.Element */
34174         this.bodyEl = this.el.createChild({
34175                 tag: "div",
34176                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34177         });
34178     },
34179
34180     onRender: function(ctr, pos)
34181     {
34182         var dh = Roo.DomHelper;
34183         /** This region's container element 
34184         * @type Roo.Element */
34185         this.el = dh.append(ctr.dom, {
34186                 tag: "div",
34187                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34188             }, true);
34189         /** This region's title element 
34190         * @type Roo.Element */
34191     
34192         this.titleEl = dh.append(this.el.dom,
34193             {
34194                     tag: "div",
34195                     unselectable: "on",
34196                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34197                     children:[
34198                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34199                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34200                     ]}, true);
34201         
34202         this.titleEl.enableDisplayMode();
34203         /** This region's title text element 
34204         * @type HTMLElement */
34205         this.titleTextEl = this.titleEl.dom.firstChild;
34206         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34207         /*
34208         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34209         this.closeBtn.enableDisplayMode();
34210         this.closeBtn.on("click", this.closeClicked, this);
34211         this.closeBtn.hide();
34212     */
34213         this.createBody(this.config);
34214         if(this.config.hideWhenEmpty){
34215             this.hide();
34216             this.on("paneladded", this.validateVisibility, this);
34217             this.on("panelremoved", this.validateVisibility, this);
34218         }
34219         if(this.autoScroll){
34220             this.bodyEl.setStyle("overflow", "auto");
34221         }else{
34222             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34223         }
34224         //if(c.titlebar !== false){
34225             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34226                 this.titleEl.hide();
34227             }else{
34228                 this.titleEl.show();
34229                 if(this.config.title){
34230                     this.titleTextEl.innerHTML = this.config.title;
34231                 }
34232             }
34233         //}
34234         if(this.config.collapsed){
34235             this.collapse(true);
34236         }
34237         if(this.config.hidden){
34238             this.hide();
34239         }
34240         
34241         if (this.unrendered_panels && this.unrendered_panels.length) {
34242             for (var i =0;i< this.unrendered_panels.length; i++) {
34243                 this.add(this.unrendered_panels[i]);
34244             }
34245             this.unrendered_panels = null;
34246             
34247         }
34248         
34249     },
34250     
34251     applyConfig : function(c)
34252     {
34253         /*
34254          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34255             var dh = Roo.DomHelper;
34256             if(c.titlebar !== false){
34257                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34258                 this.collapseBtn.on("click", this.collapse, this);
34259                 this.collapseBtn.enableDisplayMode();
34260                 /*
34261                 if(c.showPin === true || this.showPin){
34262                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34263                     this.stickBtn.enableDisplayMode();
34264                     this.stickBtn.on("click", this.expand, this);
34265                     this.stickBtn.hide();
34266                 }
34267                 
34268             }
34269             */
34270             /** This region's collapsed element
34271             * @type Roo.Element */
34272             /*
34273              *
34274             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34275                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34276             ]}, true);
34277             
34278             if(c.floatable !== false){
34279                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34280                this.collapsedEl.on("click", this.collapseClick, this);
34281             }
34282
34283             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34284                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34285                    id: "message", unselectable: "on", style:{"float":"left"}});
34286                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34287              }
34288             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34289             this.expandBtn.on("click", this.expand, this);
34290             
34291         }
34292         
34293         if(this.collapseBtn){
34294             this.collapseBtn.setVisible(c.collapsible == true);
34295         }
34296         
34297         this.cmargins = c.cmargins || this.cmargins ||
34298                          (this.position == "west" || this.position == "east" ?
34299                              {top: 0, left: 2, right:2, bottom: 0} :
34300                              {top: 2, left: 0, right:0, bottom: 2});
34301         */
34302         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34303         
34304         
34305         this.bottomTabs = c.tabPosition != "top";
34306         
34307         this.autoScroll = c.autoScroll || false;
34308         
34309         
34310        
34311         
34312         this.duration = c.duration || .30;
34313         this.slideDuration = c.slideDuration || .45;
34314         this.config = c;
34315        
34316     },
34317     /**
34318      * Returns true if this region is currently visible.
34319      * @return {Boolean}
34320      */
34321     isVisible : function(){
34322         return this.visible;
34323     },
34324
34325     /**
34326      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34327      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34328      */
34329     //setCollapsedTitle : function(title){
34330     //    title = title || "&#160;";
34331      //   if(this.collapsedTitleTextEl){
34332       //      this.collapsedTitleTextEl.innerHTML = title;
34333        // }
34334     //},
34335
34336     getBox : function(){
34337         var b;
34338       //  if(!this.collapsed){
34339             b = this.el.getBox(false, true);
34340        // }else{
34341           //  b = this.collapsedEl.getBox(false, true);
34342         //}
34343         return b;
34344     },
34345
34346     getMargins : function(){
34347         return this.margins;
34348         //return this.collapsed ? this.cmargins : this.margins;
34349     },
34350 /*
34351     highlight : function(){
34352         this.el.addClass("x-layout-panel-dragover");
34353     },
34354
34355     unhighlight : function(){
34356         this.el.removeClass("x-layout-panel-dragover");
34357     },
34358 */
34359     updateBox : function(box)
34360     {
34361         if (!this.bodyEl) {
34362             return; // not rendered yet..
34363         }
34364         
34365         this.box = box;
34366         if(!this.collapsed){
34367             this.el.dom.style.left = box.x + "px";
34368             this.el.dom.style.top = box.y + "px";
34369             this.updateBody(box.width, box.height);
34370         }else{
34371             this.collapsedEl.dom.style.left = box.x + "px";
34372             this.collapsedEl.dom.style.top = box.y + "px";
34373             this.collapsedEl.setSize(box.width, box.height);
34374         }
34375         if(this.tabs){
34376             this.tabs.autoSizeTabs();
34377         }
34378     },
34379
34380     updateBody : function(w, h)
34381     {
34382         if(w !== null){
34383             this.el.setWidth(w);
34384             w -= this.el.getBorderWidth("rl");
34385             if(this.config.adjustments){
34386                 w += this.config.adjustments[0];
34387             }
34388         }
34389         if(h !== null && h > 0){
34390             this.el.setHeight(h);
34391             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34392             h -= this.el.getBorderWidth("tb");
34393             if(this.config.adjustments){
34394                 h += this.config.adjustments[1];
34395             }
34396             this.bodyEl.setHeight(h);
34397             if(this.tabs){
34398                 h = this.tabs.syncHeight(h);
34399             }
34400         }
34401         if(this.panelSize){
34402             w = w !== null ? w : this.panelSize.width;
34403             h = h !== null ? h : this.panelSize.height;
34404         }
34405         if(this.activePanel){
34406             var el = this.activePanel.getEl();
34407             w = w !== null ? w : el.getWidth();
34408             h = h !== null ? h : el.getHeight();
34409             this.panelSize = {width: w, height: h};
34410             this.activePanel.setSize(w, h);
34411         }
34412         if(Roo.isIE && this.tabs){
34413             this.tabs.el.repaint();
34414         }
34415     },
34416
34417     /**
34418      * Returns the container element for this region.
34419      * @return {Roo.Element}
34420      */
34421     getEl : function(){
34422         return this.el;
34423     },
34424
34425     /**
34426      * Hides this region.
34427      */
34428     hide : function(){
34429         //if(!this.collapsed){
34430             this.el.dom.style.left = "-2000px";
34431             this.el.hide();
34432         //}else{
34433          //   this.collapsedEl.dom.style.left = "-2000px";
34434          //   this.collapsedEl.hide();
34435        // }
34436         this.visible = false;
34437         this.fireEvent("visibilitychange", this, false);
34438     },
34439
34440     /**
34441      * Shows this region if it was previously hidden.
34442      */
34443     show : function(){
34444         //if(!this.collapsed){
34445             this.el.show();
34446         //}else{
34447         //    this.collapsedEl.show();
34448        // }
34449         this.visible = true;
34450         this.fireEvent("visibilitychange", this, true);
34451     },
34452 /*
34453     closeClicked : function(){
34454         if(this.activePanel){
34455             this.remove(this.activePanel);
34456         }
34457     },
34458
34459     collapseClick : function(e){
34460         if(this.isSlid){
34461            e.stopPropagation();
34462            this.slideIn();
34463         }else{
34464            e.stopPropagation();
34465            this.slideOut();
34466         }
34467     },
34468 */
34469     /**
34470      * Collapses this region.
34471      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34472      */
34473     /*
34474     collapse : function(skipAnim, skipCheck = false){
34475         if(this.collapsed) {
34476             return;
34477         }
34478         
34479         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34480             
34481             this.collapsed = true;
34482             if(this.split){
34483                 this.split.el.hide();
34484             }
34485             if(this.config.animate && skipAnim !== true){
34486                 this.fireEvent("invalidated", this);
34487                 this.animateCollapse();
34488             }else{
34489                 this.el.setLocation(-20000,-20000);
34490                 this.el.hide();
34491                 this.collapsedEl.show();
34492                 this.fireEvent("collapsed", this);
34493                 this.fireEvent("invalidated", this);
34494             }
34495         }
34496         
34497     },
34498 */
34499     animateCollapse : function(){
34500         // overridden
34501     },
34502
34503     /**
34504      * Expands this region if it was previously collapsed.
34505      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34506      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34507      */
34508     /*
34509     expand : function(e, skipAnim){
34510         if(e) {
34511             e.stopPropagation();
34512         }
34513         if(!this.collapsed || this.el.hasActiveFx()) {
34514             return;
34515         }
34516         if(this.isSlid){
34517             this.afterSlideIn();
34518             skipAnim = true;
34519         }
34520         this.collapsed = false;
34521         if(this.config.animate && skipAnim !== true){
34522             this.animateExpand();
34523         }else{
34524             this.el.show();
34525             if(this.split){
34526                 this.split.el.show();
34527             }
34528             this.collapsedEl.setLocation(-2000,-2000);
34529             this.collapsedEl.hide();
34530             this.fireEvent("invalidated", this);
34531             this.fireEvent("expanded", this);
34532         }
34533     },
34534 */
34535     animateExpand : function(){
34536         // overridden
34537     },
34538
34539     initTabs : function()
34540     {
34541         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34542         
34543         var ts = new Roo.bootstrap.panel.Tabs({
34544                 el: this.bodyEl.dom,
34545                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34546                 disableTooltips: this.config.disableTabTips,
34547                 toolbar : this.config.toolbar
34548             });
34549         
34550         if(this.config.hideTabs){
34551             ts.stripWrap.setDisplayed(false);
34552         }
34553         this.tabs = ts;
34554         ts.resizeTabs = this.config.resizeTabs === true;
34555         ts.minTabWidth = this.config.minTabWidth || 40;
34556         ts.maxTabWidth = this.config.maxTabWidth || 250;
34557         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34558         ts.monitorResize = false;
34559         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34560         ts.bodyEl.addClass('roo-layout-tabs-body');
34561         this.panels.each(this.initPanelAsTab, this);
34562     },
34563
34564     initPanelAsTab : function(panel){
34565         var ti = this.tabs.addTab(
34566             panel.getEl().id,
34567             panel.getTitle(),
34568             null,
34569             this.config.closeOnTab && panel.isClosable(),
34570             panel.tpl
34571         );
34572         if(panel.tabTip !== undefined){
34573             ti.setTooltip(panel.tabTip);
34574         }
34575         ti.on("activate", function(){
34576               this.setActivePanel(panel);
34577         }, this);
34578         
34579         if(this.config.closeOnTab){
34580             ti.on("beforeclose", function(t, e){
34581                 e.cancel = true;
34582                 this.remove(panel);
34583             }, this);
34584         }
34585         
34586         panel.tabItem = ti;
34587         
34588         return ti;
34589     },
34590
34591     updatePanelTitle : function(panel, title)
34592     {
34593         if(this.activePanel == panel){
34594             this.updateTitle(title);
34595         }
34596         if(this.tabs){
34597             var ti = this.tabs.getTab(panel.getEl().id);
34598             ti.setText(title);
34599             if(panel.tabTip !== undefined){
34600                 ti.setTooltip(panel.tabTip);
34601             }
34602         }
34603     },
34604
34605     updateTitle : function(title){
34606         if(this.titleTextEl && !this.config.title){
34607             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34608         }
34609     },
34610
34611     setActivePanel : function(panel)
34612     {
34613         panel = this.getPanel(panel);
34614         if(this.activePanel && this.activePanel != panel){
34615             this.activePanel.setActiveState(false);
34616         }
34617         this.activePanel = panel;
34618         panel.setActiveState(true);
34619         if(this.panelSize){
34620             panel.setSize(this.panelSize.width, this.panelSize.height);
34621         }
34622         if(this.closeBtn){
34623             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34624         }
34625         this.updateTitle(panel.getTitle());
34626         if(this.tabs){
34627             this.fireEvent("invalidated", this);
34628         }
34629         this.fireEvent("panelactivated", this, panel);
34630     },
34631
34632     /**
34633      * Shows the specified panel.
34634      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34635      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34636      */
34637     showPanel : function(panel)
34638     {
34639         panel = this.getPanel(panel);
34640         if(panel){
34641             if(this.tabs){
34642                 var tab = this.tabs.getTab(panel.getEl().id);
34643                 if(tab.isHidden()){
34644                     this.tabs.unhideTab(tab.id);
34645                 }
34646                 tab.activate();
34647             }else{
34648                 this.setActivePanel(panel);
34649             }
34650         }
34651         return panel;
34652     },
34653
34654     /**
34655      * Get the active panel for this region.
34656      * @return {Roo.ContentPanel} The active panel or null
34657      */
34658     getActivePanel : function(){
34659         return this.activePanel;
34660     },
34661
34662     validateVisibility : function(){
34663         if(this.panels.getCount() < 1){
34664             this.updateTitle("&#160;");
34665             this.closeBtn.hide();
34666             this.hide();
34667         }else{
34668             if(!this.isVisible()){
34669                 this.show();
34670             }
34671         }
34672     },
34673
34674     /**
34675      * Adds the passed ContentPanel(s) to this region.
34676      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34677      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34678      */
34679     add : function(panel)
34680     {
34681         if(arguments.length > 1){
34682             for(var i = 0, len = arguments.length; i < len; i++) {
34683                 this.add(arguments[i]);
34684             }
34685             return null;
34686         }
34687         
34688         // if we have not been rendered yet, then we can not really do much of this..
34689         if (!this.bodyEl) {
34690             this.unrendered_panels.push(panel);
34691             return panel;
34692         }
34693         
34694         
34695         
34696         
34697         if(this.hasPanel(panel)){
34698             this.showPanel(panel);
34699             return panel;
34700         }
34701         panel.setRegion(this);
34702         this.panels.add(panel);
34703        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34704             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34705             // and hide them... ???
34706             this.bodyEl.dom.appendChild(panel.getEl().dom);
34707             if(panel.background !== true){
34708                 this.setActivePanel(panel);
34709             }
34710             this.fireEvent("paneladded", this, panel);
34711             return panel;
34712         }
34713         */
34714         if(!this.tabs){
34715             this.initTabs();
34716         }else{
34717             this.initPanelAsTab(panel);
34718         }
34719         
34720         
34721         if(panel.background !== true){
34722             this.tabs.activate(panel.getEl().id);
34723         }
34724         this.fireEvent("paneladded", this, panel);
34725         return panel;
34726     },
34727
34728     /**
34729      * Hides the tab for the specified panel.
34730      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34731      */
34732     hidePanel : function(panel){
34733         if(this.tabs && (panel = this.getPanel(panel))){
34734             this.tabs.hideTab(panel.getEl().id);
34735         }
34736     },
34737
34738     /**
34739      * Unhides the tab for a previously hidden panel.
34740      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34741      */
34742     unhidePanel : function(panel){
34743         if(this.tabs && (panel = this.getPanel(panel))){
34744             this.tabs.unhideTab(panel.getEl().id);
34745         }
34746     },
34747
34748     clearPanels : function(){
34749         while(this.panels.getCount() > 0){
34750              this.remove(this.panels.first());
34751         }
34752     },
34753
34754     /**
34755      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34756      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34757      * @param {Boolean} preservePanel Overrides the config preservePanel option
34758      * @return {Roo.ContentPanel} The panel that was removed
34759      */
34760     remove : function(panel, preservePanel)
34761     {
34762         panel = this.getPanel(panel);
34763         if(!panel){
34764             return null;
34765         }
34766         var e = {};
34767         this.fireEvent("beforeremove", this, panel, e);
34768         if(e.cancel === true){
34769             return null;
34770         }
34771         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34772         var panelId = panel.getId();
34773         this.panels.removeKey(panelId);
34774         if(preservePanel){
34775             document.body.appendChild(panel.getEl().dom);
34776         }
34777         if(this.tabs){
34778             this.tabs.removeTab(panel.getEl().id);
34779         }else if (!preservePanel){
34780             this.bodyEl.dom.removeChild(panel.getEl().dom);
34781         }
34782         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34783             var p = this.panels.first();
34784             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34785             tempEl.appendChild(p.getEl().dom);
34786             this.bodyEl.update("");
34787             this.bodyEl.dom.appendChild(p.getEl().dom);
34788             tempEl = null;
34789             this.updateTitle(p.getTitle());
34790             this.tabs = null;
34791             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34792             this.setActivePanel(p);
34793         }
34794         panel.setRegion(null);
34795         if(this.activePanel == panel){
34796             this.activePanel = null;
34797         }
34798         if(this.config.autoDestroy !== false && preservePanel !== true){
34799             try{panel.destroy();}catch(e){}
34800         }
34801         this.fireEvent("panelremoved", this, panel);
34802         return panel;
34803     },
34804
34805     /**
34806      * Returns the TabPanel component used by this region
34807      * @return {Roo.TabPanel}
34808      */
34809     getTabs : function(){
34810         return this.tabs;
34811     },
34812
34813     createTool : function(parentEl, className){
34814         var btn = Roo.DomHelper.append(parentEl, {
34815             tag: "div",
34816             cls: "x-layout-tools-button",
34817             children: [ {
34818                 tag: "div",
34819                 cls: "roo-layout-tools-button-inner " + className,
34820                 html: "&#160;"
34821             }]
34822         }, true);
34823         btn.addClassOnOver("roo-layout-tools-button-over");
34824         return btn;
34825     }
34826 });/*
34827  * Based on:
34828  * Ext JS Library 1.1.1
34829  * Copyright(c) 2006-2007, Ext JS, LLC.
34830  *
34831  * Originally Released Under LGPL - original licence link has changed is not relivant.
34832  *
34833  * Fork - LGPL
34834  * <script type="text/javascript">
34835  */
34836  
34837
34838
34839 /**
34840  * @class Roo.SplitLayoutRegion
34841  * @extends Roo.LayoutRegion
34842  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34843  */
34844 Roo.bootstrap.layout.Split = function(config){
34845     this.cursor = config.cursor;
34846     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34847 };
34848
34849 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34850 {
34851     splitTip : "Drag to resize.",
34852     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34853     useSplitTips : false,
34854
34855     applyConfig : function(config){
34856         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34857     },
34858     
34859     onRender : function(ctr,pos) {
34860         
34861         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34862         if(!this.config.split){
34863             return;
34864         }
34865         if(!this.split){
34866             
34867             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34868                             tag: "div",
34869                             id: this.el.id + "-split",
34870                             cls: "roo-layout-split roo-layout-split-"+this.position,
34871                             html: "&#160;"
34872             });
34873             /** The SplitBar for this region 
34874             * @type Roo.SplitBar */
34875             // does not exist yet...
34876             Roo.log([this.position, this.orientation]);
34877             
34878             this.split = new Roo.bootstrap.SplitBar({
34879                 dragElement : splitEl,
34880                 resizingElement: this.el,
34881                 orientation : this.orientation
34882             });
34883             
34884             this.split.on("moved", this.onSplitMove, this);
34885             this.split.useShim = this.config.useShim === true;
34886             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34887             if(this.useSplitTips){
34888                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34889             }
34890             //if(config.collapsible){
34891             //    this.split.el.on("dblclick", this.collapse,  this);
34892             //}
34893         }
34894         if(typeof this.config.minSize != "undefined"){
34895             this.split.minSize = this.config.minSize;
34896         }
34897         if(typeof this.config.maxSize != "undefined"){
34898             this.split.maxSize = this.config.maxSize;
34899         }
34900         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34901             this.hideSplitter();
34902         }
34903         
34904     },
34905
34906     getHMaxSize : function(){
34907          var cmax = this.config.maxSize || 10000;
34908          var center = this.mgr.getRegion("center");
34909          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34910     },
34911
34912     getVMaxSize : function(){
34913          var cmax = this.config.maxSize || 10000;
34914          var center = this.mgr.getRegion("center");
34915          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34916     },
34917
34918     onSplitMove : function(split, newSize){
34919         this.fireEvent("resized", this, newSize);
34920     },
34921     
34922     /** 
34923      * Returns the {@link Roo.SplitBar} for this region.
34924      * @return {Roo.SplitBar}
34925      */
34926     getSplitBar : function(){
34927         return this.split;
34928     },
34929     
34930     hide : function(){
34931         this.hideSplitter();
34932         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34933     },
34934
34935     hideSplitter : function(){
34936         if(this.split){
34937             this.split.el.setLocation(-2000,-2000);
34938             this.split.el.hide();
34939         }
34940     },
34941
34942     show : function(){
34943         if(this.split){
34944             this.split.el.show();
34945         }
34946         Roo.bootstrap.layout.Split.superclass.show.call(this);
34947     },
34948     
34949     beforeSlide: function(){
34950         if(Roo.isGecko){// firefox overflow auto bug workaround
34951             this.bodyEl.clip();
34952             if(this.tabs) {
34953                 this.tabs.bodyEl.clip();
34954             }
34955             if(this.activePanel){
34956                 this.activePanel.getEl().clip();
34957                 
34958                 if(this.activePanel.beforeSlide){
34959                     this.activePanel.beforeSlide();
34960                 }
34961             }
34962         }
34963     },
34964     
34965     afterSlide : function(){
34966         if(Roo.isGecko){// firefox overflow auto bug workaround
34967             this.bodyEl.unclip();
34968             if(this.tabs) {
34969                 this.tabs.bodyEl.unclip();
34970             }
34971             if(this.activePanel){
34972                 this.activePanel.getEl().unclip();
34973                 if(this.activePanel.afterSlide){
34974                     this.activePanel.afterSlide();
34975                 }
34976             }
34977         }
34978     },
34979
34980     initAutoHide : function(){
34981         if(this.autoHide !== false){
34982             if(!this.autoHideHd){
34983                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34984                 this.autoHideHd = {
34985                     "mouseout": function(e){
34986                         if(!e.within(this.el, true)){
34987                             st.delay(500);
34988                         }
34989                     },
34990                     "mouseover" : function(e){
34991                         st.cancel();
34992                     },
34993                     scope : this
34994                 };
34995             }
34996             this.el.on(this.autoHideHd);
34997         }
34998     },
34999
35000     clearAutoHide : function(){
35001         if(this.autoHide !== false){
35002             this.el.un("mouseout", this.autoHideHd.mouseout);
35003             this.el.un("mouseover", this.autoHideHd.mouseover);
35004         }
35005     },
35006
35007     clearMonitor : function(){
35008         Roo.get(document).un("click", this.slideInIf, this);
35009     },
35010
35011     // these names are backwards but not changed for compat
35012     slideOut : function(){
35013         if(this.isSlid || this.el.hasActiveFx()){
35014             return;
35015         }
35016         this.isSlid = true;
35017         if(this.collapseBtn){
35018             this.collapseBtn.hide();
35019         }
35020         this.closeBtnState = this.closeBtn.getStyle('display');
35021         this.closeBtn.hide();
35022         if(this.stickBtn){
35023             this.stickBtn.show();
35024         }
35025         this.el.show();
35026         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35027         this.beforeSlide();
35028         this.el.setStyle("z-index", 10001);
35029         this.el.slideIn(this.getSlideAnchor(), {
35030             callback: function(){
35031                 this.afterSlide();
35032                 this.initAutoHide();
35033                 Roo.get(document).on("click", this.slideInIf, this);
35034                 this.fireEvent("slideshow", this);
35035             },
35036             scope: this,
35037             block: true
35038         });
35039     },
35040
35041     afterSlideIn : function(){
35042         this.clearAutoHide();
35043         this.isSlid = false;
35044         this.clearMonitor();
35045         this.el.setStyle("z-index", "");
35046         if(this.collapseBtn){
35047             this.collapseBtn.show();
35048         }
35049         this.closeBtn.setStyle('display', this.closeBtnState);
35050         if(this.stickBtn){
35051             this.stickBtn.hide();
35052         }
35053         this.fireEvent("slidehide", this);
35054     },
35055
35056     slideIn : function(cb){
35057         if(!this.isSlid || this.el.hasActiveFx()){
35058             Roo.callback(cb);
35059             return;
35060         }
35061         this.isSlid = false;
35062         this.beforeSlide();
35063         this.el.slideOut(this.getSlideAnchor(), {
35064             callback: function(){
35065                 this.el.setLeftTop(-10000, -10000);
35066                 this.afterSlide();
35067                 this.afterSlideIn();
35068                 Roo.callback(cb);
35069             },
35070             scope: this,
35071             block: true
35072         });
35073     },
35074     
35075     slideInIf : function(e){
35076         if(!e.within(this.el)){
35077             this.slideIn();
35078         }
35079     },
35080
35081     animateCollapse : function(){
35082         this.beforeSlide();
35083         this.el.setStyle("z-index", 20000);
35084         var anchor = this.getSlideAnchor();
35085         this.el.slideOut(anchor, {
35086             callback : function(){
35087                 this.el.setStyle("z-index", "");
35088                 this.collapsedEl.slideIn(anchor, {duration:.3});
35089                 this.afterSlide();
35090                 this.el.setLocation(-10000,-10000);
35091                 this.el.hide();
35092                 this.fireEvent("collapsed", this);
35093             },
35094             scope: this,
35095             block: true
35096         });
35097     },
35098
35099     animateExpand : function(){
35100         this.beforeSlide();
35101         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35102         this.el.setStyle("z-index", 20000);
35103         this.collapsedEl.hide({
35104             duration:.1
35105         });
35106         this.el.slideIn(this.getSlideAnchor(), {
35107             callback : function(){
35108                 this.el.setStyle("z-index", "");
35109                 this.afterSlide();
35110                 if(this.split){
35111                     this.split.el.show();
35112                 }
35113                 this.fireEvent("invalidated", this);
35114                 this.fireEvent("expanded", this);
35115             },
35116             scope: this,
35117             block: true
35118         });
35119     },
35120
35121     anchors : {
35122         "west" : "left",
35123         "east" : "right",
35124         "north" : "top",
35125         "south" : "bottom"
35126     },
35127
35128     sanchors : {
35129         "west" : "l",
35130         "east" : "r",
35131         "north" : "t",
35132         "south" : "b"
35133     },
35134
35135     canchors : {
35136         "west" : "tl-tr",
35137         "east" : "tr-tl",
35138         "north" : "tl-bl",
35139         "south" : "bl-tl"
35140     },
35141
35142     getAnchor : function(){
35143         return this.anchors[this.position];
35144     },
35145
35146     getCollapseAnchor : function(){
35147         return this.canchors[this.position];
35148     },
35149
35150     getSlideAnchor : function(){
35151         return this.sanchors[this.position];
35152     },
35153
35154     getAlignAdj : function(){
35155         var cm = this.cmargins;
35156         switch(this.position){
35157             case "west":
35158                 return [0, 0];
35159             break;
35160             case "east":
35161                 return [0, 0];
35162             break;
35163             case "north":
35164                 return [0, 0];
35165             break;
35166             case "south":
35167                 return [0, 0];
35168             break;
35169         }
35170     },
35171
35172     getExpandAdj : function(){
35173         var c = this.collapsedEl, cm = this.cmargins;
35174         switch(this.position){
35175             case "west":
35176                 return [-(cm.right+c.getWidth()+cm.left), 0];
35177             break;
35178             case "east":
35179                 return [cm.right+c.getWidth()+cm.left, 0];
35180             break;
35181             case "north":
35182                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35183             break;
35184             case "south":
35185                 return [0, cm.top+cm.bottom+c.getHeight()];
35186             break;
35187         }
35188     }
35189 });/*
35190  * Based on:
35191  * Ext JS Library 1.1.1
35192  * Copyright(c) 2006-2007, Ext JS, LLC.
35193  *
35194  * Originally Released Under LGPL - original licence link has changed is not relivant.
35195  *
35196  * Fork - LGPL
35197  * <script type="text/javascript">
35198  */
35199 /*
35200  * These classes are private internal classes
35201  */
35202 Roo.bootstrap.layout.Center = function(config){
35203     config.region = "center";
35204     Roo.bootstrap.layout.Region.call(this, config);
35205     this.visible = true;
35206     this.minWidth = config.minWidth || 20;
35207     this.minHeight = config.minHeight || 20;
35208 };
35209
35210 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35211     hide : function(){
35212         // center panel can't be hidden
35213     },
35214     
35215     show : function(){
35216         // center panel can't be hidden
35217     },
35218     
35219     getMinWidth: function(){
35220         return this.minWidth;
35221     },
35222     
35223     getMinHeight: function(){
35224         return this.minHeight;
35225     }
35226 });
35227
35228
35229
35230
35231  
35232
35233
35234
35235
35236
35237 Roo.bootstrap.layout.North = function(config)
35238 {
35239     config.region = 'north';
35240     config.cursor = 'n-resize';
35241     
35242     Roo.bootstrap.layout.Split.call(this, config);
35243     
35244     
35245     if(this.split){
35246         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35247         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35248         this.split.el.addClass("roo-layout-split-v");
35249     }
35250     var size = config.initialSize || config.height;
35251     if(typeof size != "undefined"){
35252         this.el.setHeight(size);
35253     }
35254 };
35255 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35256 {
35257     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35258     
35259     
35260     
35261     getBox : function(){
35262         if(this.collapsed){
35263             return this.collapsedEl.getBox();
35264         }
35265         var box = this.el.getBox();
35266         if(this.split){
35267             box.height += this.split.el.getHeight();
35268         }
35269         return box;
35270     },
35271     
35272     updateBox : function(box){
35273         if(this.split && !this.collapsed){
35274             box.height -= this.split.el.getHeight();
35275             this.split.el.setLeft(box.x);
35276             this.split.el.setTop(box.y+box.height);
35277             this.split.el.setWidth(box.width);
35278         }
35279         if(this.collapsed){
35280             this.updateBody(box.width, null);
35281         }
35282         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35283     }
35284 });
35285
35286
35287
35288
35289
35290 Roo.bootstrap.layout.South = function(config){
35291     config.region = 'south';
35292     config.cursor = 's-resize';
35293     Roo.bootstrap.layout.Split.call(this, config);
35294     if(this.split){
35295         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35296         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35297         this.split.el.addClass("roo-layout-split-v");
35298     }
35299     var size = config.initialSize || config.height;
35300     if(typeof size != "undefined"){
35301         this.el.setHeight(size);
35302     }
35303 };
35304
35305 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35306     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35307     getBox : function(){
35308         if(this.collapsed){
35309             return this.collapsedEl.getBox();
35310         }
35311         var box = this.el.getBox();
35312         if(this.split){
35313             var sh = this.split.el.getHeight();
35314             box.height += sh;
35315             box.y -= sh;
35316         }
35317         return box;
35318     },
35319     
35320     updateBox : function(box){
35321         if(this.split && !this.collapsed){
35322             var sh = this.split.el.getHeight();
35323             box.height -= sh;
35324             box.y += sh;
35325             this.split.el.setLeft(box.x);
35326             this.split.el.setTop(box.y-sh);
35327             this.split.el.setWidth(box.width);
35328         }
35329         if(this.collapsed){
35330             this.updateBody(box.width, null);
35331         }
35332         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35333     }
35334 });
35335
35336 Roo.bootstrap.layout.East = function(config){
35337     config.region = "east";
35338     config.cursor = "e-resize";
35339     Roo.bootstrap.layout.Split.call(this, config);
35340     if(this.split){
35341         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35342         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35343         this.split.el.addClass("roo-layout-split-h");
35344     }
35345     var size = config.initialSize || config.width;
35346     if(typeof size != "undefined"){
35347         this.el.setWidth(size);
35348     }
35349 };
35350 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35351     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35352     getBox : function(){
35353         if(this.collapsed){
35354             return this.collapsedEl.getBox();
35355         }
35356         var box = this.el.getBox();
35357         if(this.split){
35358             var sw = this.split.el.getWidth();
35359             box.width += sw;
35360             box.x -= sw;
35361         }
35362         return box;
35363     },
35364
35365     updateBox : function(box){
35366         if(this.split && !this.collapsed){
35367             var sw = this.split.el.getWidth();
35368             box.width -= sw;
35369             this.split.el.setLeft(box.x);
35370             this.split.el.setTop(box.y);
35371             this.split.el.setHeight(box.height);
35372             box.x += sw;
35373         }
35374         if(this.collapsed){
35375             this.updateBody(null, box.height);
35376         }
35377         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35378     }
35379 });
35380
35381 Roo.bootstrap.layout.West = function(config){
35382     config.region = "west";
35383     config.cursor = "w-resize";
35384     
35385     Roo.bootstrap.layout.Split.call(this, config);
35386     if(this.split){
35387         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35388         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35389         this.split.el.addClass("roo-layout-split-h");
35390     }
35391     
35392 };
35393 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35394     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35395     
35396     onRender: function(ctr, pos)
35397     {
35398         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35399         var size = this.config.initialSize || this.config.width;
35400         if(typeof size != "undefined"){
35401             this.el.setWidth(size);
35402         }
35403     },
35404     
35405     getBox : function(){
35406         if(this.collapsed){
35407             return this.collapsedEl.getBox();
35408         }
35409         var box = this.el.getBox();
35410         if(this.split){
35411             box.width += this.split.el.getWidth();
35412         }
35413         return box;
35414     },
35415     
35416     updateBox : function(box){
35417         if(this.split && !this.collapsed){
35418             var sw = this.split.el.getWidth();
35419             box.width -= sw;
35420             this.split.el.setLeft(box.x+box.width);
35421             this.split.el.setTop(box.y);
35422             this.split.el.setHeight(box.height);
35423         }
35424         if(this.collapsed){
35425             this.updateBody(null, box.height);
35426         }
35427         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35428     }
35429 });
35430 Roo.namespace("Roo.bootstrap.panel");/*
35431  * Based on:
35432  * Ext JS Library 1.1.1
35433  * Copyright(c) 2006-2007, Ext JS, LLC.
35434  *
35435  * Originally Released Under LGPL - original licence link has changed is not relivant.
35436  *
35437  * Fork - LGPL
35438  * <script type="text/javascript">
35439  */
35440 /**
35441  * @class Roo.ContentPanel
35442  * @extends Roo.util.Observable
35443  * A basic ContentPanel element.
35444  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35445  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35446  * @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
35447  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35448  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35449  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35450  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35451  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35452  * @cfg {String} title          The title for this panel
35453  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35454  * @cfg {String} url            Calls {@link #setUrl} with this value
35455  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35456  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35457  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35458  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35459  * @cfg {Boolean} badges render the badges
35460
35461  * @constructor
35462  * Create a new ContentPanel.
35463  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35464  * @param {String/Object} config A string to set only the title or a config object
35465  * @param {String} content (optional) Set the HTML content for this panel
35466  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35467  */
35468 Roo.bootstrap.panel.Content = function( config){
35469     
35470     this.tpl = config.tpl || false;
35471     
35472     var el = config.el;
35473     var content = config.content;
35474
35475     if(config.autoCreate){ // xtype is available if this is called from factory
35476         el = Roo.id();
35477     }
35478     this.el = Roo.get(el);
35479     if(!this.el && config && config.autoCreate){
35480         if(typeof config.autoCreate == "object"){
35481             if(!config.autoCreate.id){
35482                 config.autoCreate.id = config.id||el;
35483             }
35484             this.el = Roo.DomHelper.append(document.body,
35485                         config.autoCreate, true);
35486         }else{
35487             var elcfg =  {   tag: "div",
35488                             cls: "roo-layout-inactive-content",
35489                             id: config.id||el
35490                             };
35491             if (config.html) {
35492                 elcfg.html = config.html;
35493                 
35494             }
35495                         
35496             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35497         }
35498     } 
35499     this.closable = false;
35500     this.loaded = false;
35501     this.active = false;
35502    
35503       
35504     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35505         
35506         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35507         
35508         this.wrapEl = this.el; //this.el.wrap();
35509         var ti = [];
35510         if (config.toolbar.items) {
35511             ti = config.toolbar.items ;
35512             delete config.toolbar.items ;
35513         }
35514         
35515         var nitems = [];
35516         this.toolbar.render(this.wrapEl, 'before');
35517         for(var i =0;i < ti.length;i++) {
35518           //  Roo.log(['add child', items[i]]);
35519             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35520         }
35521         this.toolbar.items = nitems;
35522         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35523         delete config.toolbar;
35524         
35525     }
35526     /*
35527     // xtype created footer. - not sure if will work as we normally have to render first..
35528     if (this.footer && !this.footer.el && this.footer.xtype) {
35529         if (!this.wrapEl) {
35530             this.wrapEl = this.el.wrap();
35531         }
35532     
35533         this.footer.container = this.wrapEl.createChild();
35534          
35535         this.footer = Roo.factory(this.footer, Roo);
35536         
35537     }
35538     */
35539     
35540      if(typeof config == "string"){
35541         this.title = config;
35542     }else{
35543         Roo.apply(this, config);
35544     }
35545     
35546     if(this.resizeEl){
35547         this.resizeEl = Roo.get(this.resizeEl, true);
35548     }else{
35549         this.resizeEl = this.el;
35550     }
35551     // handle view.xtype
35552     
35553  
35554     
35555     
35556     this.addEvents({
35557         /**
35558          * @event activate
35559          * Fires when this panel is activated. 
35560          * @param {Roo.ContentPanel} this
35561          */
35562         "activate" : true,
35563         /**
35564          * @event deactivate
35565          * Fires when this panel is activated. 
35566          * @param {Roo.ContentPanel} this
35567          */
35568         "deactivate" : true,
35569
35570         /**
35571          * @event resize
35572          * Fires when this panel is resized if fitToFrame is true.
35573          * @param {Roo.ContentPanel} this
35574          * @param {Number} width The width after any component adjustments
35575          * @param {Number} height The height after any component adjustments
35576          */
35577         "resize" : true,
35578         
35579          /**
35580          * @event render
35581          * Fires when this tab is created
35582          * @param {Roo.ContentPanel} this
35583          */
35584         "render" : true
35585         
35586         
35587         
35588     });
35589     
35590
35591     
35592     
35593     if(this.autoScroll){
35594         this.resizeEl.setStyle("overflow", "auto");
35595     } else {
35596         // fix randome scrolling
35597         //this.el.on('scroll', function() {
35598         //    Roo.log('fix random scolling');
35599         //    this.scrollTo('top',0); 
35600         //});
35601     }
35602     content = content || this.content;
35603     if(content){
35604         this.setContent(content);
35605     }
35606     if(config && config.url){
35607         this.setUrl(this.url, this.params, this.loadOnce);
35608     }
35609     
35610     
35611     
35612     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35613     
35614     if (this.view && typeof(this.view.xtype) != 'undefined') {
35615         this.view.el = this.el.appendChild(document.createElement("div"));
35616         this.view = Roo.factory(this.view); 
35617         this.view.render  &&  this.view.render(false, '');  
35618     }
35619     
35620     
35621     this.fireEvent('render', this);
35622 };
35623
35624 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35625     
35626     tabTip : '',
35627     
35628     setRegion : function(region){
35629         this.region = region;
35630         this.setActiveClass(region && !this.background);
35631     },
35632     
35633     
35634     setActiveClass: function(state)
35635     {
35636         if(state){
35637            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35638            this.el.setStyle('position','relative');
35639         }else{
35640            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35641            this.el.setStyle('position', 'absolute');
35642         } 
35643     },
35644     
35645     /**
35646      * Returns the toolbar for this Panel if one was configured. 
35647      * @return {Roo.Toolbar} 
35648      */
35649     getToolbar : function(){
35650         return this.toolbar;
35651     },
35652     
35653     setActiveState : function(active)
35654     {
35655         this.active = active;
35656         this.setActiveClass(active);
35657         if(!active){
35658             this.fireEvent("deactivate", this);
35659         }else{
35660             this.fireEvent("activate", this);
35661         }
35662     },
35663     /**
35664      * Updates this panel's element
35665      * @param {String} content The new content
35666      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35667     */
35668     setContent : function(content, loadScripts){
35669         this.el.update(content, loadScripts);
35670     },
35671
35672     ignoreResize : function(w, h){
35673         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35674             return true;
35675         }else{
35676             this.lastSize = {width: w, height: h};
35677             return false;
35678         }
35679     },
35680     /**
35681      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35682      * @return {Roo.UpdateManager} The UpdateManager
35683      */
35684     getUpdateManager : function(){
35685         return this.el.getUpdateManager();
35686     },
35687      /**
35688      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35689      * @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:
35690 <pre><code>
35691 panel.load({
35692     url: "your-url.php",
35693     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35694     callback: yourFunction,
35695     scope: yourObject, //(optional scope)
35696     discardUrl: false,
35697     nocache: false,
35698     text: "Loading...",
35699     timeout: 30,
35700     scripts: false
35701 });
35702 </code></pre>
35703      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35704      * 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.
35705      * @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}
35706      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35707      * @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.
35708      * @return {Roo.ContentPanel} this
35709      */
35710     load : function(){
35711         var um = this.el.getUpdateManager();
35712         um.update.apply(um, arguments);
35713         return this;
35714     },
35715
35716
35717     /**
35718      * 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.
35719      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35720      * @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)
35721      * @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)
35722      * @return {Roo.UpdateManager} The UpdateManager
35723      */
35724     setUrl : function(url, params, loadOnce){
35725         if(this.refreshDelegate){
35726             this.removeListener("activate", this.refreshDelegate);
35727         }
35728         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35729         this.on("activate", this.refreshDelegate);
35730         return this.el.getUpdateManager();
35731     },
35732     
35733     _handleRefresh : function(url, params, loadOnce){
35734         if(!loadOnce || !this.loaded){
35735             var updater = this.el.getUpdateManager();
35736             updater.update(url, params, this._setLoaded.createDelegate(this));
35737         }
35738     },
35739     
35740     _setLoaded : function(){
35741         this.loaded = true;
35742     }, 
35743     
35744     /**
35745      * Returns this panel's id
35746      * @return {String} 
35747      */
35748     getId : function(){
35749         return this.el.id;
35750     },
35751     
35752     /** 
35753      * Returns this panel's element - used by regiosn to add.
35754      * @return {Roo.Element} 
35755      */
35756     getEl : function(){
35757         return this.wrapEl || this.el;
35758     },
35759     
35760    
35761     
35762     adjustForComponents : function(width, height)
35763     {
35764         //Roo.log('adjustForComponents ');
35765         if(this.resizeEl != this.el){
35766             width -= this.el.getFrameWidth('lr');
35767             height -= this.el.getFrameWidth('tb');
35768         }
35769         if(this.toolbar){
35770             var te = this.toolbar.getEl();
35771             height -= te.getHeight();
35772             te.setWidth(width);
35773         }
35774         if(this.footer){
35775             var te = this.footer.getEl();
35776             Roo.log("footer:" + te.getHeight());
35777             
35778             height -= te.getHeight();
35779             te.setWidth(width);
35780         }
35781         
35782         
35783         if(this.adjustments){
35784             width += this.adjustments[0];
35785             height += this.adjustments[1];
35786         }
35787         return {"width": width, "height": height};
35788     },
35789     
35790     setSize : function(width, height){
35791         if(this.fitToFrame && !this.ignoreResize(width, height)){
35792             if(this.fitContainer && this.resizeEl != this.el){
35793                 this.el.setSize(width, height);
35794             }
35795             var size = this.adjustForComponents(width, height);
35796             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35797             this.fireEvent('resize', this, size.width, size.height);
35798         }
35799     },
35800     
35801     /**
35802      * Returns this panel's title
35803      * @return {String} 
35804      */
35805     getTitle : function(){
35806         return this.title;
35807     },
35808     
35809     /**
35810      * Set this panel's title
35811      * @param {String} title
35812      */
35813     setTitle : function(title){
35814         this.title = title;
35815         if(this.region){
35816             this.region.updatePanelTitle(this, title);
35817         }
35818     },
35819     
35820     /**
35821      * Returns true is this panel was configured to be closable
35822      * @return {Boolean} 
35823      */
35824     isClosable : function(){
35825         return this.closable;
35826     },
35827     
35828     beforeSlide : function(){
35829         this.el.clip();
35830         this.resizeEl.clip();
35831     },
35832     
35833     afterSlide : function(){
35834         this.el.unclip();
35835         this.resizeEl.unclip();
35836     },
35837     
35838     /**
35839      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35840      *   Will fail silently if the {@link #setUrl} method has not been called.
35841      *   This does not activate the panel, just updates its content.
35842      */
35843     refresh : function(){
35844         if(this.refreshDelegate){
35845            this.loaded = false;
35846            this.refreshDelegate();
35847         }
35848     },
35849     
35850     /**
35851      * Destroys this panel
35852      */
35853     destroy : function(){
35854         this.el.removeAllListeners();
35855         var tempEl = document.createElement("span");
35856         tempEl.appendChild(this.el.dom);
35857         tempEl.innerHTML = "";
35858         this.el.remove();
35859         this.el = null;
35860     },
35861     
35862     /**
35863      * form - if the content panel contains a form - this is a reference to it.
35864      * @type {Roo.form.Form}
35865      */
35866     form : false,
35867     /**
35868      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35869      *    This contains a reference to it.
35870      * @type {Roo.View}
35871      */
35872     view : false,
35873     
35874       /**
35875      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35876      * <pre><code>
35877
35878 layout.addxtype({
35879        xtype : 'Form',
35880        items: [ .... ]
35881    }
35882 );
35883
35884 </code></pre>
35885      * @param {Object} cfg Xtype definition of item to add.
35886      */
35887     
35888     
35889     getChildContainer: function () {
35890         return this.getEl();
35891     }
35892     
35893     
35894     /*
35895         var  ret = new Roo.factory(cfg);
35896         return ret;
35897         
35898         
35899         // add form..
35900         if (cfg.xtype.match(/^Form$/)) {
35901             
35902             var el;
35903             //if (this.footer) {
35904             //    el = this.footer.container.insertSibling(false, 'before');
35905             //} else {
35906                 el = this.el.createChild();
35907             //}
35908
35909             this.form = new  Roo.form.Form(cfg);
35910             
35911             
35912             if ( this.form.allItems.length) {
35913                 this.form.render(el.dom);
35914             }
35915             return this.form;
35916         }
35917         // should only have one of theses..
35918         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35919             // views.. should not be just added - used named prop 'view''
35920             
35921             cfg.el = this.el.appendChild(document.createElement("div"));
35922             // factory?
35923             
35924             var ret = new Roo.factory(cfg);
35925              
35926              ret.render && ret.render(false, ''); // render blank..
35927             this.view = ret;
35928             return ret;
35929         }
35930         return false;
35931     }
35932     \*/
35933 });
35934  
35935 /**
35936  * @class Roo.bootstrap.panel.Grid
35937  * @extends Roo.bootstrap.panel.Content
35938  * @constructor
35939  * Create a new GridPanel.
35940  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35941  * @param {Object} config A the config object
35942   
35943  */
35944
35945
35946
35947 Roo.bootstrap.panel.Grid = function(config)
35948 {
35949     
35950       
35951     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35952         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35953
35954     config.el = this.wrapper;
35955     //this.el = this.wrapper;
35956     
35957       if (config.container) {
35958         // ctor'ed from a Border/panel.grid
35959         
35960         
35961         this.wrapper.setStyle("overflow", "hidden");
35962         this.wrapper.addClass('roo-grid-container');
35963
35964     }
35965     
35966     
35967     if(config.toolbar){
35968         var tool_el = this.wrapper.createChild();    
35969         this.toolbar = Roo.factory(config.toolbar);
35970         var ti = [];
35971         if (config.toolbar.items) {
35972             ti = config.toolbar.items ;
35973             delete config.toolbar.items ;
35974         }
35975         
35976         var nitems = [];
35977         this.toolbar.render(tool_el);
35978         for(var i =0;i < ti.length;i++) {
35979           //  Roo.log(['add child', items[i]]);
35980             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35981         }
35982         this.toolbar.items = nitems;
35983         
35984         delete config.toolbar;
35985     }
35986     
35987     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35988     config.grid.scrollBody = true;;
35989     config.grid.monitorWindowResize = false; // turn off autosizing
35990     config.grid.autoHeight = false;
35991     config.grid.autoWidth = false;
35992     
35993     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35994     
35995     if (config.background) {
35996         // render grid on panel activation (if panel background)
35997         this.on('activate', function(gp) {
35998             if (!gp.grid.rendered) {
35999                 gp.grid.render(this.wrapper);
36000                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36001             }
36002         });
36003             
36004     } else {
36005         this.grid.render(this.wrapper);
36006         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36007
36008     }
36009     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36010     // ??? needed ??? config.el = this.wrapper;
36011     
36012     
36013     
36014   
36015     // xtype created footer. - not sure if will work as we normally have to render first..
36016     if (this.footer && !this.footer.el && this.footer.xtype) {
36017         
36018         var ctr = this.grid.getView().getFooterPanel(true);
36019         this.footer.dataSource = this.grid.dataSource;
36020         this.footer = Roo.factory(this.footer, Roo);
36021         this.footer.render(ctr);
36022         
36023     }
36024     
36025     
36026     
36027     
36028      
36029 };
36030
36031 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36032     getId : function(){
36033         return this.grid.id;
36034     },
36035     
36036     /**
36037      * Returns the grid for this panel
36038      * @return {Roo.bootstrap.Table} 
36039      */
36040     getGrid : function(){
36041         return this.grid;    
36042     },
36043     
36044     setSize : function(width, height){
36045         if(!this.ignoreResize(width, height)){
36046             var grid = this.grid;
36047             var size = this.adjustForComponents(width, height);
36048             var gridel = grid.getGridEl();
36049             gridel.setSize(size.width, size.height);
36050             /*
36051             var thd = grid.getGridEl().select('thead',true).first();
36052             var tbd = grid.getGridEl().select('tbody', true).first();
36053             if (tbd) {
36054                 tbd.setSize(width, height - thd.getHeight());
36055             }
36056             */
36057             grid.autoSize();
36058         }
36059     },
36060      
36061     
36062     
36063     beforeSlide : function(){
36064         this.grid.getView().scroller.clip();
36065     },
36066     
36067     afterSlide : function(){
36068         this.grid.getView().scroller.unclip();
36069     },
36070     
36071     destroy : function(){
36072         this.grid.destroy();
36073         delete this.grid;
36074         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36075     }
36076 });
36077
36078 /**
36079  * @class Roo.bootstrap.panel.Nest
36080  * @extends Roo.bootstrap.panel.Content
36081  * @constructor
36082  * Create a new Panel, that can contain a layout.Border.
36083  * 
36084  * 
36085  * @param {Roo.BorderLayout} layout The layout for this panel
36086  * @param {String/Object} config A string to set only the title or a config object
36087  */
36088 Roo.bootstrap.panel.Nest = function(config)
36089 {
36090     // construct with only one argument..
36091     /* FIXME - implement nicer consturctors
36092     if (layout.layout) {
36093         config = layout;
36094         layout = config.layout;
36095         delete config.layout;
36096     }
36097     if (layout.xtype && !layout.getEl) {
36098         // then layout needs constructing..
36099         layout = Roo.factory(layout, Roo);
36100     }
36101     */
36102     
36103     config.el =  config.layout.getEl();
36104     
36105     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36106     
36107     config.layout.monitorWindowResize = false; // turn off autosizing
36108     this.layout = config.layout;
36109     this.layout.getEl().addClass("roo-layout-nested-layout");
36110     
36111     
36112     
36113     
36114 };
36115
36116 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36117
36118     setSize : function(width, height){
36119         if(!this.ignoreResize(width, height)){
36120             var size = this.adjustForComponents(width, height);
36121             var el = this.layout.getEl();
36122             if (size.height < 1) {
36123                 el.setWidth(size.width);   
36124             } else {
36125                 el.setSize(size.width, size.height);
36126             }
36127             var touch = el.dom.offsetWidth;
36128             this.layout.layout();
36129             // ie requires a double layout on the first pass
36130             if(Roo.isIE && !this.initialized){
36131                 this.initialized = true;
36132                 this.layout.layout();
36133             }
36134         }
36135     },
36136     
36137     // activate all subpanels if not currently active..
36138     
36139     setActiveState : function(active){
36140         this.active = active;
36141         this.setActiveClass(active);
36142         
36143         if(!active){
36144             this.fireEvent("deactivate", this);
36145             return;
36146         }
36147         
36148         this.fireEvent("activate", this);
36149         // not sure if this should happen before or after..
36150         if (!this.layout) {
36151             return; // should not happen..
36152         }
36153         var reg = false;
36154         for (var r in this.layout.regions) {
36155             reg = this.layout.getRegion(r);
36156             if (reg.getActivePanel()) {
36157                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36158                 reg.setActivePanel(reg.getActivePanel());
36159                 continue;
36160             }
36161             if (!reg.panels.length) {
36162                 continue;
36163             }
36164             reg.showPanel(reg.getPanel(0));
36165         }
36166         
36167         
36168         
36169         
36170     },
36171     
36172     /**
36173      * Returns the nested BorderLayout for this panel
36174      * @return {Roo.BorderLayout} 
36175      */
36176     getLayout : function(){
36177         return this.layout;
36178     },
36179     
36180      /**
36181      * Adds a xtype elements to the layout of the nested panel
36182      * <pre><code>
36183
36184 panel.addxtype({
36185        xtype : 'ContentPanel',
36186        region: 'west',
36187        items: [ .... ]
36188    }
36189 );
36190
36191 panel.addxtype({
36192         xtype : 'NestedLayoutPanel',
36193         region: 'west',
36194         layout: {
36195            center: { },
36196            west: { }   
36197         },
36198         items : [ ... list of content panels or nested layout panels.. ]
36199    }
36200 );
36201 </code></pre>
36202      * @param {Object} cfg Xtype definition of item to add.
36203      */
36204     addxtype : function(cfg) {
36205         return this.layout.addxtype(cfg);
36206     
36207     }
36208 });        /*
36209  * Based on:
36210  * Ext JS Library 1.1.1
36211  * Copyright(c) 2006-2007, Ext JS, LLC.
36212  *
36213  * Originally Released Under LGPL - original licence link has changed is not relivant.
36214  *
36215  * Fork - LGPL
36216  * <script type="text/javascript">
36217  */
36218 /**
36219  * @class Roo.TabPanel
36220  * @extends Roo.util.Observable
36221  * A lightweight tab container.
36222  * <br><br>
36223  * Usage:
36224  * <pre><code>
36225 // basic tabs 1, built from existing content
36226 var tabs = new Roo.TabPanel("tabs1");
36227 tabs.addTab("script", "View Script");
36228 tabs.addTab("markup", "View Markup");
36229 tabs.activate("script");
36230
36231 // more advanced tabs, built from javascript
36232 var jtabs = new Roo.TabPanel("jtabs");
36233 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36234
36235 // set up the UpdateManager
36236 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36237 var updater = tab2.getUpdateManager();
36238 updater.setDefaultUrl("ajax1.htm");
36239 tab2.on('activate', updater.refresh, updater, true);
36240
36241 // Use setUrl for Ajax loading
36242 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36243 tab3.setUrl("ajax2.htm", null, true);
36244
36245 // Disabled tab
36246 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36247 tab4.disable();
36248
36249 jtabs.activate("jtabs-1");
36250  * </code></pre>
36251  * @constructor
36252  * Create a new TabPanel.
36253  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36254  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36255  */
36256 Roo.bootstrap.panel.Tabs = function(config){
36257     /**
36258     * The container element for this TabPanel.
36259     * @type Roo.Element
36260     */
36261     this.el = Roo.get(config.el);
36262     delete config.el;
36263     if(config){
36264         if(typeof config == "boolean"){
36265             this.tabPosition = config ? "bottom" : "top";
36266         }else{
36267             Roo.apply(this, config);
36268         }
36269     }
36270     
36271     if(this.tabPosition == "bottom"){
36272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36273         this.el.addClass("roo-tabs-bottom");
36274     }
36275     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36276     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36277     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36278     if(Roo.isIE){
36279         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36280     }
36281     if(this.tabPosition != "bottom"){
36282         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36283          * @type Roo.Element
36284          */
36285         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36286         this.el.addClass("roo-tabs-top");
36287     }
36288     this.items = [];
36289
36290     this.bodyEl.setStyle("position", "relative");
36291
36292     this.active = null;
36293     this.activateDelegate = this.activate.createDelegate(this);
36294
36295     this.addEvents({
36296         /**
36297          * @event tabchange
36298          * Fires when the active tab changes
36299          * @param {Roo.TabPanel} this
36300          * @param {Roo.TabPanelItem} activePanel The new active tab
36301          */
36302         "tabchange": true,
36303         /**
36304          * @event beforetabchange
36305          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36306          * @param {Roo.TabPanel} this
36307          * @param {Object} e Set cancel to true on this object to cancel the tab change
36308          * @param {Roo.TabPanelItem} tab The tab being changed to
36309          */
36310         "beforetabchange" : true
36311     });
36312
36313     Roo.EventManager.onWindowResize(this.onResize, this);
36314     this.cpad = this.el.getPadding("lr");
36315     this.hiddenCount = 0;
36316
36317
36318     // toolbar on the tabbar support...
36319     if (this.toolbar) {
36320         alert("no toolbar support yet");
36321         this.toolbar  = false;
36322         /*
36323         var tcfg = this.toolbar;
36324         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36325         this.toolbar = new Roo.Toolbar(tcfg);
36326         if (Roo.isSafari) {
36327             var tbl = tcfg.container.child('table', true);
36328             tbl.setAttribute('width', '100%');
36329         }
36330         */
36331         
36332     }
36333    
36334
36335
36336     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36337 };
36338
36339 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36340     /*
36341      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36342      */
36343     tabPosition : "top",
36344     /*
36345      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36346      */
36347     currentTabWidth : 0,
36348     /*
36349      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36350      */
36351     minTabWidth : 40,
36352     /*
36353      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36354      */
36355     maxTabWidth : 250,
36356     /*
36357      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36358      */
36359     preferredTabWidth : 175,
36360     /*
36361      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36362      */
36363     resizeTabs : false,
36364     /*
36365      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36366      */
36367     monitorResize : true,
36368     /*
36369      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36370      */
36371     toolbar : false,
36372
36373     /**
36374      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36375      * @param {String} id The id of the div to use <b>or create</b>
36376      * @param {String} text The text for the tab
36377      * @param {String} content (optional) Content to put in the TabPanelItem body
36378      * @param {Boolean} closable (optional) True to create a close icon on the tab
36379      * @return {Roo.TabPanelItem} The created TabPanelItem
36380      */
36381     addTab : function(id, text, content, closable, tpl)
36382     {
36383         var item = new Roo.bootstrap.panel.TabItem({
36384             panel: this,
36385             id : id,
36386             text : text,
36387             closable : closable,
36388             tpl : tpl
36389         });
36390         this.addTabItem(item);
36391         if(content){
36392             item.setContent(content);
36393         }
36394         return item;
36395     },
36396
36397     /**
36398      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36399      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36400      * @return {Roo.TabPanelItem}
36401      */
36402     getTab : function(id){
36403         return this.items[id];
36404     },
36405
36406     /**
36407      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36408      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36409      */
36410     hideTab : function(id){
36411         var t = this.items[id];
36412         if(!t.isHidden()){
36413            t.setHidden(true);
36414            this.hiddenCount++;
36415            this.autoSizeTabs();
36416         }
36417     },
36418
36419     /**
36420      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36421      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36422      */
36423     unhideTab : function(id){
36424         var t = this.items[id];
36425         if(t.isHidden()){
36426            t.setHidden(false);
36427            this.hiddenCount--;
36428            this.autoSizeTabs();
36429         }
36430     },
36431
36432     /**
36433      * Adds an existing {@link Roo.TabPanelItem}.
36434      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36435      */
36436     addTabItem : function(item){
36437         this.items[item.id] = item;
36438         this.items.push(item);
36439       //  if(this.resizeTabs){
36440     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36441   //         this.autoSizeTabs();
36442 //        }else{
36443 //            item.autoSize();
36444        // }
36445     },
36446
36447     /**
36448      * Removes a {@link Roo.TabPanelItem}.
36449      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36450      */
36451     removeTab : function(id){
36452         var items = this.items;
36453         var tab = items[id];
36454         if(!tab) { return; }
36455         var index = items.indexOf(tab);
36456         if(this.active == tab && items.length > 1){
36457             var newTab = this.getNextAvailable(index);
36458             if(newTab) {
36459                 newTab.activate();
36460             }
36461         }
36462         this.stripEl.dom.removeChild(tab.pnode.dom);
36463         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36464             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36465         }
36466         items.splice(index, 1);
36467         delete this.items[tab.id];
36468         tab.fireEvent("close", tab);
36469         tab.purgeListeners();
36470         this.autoSizeTabs();
36471     },
36472
36473     getNextAvailable : function(start){
36474         var items = this.items;
36475         var index = start;
36476         // look for a next tab that will slide over to
36477         // replace the one being removed
36478         while(index < items.length){
36479             var item = items[++index];
36480             if(item && !item.isHidden()){
36481                 return item;
36482             }
36483         }
36484         // if one isn't found select the previous tab (on the left)
36485         index = start;
36486         while(index >= 0){
36487             var item = items[--index];
36488             if(item && !item.isHidden()){
36489                 return item;
36490             }
36491         }
36492         return null;
36493     },
36494
36495     /**
36496      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36497      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36498      */
36499     disableTab : function(id){
36500         var tab = this.items[id];
36501         if(tab && this.active != tab){
36502             tab.disable();
36503         }
36504     },
36505
36506     /**
36507      * Enables a {@link Roo.TabPanelItem} that is disabled.
36508      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36509      */
36510     enableTab : function(id){
36511         var tab = this.items[id];
36512         tab.enable();
36513     },
36514
36515     /**
36516      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36517      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36518      * @return {Roo.TabPanelItem} The TabPanelItem.
36519      */
36520     activate : function(id){
36521         var tab = this.items[id];
36522         if(!tab){
36523             return null;
36524         }
36525         if(tab == this.active || tab.disabled){
36526             return tab;
36527         }
36528         var e = {};
36529         this.fireEvent("beforetabchange", this, e, tab);
36530         if(e.cancel !== true && !tab.disabled){
36531             if(this.active){
36532                 this.active.hide();
36533             }
36534             this.active = this.items[id];
36535             this.active.show();
36536             this.fireEvent("tabchange", this, this.active);
36537         }
36538         return tab;
36539     },
36540
36541     /**
36542      * Gets the active {@link Roo.TabPanelItem}.
36543      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36544      */
36545     getActiveTab : function(){
36546         return this.active;
36547     },
36548
36549     /**
36550      * Updates the tab body element to fit the height of the container element
36551      * for overflow scrolling
36552      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36553      */
36554     syncHeight : function(targetHeight){
36555         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36556         var bm = this.bodyEl.getMargins();
36557         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36558         this.bodyEl.setHeight(newHeight);
36559         return newHeight;
36560     },
36561
36562     onResize : function(){
36563         if(this.monitorResize){
36564             this.autoSizeTabs();
36565         }
36566     },
36567
36568     /**
36569      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36570      */
36571     beginUpdate : function(){
36572         this.updating = true;
36573     },
36574
36575     /**
36576      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36577      */
36578     endUpdate : function(){
36579         this.updating = false;
36580         this.autoSizeTabs();
36581     },
36582
36583     /**
36584      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36585      */
36586     autoSizeTabs : function(){
36587         var count = this.items.length;
36588         var vcount = count - this.hiddenCount;
36589         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36590             return;
36591         }
36592         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36593         var availWidth = Math.floor(w / vcount);
36594         var b = this.stripBody;
36595         if(b.getWidth() > w){
36596             var tabs = this.items;
36597             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36598             if(availWidth < this.minTabWidth){
36599                 /*if(!this.sleft){    // incomplete scrolling code
36600                     this.createScrollButtons();
36601                 }
36602                 this.showScroll();
36603                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36604             }
36605         }else{
36606             if(this.currentTabWidth < this.preferredTabWidth){
36607                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36608             }
36609         }
36610     },
36611
36612     /**
36613      * Returns the number of tabs in this TabPanel.
36614      * @return {Number}
36615      */
36616      getCount : function(){
36617          return this.items.length;
36618      },
36619
36620     /**
36621      * Resizes all the tabs to the passed width
36622      * @param {Number} The new width
36623      */
36624     setTabWidth : function(width){
36625         this.currentTabWidth = width;
36626         for(var i = 0, len = this.items.length; i < len; i++) {
36627                 if(!this.items[i].isHidden()) {
36628                 this.items[i].setWidth(width);
36629             }
36630         }
36631     },
36632
36633     /**
36634      * Destroys this TabPanel
36635      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36636      */
36637     destroy : function(removeEl){
36638         Roo.EventManager.removeResizeListener(this.onResize, this);
36639         for(var i = 0, len = this.items.length; i < len; i++){
36640             this.items[i].purgeListeners();
36641         }
36642         if(removeEl === true){
36643             this.el.update("");
36644             this.el.remove();
36645         }
36646     },
36647     
36648     createStrip : function(container)
36649     {
36650         var strip = document.createElement("nav");
36651         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36652         container.appendChild(strip);
36653         return strip;
36654     },
36655     
36656     createStripList : function(strip)
36657     {
36658         // div wrapper for retard IE
36659         // returns the "tr" element.
36660         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36661         //'<div class="x-tabs-strip-wrap">'+
36662           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36663           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36664         return strip.firstChild; //.firstChild.firstChild.firstChild;
36665     },
36666     createBody : function(container)
36667     {
36668         var body = document.createElement("div");
36669         Roo.id(body, "tab-body");
36670         //Roo.fly(body).addClass("x-tabs-body");
36671         Roo.fly(body).addClass("tab-content");
36672         container.appendChild(body);
36673         return body;
36674     },
36675     createItemBody :function(bodyEl, id){
36676         var body = Roo.getDom(id);
36677         if(!body){
36678             body = document.createElement("div");
36679             body.id = id;
36680         }
36681         //Roo.fly(body).addClass("x-tabs-item-body");
36682         Roo.fly(body).addClass("tab-pane");
36683          bodyEl.insertBefore(body, bodyEl.firstChild);
36684         return body;
36685     },
36686     /** @private */
36687     createStripElements :  function(stripEl, text, closable, tpl)
36688     {
36689         var td = document.createElement("li"); // was td..
36690         
36691         
36692         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36693         
36694         
36695         stripEl.appendChild(td);
36696         /*if(closable){
36697             td.className = "x-tabs-closable";
36698             if(!this.closeTpl){
36699                 this.closeTpl = new Roo.Template(
36700                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36701                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36702                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36703                 );
36704             }
36705             var el = this.closeTpl.overwrite(td, {"text": text});
36706             var close = el.getElementsByTagName("div")[0];
36707             var inner = el.getElementsByTagName("em")[0];
36708             return {"el": el, "close": close, "inner": inner};
36709         } else {
36710         */
36711         // not sure what this is..
36712 //            if(!this.tabTpl){
36713                 //this.tabTpl = new Roo.Template(
36714                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36715                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36716                 //);
36717 //                this.tabTpl = new Roo.Template(
36718 //                   '<a href="#">' +
36719 //                   '<span unselectable="on"' +
36720 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36721 //                            ' >{text}</span></a>'
36722 //                );
36723 //                
36724 //            }
36725
36726
36727             var template = tpl || this.tabTpl || false;
36728             
36729             if(!template){
36730                 
36731                 template = new Roo.Template(
36732                    '<a href="#">' +
36733                    '<span unselectable="on"' +
36734                             (this.disableTooltips ? '' : ' title="{text}"') +
36735                             ' >{text}</span></a>'
36736                 );
36737             }
36738             
36739             switch (typeof(template)) {
36740                 case 'object' :
36741                     break;
36742                 case 'string' :
36743                     template = new Roo.Template(template);
36744                     break;
36745                 default :
36746                     break;
36747             }
36748             
36749             var el = template.overwrite(td, {"text": text});
36750             
36751             var inner = el.getElementsByTagName("span")[0];
36752             
36753             return {"el": el, "inner": inner};
36754             
36755     }
36756         
36757     
36758 });
36759
36760 /**
36761  * @class Roo.TabPanelItem
36762  * @extends Roo.util.Observable
36763  * Represents an individual item (tab plus body) in a TabPanel.
36764  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36765  * @param {String} id The id of this TabPanelItem
36766  * @param {String} text The text for the tab of this TabPanelItem
36767  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36768  */
36769 Roo.bootstrap.panel.TabItem = function(config){
36770     /**
36771      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36772      * @type Roo.TabPanel
36773      */
36774     this.tabPanel = config.panel;
36775     /**
36776      * The id for this TabPanelItem
36777      * @type String
36778      */
36779     this.id = config.id;
36780     /** @private */
36781     this.disabled = false;
36782     /** @private */
36783     this.text = config.text;
36784     /** @private */
36785     this.loaded = false;
36786     this.closable = config.closable;
36787
36788     /**
36789      * The body element for this TabPanelItem.
36790      * @type Roo.Element
36791      */
36792     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36793     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36794     this.bodyEl.setStyle("display", "block");
36795     this.bodyEl.setStyle("zoom", "1");
36796     //this.hideAction();
36797
36798     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36799     /** @private */
36800     this.el = Roo.get(els.el);
36801     this.inner = Roo.get(els.inner, true);
36802     this.textEl = Roo.get(this.el.dom.firstChild, true);
36803     this.pnode = Roo.get(els.el.parentNode, true);
36804     this.el.on("mousedown", this.onTabMouseDown, this);
36805     this.el.on("click", this.onTabClick, this);
36806     /** @private */
36807     if(config.closable){
36808         var c = Roo.get(els.close, true);
36809         c.dom.title = this.closeText;
36810         c.addClassOnOver("close-over");
36811         c.on("click", this.closeClick, this);
36812      }
36813
36814     this.addEvents({
36815          /**
36816          * @event activate
36817          * Fires when this tab becomes the active tab.
36818          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36819          * @param {Roo.TabPanelItem} this
36820          */
36821         "activate": true,
36822         /**
36823          * @event beforeclose
36824          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36825          * @param {Roo.TabPanelItem} this
36826          * @param {Object} e Set cancel to true on this object to cancel the close.
36827          */
36828         "beforeclose": true,
36829         /**
36830          * @event close
36831          * Fires when this tab is closed.
36832          * @param {Roo.TabPanelItem} this
36833          */
36834          "close": true,
36835         /**
36836          * @event deactivate
36837          * Fires when this tab is no longer the active tab.
36838          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36839          * @param {Roo.TabPanelItem} this
36840          */
36841          "deactivate" : true
36842     });
36843     this.hidden = false;
36844
36845     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36846 };
36847
36848 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36849            {
36850     purgeListeners : function(){
36851        Roo.util.Observable.prototype.purgeListeners.call(this);
36852        this.el.removeAllListeners();
36853     },
36854     /**
36855      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36856      */
36857     show : function(){
36858         this.pnode.addClass("active");
36859         this.showAction();
36860         if(Roo.isOpera){
36861             this.tabPanel.stripWrap.repaint();
36862         }
36863         this.fireEvent("activate", this.tabPanel, this);
36864     },
36865
36866     /**
36867      * Returns true if this tab is the active tab.
36868      * @return {Boolean}
36869      */
36870     isActive : function(){
36871         return this.tabPanel.getActiveTab() == this;
36872     },
36873
36874     /**
36875      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36876      */
36877     hide : function(){
36878         this.pnode.removeClass("active");
36879         this.hideAction();
36880         this.fireEvent("deactivate", this.tabPanel, this);
36881     },
36882
36883     hideAction : function(){
36884         this.bodyEl.hide();
36885         this.bodyEl.setStyle("position", "absolute");
36886         this.bodyEl.setLeft("-20000px");
36887         this.bodyEl.setTop("-20000px");
36888     },
36889
36890     showAction : function(){
36891         this.bodyEl.setStyle("position", "relative");
36892         this.bodyEl.setTop("");
36893         this.bodyEl.setLeft("");
36894         this.bodyEl.show();
36895     },
36896
36897     /**
36898      * Set the tooltip for the tab.
36899      * @param {String} tooltip The tab's tooltip
36900      */
36901     setTooltip : function(text){
36902         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36903             this.textEl.dom.qtip = text;
36904             this.textEl.dom.removeAttribute('title');
36905         }else{
36906             this.textEl.dom.title = text;
36907         }
36908     },
36909
36910     onTabClick : function(e){
36911         e.preventDefault();
36912         this.tabPanel.activate(this.id);
36913     },
36914
36915     onTabMouseDown : function(e){
36916         e.preventDefault();
36917         this.tabPanel.activate(this.id);
36918     },
36919 /*
36920     getWidth : function(){
36921         return this.inner.getWidth();
36922     },
36923
36924     setWidth : function(width){
36925         var iwidth = width - this.pnode.getPadding("lr");
36926         this.inner.setWidth(iwidth);
36927         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36928         this.pnode.setWidth(width);
36929     },
36930 */
36931     /**
36932      * Show or hide the tab
36933      * @param {Boolean} hidden True to hide or false to show.
36934      */
36935     setHidden : function(hidden){
36936         this.hidden = hidden;
36937         this.pnode.setStyle("display", hidden ? "none" : "");
36938     },
36939
36940     /**
36941      * Returns true if this tab is "hidden"
36942      * @return {Boolean}
36943      */
36944     isHidden : function(){
36945         return this.hidden;
36946     },
36947
36948     /**
36949      * Returns the text for this tab
36950      * @return {String}
36951      */
36952     getText : function(){
36953         return this.text;
36954     },
36955     /*
36956     autoSize : function(){
36957         //this.el.beginMeasure();
36958         this.textEl.setWidth(1);
36959         /*
36960          *  #2804 [new] Tabs in Roojs
36961          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36962          */
36963         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36964         //this.el.endMeasure();
36965     //},
36966
36967     /**
36968      * Sets the text for the tab (Note: this also sets the tooltip text)
36969      * @param {String} text The tab's text and tooltip
36970      */
36971     setText : function(text){
36972         this.text = text;
36973         this.textEl.update(text);
36974         this.setTooltip(text);
36975         //if(!this.tabPanel.resizeTabs){
36976         //    this.autoSize();
36977         //}
36978     },
36979     /**
36980      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36981      */
36982     activate : function(){
36983         this.tabPanel.activate(this.id);
36984     },
36985
36986     /**
36987      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36988      */
36989     disable : function(){
36990         if(this.tabPanel.active != this){
36991             this.disabled = true;
36992             this.pnode.addClass("disabled");
36993         }
36994     },
36995
36996     /**
36997      * Enables this TabPanelItem if it was previously disabled.
36998      */
36999     enable : function(){
37000         this.disabled = false;
37001         this.pnode.removeClass("disabled");
37002     },
37003
37004     /**
37005      * Sets the content for this TabPanelItem.
37006      * @param {String} content The content
37007      * @param {Boolean} loadScripts true to look for and load scripts
37008      */
37009     setContent : function(content, loadScripts){
37010         this.bodyEl.update(content, loadScripts);
37011     },
37012
37013     /**
37014      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37015      * @return {Roo.UpdateManager} The UpdateManager
37016      */
37017     getUpdateManager : function(){
37018         return this.bodyEl.getUpdateManager();
37019     },
37020
37021     /**
37022      * Set a URL to be used to load the content for this TabPanelItem.
37023      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37024      * @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)
37025      * @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)
37026      * @return {Roo.UpdateManager} The UpdateManager
37027      */
37028     setUrl : function(url, params, loadOnce){
37029         if(this.refreshDelegate){
37030             this.un('activate', this.refreshDelegate);
37031         }
37032         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37033         this.on("activate", this.refreshDelegate);
37034         return this.bodyEl.getUpdateManager();
37035     },
37036
37037     /** @private */
37038     _handleRefresh : function(url, params, loadOnce){
37039         if(!loadOnce || !this.loaded){
37040             var updater = this.bodyEl.getUpdateManager();
37041             updater.update(url, params, this._setLoaded.createDelegate(this));
37042         }
37043     },
37044
37045     /**
37046      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37047      *   Will fail silently if the setUrl method has not been called.
37048      *   This does not activate the panel, just updates its content.
37049      */
37050     refresh : function(){
37051         if(this.refreshDelegate){
37052            this.loaded = false;
37053            this.refreshDelegate();
37054         }
37055     },
37056
37057     /** @private */
37058     _setLoaded : function(){
37059         this.loaded = true;
37060     },
37061
37062     /** @private */
37063     closeClick : function(e){
37064         var o = {};
37065         e.stopEvent();
37066         this.fireEvent("beforeclose", this, o);
37067         if(o.cancel !== true){
37068             this.tabPanel.removeTab(this.id);
37069         }
37070     },
37071     /**
37072      * The text displayed in the tooltip for the close icon.
37073      * @type String
37074      */
37075     closeText : "Close this tab"
37076 });